From: Álvaro Fernández Rojas Date: Thu, 7 Apr 2016 19:25:10 +0000 (+0200) Subject: brcm2708: update to latest version X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=a8d4d71c41ff0158c2026cac5981e39702167da9;p=librecmc%2Flibrecmc.git brcm2708: update to latest version As usual these patches were extracted from the raspberry pi repo: https://github.com/raspberrypi/linux/commits/rpi-4.4.y Signed-off-by: Álvaro Fernández Rojas --- diff --git a/target/linux/brcm2708/bcm2708/config-4.4 b/target/linux/brcm2708/bcm2708/config-4.4 index 1209f5e48a..2b66d0ff00 100644 --- a/target/linux/brcm2708/bcm2708/config-4.4 +++ b/target/linux/brcm2708/bcm2708/config-4.4 @@ -266,6 +266,7 @@ CONFIG_NO_HZ_IDLE=y CONFIG_OABI_COMPAT=y CONFIG_OF=y CONFIG_OF_ADDRESS=y +CONFIG_OF_CONFIGFS=y CONFIG_OF_EARLY_FLATTREE=y CONFIG_OF_FLATTREE=y CONFIG_OF_GPIO=y diff --git a/target/linux/brcm2708/bcm2709/config-4.4 b/target/linux/brcm2708/bcm2709/config-4.4 index 87ca365f6a..3127775c3a 100644 --- a/target/linux/brcm2708/bcm2709/config-4.4 +++ b/target/linux/brcm2708/bcm2709/config-4.4 @@ -283,6 +283,7 @@ CONFIG_NR_CPUS=4 CONFIG_OABI_COMPAT=y CONFIG_OF=y CONFIG_OF_ADDRESS=y +CONFIG_OF_CONFIGFS=y CONFIG_OF_EARLY_FLATTREE=y CONFIG_OF_FLATTREE=y CONFIG_OF_GPIO=y diff --git a/target/linux/brcm2708/bcm2710/config-4.4 b/target/linux/brcm2708/bcm2710/config-4.4 index 8614c8f828..8ff865685d 100644 --- a/target/linux/brcm2708/bcm2710/config-4.4 +++ b/target/linux/brcm2708/bcm2710/config-4.4 @@ -283,6 +283,7 @@ CONFIG_NR_CPUS=4 CONFIG_OABI_COMPAT=y CONFIG_OF=y CONFIG_OF_ADDRESS=y +CONFIG_OF_CONFIGFS=y CONFIG_OF_EARLY_FLATTREE=y CONFIG_OF_FLATTREE=y CONFIG_OF_GPIO=y diff --git a/target/linux/brcm2708/image/Makefile b/target/linux/brcm2708/image/Makefile index ad71c5679f..04c27ccf83 100644 --- a/target/linux/brcm2708/image/Makefile +++ b/target/linux/brcm2708/image/Makefile @@ -33,7 +33,7 @@ define Build/boot-img mcopy -i $@.boot $(word 1,$^) ::kernel.img $(foreach dts,$(shell echo $(DEVICE_DTS)),mcopy -i $@.boot $(DTS_DIR)/$(dts).dtb ::;) mmd -i $@.boot ::/overlays - mcopy -i $@.boot $(DTS_DIR)/overlays/*.dtb ::/overlays/ + mcopy -i $@.boot $(DTS_DIR)/overlays/*.dtbo ::/overlays/ mcopy -i $@.boot $(DTS_DIR)/overlays/README ::/overlays/ endef diff --git a/target/linux/brcm2708/image/config.txt b/target/linux/brcm2708/image/config.txt index 75b1cab7af..8c96f1fe97 100644 --- a/target/linux/brcm2708/image/config.txt +++ b/target/linux/brcm2708/image/config.txt @@ -476,7 +476,14 @@ ## Stop start.elf from filling in ATAGS (memory from 0x100) before ## launching kernel ## -disable_commandline_tags=2 +#disable_commandline_tags=2 + +## enable_uart +## Enables or disables uart pin multiplexing +## Raspberry Pi 1/2: if unset, uart is enabled by default +## Raspberry Pi 3: if unset uart is disabled by default +## +enable_uart=1 ## cmdline (string) ## Command line parameters. Can be used instead of cmdline.txt file @@ -491,7 +498,7 @@ disable_commandline_tags=2 ## kernel_address ## Address to load kernel.img file at ## -kernel_address=0x8000 +#kernel_address=0x8000 ## ramfsfile (string) ## ramfs file to load @@ -515,21 +522,21 @@ kernel_address=0x8000 ## device_tree_address ## Address to load device_tree at ## -device_tree_address=0x100 +#device_tree_address=0x100 ## init_uart_baud ## Initial uart baud rate. ## ## Default 115200 ## -init_uart_baud=115200 +#init_uart_baud=115200 ## init_uart_clock ## Initial uart clock. ## ## Default 3000000 (3MHz) ## -init_uart_clock=3000000 +#init_uart_clock=3000000 ## init_emmc_clock ## Initial emmc clock, increasing this can speedup your SD-card. diff --git a/target/linux/brcm2708/patches-4.4/0001-smsx95xx-fix-crimes-against-truesize.patch b/target/linux/brcm2708/patches-4.4/0001-smsx95xx-fix-crimes-against-truesize.patch index 960b448932..ee4ff9d2b3 100644 --- a/target/linux/brcm2708/patches-4.4/0001-smsx95xx-fix-crimes-against-truesize.patch +++ b/target/linux/brcm2708/patches-4.4/0001-smsx95xx-fix-crimes-against-truesize.patch @@ -1,7 +1,7 @@ -From b6cc28abbbf710379772d3da9227ce66926c46a3 Mon Sep 17 00:00:00 2001 +From 23b133903a357066adb48bceb76aba8bd30d8c66 Mon Sep 17 00:00:00 2001 From: Steve Glendinning Date: Thu, 19 Feb 2015 18:47:12 +0000 -Subject: [PATCH 001/170] smsx95xx: fix crimes against truesize +Subject: [PATCH 001/232] smsx95xx: fix crimes against truesize smsc95xx is adjusting truesize when it shouldn't, and following a recent patch from Eric this is now triggering warnings. diff --git a/target/linux/brcm2708/patches-4.4/0002-smsc95xx-Disable-turbo-mode-by-default.patch b/target/linux/brcm2708/patches-4.4/0002-smsc95xx-Disable-turbo-mode-by-default.patch index 8ed786b538..f3f7a4bf2d 100644 --- a/target/linux/brcm2708/patches-4.4/0002-smsc95xx-Disable-turbo-mode-by-default.patch +++ b/target/linux/brcm2708/patches-4.4/0002-smsc95xx-Disable-turbo-mode-by-default.patch @@ -1,7 +1,7 @@ -From 37e24d2f0af32632a17692076dddcf176d1af1f7 Mon Sep 17 00:00:00 2001 +From e7515ee9af1d150d50b55d8f8f53d735a23cb253 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Fri, 17 Apr 2015 16:58:45 +0100 -Subject: [PATCH 002/170] smsc95xx: Disable turbo mode by default +Subject: [PATCH 002/232] smsc95xx: Disable turbo mode by default --- drivers/net/usb/smsc95xx.c | 2 +- diff --git a/target/linux/brcm2708/patches-4.4/0003-vmstat-Workaround-for-issue-where-dirty-page-count-g.patch b/target/linux/brcm2708/patches-4.4/0003-vmstat-Workaround-for-issue-where-dirty-page-count-g.patch index 63d51ba506..9a6d94f9e7 100644 --- a/target/linux/brcm2708/patches-4.4/0003-vmstat-Workaround-for-issue-where-dirty-page-count-g.patch +++ b/target/linux/brcm2708/patches-4.4/0003-vmstat-Workaround-for-issue-where-dirty-page-count-g.patch @@ -1,7 +1,7 @@ -From d5cb475205138f9df1fdbb74d8810ab1928696ea Mon Sep 17 00:00:00 2001 +From 849ff5353023c95d40040ecfd279e216074b3d1c Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 18 Jun 2014 13:42:01 +0100 -Subject: [PATCH 003/170] vmstat: Workaround for issue where dirty page count +Subject: [PATCH 003/232] vmstat: Workaround for issue where dirty page count goes negative See: diff --git a/target/linux/brcm2708/patches-4.4/0004-BCM2835_DT-Fix-I2S-register-map.patch b/target/linux/brcm2708/patches-4.4/0004-BCM2835_DT-Fix-I2S-register-map.patch index 0d348cabcd..a171fbfa40 100644 --- a/target/linux/brcm2708/patches-4.4/0004-BCM2835_DT-Fix-I2S-register-map.patch +++ b/target/linux/brcm2708/patches-4.4/0004-BCM2835_DT-Fix-I2S-register-map.patch @@ -1,7 +1,7 @@ -From 249a499b1d4b6f8a57b3377d4f505841e99f60c8 Mon Sep 17 00:00:00 2001 +From d29365673ed37e7688bd3f35c4a5ad719f878190 Mon Sep 17 00:00:00 2001 From: Robert Tiemann Date: Mon, 20 Jul 2015 11:01:25 +0200 -Subject: [PATCH 004/170] BCM2835_DT: Fix I2S register map +Subject: [PATCH 004/232] BCM2835_DT: Fix I2S register map --- Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.txt | 4 ++-- diff --git a/target/linux/brcm2708/patches-4.4/0005-irq-bcm2836-Prevent-spurious-interrupts-and-trap-the.patch b/target/linux/brcm2708/patches-4.4/0005-irq-bcm2836-Prevent-spurious-interrupts-and-trap-the.patch index a2e71925cd..7db83aec0d 100644 --- a/target/linux/brcm2708/patches-4.4/0005-irq-bcm2836-Prevent-spurious-interrupts-and-trap-the.patch +++ b/target/linux/brcm2708/patches-4.4/0005-irq-bcm2836-Prevent-spurious-interrupts-and-trap-the.patch @@ -1,7 +1,7 @@ -From c24657505a2aac71d0409b0ef703fc21ecab4b2e Mon Sep 17 00:00:00 2001 +From c891de13b385afb3e4b2c97ac5bbea7e2d97252f Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Fri, 4 Dec 2015 17:41:50 +0000 -Subject: [PATCH 005/170] irq-bcm2836: Prevent spurious interrupts, and trap +Subject: [PATCH 005/232] irq-bcm2836: Prevent spurious interrupts, and trap them early The old arch-specific IRQ macros included a dsb to ensure the diff --git a/target/linux/brcm2708/patches-4.4/0006-irqchip-bcm2835-Add-FIQ-support.patch b/target/linux/brcm2708/patches-4.4/0006-irqchip-bcm2835-Add-FIQ-support.patch index 16d431e4cb..81d1295ece 100644 --- a/target/linux/brcm2708/patches-4.4/0006-irqchip-bcm2835-Add-FIQ-support.patch +++ b/target/linux/brcm2708/patches-4.4/0006-irqchip-bcm2835-Add-FIQ-support.patch @@ -1,7 +1,7 @@ -From 229bcf2e73ce37e8a18d68773471024c848d84bd Mon Sep 17 00:00:00 2001 +From 60f7ee79b375d72f5ef46ec542b925b1f1428bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Fri, 12 Jun 2015 19:01:05 +0200 -Subject: [PATCH 006/170] irqchip: bcm2835: Add FIQ support +Subject: [PATCH 006/232] irqchip: bcm2835: Add FIQ support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/target/linux/brcm2708/patches-4.4/0007-irqchip-irq-bcm2835-Add-2836-FIQ-support.patch b/target/linux/brcm2708/patches-4.4/0007-irqchip-irq-bcm2835-Add-2836-FIQ-support.patch index 6025b7a9b1..e95993c86a 100644 --- a/target/linux/brcm2708/patches-4.4/0007-irqchip-irq-bcm2835-Add-2836-FIQ-support.patch +++ b/target/linux/brcm2708/patches-4.4/0007-irqchip-irq-bcm2835-Add-2836-FIQ-support.patch @@ -1,7 +1,7 @@ -From 6780f0a5c6af19f2b9850e961a0f81fbfdfa48fe Mon Sep 17 00:00:00 2001 +From 890dccbb9c60be182adefb61b3ede805ccba1a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Fri, 23 Oct 2015 16:26:55 +0200 -Subject: [PATCH 007/170] irqchip: irq-bcm2835: Add 2836 FIQ support +Subject: [PATCH 007/232] irqchip: irq-bcm2835: Add 2836 FIQ support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/target/linux/brcm2708/patches-4.4/0008-serial-8250-Don-t-crash-when-nr_uarts-is-0.patch b/target/linux/brcm2708/patches-4.4/0008-serial-8250-Don-t-crash-when-nr_uarts-is-0.patch index 5c624a8db0..6961269a32 100644 --- a/target/linux/brcm2708/patches-4.4/0008-serial-8250-Don-t-crash-when-nr_uarts-is-0.patch +++ b/target/linux/brcm2708/patches-4.4/0008-serial-8250-Don-t-crash-when-nr_uarts-is-0.patch @@ -1,7 +1,7 @@ -From 4b9529b98c01569dff06f4e67ba28a47318e5ea5 Mon Sep 17 00:00:00 2001 +From 9a27662aac88f95b3de1915333a0265614f88a0d Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 30 Jun 2015 14:12:42 +0100 -Subject: [PATCH 008/170] serial: 8250: Don't crash when nr_uarts is 0 +Subject: [PATCH 008/232] serial: 8250: Don't crash when nr_uarts is 0 --- drivers/tty/serial/8250/8250_core.c | 2 ++ diff --git a/target/linux/brcm2708/patches-4.4/0009-pinctrl-bcm2835-Set-base-to-0-give-expected-gpio-num.patch b/target/linux/brcm2708/patches-4.4/0009-pinctrl-bcm2835-Set-base-to-0-give-expected-gpio-num.patch index fca587747c..a75621d54e 100644 --- a/target/linux/brcm2708/patches-4.4/0009-pinctrl-bcm2835-Set-base-to-0-give-expected-gpio-num.patch +++ b/target/linux/brcm2708/patches-4.4/0009-pinctrl-bcm2835-Set-base-to-0-give-expected-gpio-num.patch @@ -1,7 +1,7 @@ -From 5fd6ab82b1027504faa66cf4fc8b999fafaacd12 Mon Sep 17 00:00:00 2001 +From 4d7ff05e4d4d8d8adb4e7b4dd5fa8f63ae2be0f6 Mon Sep 17 00:00:00 2001 From: notro Date: Thu, 10 Jul 2014 13:59:47 +0200 -Subject: [PATCH 009/170] pinctrl-bcm2835: Set base to 0 give expected gpio +Subject: [PATCH 009/232] pinctrl-bcm2835: Set base to 0 give expected gpio numbering Signed-off-by: Noralf Tronnes diff --git a/target/linux/brcm2708/patches-4.4/0010-pinctrl-bcm2835-Fix-interrupt-handling-for-GPIOs-28-.patch b/target/linux/brcm2708/patches-4.4/0010-pinctrl-bcm2835-Fix-interrupt-handling-for-GPIOs-28-.patch index da3406b413..278ff9fec5 100644 --- a/target/linux/brcm2708/patches-4.4/0010-pinctrl-bcm2835-Fix-interrupt-handling-for-GPIOs-28-.patch +++ b/target/linux/brcm2708/patches-4.4/0010-pinctrl-bcm2835-Fix-interrupt-handling-for-GPIOs-28-.patch @@ -1,7 +1,7 @@ -From 4254ef2647e89061427836443eac60351b3306a7 Mon Sep 17 00:00:00 2001 +From 969dc44a700e1be13c3f80445d40c1fe51cc4370 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 24 Feb 2015 13:40:50 +0000 -Subject: [PATCH 010/170] pinctrl-bcm2835: Fix interrupt handling for GPIOs +Subject: [PATCH 010/232] pinctrl-bcm2835: Fix interrupt handling for GPIOs 28-31 and 46-53 Contrary to the documentation, the BCM2835 GPIO controller actually has diff --git a/target/linux/brcm2708/patches-4.4/0011-pinctrl-bcm2835-Only-request-the-interrupts-listed-i.patch b/target/linux/brcm2708/patches-4.4/0011-pinctrl-bcm2835-Only-request-the-interrupts-listed-i.patch index 02656d9060..49315e5884 100644 --- a/target/linux/brcm2708/patches-4.4/0011-pinctrl-bcm2835-Only-request-the-interrupts-listed-i.patch +++ b/target/linux/brcm2708/patches-4.4/0011-pinctrl-bcm2835-Only-request-the-interrupts-listed-i.patch @@ -1,7 +1,7 @@ -From 93b12e64d0e2a8a65189dedd667e00b0a0fab60a Mon Sep 17 00:00:00 2001 +From b949246bf51fa852bad45136cdd6bcd91a29539e Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 26 Feb 2015 09:58:22 +0000 -Subject: [PATCH 011/170] pinctrl-bcm2835: Only request the interrupts listed +Subject: [PATCH 011/232] pinctrl-bcm2835: Only request the interrupts listed in the DTB Although the GPIO controller can generate three interrupts (four counting diff --git a/target/linux/brcm2708/patches-4.4/0012-spi-bcm2835-Support-pin-groups-other-than-7-11.patch b/target/linux/brcm2708/patches-4.4/0012-spi-bcm2835-Support-pin-groups-other-than-7-11.patch index 46c37ec973..92aa046f22 100644 --- a/target/linux/brcm2708/patches-4.4/0012-spi-bcm2835-Support-pin-groups-other-than-7-11.patch +++ b/target/linux/brcm2708/patches-4.4/0012-spi-bcm2835-Support-pin-groups-other-than-7-11.patch @@ -1,7 +1,7 @@ -From bb8ff388ad26721eebb7249afb6f6f57bc196de4 Mon Sep 17 00:00:00 2001 +From 19bcdacc5a67abe6a2d604a39ddfed5a54c77b2e Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Wed, 24 Jun 2015 14:10:44 +0100 -Subject: [PATCH 012/170] spi-bcm2835: Support pin groups other than 7-11 +Subject: [PATCH 012/232] spi-bcm2835: Support pin groups other than 7-11 The spi-bcm2835 driver automatically uses GPIO chip-selects due to some unreliability of the native ones. In doing so it chooses the diff --git a/target/linux/brcm2708/patches-4.4/0013-ARM-bcm2835-Set-Serial-number-and-Revision.patch b/target/linux/brcm2708/patches-4.4/0013-ARM-bcm2835-Set-Serial-number-and-Revision.patch index a0860d1dbf..a571cdb202 100644 --- a/target/linux/brcm2708/patches-4.4/0013-ARM-bcm2835-Set-Serial-number-and-Revision.patch +++ b/target/linux/brcm2708/patches-4.4/0013-ARM-bcm2835-Set-Serial-number-and-Revision.patch @@ -1,7 +1,7 @@ -From bfcfed788d152e828747ea889a88f8b179217ddb Mon Sep 17 00:00:00 2001 +From 7ce5cbfba6e930c2178f342a4390972f1efd75ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Wed, 3 Jun 2015 12:26:13 +0200 -Subject: [PATCH 013/170] ARM: bcm2835: Set Serial number and Revision +Subject: [PATCH 013/232] ARM: bcm2835: Set Serial number and Revision MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/target/linux/brcm2708/patches-4.4/0014-bcm2835-i2s-get-base-address-for-DMA-from-devicetree.patch b/target/linux/brcm2708/patches-4.4/0014-bcm2835-i2s-get-base-address-for-DMA-from-devicetree.patch index 0e4726d458..fa35faaf56 100644 --- a/target/linux/brcm2708/patches-4.4/0014-bcm2835-i2s-get-base-address-for-DMA-from-devicetree.patch +++ b/target/linux/brcm2708/patches-4.4/0014-bcm2835-i2s-get-base-address-for-DMA-from-devicetree.patch @@ -1,7 +1,7 @@ -From fb968a02c00e2722df2dcfec6f54b7524ff5a155 Mon Sep 17 00:00:00 2001 +From 569baa856d805e594011f2d1444b6849c291544d Mon Sep 17 00:00:00 2001 From: Matthias Reichl Date: Sun, 11 Oct 2015 16:44:05 +0200 -Subject: [PATCH 014/170] bcm2835-i2s: get base address for DMA from devicetree +Subject: [PATCH 014/232] bcm2835-i2s: get base address for DMA from devicetree Code copied from spi-bcm2835. Get physical address from devicetree instead of using hardcoded constant. diff --git a/target/linux/brcm2708/patches-4.4/0015-bcm2835-i2s-add-24bit-support-update-bclk_ratio-to-m.patch b/target/linux/brcm2708/patches-4.4/0015-bcm2835-i2s-add-24bit-support-update-bclk_ratio-to-m.patch index 79fe1b43a9..a1fbf79ef0 100644 --- a/target/linux/brcm2708/patches-4.4/0015-bcm2835-i2s-add-24bit-support-update-bclk_ratio-to-m.patch +++ b/target/linux/brcm2708/patches-4.4/0015-bcm2835-i2s-add-24bit-support-update-bclk_ratio-to-m.patch @@ -1,7 +1,7 @@ -From abbe565abfe075b76e01b9671c6fc4cf42cc0328 Mon Sep 17 00:00:00 2001 +From c61a7aef15a1aee86e1de59dca2ad7002995f9cf Mon Sep 17 00:00:00 2001 From: Matthias Reichl Date: Sun, 11 Oct 2015 15:21:16 +0200 -Subject: [PATCH 015/170] bcm2835-i2s: add 24bit support, update bclk_ratio to +Subject: [PATCH 015/232] bcm2835-i2s: add 24bit support, update bclk_ratio to more correct values Code ported from bcm2708-i2s driver in Raspberry Pi tree. diff --git a/target/linux/brcm2708/patches-4.4/0016-bcm2835-i2s-setup-clock-only-if-CPU-is-clock-master.patch b/target/linux/brcm2708/patches-4.4/0016-bcm2835-i2s-setup-clock-only-if-CPU-is-clock-master.patch index 810f83fb8e..ec224b93e0 100644 --- a/target/linux/brcm2708/patches-4.4/0016-bcm2835-i2s-setup-clock-only-if-CPU-is-clock-master.patch +++ b/target/linux/brcm2708/patches-4.4/0016-bcm2835-i2s-setup-clock-only-if-CPU-is-clock-master.patch @@ -1,7 +1,7 @@ -From b2cc62cd13099d6d2f21b9927df248aa3bdd8d5b Mon Sep 17 00:00:00 2001 +From e2a75ce0e2b6319150e14401f9822adba7895c9c Mon Sep 17 00:00:00 2001 From: Matthias Reichl Date: Sun, 11 Oct 2015 15:25:51 +0200 -Subject: [PATCH 016/170] bcm2835-i2s: setup clock only if CPU is clock master +Subject: [PATCH 016/232] bcm2835-i2s: setup clock only if CPU is clock master Code ported from bcm2708-i2s driver in Raspberry Pi tree. diff --git a/target/linux/brcm2708/patches-4.4/0017-bcm2835-i2s-Eliminate-debugfs-directory-error.patch b/target/linux/brcm2708/patches-4.4/0017-bcm2835-i2s-Eliminate-debugfs-directory-error.patch index 93c83fd682..82d15f42d0 100644 --- a/target/linux/brcm2708/patches-4.4/0017-bcm2835-i2s-Eliminate-debugfs-directory-error.patch +++ b/target/linux/brcm2708/patches-4.4/0017-bcm2835-i2s-Eliminate-debugfs-directory-error.patch @@ -1,7 +1,7 @@ -From 71b2c93cd332be280b9ca7f293e463f30d5fb108 Mon Sep 17 00:00:00 2001 +From 433841fc5958e1ddbd503b579a022fb67defd74b Mon Sep 17 00:00:00 2001 From: Matthias Reichl Date: Sun, 11 Oct 2015 15:49:51 +0200 -Subject: [PATCH 017/170] bcm2835-i2s: Eliminate debugfs directory error +Subject: [PATCH 017/232] bcm2835-i2s: Eliminate debugfs directory error Code ported from bcm2708-i2s driver in Raspberry Pi tree. diff --git a/target/linux/brcm2708/patches-4.4/0018-bcm2835-i2s-Register-PCM-device.patch b/target/linux/brcm2708/patches-4.4/0018-bcm2835-i2s-Register-PCM-device.patch index 1b547fd6b9..1dc721a696 100644 --- a/target/linux/brcm2708/patches-4.4/0018-bcm2835-i2s-Register-PCM-device.patch +++ b/target/linux/brcm2708/patches-4.4/0018-bcm2835-i2s-Register-PCM-device.patch @@ -1,7 +1,7 @@ -From fae43fcaa20df1d39c09c98372100bb1dedd6090 Mon Sep 17 00:00:00 2001 +From 7b4481818fe920904457496db40d59dd3d0a83bb Mon Sep 17 00:00:00 2001 From: Matthias Reichl Date: Sun, 11 Oct 2015 15:35:20 +0200 -Subject: [PATCH 018/170] bcm2835-i2s: Register PCM device +Subject: [PATCH 018/232] bcm2835-i2s: Register PCM device Code ported from bcm2708-i2s driver in Raspberry Pi tree. diff --git a/target/linux/brcm2708/patches-4.4/0019-bcm2835-i2s-Enable-MMAP-support-via-a-DT-property.patch b/target/linux/brcm2708/patches-4.4/0019-bcm2835-i2s-Enable-MMAP-support-via-a-DT-property.patch index e68391ccce..e3d7e43823 100644 --- a/target/linux/brcm2708/patches-4.4/0019-bcm2835-i2s-Enable-MMAP-support-via-a-DT-property.patch +++ b/target/linux/brcm2708/patches-4.4/0019-bcm2835-i2s-Enable-MMAP-support-via-a-DT-property.patch @@ -1,7 +1,7 @@ -From c9e2eeee4889f965a29744718bbff2de409ac649 Mon Sep 17 00:00:00 2001 +From 027ba808e84799c97ae1c64f8424ac8eb7235cc4 Mon Sep 17 00:00:00 2001 From: Matthias Reichl Date: Sun, 11 Oct 2015 15:55:21 +0200 -Subject: [PATCH 019/170] bcm2835-i2s: Enable MMAP support via a DT property +Subject: [PATCH 019/232] bcm2835-i2s: Enable MMAP support via a DT property Code ported from bcm2708-i2s driver in Raspberry Pi tree. diff --git a/target/linux/brcm2708/patches-4.4/0020-dmaengine-bcm2835-Add-slave-dma-support.patch b/target/linux/brcm2708/patches-4.4/0020-dmaengine-bcm2835-Add-slave-dma-support.patch index 97d6af0490..344b39c3fd 100644 --- a/target/linux/brcm2708/patches-4.4/0020-dmaengine-bcm2835-Add-slave-dma-support.patch +++ b/target/linux/brcm2708/patches-4.4/0020-dmaengine-bcm2835-Add-slave-dma-support.patch @@ -1,7 +1,7 @@ -From dc5b926436bccc4efbb1695ec3696b5db6746d3b Mon Sep 17 00:00:00 2001 +From 8a349301238aabb40c9da5ca8c8492b6b8d146f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Thu, 9 Apr 2015 12:34:11 +0200 -Subject: [PATCH 020/170] dmaengine: bcm2835: Add slave dma support +Subject: [PATCH 020/232] dmaengine: bcm2835: Add slave dma support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/target/linux/brcm2708/patches-4.4/0021-dmaengine-bcm2835-set-residue_granularity-field.patch b/target/linux/brcm2708/patches-4.4/0021-dmaengine-bcm2835-set-residue_granularity-field.patch index 14d217d3b6..4655513f73 100644 --- a/target/linux/brcm2708/patches-4.4/0021-dmaengine-bcm2835-set-residue_granularity-field.patch +++ b/target/linux/brcm2708/patches-4.4/0021-dmaengine-bcm2835-set-residue_granularity-field.patch @@ -1,7 +1,7 @@ -From 9c6955f3fbfbb46242c63b4640886b9a0d87910a Mon Sep 17 00:00:00 2001 +From 290f55520c62025da992d6be0002fc1e3738cfdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Sat, 3 Oct 2015 15:58:59 +0200 -Subject: [PATCH 021/170] dmaengine: bcm2835: set residue_granularity field +Subject: [PATCH 021/232] dmaengine: bcm2835: set residue_granularity field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/target/linux/brcm2708/patches-4.4/0022-dmaengine-bcm2835-Load-driver-early-and-support-lega.patch b/target/linux/brcm2708/patches-4.4/0022-dmaengine-bcm2835-Load-driver-early-and-support-lega.patch index bc4e2028de..a03aed37fb 100644 --- a/target/linux/brcm2708/patches-4.4/0022-dmaengine-bcm2835-Load-driver-early-and-support-lega.patch +++ b/target/linux/brcm2708/patches-4.4/0022-dmaengine-bcm2835-Load-driver-early-and-support-lega.patch @@ -1,7 +1,7 @@ -From 8da7ee8d9b714dbbac842fb5ae0471b52e0393ec Mon Sep 17 00:00:00 2001 +From 6f56fff39c811953809b011f59c49a2122c4d173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Sat, 3 Oct 2015 22:22:55 +0200 -Subject: [PATCH 022/170] dmaengine: bcm2835: Load driver early and support +Subject: [PATCH 022/232] dmaengine: bcm2835: Load driver early and support legacy API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/target/linux/brcm2708/patches-4.4/0023-bcm2835-dma-Fix-dreq-not-set-for-slave-transfers.patch b/target/linux/brcm2708/patches-4.4/0023-bcm2835-dma-Fix-dreq-not-set-for-slave-transfers.patch index 2f512e2aaa..fe6dbcaf2d 100644 --- a/target/linux/brcm2708/patches-4.4/0023-bcm2835-dma-Fix-dreq-not-set-for-slave-transfers.patch +++ b/target/linux/brcm2708/patches-4.4/0023-bcm2835-dma-Fix-dreq-not-set-for-slave-transfers.patch @@ -1,7 +1,7 @@ -From 0f3a30eb7ad7e709fdc6447c70bc3f1d5704eb63 Mon Sep 17 00:00:00 2001 +From df03f00c5e21559185b5a61ea5e8238e10959ab1 Mon Sep 17 00:00:00 2001 From: Matthias Reichl Date: Sat, 10 Oct 2015 12:29:18 +0200 -Subject: [PATCH 023/170] bcm2835-dma: Fix dreq not set for slave transfers +Subject: [PATCH 023/232] bcm2835-dma: Fix dreq not set for slave transfers Set dreq to slave_id if it is not set like in bcm2708-dmaengine. --- diff --git a/target/linux/brcm2708/patches-4.4/0024-bcm2835-dma-Limit-cyclic-transfers-on-lite-channels-.patch b/target/linux/brcm2708/patches-4.4/0024-bcm2835-dma-Limit-cyclic-transfers-on-lite-channels-.patch index 01217c9784..60a0ec9217 100644 --- a/target/linux/brcm2708/patches-4.4/0024-bcm2835-dma-Limit-cyclic-transfers-on-lite-channels-.patch +++ b/target/linux/brcm2708/patches-4.4/0024-bcm2835-dma-Limit-cyclic-transfers-on-lite-channels-.patch @@ -1,7 +1,7 @@ -From 305370f7caaecafbabb77b630380dbc0c436bb4c Mon Sep 17 00:00:00 2001 +From 052c2005b6ecedc5abad86632f5781adda310aa7 Mon Sep 17 00:00:00 2001 From: Matthias Reichl Date: Sun, 11 Oct 2015 12:28:30 +0200 -Subject: [PATCH 024/170] bcm2835-dma: Limit cyclic transfers on lite channels +Subject: [PATCH 024/232] bcm2835-dma: Limit cyclic transfers on lite channels to 32k Transfers larger than 32k cause repeated clicking with I2S soundcards. diff --git a/target/linux/brcm2708/patches-4.4/0025-bcm2835-Add-support-for-uart1.patch b/target/linux/brcm2708/patches-4.4/0025-bcm2835-Add-support-for-uart1.patch index 6a5d2cb14a..9dfd26bc4e 100644 --- a/target/linux/brcm2708/patches-4.4/0025-bcm2835-Add-support-for-uart1.patch +++ b/target/linux/brcm2708/patches-4.4/0025-bcm2835-Add-support-for-uart1.patch @@ -1,7 +1,7 @@ -From d801c33b7e2fb0d02ac11b95ea92b7d692286ffc Mon Sep 17 00:00:00 2001 +From a72021ae99021409319d552dad744572b20febfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Sat, 15 Aug 2015 20:50:02 +0200 -Subject: [PATCH 025/170] bcm2835: Add support for uart1 +Subject: [PATCH 025/232] bcm2835: Add support for uart1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/target/linux/brcm2708/patches-4.4/0026-firmware-bcm2835-Add-missing-property-tags.patch b/target/linux/brcm2708/patches-4.4/0026-firmware-bcm2835-Add-missing-property-tags.patch index bde8c0a6c3..2a7252ace5 100644 --- a/target/linux/brcm2708/patches-4.4/0026-firmware-bcm2835-Add-missing-property-tags.patch +++ b/target/linux/brcm2708/patches-4.4/0026-firmware-bcm2835-Add-missing-property-tags.patch @@ -1,7 +1,7 @@ -From 5e90e32b539720ad8c6c8d7ec462378685924f22 Mon Sep 17 00:00:00 2001 +From 4364707caa6d3d9edf84fa8469a04f33e1ba5d76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Fri, 26 Jun 2015 14:21:20 +0200 -Subject: [PATCH 026/170] firmware: bcm2835: Add missing property tags +Subject: [PATCH 026/232] firmware: bcm2835: Add missing property tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/target/linux/brcm2708/patches-4.4/0027-Main-bcm2708-bcm2709-linux-port.patch b/target/linux/brcm2708/patches-4.4/0027-Main-bcm2708-bcm2709-linux-port.patch index 7a0f569c2e..83fd1db70c 100644 --- a/target/linux/brcm2708/patches-4.4/0027-Main-bcm2708-bcm2709-linux-port.patch +++ b/target/linux/brcm2708/patches-4.4/0027-Main-bcm2708-bcm2709-linux-port.patch @@ -1,7 +1,7 @@ -From 894f124497e95c9af506af2ed7335857f2e71358 Mon Sep 17 00:00:00 2001 +From cd721670ab8c729d0c62e99a1d173c2575b63282 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Sun, 12 May 2013 12:24:19 +0100 -Subject: [PATCH 027/170] Main bcm2708/bcm2709 linux port +Subject: [PATCH 027/232] Main bcm2708/bcm2709 linux port MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/target/linux/brcm2708/patches-4.4/0028-squash-include-ARCH_BCM2708-ARCH_BCM2709.patch b/target/linux/brcm2708/patches-4.4/0028-squash-include-ARCH_BCM2708-ARCH_BCM2709.patch index c35eb6fbb2..10e1a2f4c7 100644 --- a/target/linux/brcm2708/patches-4.4/0028-squash-include-ARCH_BCM2708-ARCH_BCM2709.patch +++ b/target/linux/brcm2708/patches-4.4/0028-squash-include-ARCH_BCM2708-ARCH_BCM2709.patch @@ -1,7 +1,7 @@ -From 74b2c26348d94949561979a9a3ad61adcac74398 Mon Sep 17 00:00:00 2001 +From 812484cc27980e3351360200478e7044de983f40 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 11 Nov 2015 21:01:15 +0000 -Subject: [PATCH 028/170] squash: include ARCH_BCM2708 / ARCH_BCM2709 +Subject: [PATCH 028/232] squash: include ARCH_BCM2708 / ARCH_BCM2709 --- drivers/char/hw_random/Kconfig | 2 +- diff --git a/target/linux/brcm2708/patches-4.4/0029-Add-dwc_otg-driver.patch b/target/linux/brcm2708/patches-4.4/0029-Add-dwc_otg-driver.patch index b1e5a49be0..41d79b1710 100644 --- a/target/linux/brcm2708/patches-4.4/0029-Add-dwc_otg-driver.patch +++ b/target/linux/brcm2708/patches-4.4/0029-Add-dwc_otg-driver.patch @@ -1,7 +1,7 @@ -From a03df8d0f93365689c718f3d1fb62695e9fffe31 Mon Sep 17 00:00:00 2001 +From 7efc2851a46faa1aceda5f6ec255d0e814812ccf Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 1 May 2013 19:46:17 +0100 -Subject: [PATCH 029/170] Add dwc_otg driver +Subject: [PATCH 029/232] Add dwc_otg driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/target/linux/brcm2708/patches-4.4/0030-bcm2708-framebuffer-driver.patch b/target/linux/brcm2708/patches-4.4/0030-bcm2708-framebuffer-driver.patch index 4dccb5e759..b2983fe57a 100644 --- a/target/linux/brcm2708/patches-4.4/0030-bcm2708-framebuffer-driver.patch +++ b/target/linux/brcm2708/patches-4.4/0030-bcm2708-framebuffer-driver.patch @@ -1,7 +1,7 @@ -From 845f985818d3d9ba162ab1102da78b0e39eaedba Mon Sep 17 00:00:00 2001 +From d3b7394edae550f295e185be4651ed43529e7042 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 17 Jun 2015 17:06:34 +0100 -Subject: [PATCH 030/170] bcm2708 framebuffer driver +Subject: [PATCH 030/232] bcm2708 framebuffer driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/target/linux/brcm2708/patches-4.4/0031-dmaengine-Add-support-for-BCM2708.patch b/target/linux/brcm2708/patches-4.4/0031-dmaengine-Add-support-for-BCM2708.patch index 9796cb5f49..687c3eb424 100644 --- a/target/linux/brcm2708/patches-4.4/0031-dmaengine-Add-support-for-BCM2708.patch +++ b/target/linux/brcm2708/patches-4.4/0031-dmaengine-Add-support-for-BCM2708.patch @@ -1,7 +1,7 @@ -From a35139d316b78606bfdeb0f10656ba6f1729c83f Mon Sep 17 00:00:00 2001 +From 99b7c3cfd05f5898e96d7c44c5970880f2c0cd16 Mon Sep 17 00:00:00 2001 From: Florian Meier Date: Fri, 22 Nov 2013 14:22:53 +0100 -Subject: [PATCH 031/170] dmaengine: Add support for BCM2708 +Subject: [PATCH 031/232] dmaengine: Add support for BCM2708 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/target/linux/brcm2708/patches-4.4/0032-Add-blk_pos-parameter-to-mmc-multi_io_quirk-callback.patch b/target/linux/brcm2708/patches-4.4/0032-Add-blk_pos-parameter-to-mmc-multi_io_quirk-callback.patch deleted file mode 100644 index 46835f15f3..0000000000 --- a/target/linux/brcm2708/patches-4.4/0032-Add-blk_pos-parameter-to-mmc-multi_io_quirk-callback.patch +++ /dev/null @@ -1,75 +0,0 @@ -From 462591cf98f15614620667e93b7f8bc0da6e86da Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Fri, 17 Apr 2015 19:30:22 +0100 -Subject: [PATCH 032/170] Add blk_pos parameter to mmc multi_io_quirk callback - ---- - drivers/mmc/card/block.c | 1 + - drivers/mmc/host/omap_hsmmc.c | 4 +++- - drivers/mmc/host/sh_mobile_sdhi.c | 4 +++- - drivers/mmc/host/tmio_mmc_pio.c | 4 +++- - include/linux/mmc/host.h | 4 +++- - 5 files changed, 13 insertions(+), 4 deletions(-) - ---- a/drivers/mmc/card/block.c -+++ b/drivers/mmc/card/block.c -@@ -1518,6 +1518,7 @@ static void mmc_blk_rw_rq_prep(struct mm - brq->data.blocks = card->host->ops->multi_io_quirk(card, - (rq_data_dir(req) == READ) ? - MMC_DATA_READ : MMC_DATA_WRITE, -+ blk_rq_pos(req), - brq->data.blocks); - } - ---- a/drivers/mmc/host/omap_hsmmc.c -+++ b/drivers/mmc/host/omap_hsmmc.c -@@ -1832,7 +1832,9 @@ static void omap_hsmmc_conf_bus_power(st - } - - static int omap_hsmmc_multi_io_quirk(struct mmc_card *card, -- unsigned int direction, int blk_size) -+ unsigned int direction, -+ u32 blk_pos, -+ int blk_size) - { - /* This controller can't do multiblock reads due to hw bugs */ - if (direction == MMC_DATA_READ) ---- a/drivers/mmc/host/sh_mobile_sdhi.c -+++ b/drivers/mmc/host/sh_mobile_sdhi.c -@@ -170,7 +170,9 @@ static int sh_mobile_sdhi_write16_hook(s - } - - static int sh_mobile_sdhi_multi_io_quirk(struct mmc_card *card, -- unsigned int direction, int blk_size) -+ unsigned int direction, -+ u32 blk_pos, -+ int blk_size) - { - /* - * In Renesas controllers, when performing a ---- a/drivers/mmc/host/tmio_mmc_pio.c -+++ b/drivers/mmc/host/tmio_mmc_pio.c -@@ -1003,7 +1003,9 @@ static int tmio_mmc_get_ro(struct mmc_ho - } - - static int tmio_multi_io_quirk(struct mmc_card *card, -- unsigned int direction, int blk_size) -+ unsigned int direction, -+ u32 blk_pos, -+ int blk_size) - { - struct tmio_mmc_host *host = mmc_priv(card->host); - ---- a/include/linux/mmc/host.h -+++ b/include/linux/mmc/host.h -@@ -143,7 +143,9 @@ struct mmc_host_ops { - * I/O. Returns the number of supported blocks for the request. - */ - int (*multi_io_quirk)(struct mmc_card *card, -- unsigned int direction, int blk_size); -+ unsigned int direction, -+ u32 blk_pos, -+ int blk_size); - }; - - struct mmc_card; diff --git a/target/linux/brcm2708/patches-4.4/0032-MMC-added-alternative-MMC-driver.patch b/target/linux/brcm2708/patches-4.4/0032-MMC-added-alternative-MMC-driver.patch new file mode 100644 index 0000000000..e74c70c124 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0032-MMC-added-alternative-MMC-driver.patch @@ -0,0 +1,1691 @@ +From 65c8aadfd88010efb2d2a2821fea7aac978b6c78 Mon Sep 17 00:00:00 2001 +From: gellert +Date: Fri, 15 Aug 2014 16:35:06 +0100 +Subject: [PATCH 032/232] 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 + +bcm2835-mmc: Add locks when accessing sdhost registers + +bcm2835-mmc: Add range of debug options for slowing things down + +bcm2835-mmc: Add option to disable some delays + +bcm2835-mmc: Add option to disable MMC_QUIRK_BLK_NO_CMD23 + +bcm2835-mmc: Default to disabling MMC_QUIRK_BLK_NO_CMD23 + +bcm2835-mmc: Adding overclocking option + +Allow a different clock speed to be substitued for a requested 50MHz. +This option is exposed using the "overclock_50" DT parameter. +Note that the mmc interface is restricted to EVEN integer divisions of +250MHz, and the highest sensible option is 63 (250/4 = 62.5), the +next being 125 (250/2) which is much too high. + +Use at your own risk. + +bcm2835-mmc: Round up the overclock, so 62 works for 62.5Mhz + +Also only warn once for each overclock setting. + +mmc: bcm2835-mmc: Make available on ARCH_BCM2835 + +Make the bcm2835-mmc driver available for use on ARCH_BCM2835. + +Signed-off-by: Noralf Trønnes + +BCM270x_DT: add bcm2835-mmc entry + +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 + +bcm2835-mmc: Don't overwrite MMC capabilities from DT +--- + drivers/mmc/core/quirks.c | 6 + + drivers/mmc/host/Kconfig | 29 + + drivers/mmc/host/Makefile | 1 + + drivers/mmc/host/bcm2835-mmc.c | 1542 ++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 1578 insertions(+) + create mode 100644 drivers/mmc/host/bcm2835-mmc.c + +--- a/drivers/mmc/core/quirks.c ++++ b/drivers/mmc/core/quirks.c +@@ -53,6 +53,7 @@ static const struct mmc_fixup mmc_fixup_ + + void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table) + { ++ extern unsigned mmc_debug; + const struct mmc_fixup *f; + u64 rev = cid_rev_card(card); + +@@ -77,5 +78,10 @@ void mmc_fixup_device(struct mmc_card *c + 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). ++ */ ++ if (mmc_debug & (1<<13)) ++ card->quirks |= MMC_QUIRK_BLK_NO_CMD23; + } + EXPORT_SYMBOL(mmc_fixup_device); +--- 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 || MACH_BCM2709 || ARCH_BCM2835 ++ 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 +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -18,6 +18,7 @@ obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c + 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_MTK) += mtk-sd.o +--- /dev/null ++++ b/drivers/mmc/host/bcm2835-mmc.c +@@ -0,0 +1,1542 @@ ++/* ++ * 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) ++ ++ ++unsigned mmc_debug; ++unsigned mmc_debug2; ++ ++struct bcm2835_host { ++ spinlock_t lock; ++ ++ void __iomem *ioaddr; ++ u32 bus_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 */ ++ ++ u32 overclock_50; /* frequency to use when 50MHz is requested (in MHz) */ ++ u32 max_overclock; /* Highest reported */ ++}; ++ ++ ++static inline void bcm2835_mmc_writel(struct bcm2835_host *host, u32 val, int reg, int from) ++{ ++ unsigned delay; ++ lockdep_assert_held_once(&host->lock); ++ writel(val, host->ioaddr + reg); ++ udelay(BCM2835_SDHCI_WRITE_DELAY(max(host->clock, MIN_FREQ))); ++ ++ delay = ((mmc_debug >> 16) & 0xf) << ((mmc_debug >> 20) & 0xf); ++ if (delay && !((1<lock); ++ writel(val, host->ioaddr + reg); ++ ++ delay = ((mmc_debug >> 24) & 0xf) << ((mmc_debug >> 28) & 0xf); ++ if (delay) ++ udelay(delay); ++} ++ ++static inline u32 bcm2835_mmc_readl(struct bcm2835_host *host, int reg) ++{ ++ lockdep_assert_held_once(&host->lock); ++ 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, 0); ++ ++} ++ ++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, 1); ++} ++ ++ ++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, 2); ++} ++ ++ ++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; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ 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--; ++ spin_unlock_irqrestore(&host->lock, flags); ++ mdelay(1); ++ spin_lock_irqsave(&host->lock, flags); ++ } ++ ++ 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); ++ } ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++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) ++{ ++ unsigned long flags; ++ 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; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ bcm2835_mmc_writel(host, host->ier, SDHCI_INT_ENABLE, 3); ++ bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE, 3); ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ 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) { ++ unsigned long flags; ++ spin_lock_irqsave(&host->lock, flags); ++ 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; ++ spin_unlock_irqrestore(&host->lock, flags); ++ 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, 4); ++ bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE, 4); ++} ++ ++ ++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, 5); ++ } ++ } ++ ++ 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; ++ if (!cmd->data && cmd->busy_timeout > 9000) ++ timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ; ++ else ++ 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, 6); ++ ++ 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, 7); ++ bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE, 7); ++ 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; ++ ++ 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, 8); ++ ++ ++ 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) { ++ bcm2835_mmc_enable_sdio_irq_nolock(host, false); ++ host->thread_isr |= SDHCI_INT_CARD_INT; ++ result = IRQ_WAKE_THREAD; ++ } ++ ++ 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, 9); ++ } ++ ++ 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); ++ } ++ ++ return result; ++} ++ ++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; ++} ++ ++ ++ ++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; ++ unsigned int input_clock = clock; ++ ++ if (host->overclock_50 && (clock == 50000000)) ++ clock = host->overclock_50 * 1000000 + 999999; ++ ++ 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) ++ clock = (host->max_clk * clk_mul) / real_div; ++ host->mmc->actual_clock = clock; ++ ++ if ((clock > input_clock) && (clock > host->max_overclock)) { ++ pr_warn("%s: Overclocking to %dHz\n", ++ mmc_hostname(host->mmc), clock); ++ host->max_overclock = clock; ++ } ++ ++ 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; ++ ++ pr_debug("bcm2835_mmc_set_ios: clock %d, pwr %d, bus_width %d, timing %d, vdd %d, drv_type %d\n", ++ ios->clock, ios->power_mode, ios->bus_width, ++ ios->timing, ios->signal_voltage, ios->drv_type); ++ ++ 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))))) { ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++ bcm2835_mmc_reset(host, SDHCI_RESET_CMD); ++ bcm2835_mmc_reset(host, SDHCI_RESET_DATA); ++ spin_lock_irqsave(&host->lock, flags); ++ } ++ ++ host->mrq = NULL; ++ host->cmd = NULL; ++ host->data = NULL; ++ ++ mmiowb(); ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++ mmc_request_done(host->mmc, mrq); ++} ++ ++ ++ ++static 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; ++ mmc->max_busy_timeout = (1 << 27) / host->timeout_clk; ++ ++ /* 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; ++ ++ mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; ++ ++ host->flags = SDHCI_AUTO_CMD23; ++ ++ dev_info(dev, "mmc_debug:%x mmc_debug2:%x\n", mmc_debug, mmc_debug2); ++#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->bus_addr + SDHCI_BUFFER; ++ ret = dmaengine_slave_config(host->dma_chan_tx, &cfg); ++ ++ cfg.direction = DMA_DEV_TO_MEM; ++ cfg.src_addr = host->bus_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); ++ ret = devm_request_threaded_irq(dev, host->irq, bcm2835_mmc_irq, ++ bcm2835_mmc_thread_irq, IRQF_SHARED, ++ mmc_hostname(mmc), host); ++ 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; ++ const __be32 *addr; ++ 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; ++ } ++ ++ addr = of_get_address(node, 0, NULL, NULL); ++ if (!addr) { ++ dev_err(dev, "could not get DMA-register address\n"); ++ return -ENODEV; ++ } ++ host->bus_addr = be32_to_cpup(addr); ++ pr_debug(" - ioaddr %lx, iomem->start %lx, bus_addr %lx\n", ++ (unsigned long)host->ioaddr, ++ (unsigned long)iomem->start, ++ (unsigned long)host->bus_addr); ++ ++#ifndef FORCE_PIO ++ if (node) { ++ host->dma_chan_tx = dma_request_slave_channel(dev, "tx"); ++ host->dma_chan_rx = dma_request_slave_channel(dev, "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); ++ ++ /* Read any custom properties */ ++ of_property_read_u32(node, ++ "brcm,overclock-50", ++ &host->overclock_50); ++ } 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_param(mmc_debug, uint, 0644); ++module_param(mmc_debug2, uint, 0644); ++MODULE_ALIAS("platform:mmc-bcm2835"); ++MODULE_DESCRIPTION("BCM2835 SDHCI driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Gellert Weisz"); diff --git a/target/linux/brcm2708/patches-4.4/0033-Adding-bcm2835-sdhost-driver-and-an-overlay-to-enabl.patch b/target/linux/brcm2708/patches-4.4/0033-Adding-bcm2835-sdhost-driver-and-an-overlay-to-enabl.patch new file mode 100644 index 0000000000..46b07df564 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0033-Adding-bcm2835-sdhost-driver-and-an-overlay-to-enabl.patch @@ -0,0 +1,2022 @@ +From c58bec0d84082837415562c6567396e9e5ba62f7 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 25 Mar 2015 17:49:47 +0000 +Subject: [PATCH 033/232] Adding bcm2835-sdhost driver, and an overlay to + enable it + +BCM2835 has two SD card interfaces. This driver uses the other one. + +bcm2835-sdhost: Error handling fix, and code clarification + +bcm2835-sdhost: Adding overclocking option + +Allow a different clock speed to be substitued for a requested 50MHz. +This option is exposed using the "overclock_50" DT parameter. +Note that the sdhost interface is restricted to integer divisions of +core_freq, and the highest sensible option for a core_freq of 250MHz +is 84 (250/3 = 83.3MHz), the next being 125 (250/2) which is much too +high. + +Use at your own risk. + +bcm2835-sdhost: Round up the overclock, so 62 works for 62.5Mhz + +Also only warn once for each overclock setting. + +bcm2835-sdhost: Improve error handling and recovery + +1) Expose the hw_reset method to the MMC framework, removing many + internal calls by the driver. + +2) Reduce overclock setting on error. + +3) Increase timeout to cope with high capacity cards. + +4) Add properties and parameters to control pio_limit and debug. + +5) Reduce messages at probe time. + +bcm2835-sdhost: Further improve overclock back-off + +bcm2835-sdhost: Clear HBLC for PIO mode + +Also update pio_limit default in overlay README. + +bcm2835-sdhost: Add the ERASE capability + +See: https://github.com/raspberrypi/linux/issues/1076 + +bcm2835-sdhost: Ignore CRC7 for MMC CMD1 + +It seems that the sdhost interface returns CRC7 errors for CMD1, +which is the MMC-specific SEND_OP_COND. Returning these errors to +the MMC layer causes a downward spiral, but ignoring them seems +to be harmless. + +bcm2835-mmc/sdhost: Remove ARCH_BCM2835 differences + +The bcm2835-mmc driver (and -sdhost driver that copied from it) +contains code to handle SDIO interrupts in a threaded interrupt +handler rather than waking the MMC framework thread. The change +follows a patch from Russell King that adds the facility as the +preferred way of working. + +However, the new code path is only present in ARCH_BCM2835 +builds, which I have taken to be a way of testing the waters +rather than making the change across the board; I can't see +any technical reason why it wouldn't be enabled for MACH_BCM270X +builds. So this patch standardises on the ARCH_BCM2835 code, +removing the old code paths. + +bcm2835-sdhost: Don't log timeout errors unless debug=1 + +The MMC card-discovery process generates timeouts. This is +expected behaviour, so reporting it to the user serves no purpose. +Suppress the reporting of timeout errors unless the debug flag +is on. +--- + drivers/mmc/host/Kconfig | 10 + + drivers/mmc/host/Makefile | 1 + + drivers/mmc/host/bcm2835-sdhost.c | 1907 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 1918 insertions(+) + create mode 100644 drivers/mmc/host/bcm2835-sdhost.c + +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -33,6 +33,16 @@ config MMC_BCM2835_PIO_DMA_BARRIER + + If unsure, say 2 here. + ++config MMC_BCM2835_SDHOST ++ tristate "Support for the SDHost controller on BCM2708/9" ++ depends on MACH_BCM2708 || MACH_BCM2709 || ARCH_BCM2835 ++ help ++ This selects the SDHost controller on BCM2835/6. ++ ++ If you have a controller with this interface, say Y or M here. ++ ++ If unsure, say N. ++ + config MMC_ARMMMCI + tristate "ARM AMBA Multimedia Card Interface support" + depends on ARM_AMBA +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -18,6 +18,7 @@ obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c + 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_SDHOST) += bcm2835-sdhost.o + obj-$(CONFIG_MMC_BCM2835) += bcm2835-mmc.o + obj-$(CONFIG_MMC_WBSD) += wbsd.o + obj-$(CONFIG_MMC_AU1X) += au1xmmc.o +--- /dev/null ++++ b/drivers/mmc/host/bcm2835-sdhost.c +@@ -0,0 +1,1907 @@ ++/* ++ * BCM2835 SD host driver. ++ * ++ * Author: Phil Elwell ++ * Copyright 2015 ++ * ++ * Based on ++ * mmc-bcm2835.c by Gellert Weisz ++ * which is, in turn, 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 . ++ */ ++ ++#define SAFE_READ_THRESHOLD 4 ++#define SAFE_WRITE_THRESHOLD 4 ++#define ALLOW_DMA 1 ++#define ALLOW_CMD23 0 ++#define ALLOW_FAST 1 ++#define USE_BLOCK_IRQ 1 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_NAME "sdhost-bcm2835" ++ ++#define SDCMD 0x00 /* Command to SD card - 16 R/W */ ++#define SDARG 0x04 /* Argument to SD card - 32 R/W */ ++#define SDTOUT 0x08 /* Start value for timeout counter - 32 R/W */ ++#define SDCDIV 0x0c /* Start value for clock divider - 11 R/W */ ++#define SDRSP0 0x10 /* SD card response (31:0) - 32 R */ ++#define SDRSP1 0x14 /* SD card response (63:32) - 32 R */ ++#define SDRSP2 0x18 /* SD card response (95:64) - 32 R */ ++#define SDRSP3 0x1c /* SD card response (127:96) - 32 R */ ++#define SDHSTS 0x20 /* SD host status - 11 R */ ++#define SDVDD 0x30 /* SD card power control - 1 R/W */ ++#define SDEDM 0x34 /* Emergency Debug Mode - 13 R/W */ ++#define SDHCFG 0x38 /* Host configuration - 2 R/W */ ++#define SDHBCT 0x3c /* Host byte count (debug) - 32 R/W */ ++#define SDDATA 0x40 /* Data to/from SD card - 32 R/W */ ++#define SDHBLC 0x50 /* Host block count (SDIO/SDHC) - 9 R/W */ ++ ++#define SDCMD_NEW_FLAG 0x8000 ++#define SDCMD_FAIL_FLAG 0x4000 ++#define SDCMD_BUSYWAIT 0x800 ++#define SDCMD_NO_RESPONSE 0x400 ++#define SDCMD_LONG_RESPONSE 0x200 ++#define SDCMD_WRITE_CMD 0x80 ++#define SDCMD_READ_CMD 0x40 ++#define SDCMD_CMD_MASK 0x3f ++ ++#define SDCDIV_MAX_CDIV 0x7ff ++ ++#define SDHSTS_BUSY_IRPT 0x400 ++#define SDHSTS_BLOCK_IRPT 0x200 ++#define SDHSTS_SDIO_IRPT 0x100 ++#define SDHSTS_REW_TIME_OUT 0x80 ++#define SDHSTS_CMD_TIME_OUT 0x40 ++#define SDHSTS_CRC16_ERROR 0x20 ++#define SDHSTS_CRC7_ERROR 0x10 ++#define SDHSTS_FIFO_ERROR 0x08 ++/* Reserved */ ++/* Reserved */ ++#define SDHSTS_DATA_FLAG 0x01 ++ ++#define SDHSTS_TRANSFER_ERROR_MASK (SDHSTS_CRC7_ERROR|SDHSTS_CRC16_ERROR|SDHSTS_REW_TIME_OUT|SDHSTS_FIFO_ERROR) ++#define SDHSTS_ERROR_MASK (SDHSTS_CMD_TIME_OUT|SDHSTS_TRANSFER_ERROR_MASK) ++ ++#define SDHCFG_BUSY_IRPT_EN (1<<10) ++#define SDHCFG_BLOCK_IRPT_EN (1<<8) ++#define SDHCFG_SDIO_IRPT_EN (1<<5) ++#define SDHCFG_DATA_IRPT_EN (1<<4) ++#define SDHCFG_SLOW_CARD (1<<3) ++#define SDHCFG_WIDE_EXT_BUS (1<<2) ++#define SDHCFG_WIDE_INT_BUS (1<<1) ++#define SDHCFG_REL_CMD_LINE (1<<0) ++ ++#define SDEDM_FORCE_DATA_MODE (1<<19) ++#define SDEDM_CLOCK_PULSE (1<<20) ++#define SDEDM_BYPASS (1<<21) ++ ++#define SDEDM_WRITE_THRESHOLD_SHIFT 9 ++#define SDEDM_READ_THRESHOLD_SHIFT 14 ++#define SDEDM_THRESHOLD_MASK 0x1f ++ ++#define MHZ 1000000 ++ ++ ++struct bcm2835_host { ++ spinlock_t lock; ++ ++ void __iomem *ioaddr; ++ u32 bus_addr; ++ ++ struct mmc_host *mmc; ++ ++ u32 pio_timeout; /* In jiffies */ ++ ++ int clock; /* Current clock speed */ ++ ++ bool slow_card; /* Force 11-bit divisor */ ++ ++ unsigned int max_clk; /* Max possible freq */ ++ ++ struct tasklet_struct finish_tasklet; /* Tasklet structures */ ++ ++ struct timer_list timer; /* Timer for timeouts */ ++ ++ struct timer_list pio_timer; /* PIO error detection timer */ ++ ++ struct sg_mapping_iter sg_miter; /* SG state for PIO */ ++ unsigned int blocks; /* remaining PIO blocks */ ++ ++ int irq; /* Device IRQ */ ++ ++ ++ /* cached registers */ ++ u32 hcfg; ++ u32 cdiv; ++ ++ struct mmc_request *mrq; /* Current request */ ++ struct mmc_command *cmd; /* Current command */ ++ struct mmc_data *data; /* Current data request */ ++ unsigned int data_complete:1; /* Data finished before cmd */ ++ ++ unsigned int flush_fifo:1; /* Drain the fifo when finishing */ ++ ++ unsigned int use_busy:1; /* Wait for busy interrupt */ ++ ++ unsigned int debug:1; /* Enable debug output */ ++ ++ u32 thread_isr; ++ ++ /*DMA part*/ ++ struct dma_chan *dma_chan_rx; /* DMA channel for reads */ ++ struct dma_chan *dma_chan_tx; /* DMA channel for writes */ ++ ++ bool allow_dma; ++ bool have_dma; ++ bool use_dma; ++ /*end of DMA part*/ ++ ++ int max_delay; /* maximum length of time spent waiting */ ++ struct timeval stop_time; /* when the last stop was issued */ ++ u32 delay_after_stop; /* minimum time between stop and subsequent data transfer */ ++ u32 overclock_50; /* frequency to use when 50MHz is requested (in MHz) */ ++ u32 overclock; /* Current frequency if overclocked, else zero */ ++ u32 pio_limit; /* Maximum block count for PIO (0 = always DMA) */ ++}; ++ ++ ++static inline void bcm2835_sdhost_write(struct bcm2835_host *host, u32 val, int reg) ++{ ++ writel(val, host->ioaddr + reg); ++} ++ ++static inline u32 bcm2835_sdhost_read(struct bcm2835_host *host, int reg) ++{ ++ return readl(host->ioaddr + reg); ++} ++ ++static inline u32 bcm2835_sdhost_read_relaxed(struct bcm2835_host *host, int reg) ++{ ++ return readl_relaxed(host->ioaddr + reg); ++} ++ ++static void bcm2835_sdhost_dumpcmd(struct bcm2835_host *host, ++ struct mmc_command *cmd, ++ const char *label) ++{ ++ if (cmd) ++ pr_info("%s:%c%s op %d arg 0x%x flags 0x%x - resp %08x %08x %08x %08x, err %d\n", ++ mmc_hostname(host->mmc), ++ (cmd == host->cmd) ? '>' : ' ', ++ label, cmd->opcode, cmd->arg, cmd->flags, ++ cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3], ++ cmd->error); ++} ++ ++static void bcm2835_sdhost_dumpregs(struct bcm2835_host *host) ++{ ++ bcm2835_sdhost_dumpcmd(host, host->mrq->sbc, "sbc"); ++ bcm2835_sdhost_dumpcmd(host, host->mrq->cmd, "cmd"); ++ if (host->mrq->data) ++ pr_err("%s: data blocks %x blksz %x - err %d\n", ++ mmc_hostname(host->mmc), ++ host->mrq->data->blocks, ++ host->mrq->data->blksz, ++ host->mrq->data->error); ++ bcm2835_sdhost_dumpcmd(host, host->mrq->stop, "stop"); ++ ++ pr_info("%s: =========== REGISTER DUMP ===========\n", ++ mmc_hostname(host->mmc)); ++ ++ pr_info("%s: SDCMD 0x%08x\n", ++ mmc_hostname(host->mmc), ++ bcm2835_sdhost_read(host, SDCMD)); ++ pr_info("%s: SDARG 0x%08x\n", ++ mmc_hostname(host->mmc), ++ bcm2835_sdhost_read(host, SDARG)); ++ pr_info("%s: SDTOUT 0x%08x\n", ++ mmc_hostname(host->mmc), ++ bcm2835_sdhost_read(host, SDTOUT)); ++ pr_info("%s: SDCDIV 0x%08x\n", ++ mmc_hostname(host->mmc), ++ bcm2835_sdhost_read(host, SDCDIV)); ++ pr_info("%s: SDRSP0 0x%08x\n", ++ mmc_hostname(host->mmc), ++ bcm2835_sdhost_read(host, SDRSP0)); ++ pr_info("%s: SDRSP1 0x%08x\n", ++ mmc_hostname(host->mmc), ++ bcm2835_sdhost_read(host, SDRSP1)); ++ pr_info("%s: SDRSP2 0x%08x\n", ++ mmc_hostname(host->mmc), ++ bcm2835_sdhost_read(host, SDRSP2)); ++ pr_info("%s: SDRSP3 0x%08x\n", ++ mmc_hostname(host->mmc), ++ bcm2835_sdhost_read(host, SDRSP3)); ++ pr_info("%s: SDHSTS 0x%08x\n", ++ mmc_hostname(host->mmc), ++ bcm2835_sdhost_read(host, SDHSTS)); ++ pr_info("%s: SDVDD 0x%08x\n", ++ mmc_hostname(host->mmc), ++ bcm2835_sdhost_read(host, SDVDD)); ++ pr_info("%s: SDEDM 0x%08x\n", ++ mmc_hostname(host->mmc), ++ bcm2835_sdhost_read(host, SDEDM)); ++ pr_info("%s: SDHCFG 0x%08x\n", ++ mmc_hostname(host->mmc), ++ bcm2835_sdhost_read(host, SDHCFG)); ++ pr_info("%s: SDHBCT 0x%08x\n", ++ mmc_hostname(host->mmc), ++ bcm2835_sdhost_read(host, SDHBCT)); ++ pr_info("%s: SDHBLC 0x%08x\n", ++ mmc_hostname(host->mmc), ++ bcm2835_sdhost_read(host, SDHBLC)); ++ ++ pr_info("%s: ===========================================\n", ++ mmc_hostname(host->mmc)); ++} ++ ++ ++static void bcm2835_sdhost_set_power(struct bcm2835_host *host, bool on) ++{ ++ bcm2835_sdhost_write(host, on ? 1 : 0, SDVDD); ++} ++ ++ ++static void bcm2835_sdhost_reset_internal(struct bcm2835_host *host) ++{ ++ u32 temp; ++ ++ bcm2835_sdhost_set_power(host, false); ++ ++ bcm2835_sdhost_write(host, 0, SDCMD); ++ bcm2835_sdhost_write(host, 0, SDARG); ++ bcm2835_sdhost_write(host, 0xf00000, SDTOUT); ++ bcm2835_sdhost_write(host, 0, SDCDIV); ++ bcm2835_sdhost_write(host, 0x7f8, SDHSTS); /* Write 1s to clear */ ++ bcm2835_sdhost_write(host, 0, SDHCFG); ++ bcm2835_sdhost_write(host, 0, SDHBCT); ++ bcm2835_sdhost_write(host, 0, SDHBLC); ++ ++ /* Limit fifo usage due to silicon bug */ ++ temp = bcm2835_sdhost_read(host, SDEDM); ++ temp &= ~((SDEDM_THRESHOLD_MASK<clock = 0; ++ bcm2835_sdhost_write(host, host->hcfg, SDHCFG); ++ bcm2835_sdhost_write(host, host->cdiv, SDCDIV); ++ mmiowb(); ++} ++ ++ ++static void bcm2835_sdhost_reset(struct mmc_host *mmc) ++{ ++ struct bcm2835_host *host = mmc_priv(mmc); ++ unsigned long flags; ++ if (host->debug) ++ pr_info("%s: reset\n", mmc_hostname(mmc)); ++ spin_lock_irqsave(&host->lock, flags); ++ ++ bcm2835_sdhost_reset_internal(host); ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++static void bcm2835_sdhost_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); ++ ++static void bcm2835_sdhost_init(struct bcm2835_host *host, int soft) ++{ ++ pr_debug("bcm2835_sdhost_init(%d)\n", soft); ++ ++ /* Set interrupt enables */ ++ host->hcfg = SDHCFG_BUSY_IRPT_EN; ++ ++ bcm2835_sdhost_reset_internal(host); ++ ++ if (soft) { ++ /* force clock reconfiguration */ ++ host->clock = 0; ++ bcm2835_sdhost_set_ios(host->mmc, &host->mmc->ios); ++ } ++} ++ ++static bool bcm2835_sdhost_is_write_complete(struct bcm2835_host *host) ++{ ++ bool write_complete = ((bcm2835_sdhost_read(host, SDEDM) & 0xf) == 1); ++ ++ if (!write_complete) { ++ /* Request an IRQ for the last block */ ++ host->hcfg |= SDHCFG_BLOCK_IRPT_EN; ++ bcm2835_sdhost_write(host, host->hcfg, SDHCFG); ++ if ((bcm2835_sdhost_read(host, SDEDM) & 0xf) == 1) { ++ /* The write has now completed. Disable the interrupt ++ and clear the status flag */ ++ host->hcfg &= ~SDHCFG_BLOCK_IRPT_EN; ++ bcm2835_sdhost_write(host, host->hcfg, SDHCFG); ++ bcm2835_sdhost_write(host, SDHSTS_BLOCK_IRPT, SDHSTS); ++ write_complete = true; ++ } ++ } ++ ++ return write_complete; ++} ++ ++static void bcm2835_sdhost_wait_write_complete(struct bcm2835_host *host) ++{ ++ int timediff; ++#ifdef DEBUG ++ static struct timeval start_time; ++ static int max_stall_time = 0; ++ static int total_stall_time = 0; ++ struct timeval before, after; ++ ++ do_gettimeofday(&before); ++ if (max_stall_time == 0) ++ start_time = before; ++#endif ++ ++ timediff = 0; ++ ++ while (1) { ++ u32 edm = bcm2835_sdhost_read(host, SDEDM); ++ if ((edm & 0xf) == 1) ++ break; ++ timediff++; ++ if (timediff > 5000000) { ++#ifdef DEBUG ++ do_gettimeofday(&after); ++ timediff = (after.tv_sec - before.tv_sec)*1000000 + ++ (after.tv_usec - before.tv_usec); ++ ++ pr_err(" wait_write_complete - still waiting after %dus\n", ++ timediff); ++#else ++ pr_err(" wait_write_complete - still waiting after %d retries\n", ++ timediff); ++#endif ++ bcm2835_sdhost_dumpregs(host); ++ host->data->error = -ETIMEDOUT; ++ return; ++ } ++ } ++ ++#ifdef DEBUG ++ do_gettimeofday(&after); ++ timediff = (after.tv_sec - before.tv_sec)*1000000 + (after.tv_usec - before.tv_usec); ++ ++ total_stall_time += timediff; ++ if (timediff > max_stall_time) ++ max_stall_time = timediff; ++ ++ if ((after.tv_sec - start_time.tv_sec) > 10) { ++ pr_debug(" wait_write_complete - max wait %dus, total %dus\n", ++ max_stall_time, total_stall_time); ++ start_time = after; ++ max_stall_time = 0; ++ total_stall_time = 0; ++ } ++#endif ++} ++ ++static void bcm2835_sdhost_finish_data(struct bcm2835_host *host); ++ ++static void bcm2835_sdhost_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) { ++ bool write_complete; ++ if (USE_BLOCK_IRQ) ++ write_complete = bcm2835_sdhost_is_write_complete(host); ++ else { ++ bcm2835_sdhost_wait_write_complete(host); ++ write_complete = true; ++ } ++ pr_debug("dma_complete() - write_complete=%d\n", ++ write_complete); ++ ++ if (write_complete || (host->data->flags & MMC_DATA_READ)) ++ { ++ if (write_complete) { ++ dma_chan = host->dma_chan_tx; ++ dir_data = DMA_TO_DEVICE; ++ } else { ++ 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_sdhost_finish_data(host); ++ } ++ } ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++static bool data_transfer_wait(struct bcm2835_host *host) ++{ ++ unsigned long timeout = 1000000; ++ while (timeout) ++ { ++ u32 sdhsts = bcm2835_sdhost_read(host, SDHSTS); ++ if (sdhsts & SDHSTS_DATA_FLAG) { ++ bcm2835_sdhost_write(host, SDHSTS_DATA_FLAG, SDHSTS); ++ break; ++ } ++ timeout--; ++ } ++ if (timeout == 0) { ++ pr_err("%s: Data %s timeout\n", ++ mmc_hostname(host->mmc), ++ (host->data->flags & MMC_DATA_READ) ? "read" : "write"); ++ bcm2835_sdhost_dumpregs(host); ++ host->data->error = -ETIMEDOUT; ++ return false; ++ } ++ return true; ++} ++ ++static void bcm2835_sdhost_read_block_pio(struct bcm2835_host *host) ++{ ++ unsigned long flags; ++ size_t blksize, len; ++ u32 *buf; ++ ++ blksize = host->data->blksz; ++ ++ local_irq_save(flags); ++ ++ while (blksize) { ++ if (!sg_miter_next(&host->sg_miter)) ++ BUG(); ++ ++ len = min(host->sg_miter.length, blksize); ++ BUG_ON(len % 4); ++ ++ blksize -= len; ++ host->sg_miter.consumed = len; ++ ++ buf = (u32 *)host->sg_miter.addr; ++ ++ while (len) { ++ if (!data_transfer_wait(host)) ++ break; ++ ++ *(buf++) = bcm2835_sdhost_read(host, SDDATA); ++ len -= 4; ++ } ++ ++ if (host->data->error) ++ break; ++ } ++ ++ sg_miter_stop(&host->sg_miter); ++ ++ local_irq_restore(flags); ++} ++ ++static void bcm2835_sdhost_write_block_pio(struct bcm2835_host *host) ++{ ++ unsigned long flags; ++ size_t blksize, len; ++ u32 *buf; ++ ++ blksize = host->data->blksz; ++ ++ local_irq_save(flags); ++ ++ while (blksize) { ++ if (!sg_miter_next(&host->sg_miter)) ++ BUG(); ++ ++ len = min(host->sg_miter.length, blksize); ++ BUG_ON(len % 4); ++ ++ blksize -= len; ++ host->sg_miter.consumed = len; ++ ++ buf = host->sg_miter.addr; ++ ++ while (len) { ++ if (!data_transfer_wait(host)) ++ break; ++ ++ bcm2835_sdhost_write(host, *(buf++), SDDATA); ++ len -= 4; ++ } ++ ++ if (host->data->error) ++ break; ++ } ++ ++ sg_miter_stop(&host->sg_miter); ++ ++ local_irq_restore(flags); ++} ++ ++ ++static void bcm2835_sdhost_transfer_pio(struct bcm2835_host *host) ++{ ++ u32 sdhsts; ++ bool is_read; ++ BUG_ON(!host->data); ++ ++ is_read = (host->data->flags & MMC_DATA_READ) != 0; ++ if (is_read) ++ bcm2835_sdhost_read_block_pio(host); ++ else ++ bcm2835_sdhost_write_block_pio(host); ++ ++ sdhsts = bcm2835_sdhost_read(host, SDHSTS); ++ if (sdhsts & (SDHSTS_CRC16_ERROR | ++ SDHSTS_CRC7_ERROR | ++ SDHSTS_FIFO_ERROR)) { ++ pr_err("%s: %s transfer error - HSTS %x\n", ++ mmc_hostname(host->mmc), ++ is_read ? "read" : "write", ++ sdhsts); ++ host->data->error = -EILSEQ; ++ } else if ((sdhsts & (SDHSTS_CMD_TIME_OUT | ++ SDHSTS_REW_TIME_OUT))) { ++ pr_err("%s: %s timeout error - HSTS %x\n", ++ mmc_hostname(host->mmc), ++ is_read ? "read" : "write", ++ sdhsts); ++ host->data->error = -ETIMEDOUT; ++ } else if (!is_read && !host->data->error) { ++ /* Start a timer in case a transfer error occurs because ++ there is no error interrupt */ ++ mod_timer(&host->pio_timer, jiffies + host->pio_timeout); ++ } ++} ++ ++ ++static void bcm2835_sdhost_transfer_dma(struct bcm2835_host *host) ++{ ++ u32 len, dir_data, dir_slave; ++ struct dma_async_tx_descriptor *desc = NULL; ++ struct dma_chan *dma_chan; ++ ++ pr_debug("bcm2835_sdhost_transfer_dma()\n"); ++ ++ WARN_ON(!host->data); ++ ++ if (!host->data) ++ 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) { ++ desc->callback = bcm2835_sdhost_dma_complete; ++ desc->callback_param = host; ++ dmaengine_submit(desc); ++ dma_async_issue_pending(dma_chan); ++ } ++ ++} ++ ++ ++static void bcm2835_sdhost_set_transfer_irqs(struct bcm2835_host *host) ++{ ++ u32 all_irqs = SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN | ++ SDHCFG_BUSY_IRPT_EN; ++ if (host->use_dma) ++ host->hcfg = (host->hcfg & ~all_irqs) | ++ SDHCFG_BUSY_IRPT_EN; ++ else ++ host->hcfg = (host->hcfg & ~all_irqs) | ++ SDHCFG_DATA_IRPT_EN | ++ SDHCFG_BUSY_IRPT_EN; ++ ++ bcm2835_sdhost_write(host, host->hcfg, SDHCFG); ++} ++ ++ ++static void bcm2835_sdhost_prepare_data(struct bcm2835_host *host, struct mmc_command *cmd) ++{ ++ struct mmc_data *data = cmd->data; ++ ++ WARN_ON(host->data); ++ ++ 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_complete = 0; ++ host->flush_fifo = 0; ++ host->data->bytes_xfered = 0; ++ ++ host->use_dma = host->have_dma && (data->blocks > host->pio_limit); ++ if (!host->use_dma) { ++ int flags; ++ ++ flags = SG_MITER_ATOMIC; ++ if (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; ++ } ++ ++ bcm2835_sdhost_set_transfer_irqs(host); ++ ++ bcm2835_sdhost_write(host, data->blksz, SDHBCT); ++ bcm2835_sdhost_write(host, host->use_dma ? data->blocks : 0, SDHBLC); ++ ++ BUG_ON(!host->data); ++} ++ ++ ++void bcm2835_sdhost_send_command(struct bcm2835_host *host, struct mmc_command *cmd) ++{ ++ u32 sdcmd, sdhsts; ++ unsigned long timeout; ++ int delay; ++ ++ WARN_ON(host->cmd); ++ ++ if (cmd->data) ++ pr_debug("%s: send_command %d 0x%x " ++ "(flags 0x%x) - %s %d*%d\n", ++ mmc_hostname(host->mmc), ++ cmd->opcode, cmd->arg, cmd->flags, ++ (cmd->data->flags & MMC_DATA_READ) ? ++ "read" : "write", cmd->data->blocks, ++ cmd->data->blksz); ++ else ++ pr_debug("%s: send_command %d 0x%x (flags 0x%x)\n", ++ mmc_hostname(host->mmc), ++ cmd->opcode, cmd->arg, cmd->flags); ++ ++ /* Wait max 100 ms */ ++ timeout = 10000; ++ ++ while (bcm2835_sdhost_read(host, SDCMD) & SDCMD_NEW_FLAG) { ++ if (timeout == 0) { ++ pr_err("%s: previous command never completed.\n", ++ mmc_hostname(host->mmc)); ++ bcm2835_sdhost_dumpregs(host); ++ cmd->error = -EIO; ++ tasklet_schedule(&host->finish_tasklet); ++ return; ++ } ++ timeout--; ++ udelay(10); ++ } ++ ++ delay = (10000 - timeout)/100; ++ if (delay > host->max_delay) { ++ host->max_delay = delay; ++ pr_warning("%s: controller hung for %d ms\n", ++ mmc_hostname(host->mmc), ++ host->max_delay); ++ } ++ ++ timeout = jiffies; ++ if (!cmd->data && cmd->busy_timeout > 9000) ++ timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ; ++ else ++ timeout += 10 * HZ; ++ mod_timer(&host->timer, timeout); ++ ++ host->cmd = cmd; ++ ++ /* Clear any error flags */ ++ sdhsts = bcm2835_sdhost_read(host, SDHSTS); ++ if (sdhsts & SDHSTS_ERROR_MASK) ++ bcm2835_sdhost_write(host, sdhsts, SDHSTS); ++ ++ bcm2835_sdhost_prepare_data(host, cmd); ++ ++ bcm2835_sdhost_write(host, cmd->arg, SDARG); ++ ++ 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; ++ } ++ ++ sdcmd = cmd->opcode & SDCMD_CMD_MASK; ++ ++ if (!(cmd->flags & MMC_RSP_PRESENT)) ++ sdcmd |= SDCMD_NO_RESPONSE; ++ else { ++ if (cmd->flags & MMC_RSP_136) ++ sdcmd |= SDCMD_LONG_RESPONSE; ++ if (cmd->flags & MMC_RSP_BUSY) { ++ sdcmd |= SDCMD_BUSYWAIT; ++ host->use_busy = 1; ++ } ++ } ++ ++ if (cmd->data) { ++ if (host->delay_after_stop) { ++ struct timeval now; ++ int time_since_stop; ++ do_gettimeofday(&now); ++ time_since_stop = (now.tv_sec - host->stop_time.tv_sec); ++ if (time_since_stop < 2) { ++ /* Possibly less than one second */ ++ time_since_stop = time_since_stop * 1000000 + ++ (now.tv_usec - host->stop_time.tv_usec); ++ if (time_since_stop < host->delay_after_stop) ++ udelay(host->delay_after_stop - ++ time_since_stop); ++ } ++ } ++ ++ if (cmd->data->flags & MMC_DATA_WRITE) ++ sdcmd |= SDCMD_WRITE_CMD; ++ if (cmd->data->flags & MMC_DATA_READ) ++ sdcmd |= SDCMD_READ_CMD; ++ } ++ ++ bcm2835_sdhost_write(host, sdcmd | SDCMD_NEW_FLAG, SDCMD); ++} ++ ++ ++static void bcm2835_sdhost_finish_command(struct bcm2835_host *host); ++static void bcm2835_sdhost_transfer_complete(struct bcm2835_host *host); ++ ++static void bcm2835_sdhost_finish_data(struct bcm2835_host *host) ++{ ++ struct mmc_data *data; ++ ++ data = host->data; ++ BUG_ON(!data); ++ ++ pr_debug("finish_data(error %d, stop %d, sbc %d)\n", ++ data->error, data->stop ? 1 : 0, ++ host->mrq->sbc ? 1 : 0); ++ ++ host->hcfg &= ~(SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN); ++ bcm2835_sdhost_write(host, host->hcfg, SDHCFG); ++ ++ if (data->error) { ++ data->bytes_xfered = 0; ++ } else ++ data->bytes_xfered = data->blksz * data->blocks; ++ ++ host->data_complete = 1; ++ ++ if (host->cmd) { ++ /* ++ * Data managed to finish before the ++ * command completed. Make sure we do ++ * things in the proper order. ++ */ ++ pr_debug("Finished early - HSTS %x\n", ++ bcm2835_sdhost_read(host, SDHSTS)); ++ } ++ else ++ bcm2835_sdhost_transfer_complete(host); ++} ++ ++ ++static void bcm2835_sdhost_transfer_complete(struct bcm2835_host *host) ++{ ++ struct mmc_data *data; ++ ++ BUG_ON(host->cmd); ++ BUG_ON(!host->data); ++ BUG_ON(!host->data_complete); ++ ++ data = host->data; ++ host->data = NULL; ++ ++ pr_debug("transfer_complete(error %d, stop %d)\n", ++ data->error, data->stop ? 1 : 0); ++ ++ /* ++ * 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)) { ++ host->flush_fifo = 1; ++ bcm2835_sdhost_send_command(host, data->stop); ++ if (host->delay_after_stop) ++ do_gettimeofday(&host->stop_time); ++ if (!host->use_busy) ++ bcm2835_sdhost_finish_command(host); ++ } else { ++ tasklet_schedule(&host->finish_tasklet); ++ } ++} ++ ++static void bcm2835_sdhost_finish_command(struct bcm2835_host *host) ++{ ++ u32 sdcmd; ++ unsigned long timeout; ++#ifdef DEBUG ++ struct timeval before, after; ++ int timediff = 0; ++#endif ++ ++ pr_debug("finish_command(%x)\n", bcm2835_sdhost_read(host, SDCMD)); ++ ++ BUG_ON(!host->cmd || !host->mrq); ++ ++#ifdef DEBUG ++ do_gettimeofday(&before); ++#endif ++ /* Wait max 100 ms */ ++ timeout = 10000; ++ for (sdcmd = bcm2835_sdhost_read(host, SDCMD); ++ (sdcmd & SDCMD_NEW_FLAG) && timeout; ++ timeout--) { ++ if (host->flush_fifo) { ++ while (bcm2835_sdhost_read(host, SDHSTS) & ++ SDHSTS_DATA_FLAG) ++ (void)bcm2835_sdhost_read(host, SDDATA); ++ } ++ udelay(10); ++ sdcmd = bcm2835_sdhost_read(host, SDCMD); ++ } ++#ifdef DEBUG ++ do_gettimeofday(&after); ++ timediff = (after.tv_sec - before.tv_sec)*1000000 + ++ (after.tv_usec - before.tv_usec); ++ ++ pr_debug(" finish_command - waited %dus\n", timediff); ++#endif ++ ++ if (timeout == 0) { ++ pr_err("%s: command never completed.\n", ++ mmc_hostname(host->mmc)); ++ bcm2835_sdhost_dumpregs(host); ++ host->cmd->error = -EIO; ++ tasklet_schedule(&host->finish_tasklet); ++ return; ++ } ++ ++ if (host->flush_fifo) { ++ for (timeout = 100; ++ (bcm2835_sdhost_read(host, SDHSTS) & SDHSTS_DATA_FLAG) && timeout; ++ timeout--) { ++ (void)bcm2835_sdhost_read(host, SDDATA); ++ } ++ host->flush_fifo = 0; ++ if (timeout == 0) { ++ pr_err("%s: FIFO never drained.\n", ++ mmc_hostname(host->mmc)); ++ bcm2835_sdhost_dumpregs(host); ++ host->cmd->error = -EIO; ++ tasklet_schedule(&host->finish_tasklet); ++ return; ++ } ++ } ++ ++ /* Check for errors */ ++ if (sdcmd & SDCMD_FAIL_FLAG) ++ { ++ u32 sdhsts = bcm2835_sdhost_read(host, SDHSTS); ++ ++ if (host->debug) ++ pr_info("%s: error detected - CMD %x, HSTS %03x, EDM %x\n", ++ mmc_hostname(host->mmc), sdcmd, sdhsts, ++ bcm2835_sdhost_read(host, SDEDM)); ++ ++ if ((sdhsts & SDHSTS_CRC7_ERROR) && ++ (host->cmd->opcode == 1)) { ++ if (host->debug) ++ pr_info("%s: ignoring CRC7 error for CMD1\n", ++ mmc_hostname(host->mmc)); ++ } else { ++ if (sdhsts & SDHSTS_CMD_TIME_OUT) { ++ if (host->debug) ++ pr_err("%s: command %d timeout\n", ++ mmc_hostname(host->mmc), ++ host->cmd->opcode); ++ host->cmd->error = -ETIMEDOUT; ++ } else { ++ pr_err("%s: unexpected command %d error\n", ++ mmc_hostname(host->mmc), ++ host->cmd->opcode); ++ bcm2835_sdhost_dumpregs(host); ++ host->cmd->error = -EIO; ++ } ++ tasklet_schedule(&host->finish_tasklet); ++ return; ++ } ++ } ++ ++ if (host->cmd->flags & MMC_RSP_PRESENT) { ++ if (host->cmd->flags & MMC_RSP_136) { ++ int i; ++ for (i = 0; i < 4; i++) ++ host->cmd->resp[3 - i] = bcm2835_sdhost_read(host, SDRSP0 + i*4); ++ pr_debug("%s: finish_command %08x %08x %08x %08x\n", ++ mmc_hostname(host->mmc), ++ host->cmd->resp[0], host->cmd->resp[1], host->cmd->resp[2], host->cmd->resp[3]); ++ } else { ++ host->cmd->resp[0] = bcm2835_sdhost_read(host, SDRSP0); ++ pr_debug("%s: finish_command %08x\n", ++ mmc_hostname(host->mmc), ++ host->cmd->resp[0]); ++ } ++ } ++ ++ host->cmd->error = 0; ++ ++ if (host->cmd == host->mrq->sbc) { ++ /* Finished CMD23, now send actual command. */ ++ host->cmd = NULL; ++ bcm2835_sdhost_send_command(host, host->mrq->cmd); ++ ++ if (host->cmd->data && host->use_dma) ++ /* DMA transfer starts now, PIO starts after irq */ ++ bcm2835_sdhost_transfer_dma(host); ++ ++ if (!host->use_busy) ++ bcm2835_sdhost_finish_command(host); ++ } else if (host->cmd == host->mrq->stop) ++ /* Finished CMD12 */ ++ tasklet_schedule(&host->finish_tasklet); ++ else { ++ /* Processed actual command. */ ++ host->cmd = NULL; ++ if (!host->data) ++ tasklet_schedule(&host->finish_tasklet); ++ else if (host->data_complete) ++ bcm2835_sdhost_transfer_complete(host); ++ } ++} ++ ++static void bcm2835_sdhost_timeout(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_sdhost_dumpregs(host); ++ ++ if (host->data) { ++ host->data->error = -ETIMEDOUT; ++ bcm2835_sdhost_finish_data(host); ++ } else { ++ if (host->cmd) ++ host->cmd->error = -ETIMEDOUT; ++ else ++ host->mrq->cmd->error = -ETIMEDOUT; ++ ++ pr_debug("timeout_timer tasklet_schedule\n"); ++ tasklet_schedule(&host->finish_tasklet); ++ } ++ } ++ ++ mmiowb(); ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++static void bcm2835_sdhost_pio_timeout(unsigned long data) ++{ ++ struct bcm2835_host *host; ++ unsigned long flags; ++ ++ host = (struct bcm2835_host *)data; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ if (host->data) { ++ u32 sdhsts = bcm2835_sdhost_read(host, SDHSTS); ++ ++ if (sdhsts & SDHSTS_REW_TIME_OUT) { ++ pr_err("%s: transfer timeout\n", ++ mmc_hostname(host->mmc)); ++ if (host->debug) ++ bcm2835_sdhost_dumpregs(host); ++ } else { ++ pr_err("%s: unexpected transfer timeout\n", ++ mmc_hostname(host->mmc)); ++ bcm2835_sdhost_dumpregs(host); ++ } ++ ++ bcm2835_sdhost_write(host, SDHSTS_TRANSFER_ERROR_MASK, ++ SDHSTS); ++ ++ host->data->error = -ETIMEDOUT; ++ ++ bcm2835_sdhost_finish_data(host); ++ } ++ ++ mmiowb(); ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++static void bcm2835_sdhost_enable_sdio_irq_nolock(struct bcm2835_host *host, int enable) ++{ ++ if (enable) ++ host->hcfg |= SDHCFG_SDIO_IRPT_EN; ++ else ++ host->hcfg &= ~SDHCFG_SDIO_IRPT_EN; ++ bcm2835_sdhost_write(host, host->hcfg, SDHCFG); ++ mmiowb(); ++} ++ ++static void bcm2835_sdhost_enable_sdio_irq(struct mmc_host *mmc, int enable) ++{ ++ struct bcm2835_host *host = mmc_priv(mmc); ++ unsigned long flags; ++ ++ pr_debug("%s: enable_sdio_irq(%d)\n", mmc_hostname(mmc), enable); ++ spin_lock_irqsave(&host->lock, flags); ++ bcm2835_sdhost_enable_sdio_irq_nolock(host, enable); ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++static u32 bcm2835_sdhost_busy_irq(struct bcm2835_host *host, u32 intmask) ++{ ++ const u32 handled = (SDHSTS_REW_TIME_OUT | SDHSTS_CMD_TIME_OUT | ++ SDHSTS_CRC16_ERROR | SDHSTS_CRC7_ERROR | ++ SDHSTS_FIFO_ERROR); ++ ++ if (!host->cmd) { ++ pr_err("%s: got command busy interrupt 0x%08x even " ++ "though no command operation was in progress.\n", ++ mmc_hostname(host->mmc), (unsigned)intmask); ++ bcm2835_sdhost_dumpregs(host); ++ return 0; ++ } ++ ++ if (!host->use_busy) { ++ pr_err("%s: got command busy interrupt 0x%08x even " ++ "though not expecting one.\n", ++ mmc_hostname(host->mmc), (unsigned)intmask); ++ bcm2835_sdhost_dumpregs(host); ++ return 0; ++ } ++ host->use_busy = 0; ++ ++ if (intmask & SDHSTS_ERROR_MASK) ++ { ++ pr_err("sdhost_busy_irq: intmask %x, data %p\n", intmask, host->mrq->data); ++ if (intmask & SDHSTS_CRC7_ERROR) ++ host->cmd->error = -EILSEQ; ++ else if (intmask & (SDHSTS_CRC16_ERROR | ++ SDHSTS_FIFO_ERROR)) { ++ if (host->mrq->data) ++ host->mrq->data->error = -EILSEQ; ++ else ++ host->cmd->error = -EILSEQ; ++ } else if (intmask & SDHSTS_REW_TIME_OUT) { ++ if (host->mrq->data) ++ host->mrq->data->error = -ETIMEDOUT; ++ else ++ host->cmd->error = -ETIMEDOUT; ++ } else if (intmask & SDHSTS_CMD_TIME_OUT) ++ host->cmd->error = -ETIMEDOUT; ++ ++ bcm2835_sdhost_dumpregs(host); ++ tasklet_schedule(&host->finish_tasklet); ++ } ++ else ++ bcm2835_sdhost_finish_command(host); ++ ++ return handled; ++} ++ ++static u32 bcm2835_sdhost_data_irq(struct bcm2835_host *host, u32 intmask) ++{ ++ const u32 handled = (SDHSTS_REW_TIME_OUT | ++ SDHSTS_CRC16_ERROR | ++ SDHSTS_FIFO_ERROR); ++ ++ /* There are no dedicated data/space available interrupt ++ status bits, so it is necessary to use the single shared ++ data/space available FIFO status bits. It is therefore not ++ an error to get here when there is no data transfer in ++ progress. */ ++ if (!host->data) ++ return 0; ++ ++ if (intmask & (SDHSTS_CRC16_ERROR | ++ SDHSTS_FIFO_ERROR | ++ SDHSTS_REW_TIME_OUT)) { ++ if (intmask & (SDHSTS_CRC16_ERROR | ++ SDHSTS_FIFO_ERROR)) ++ host->data->error = -EILSEQ; ++ else ++ host->data->error = -ETIMEDOUT; ++ ++ bcm2835_sdhost_dumpregs(host); ++ tasklet_schedule(&host->finish_tasklet); ++ return handled; ++ } ++ ++ /* Use the block interrupt for writes after the first block */ ++ if (host->data->flags & MMC_DATA_WRITE) { ++ host->hcfg &= ~(SDHCFG_DATA_IRPT_EN); ++ host->hcfg |= SDHCFG_BLOCK_IRPT_EN; ++ bcm2835_sdhost_write(host, host->hcfg, SDHCFG); ++ if (host->data->error) ++ bcm2835_sdhost_finish_data(host); ++ else ++ bcm2835_sdhost_transfer_pio(host); ++ } else { ++ if (!host->data->error) { ++ bcm2835_sdhost_transfer_pio(host); ++ host->blocks--; ++ } ++ if ((host->blocks == 0) || host->data->error) ++ bcm2835_sdhost_finish_data(host); ++ } ++ ++ return handled; ++} ++ ++static u32 bcm2835_sdhost_block_irq(struct bcm2835_host *host, u32 intmask) ++{ ++ struct dma_chan *dma_chan; ++ u32 dir_data; ++ const u32 handled = (SDHSTS_REW_TIME_OUT | ++ SDHSTS_CRC16_ERROR | ++ SDHSTS_FIFO_ERROR); ++ ++ if (!host->data) { ++ pr_err("%s: got block interrupt 0x%08x even " ++ "though no data operation was in progress.\n", ++ mmc_hostname(host->mmc), (unsigned)intmask); ++ bcm2835_sdhost_dumpregs(host); ++ return handled; ++ } ++ ++ if (intmask & (SDHSTS_CRC16_ERROR | ++ SDHSTS_FIFO_ERROR | ++ SDHSTS_REW_TIME_OUT)) { ++ if (intmask & (SDHSTS_CRC16_ERROR | ++ SDHSTS_FIFO_ERROR)) ++ host->data->error = -EILSEQ; ++ else ++ host->data->error = -ETIMEDOUT; ++ ++ if (host->debug) ++ bcm2835_sdhost_dumpregs(host); ++ tasklet_schedule(&host->finish_tasklet); ++ return handled; ++ } ++ ++ if (!host->use_dma) { ++ BUG_ON(!host->blocks); ++ host->blocks--; ++ if ((host->blocks == 0) || host->data->error) { ++ /* Cancel the timer */ ++ del_timer(&host->pio_timer); ++ ++ bcm2835_sdhost_finish_data(host); ++ } else { ++ bcm2835_sdhost_transfer_pio(host); ++ ++ /* Reset the timer */ ++ mod_timer(&host->pio_timer, ++ jiffies + host->pio_timeout); ++ } ++ } else if (host->data->flags & MMC_DATA_WRITE) { ++ 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_sdhost_finish_data(host); ++ } ++ ++ return handled; ++} ++ ++ ++static irqreturn_t bcm2835_sdhost_irq(int irq, void *dev_id) ++{ ++ irqreturn_t result = IRQ_NONE; ++ struct bcm2835_host *host = dev_id; ++ u32 unexpected = 0, early = 0; ++ int loops = 0; ++ ++ spin_lock(&host->lock); ++ ++ for (loops = 0; loops < 1; loops++) { ++ u32 intmask, handled; ++ ++ intmask = bcm2835_sdhost_read(host, SDHSTS); ++ handled = intmask & (SDHSTS_BUSY_IRPT | ++ SDHSTS_BLOCK_IRPT | ++ SDHSTS_SDIO_IRPT | ++ SDHSTS_DATA_FLAG); ++ if ((handled == SDHSTS_DATA_FLAG) && ++ (loops == 0) && !host->data) { ++ pr_err("%s: sdhost_irq data interrupt 0x%08x even " ++ "though no data operation was in progress.\n", ++ mmc_hostname(host->mmc), ++ (unsigned)intmask); ++ ++ bcm2835_sdhost_dumpregs(host); ++ } ++ ++ if (!handled) ++ break; ++ ++ if (loops) ++ early |= handled; ++ ++ result = IRQ_HANDLED; ++ ++ /* Clear all interrupts and notifications */ ++ bcm2835_sdhost_write(host, intmask, SDHSTS); ++ ++ if (intmask & SDHSTS_BUSY_IRPT) ++ handled |= bcm2835_sdhost_busy_irq(host, intmask); ++ ++ /* There is no true data interrupt status bit, so it is ++ necessary to qualify the data flag with the interrupt ++ enable bit */ ++ if ((intmask & SDHSTS_DATA_FLAG) && ++ (host->hcfg & SDHCFG_DATA_IRPT_EN)) ++ handled |= bcm2835_sdhost_data_irq(host, intmask); ++ ++ if (intmask & SDHSTS_BLOCK_IRPT) ++ handled |= bcm2835_sdhost_block_irq(host, intmask); ++ ++ if (intmask & SDHSTS_SDIO_IRPT) { ++ bcm2835_sdhost_enable_sdio_irq_nolock(host, false); ++ host->thread_isr |= SDHSTS_SDIO_IRPT; ++ result = IRQ_WAKE_THREAD; ++ } ++ ++ unexpected |= (intmask & ~handled); ++ } ++ ++ mmiowb(); ++ ++ spin_unlock(&host->lock); ++ ++ if (early) ++ pr_debug("%s: early %x (loops %d)\n", ++ mmc_hostname(host->mmc), early, loops); ++ ++ if (unexpected) { ++ pr_err("%s: unexpected interrupt 0x%08x.\n", ++ mmc_hostname(host->mmc), unexpected); ++ bcm2835_sdhost_dumpregs(host); ++ } ++ ++ return result; ++} ++ ++static irqreturn_t bcm2835_sdhost_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 & SDHSTS_SDIO_IRPT) { ++ sdio_run_irqs(host->mmc); ++ ++/* Is this necessary? Why re-enable an interrupt which is enabled? ++ spin_lock_irqsave(&host->lock, flags); ++ if (host->flags & SDHSTS_SDIO_IRPT_ENABLED) ++ bcm2835_sdhost_enable_sdio_irq_nolock(host, true); ++ spin_unlock_irqrestore(&host->lock, flags); ++*/ ++ } ++ ++ return isr ? IRQ_HANDLED : IRQ_NONE; ++} ++ ++ ++ ++void bcm2835_sdhost_set_clock(struct bcm2835_host *host, unsigned int clock) ++{ ++ int div = 0; /* Initialized for compiler warning */ ++ unsigned int input_clock = clock; ++ ++ if (host->debug) ++ pr_info("%s: set_clock(%d)\n", mmc_hostname(host->mmc), clock); ++ ++ if ((host->overclock_50 > 50) && ++ (clock == 50*MHZ)) { ++ clock = host->overclock_50 * MHZ + (MHZ - 1); ++ } ++ ++ /* The SDCDIV register has 11 bits, and holds (div - 2). ++ But in data mode the max is 50MHz wihout a minimum, and only the ++ bottom 3 bits are used. Since the switch over is automatic (unless ++ we have marked the card as slow...), chosen values have to make ++ sense in both modes. ++ Ident mode must be 100-400KHz, so can range check the requested ++ clock. CMD15 must be used to return to data mode, so this can be ++ monitored. ++ ++ clock 250MHz -> 0->125MHz, 1->83.3MHz, 2->62.5MHz, 3->50.0MHz ++ 4->41.7MHz, 5->35.7MHz, 6->31.3MHz, 7->27.8MHz ++ ++ 623->400KHz/27.8MHz ++ reset value (507)->491159/50MHz ++ ++ BUT, the 3-bit clock divisor in data mode is too small if the ++ core clock is higher than 250MHz, so instead use the SLOW_CARD ++ configuration bit to force the use of the ident clock divisor ++ at all times. ++ */ ++ ++ host->mmc->actual_clock = 0; ++ ++ if (clock < 100000) { ++ /* Can't stop the clock, but make it as slow as possible ++ * to show willing ++ */ ++ host->cdiv = SDCDIV_MAX_CDIV; ++ bcm2835_sdhost_write(host, host->cdiv, SDCDIV); ++ return; ++ } ++ ++ div = host->max_clk / clock; ++ if (div < 2) ++ div = 2; ++ if ((host->max_clk / div) > clock) ++ div++; ++ div -= 2; ++ ++ if (div > SDCDIV_MAX_CDIV) ++ div = SDCDIV_MAX_CDIV; ++ ++ clock = host->max_clk / (div + 2); ++ host->mmc->actual_clock = clock; ++ ++ if (clock > input_clock) { ++ /* Save the closest value, to make it easier ++ to reduce in the event of error */ ++ host->overclock_50 = (clock/MHZ); ++ ++ if (clock != host->overclock) { ++ pr_warn("%s: overclocking to %dHz\n", ++ mmc_hostname(host->mmc), clock); ++ host->overclock = clock; ++ } ++ } ++ else if (host->overclock) ++ { ++ host->overclock = 0; ++ if (clock == 50 * MHZ) ++ pr_warn("%s: cancelling overclock\n", ++ mmc_hostname(host->mmc)); ++ } ++ ++ host->cdiv = div; ++ bcm2835_sdhost_write(host, host->cdiv, SDCDIV); ++ ++ /* Set the timeout to 500ms */ ++ bcm2835_sdhost_write(host, host->mmc->actual_clock/2, SDTOUT); ++ ++ if (host->debug) ++ pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x (actual clock %d)\n", ++ mmc_hostname(host->mmc), input_clock, ++ host->max_clk, host->cdiv, host->mmc->actual_clock); ++} ++ ++static void bcm2835_sdhost_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct bcm2835_host *host; ++ unsigned long flags; ++ ++ host = mmc_priv(mmc); ++ ++ if (host->debug) { ++ struct mmc_command *cmd = mrq->cmd; ++ BUG_ON(!cmd); ++ if (cmd->data) ++ pr_info("%s: cmd %d 0x%x (flags 0x%x) - %s %d*%d\n", ++ mmc_hostname(mmc), ++ cmd->opcode, cmd->arg, cmd->flags, ++ (cmd->data->flags & MMC_DATA_READ) ? ++ "read" : "write", cmd->data->blocks, ++ cmd->data->blksz); ++ else ++ pr_info("%s: cmd %d 0x%x (flags 0x%x)\n", ++ mmc_hostname(mmc), ++ cmd->opcode, cmd->arg, cmd->flags); ++ } ++ ++ /* Reset the error statuses in case this is a retry */ ++ if (mrq->cmd) ++ mrq->cmd->error = 0; ++ if (mrq->data) ++ mrq->data->error = 0; ++ if (mrq->stop) ++ mrq->stop->error = 0; ++ ++ if (mrq->data && !is_power_of_2(mrq->data->blksz)) { ++ pr_err("%s: unsupported block size (%d bytes)\n", ++ mmc_hostname(mmc), mrq->data->blksz); ++ mrq->cmd->error = -EINVAL; ++ mmc_request_done(mmc, mrq); ++ return; ++ } ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ WARN_ON(host->mrq != NULL); ++ ++ host->mrq = mrq; ++ ++ if (mrq->sbc) ++ bcm2835_sdhost_send_command(host, mrq->sbc); ++ else ++ bcm2835_sdhost_send_command(host, mrq->cmd); ++ ++ mmiowb(); ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ if (!mrq->sbc && mrq->cmd->data && host->use_dma) ++ /* DMA transfer starts now, PIO starts after irq */ ++ bcm2835_sdhost_transfer_dma(host); ++ ++ if (!host->use_busy) ++ bcm2835_sdhost_finish_command(host); ++} ++ ++ ++static void bcm2835_sdhost_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ ++ struct bcm2835_host *host = mmc_priv(mmc); ++ unsigned long flags; ++ ++ if (host->debug) ++ pr_info("%s: ios clock %d, pwr %d, bus_width %d, " ++ "timing %d, vdd %d, drv_type %d\n", ++ mmc_hostname(mmc), ++ ios->clock, ios->power_mode, ios->bus_width, ++ ios->timing, ios->signal_voltage, ios->drv_type); ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ if (!ios->clock || ios->clock != host->clock) { ++ bcm2835_sdhost_set_clock(host, ios->clock); ++ host->clock = ios->clock; ++ } ++ ++ /* set bus width */ ++ host->hcfg &= ~SDHCFG_WIDE_EXT_BUS; ++ if (ios->bus_width == MMC_BUS_WIDTH_4) ++ host->hcfg |= SDHCFG_WIDE_EXT_BUS; ++ ++ host->hcfg |= SDHCFG_WIDE_INT_BUS; ++ ++ /* Disable clever clock switching, to cope with fast core clocks */ ++ host->hcfg |= SDHCFG_SLOW_CARD; ++ ++ bcm2835_sdhost_write(host, host->hcfg, SDHCFG); ++ ++ mmiowb(); ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++static int bcm2835_sdhost_multi_io_quirk(struct mmc_card *card, ++ unsigned int direction, ++ u32 blk_pos, int blk_size) ++{ ++ /* There is a bug in the host controller hardware that makes ++ reading the final sector of the card as part of a multiple read ++ problematic. Detect that case and shorten the read accordingly. ++ */ ++ /* csd.capacity is in weird units - convert to sectors */ ++ u32 card_sectors = (card->csd.capacity << (card->csd.read_blkbits - 9)); ++ ++ if ((direction == MMC_DATA_READ) && ++ ((blk_pos + blk_size) == card_sectors)) ++ blk_size--; ++ ++ return blk_size; ++} ++ ++ ++static struct mmc_host_ops bcm2835_sdhost_ops = { ++ .request = bcm2835_sdhost_request, ++ .set_ios = bcm2835_sdhost_set_ios, ++ .enable_sdio_irq = bcm2835_sdhost_enable_sdio_irq, ++ .hw_reset = bcm2835_sdhost_reset, ++ .multi_io_quirk = bcm2835_sdhost_multi_io_quirk, ++}; ++ ++ ++static void bcm2835_sdhost_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; ++ ++ /* Drop the overclock after any data corruption, or after any ++ error overclocked */ ++ if (host->overclock) { ++ if ((mrq->cmd && mrq->cmd->error) || ++ (mrq->data && mrq->data->error) || ++ (mrq->stop && mrq->stop->error)) { ++ host->overclock_50--; ++ pr_warn("%s: reducing overclock due to errors\n", ++ mmc_hostname(host->mmc)); ++ bcm2835_sdhost_set_clock(host,50*MHZ); ++ mrq->cmd->error = -EILSEQ; ++ mrq->cmd->retries = 1; ++ } ++ } ++ ++ host->mrq = NULL; ++ host->cmd = NULL; ++ host->data = NULL; ++ ++ mmiowb(); ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++ mmc_request_done(host->mmc, mrq); ++} ++ ++ ++ ++int bcm2835_sdhost_add_host(struct bcm2835_host *host) ++{ ++ struct mmc_host *mmc; ++ struct dma_slave_config cfg; ++ char pio_limit_string[20]; ++ int ret; ++ ++ mmc = host->mmc; ++ ++ bcm2835_sdhost_reset_internal(host); ++ ++ mmc->f_max = host->max_clk; ++ mmc->f_min = host->max_clk / SDCDIV_MAX_CDIV; ++ ++ mmc->max_busy_timeout = (~(unsigned int)0)/(mmc->f_max/1000); ++ ++ pr_debug("f_max %d, f_min %d, max_busy_timeout %d\n", ++ mmc->f_max, mmc->f_min, mmc->max_busy_timeout); ++ ++ /* host controller capabilities */ ++ mmc->caps |= /* MMC_CAP_SDIO_IRQ |*/ MMC_CAP_4_BIT_DATA | ++ MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | ++ MMC_CAP_NEEDS_POLL | MMC_CAP_HW_RESET | MMC_CAP_ERASE | ++ (ALLOW_CMD23 * MMC_CAP_CMD23); ++ ++ spin_lock_init(&host->lock); ++ ++ if (host->allow_dma) { ++ if (IS_ERR_OR_NULL(host->dma_chan_tx) || ++ IS_ERR_OR_NULL(host->dma_chan_rx)) { ++ pr_err("%s: unable to initialise DMA channels. " ++ "Falling back to PIO\n", ++ mmc_hostname(mmc)); ++ host->have_dma = false; ++ } else { ++ 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 = 13; /* DREQ channel */ ++ ++ cfg.direction = DMA_MEM_TO_DEV; ++ cfg.src_addr = 0; ++ cfg.dst_addr = host->bus_addr + SDDATA; ++ ret = dmaengine_slave_config(host->dma_chan_tx, &cfg); ++ ++ cfg.direction = DMA_DEV_TO_MEM; ++ cfg.src_addr = host->bus_addr + SDDATA; ++ cfg.dst_addr = 0; ++ ret = dmaengine_slave_config(host->dma_chan_rx, &cfg); ++ } ++ } else { ++ host->have_dma = false; ++ } ++ ++ 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_sdhost_tasklet_finish, (unsigned long)host); ++ ++ setup_timer(&host->timer, bcm2835_sdhost_timeout, ++ (unsigned long)host); ++ ++ setup_timer(&host->pio_timer, bcm2835_sdhost_pio_timeout, ++ (unsigned long)host); ++ ++ bcm2835_sdhost_init(host, 0); ++ ret = request_threaded_irq(host->irq, bcm2835_sdhost_irq, ++ bcm2835_sdhost_thread_irq, ++ IRQF_SHARED, mmc_hostname(mmc), host); ++ 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); ++ ++ pio_limit_string[0] = '\0'; ++ if (host->have_dma && (host->pio_limit > 0)) ++ sprintf(pio_limit_string, " (>%d)", host->pio_limit); ++ pr_info("%s: %s loaded - DMA %s%s\n", ++ mmc_hostname(mmc), DRIVER_NAME, ++ host->have_dma ? "enabled" : "disabled", ++ pio_limit_string); ++ ++ return 0; ++ ++untasklet: ++ tasklet_kill(&host->finish_tasklet); ++ ++ return ret; ++} ++ ++static int bcm2835_sdhost_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; ++ const __be32 *addr; ++ int ret; ++ ++ pr_debug("bcm2835_sdhost_probe\n"); ++ mmc = mmc_alloc_host(sizeof(*host), dev); ++ if (!mmc) ++ return -ENOMEM; ++ ++ mmc->ops = &bcm2835_sdhost_ops; ++ host = mmc_priv(mmc); ++ host->mmc = mmc; ++ host->pio_timeout = msecs_to_jiffies(500); ++ host->max_delay = 1; /* Warn if over 1ms */ ++ 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; ++ } ++ ++ addr = of_get_address(node, 0, NULL, NULL); ++ if (!addr) { ++ dev_err(dev, "could not get DMA-register address\n"); ++ return -ENODEV; ++ } ++ host->bus_addr = be32_to_cpup(addr); ++ pr_debug(" - ioaddr %lx, iomem->start %lx, bus_addr %lx\n", ++ (unsigned long)host->ioaddr, ++ (unsigned long)iomem->start, ++ (unsigned long)host->bus_addr); ++ ++ host->allow_dma = ALLOW_DMA; ++ ++ if (node) { ++ /* Read any custom properties */ ++ of_property_read_u32(node, ++ "brcm,delay-after-stop", ++ &host->delay_after_stop); ++ of_property_read_u32(node, ++ "brcm,overclock-50", ++ &host->overclock_50); ++ of_property_read_u32(node, ++ "brcm,pio-limit", ++ &host->pio_limit); ++ host->allow_dma = ALLOW_DMA && ++ !of_property_read_bool(node, "brcm,force-pio"); ++ host->debug = of_property_read_bool(node, "brcm,debug"); ++ } ++ ++ if (host->allow_dma) { ++ if (node) { ++ host->dma_chan_tx = ++ dma_request_slave_channel(dev, "tx"); ++ host->dma_chan_rx = ++ dma_request_slave_channel(dev, "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); ++ } ++ } ++ ++ 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; ++ } ++ ++ pr_debug(" - max_clk %lx, irq %d\n", ++ (unsigned long)host->max_clk, ++ (int)host->irq); ++ ++ if (node) ++ mmc_of_parse(mmc); ++ else ++ mmc->caps |= MMC_CAP_4_BIT_DATA; ++ ++ ret = bcm2835_sdhost_add_host(host); ++ if (ret) ++ goto err; ++ ++ platform_set_drvdata(pdev, host); ++ ++ pr_debug("bcm2835_sdhost_probe -> OK\n"); ++ ++ return 0; ++ ++err: ++ pr_debug("bcm2835_sdhost_probe -> err %d\n", ret); ++ mmc_free_host(mmc); ++ ++ return ret; ++} ++ ++static int bcm2835_sdhost_remove(struct platform_device *pdev) ++{ ++ struct bcm2835_host *host = platform_get_drvdata(pdev); ++ ++ pr_debug("bcm2835_sdhost_remove\n"); ++ ++ mmc_remove_host(host->mmc); ++ ++ bcm2835_sdhost_set_power(host, false); ++ ++ 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); ++ ++ pr_debug("bcm2835_sdhost_remove - OK\n"); ++ return 0; ++} ++ ++ ++static const struct of_device_id bcm2835_sdhost_match[] = { ++ { .compatible = "brcm,bcm2835-sdhost" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, bcm2835_sdhost_match); ++ ++ ++ ++static struct platform_driver bcm2835_sdhost_driver = { ++ .probe = bcm2835_sdhost_probe, ++ .remove = bcm2835_sdhost_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = bcm2835_sdhost_match, ++ }, ++}; ++module_platform_driver(bcm2835_sdhost_driver); ++ ++MODULE_ALIAS("platform:sdhost-bcm2835"); ++MODULE_DESCRIPTION("BCM2835 SDHost driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Phil Elwell"); diff --git a/target/linux/brcm2708/patches-4.4/0033-MMC-added-alternative-MMC-driver.patch b/target/linux/brcm2708/patches-4.4/0033-MMC-added-alternative-MMC-driver.patch deleted file mode 100644 index f5996286c8..0000000000 --- a/target/linux/brcm2708/patches-4.4/0033-MMC-added-alternative-MMC-driver.patch +++ /dev/null @@ -1,1691 +0,0 @@ -From 3d0ca77d187abbbde572f4a73b1f864ffb8b5d8a Mon Sep 17 00:00:00 2001 -From: gellert -Date: Fri, 15 Aug 2014 16:35:06 +0100 -Subject: [PATCH 033/170] 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 - -bcm2835-mmc: Add locks when accessing sdhost registers - -bcm2835-mmc: Add range of debug options for slowing things down - -bcm2835-mmc: Add option to disable some delays - -bcm2835-mmc: Add option to disable MMC_QUIRK_BLK_NO_CMD23 - -bcm2835-mmc: Default to disabling MMC_QUIRK_BLK_NO_CMD23 - -bcm2835-mmc: Adding overclocking option - -Allow a different clock speed to be substitued for a requested 50MHz. -This option is exposed using the "overclock_50" DT parameter. -Note that the mmc interface is restricted to EVEN integer divisions of -250MHz, and the highest sensible option is 63 (250/4 = 62.5), the -next being 125 (250/2) which is much too high. - -Use at your own risk. - -bcm2835-mmc: Round up the overclock, so 62 works for 62.5Mhz - -Also only warn once for each overclock setting. - -mmc: bcm2835-mmc: Make available on ARCH_BCM2835 - -Make the bcm2835-mmc driver available for use on ARCH_BCM2835. - -Signed-off-by: Noralf Trønnes - -BCM270x_DT: add bcm2835-mmc entry - -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 - -bcm2835-mmc: Don't overwrite MMC capabilities from DT ---- - drivers/mmc/core/quirks.c | 6 + - drivers/mmc/host/Kconfig | 29 + - drivers/mmc/host/Makefile | 1 + - drivers/mmc/host/bcm2835-mmc.c | 1542 ++++++++++++++++++++++++++++++++++++++++ - 4 files changed, 1578 insertions(+) - create mode 100644 drivers/mmc/host/bcm2835-mmc.c - ---- a/drivers/mmc/core/quirks.c -+++ b/drivers/mmc/core/quirks.c -@@ -53,6 +53,7 @@ static const struct mmc_fixup mmc_fixup_ - - void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table) - { -+ extern unsigned mmc_debug; - const struct mmc_fixup *f; - u64 rev = cid_rev_card(card); - -@@ -77,5 +78,10 @@ void mmc_fixup_device(struct mmc_card *c - 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). -+ */ -+ if (mmc_debug & (1<<13)) -+ card->quirks |= MMC_QUIRK_BLK_NO_CMD23; - } - EXPORT_SYMBOL(mmc_fixup_device); ---- 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 || MACH_BCM2709 || ARCH_BCM2835 -+ 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 ---- a/drivers/mmc/host/Makefile -+++ b/drivers/mmc/host/Makefile -@@ -18,6 +18,7 @@ obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c - 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_MTK) += mtk-sd.o ---- /dev/null -+++ b/drivers/mmc/host/bcm2835-mmc.c -@@ -0,0 +1,1542 @@ -+/* -+ * 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) -+ -+ -+unsigned mmc_debug; -+unsigned mmc_debug2; -+ -+struct bcm2835_host { -+ spinlock_t lock; -+ -+ void __iomem *ioaddr; -+ u32 bus_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 */ -+ -+ u32 overclock_50; /* frequency to use when 50MHz is requested (in MHz) */ -+ u32 max_overclock; /* Highest reported */ -+}; -+ -+ -+static inline void bcm2835_mmc_writel(struct bcm2835_host *host, u32 val, int reg, int from) -+{ -+ unsigned delay; -+ lockdep_assert_held_once(&host->lock); -+ writel(val, host->ioaddr + reg); -+ udelay(BCM2835_SDHCI_WRITE_DELAY(max(host->clock, MIN_FREQ))); -+ -+ delay = ((mmc_debug >> 16) & 0xf) << ((mmc_debug >> 20) & 0xf); -+ if (delay && !((1<lock); -+ writel(val, host->ioaddr + reg); -+ -+ delay = ((mmc_debug >> 24) & 0xf) << ((mmc_debug >> 28) & 0xf); -+ if (delay) -+ udelay(delay); -+} -+ -+static inline u32 bcm2835_mmc_readl(struct bcm2835_host *host, int reg) -+{ -+ lockdep_assert_held_once(&host->lock); -+ 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, 0); -+ -+} -+ -+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, 1); -+} -+ -+ -+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, 2); -+} -+ -+ -+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; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&host->lock, flags); -+ 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--; -+ spin_unlock_irqrestore(&host->lock, flags); -+ mdelay(1); -+ spin_lock_irqsave(&host->lock, flags); -+ } -+ -+ 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); -+ } -+ spin_unlock_irqrestore(&host->lock, flags); -+} -+ -+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) -+{ -+ unsigned long flags; -+ 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; -+ -+ spin_lock_irqsave(&host->lock, flags); -+ bcm2835_mmc_writel(host, host->ier, SDHCI_INT_ENABLE, 3); -+ bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE, 3); -+ spin_unlock_irqrestore(&host->lock, flags); -+ -+ 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) { -+ unsigned long flags; -+ spin_lock_irqsave(&host->lock, flags); -+ 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; -+ spin_unlock_irqrestore(&host->lock, flags); -+ 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, 4); -+ bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE, 4); -+} -+ -+ -+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, 5); -+ } -+ } -+ -+ 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; -+ if (!cmd->data && cmd->busy_timeout > 9000) -+ timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ; -+ else -+ 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, 6); -+ -+ 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, 7); -+ bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE, 7); -+ 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; -+ -+ 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, 8); -+ -+ -+ 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) { -+ bcm2835_mmc_enable_sdio_irq_nolock(host, false); -+ host->thread_isr |= SDHCI_INT_CARD_INT; -+ result = IRQ_WAKE_THREAD; -+ } -+ -+ 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, 9); -+ } -+ -+ 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); -+ } -+ -+ return result; -+} -+ -+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; -+} -+ -+ -+ -+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; -+ unsigned int input_clock = clock; -+ -+ if (host->overclock_50 && (clock == 50000000)) -+ clock = host->overclock_50 * 1000000 + 999999; -+ -+ 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) -+ clock = (host->max_clk * clk_mul) / real_div; -+ host->mmc->actual_clock = clock; -+ -+ if ((clock > input_clock) && (clock > host->max_overclock)) { -+ pr_warn("%s: Overclocking to %dHz\n", -+ mmc_hostname(host->mmc), clock); -+ host->max_overclock = clock; -+ } -+ -+ 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; -+ -+ pr_debug("bcm2835_mmc_set_ios: clock %d, pwr %d, bus_width %d, timing %d, vdd %d, drv_type %d\n", -+ ios->clock, ios->power_mode, ios->bus_width, -+ ios->timing, ios->signal_voltage, ios->drv_type); -+ -+ 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))))) { -+ -+ spin_unlock_irqrestore(&host->lock, flags); -+ bcm2835_mmc_reset(host, SDHCI_RESET_CMD); -+ bcm2835_mmc_reset(host, SDHCI_RESET_DATA); -+ spin_lock_irqsave(&host->lock, flags); -+ } -+ -+ host->mrq = NULL; -+ host->cmd = NULL; -+ host->data = NULL; -+ -+ mmiowb(); -+ -+ spin_unlock_irqrestore(&host->lock, flags); -+ mmc_request_done(host->mmc, mrq); -+} -+ -+ -+ -+static 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; -+ mmc->max_busy_timeout = (1 << 27) / host->timeout_clk; -+ -+ /* 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; -+ -+ mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; -+ -+ host->flags = SDHCI_AUTO_CMD23; -+ -+ dev_info(dev, "mmc_debug:%x mmc_debug2:%x\n", mmc_debug, mmc_debug2); -+#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->bus_addr + SDHCI_BUFFER; -+ ret = dmaengine_slave_config(host->dma_chan_tx, &cfg); -+ -+ cfg.direction = DMA_DEV_TO_MEM; -+ cfg.src_addr = host->bus_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); -+ ret = devm_request_threaded_irq(dev, host->irq, bcm2835_mmc_irq, -+ bcm2835_mmc_thread_irq, IRQF_SHARED, -+ mmc_hostname(mmc), host); -+ 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; -+ const __be32 *addr; -+ 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; -+ } -+ -+ addr = of_get_address(node, 0, NULL, NULL); -+ if (!addr) { -+ dev_err(dev, "could not get DMA-register address\n"); -+ return -ENODEV; -+ } -+ host->bus_addr = be32_to_cpup(addr); -+ pr_debug(" - ioaddr %lx, iomem->start %lx, bus_addr %lx\n", -+ (unsigned long)host->ioaddr, -+ (unsigned long)iomem->start, -+ (unsigned long)host->bus_addr); -+ -+#ifndef FORCE_PIO -+ if (node) { -+ host->dma_chan_tx = dma_request_slave_channel(dev, "tx"); -+ host->dma_chan_rx = dma_request_slave_channel(dev, "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); -+ -+ /* Read any custom properties */ -+ of_property_read_u32(node, -+ "brcm,overclock-50", -+ &host->overclock_50); -+ } 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_param(mmc_debug, uint, 0644); -+module_param(mmc_debug2, uint, 0644); -+MODULE_ALIAS("platform:mmc-bcm2835"); -+MODULE_DESCRIPTION("BCM2835 SDHCI driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Gellert Weisz"); diff --git a/target/linux/brcm2708/patches-4.4/0034-Adding-bcm2835-sdhost-driver-and-an-overlay-to-enabl.patch b/target/linux/brcm2708/patches-4.4/0034-Adding-bcm2835-sdhost-driver-and-an-overlay-to-enabl.patch deleted file mode 100644 index c6631e8b4e..0000000000 --- a/target/linux/brcm2708/patches-4.4/0034-Adding-bcm2835-sdhost-driver-and-an-overlay-to-enabl.patch +++ /dev/null @@ -1,2022 +0,0 @@ -From 756bf8b8f081be45e16d4d58d3fbe2ca073df298 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Wed, 25 Mar 2015 17:49:47 +0000 -Subject: [PATCH 034/170] Adding bcm2835-sdhost driver, and an overlay to - enable it - -BCM2835 has two SD card interfaces. This driver uses the other one. - -bcm2835-sdhost: Error handling fix, and code clarification - -bcm2835-sdhost: Adding overclocking option - -Allow a different clock speed to be substitued for a requested 50MHz. -This option is exposed using the "overclock_50" DT parameter. -Note that the sdhost interface is restricted to integer divisions of -core_freq, and the highest sensible option for a core_freq of 250MHz -is 84 (250/3 = 83.3MHz), the next being 125 (250/2) which is much too -high. - -Use at your own risk. - -bcm2835-sdhost: Round up the overclock, so 62 works for 62.5Mhz - -Also only warn once for each overclock setting. - -bcm2835-sdhost: Improve error handling and recovery - -1) Expose the hw_reset method to the MMC framework, removing many - internal calls by the driver. - -2) Reduce overclock setting on error. - -3) Increase timeout to cope with high capacity cards. - -4) Add properties and parameters to control pio_limit and debug. - -5) Reduce messages at probe time. - -bcm2835-sdhost: Further improve overclock back-off - -bcm2835-sdhost: Clear HBLC for PIO mode - -Also update pio_limit default in overlay README. - -bcm2835-sdhost: Add the ERASE capability - -See: https://github.com/raspberrypi/linux/issues/1076 - -bcm2835-sdhost: Ignore CRC7 for MMC CMD1 - -It seems that the sdhost interface returns CRC7 errors for CMD1, -which is the MMC-specific SEND_OP_COND. Returning these errors to -the MMC layer causes a downward spiral, but ignoring them seems -to be harmless. - -bcm2835-mmc/sdhost: Remove ARCH_BCM2835 differences - -The bcm2835-mmc driver (and -sdhost driver that copied from it) -contains code to handle SDIO interrupts in a threaded interrupt -handler rather than waking the MMC framework thread. The change -follows a patch from Russell King that adds the facility as the -preferred way of working. - -However, the new code path is only present in ARCH_BCM2835 -builds, which I have taken to be a way of testing the waters -rather than making the change across the board; I can't see -any technical reason why it wouldn't be enabled for MACH_BCM270X -builds. So this patch standardises on the ARCH_BCM2835 code, -removing the old code paths. - -bcm2835-sdhost: Don't log timeout errors unless debug=1 - -The MMC card-discovery process generates timeouts. This is -expected behaviour, so reporting it to the user serves no purpose. -Suppress the reporting of timeout errors unless the debug flag -is on. ---- - drivers/mmc/host/Kconfig | 10 + - drivers/mmc/host/Makefile | 1 + - drivers/mmc/host/bcm2835-sdhost.c | 1907 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 1918 insertions(+) - create mode 100644 drivers/mmc/host/bcm2835-sdhost.c - ---- a/drivers/mmc/host/Kconfig -+++ b/drivers/mmc/host/Kconfig -@@ -33,6 +33,16 @@ config MMC_BCM2835_PIO_DMA_BARRIER - - If unsure, say 2 here. - -+config MMC_BCM2835_SDHOST -+ tristate "Support for the SDHost controller on BCM2708/9" -+ depends on MACH_BCM2708 || MACH_BCM2709 || ARCH_BCM2835 -+ help -+ This selects the SDHost controller on BCM2835/6. -+ -+ If you have a controller with this interface, say Y or M here. -+ -+ If unsure, say N. -+ - config MMC_ARMMMCI - tristate "ARM AMBA Multimedia Card Interface support" - depends on ARM_AMBA ---- a/drivers/mmc/host/Makefile -+++ b/drivers/mmc/host/Makefile -@@ -18,6 +18,7 @@ obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c - 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_SDHOST) += bcm2835-sdhost.o - obj-$(CONFIG_MMC_BCM2835) += bcm2835-mmc.o - obj-$(CONFIG_MMC_WBSD) += wbsd.o - obj-$(CONFIG_MMC_AU1X) += au1xmmc.o ---- /dev/null -+++ b/drivers/mmc/host/bcm2835-sdhost.c -@@ -0,0 +1,1907 @@ -+/* -+ * BCM2835 SD host driver. -+ * -+ * Author: Phil Elwell -+ * Copyright 2015 -+ * -+ * Based on -+ * mmc-bcm2835.c by Gellert Weisz -+ * which is, in turn, 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 . -+ */ -+ -+#define SAFE_READ_THRESHOLD 4 -+#define SAFE_WRITE_THRESHOLD 4 -+#define ALLOW_DMA 1 -+#define ALLOW_CMD23 0 -+#define ALLOW_FAST 1 -+#define USE_BLOCK_IRQ 1 -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define DRIVER_NAME "sdhost-bcm2835" -+ -+#define SDCMD 0x00 /* Command to SD card - 16 R/W */ -+#define SDARG 0x04 /* Argument to SD card - 32 R/W */ -+#define SDTOUT 0x08 /* Start value for timeout counter - 32 R/W */ -+#define SDCDIV 0x0c /* Start value for clock divider - 11 R/W */ -+#define SDRSP0 0x10 /* SD card response (31:0) - 32 R */ -+#define SDRSP1 0x14 /* SD card response (63:32) - 32 R */ -+#define SDRSP2 0x18 /* SD card response (95:64) - 32 R */ -+#define SDRSP3 0x1c /* SD card response (127:96) - 32 R */ -+#define SDHSTS 0x20 /* SD host status - 11 R */ -+#define SDVDD 0x30 /* SD card power control - 1 R/W */ -+#define SDEDM 0x34 /* Emergency Debug Mode - 13 R/W */ -+#define SDHCFG 0x38 /* Host configuration - 2 R/W */ -+#define SDHBCT 0x3c /* Host byte count (debug) - 32 R/W */ -+#define SDDATA 0x40 /* Data to/from SD card - 32 R/W */ -+#define SDHBLC 0x50 /* Host block count (SDIO/SDHC) - 9 R/W */ -+ -+#define SDCMD_NEW_FLAG 0x8000 -+#define SDCMD_FAIL_FLAG 0x4000 -+#define SDCMD_BUSYWAIT 0x800 -+#define SDCMD_NO_RESPONSE 0x400 -+#define SDCMD_LONG_RESPONSE 0x200 -+#define SDCMD_WRITE_CMD 0x80 -+#define SDCMD_READ_CMD 0x40 -+#define SDCMD_CMD_MASK 0x3f -+ -+#define SDCDIV_MAX_CDIV 0x7ff -+ -+#define SDHSTS_BUSY_IRPT 0x400 -+#define SDHSTS_BLOCK_IRPT 0x200 -+#define SDHSTS_SDIO_IRPT 0x100 -+#define SDHSTS_REW_TIME_OUT 0x80 -+#define SDHSTS_CMD_TIME_OUT 0x40 -+#define SDHSTS_CRC16_ERROR 0x20 -+#define SDHSTS_CRC7_ERROR 0x10 -+#define SDHSTS_FIFO_ERROR 0x08 -+/* Reserved */ -+/* Reserved */ -+#define SDHSTS_DATA_FLAG 0x01 -+ -+#define SDHSTS_TRANSFER_ERROR_MASK (SDHSTS_CRC7_ERROR|SDHSTS_CRC16_ERROR|SDHSTS_REW_TIME_OUT|SDHSTS_FIFO_ERROR) -+#define SDHSTS_ERROR_MASK (SDHSTS_CMD_TIME_OUT|SDHSTS_TRANSFER_ERROR_MASK) -+ -+#define SDHCFG_BUSY_IRPT_EN (1<<10) -+#define SDHCFG_BLOCK_IRPT_EN (1<<8) -+#define SDHCFG_SDIO_IRPT_EN (1<<5) -+#define SDHCFG_DATA_IRPT_EN (1<<4) -+#define SDHCFG_SLOW_CARD (1<<3) -+#define SDHCFG_WIDE_EXT_BUS (1<<2) -+#define SDHCFG_WIDE_INT_BUS (1<<1) -+#define SDHCFG_REL_CMD_LINE (1<<0) -+ -+#define SDEDM_FORCE_DATA_MODE (1<<19) -+#define SDEDM_CLOCK_PULSE (1<<20) -+#define SDEDM_BYPASS (1<<21) -+ -+#define SDEDM_WRITE_THRESHOLD_SHIFT 9 -+#define SDEDM_READ_THRESHOLD_SHIFT 14 -+#define SDEDM_THRESHOLD_MASK 0x1f -+ -+#define MHZ 1000000 -+ -+ -+struct bcm2835_host { -+ spinlock_t lock; -+ -+ void __iomem *ioaddr; -+ u32 bus_addr; -+ -+ struct mmc_host *mmc; -+ -+ u32 pio_timeout; /* In jiffies */ -+ -+ int clock; /* Current clock speed */ -+ -+ bool slow_card; /* Force 11-bit divisor */ -+ -+ unsigned int max_clk; /* Max possible freq */ -+ -+ struct tasklet_struct finish_tasklet; /* Tasklet structures */ -+ -+ struct timer_list timer; /* Timer for timeouts */ -+ -+ struct timer_list pio_timer; /* PIO error detection timer */ -+ -+ struct sg_mapping_iter sg_miter; /* SG state for PIO */ -+ unsigned int blocks; /* remaining PIO blocks */ -+ -+ int irq; /* Device IRQ */ -+ -+ -+ /* cached registers */ -+ u32 hcfg; -+ u32 cdiv; -+ -+ struct mmc_request *mrq; /* Current request */ -+ struct mmc_command *cmd; /* Current command */ -+ struct mmc_data *data; /* Current data request */ -+ unsigned int data_complete:1; /* Data finished before cmd */ -+ -+ unsigned int flush_fifo:1; /* Drain the fifo when finishing */ -+ -+ unsigned int use_busy:1; /* Wait for busy interrupt */ -+ -+ unsigned int debug:1; /* Enable debug output */ -+ -+ u32 thread_isr; -+ -+ /*DMA part*/ -+ struct dma_chan *dma_chan_rx; /* DMA channel for reads */ -+ struct dma_chan *dma_chan_tx; /* DMA channel for writes */ -+ -+ bool allow_dma; -+ bool have_dma; -+ bool use_dma; -+ /*end of DMA part*/ -+ -+ int max_delay; /* maximum length of time spent waiting */ -+ struct timeval stop_time; /* when the last stop was issued */ -+ u32 delay_after_stop; /* minimum time between stop and subsequent data transfer */ -+ u32 overclock_50; /* frequency to use when 50MHz is requested (in MHz) */ -+ u32 overclock; /* Current frequency if overclocked, else zero */ -+ u32 pio_limit; /* Maximum block count for PIO (0 = always DMA) */ -+}; -+ -+ -+static inline void bcm2835_sdhost_write(struct bcm2835_host *host, u32 val, int reg) -+{ -+ writel(val, host->ioaddr + reg); -+} -+ -+static inline u32 bcm2835_sdhost_read(struct bcm2835_host *host, int reg) -+{ -+ return readl(host->ioaddr + reg); -+} -+ -+static inline u32 bcm2835_sdhost_read_relaxed(struct bcm2835_host *host, int reg) -+{ -+ return readl_relaxed(host->ioaddr + reg); -+} -+ -+static void bcm2835_sdhost_dumpcmd(struct bcm2835_host *host, -+ struct mmc_command *cmd, -+ const char *label) -+{ -+ if (cmd) -+ pr_info("%s:%c%s op %d arg 0x%x flags 0x%x - resp %08x %08x %08x %08x, err %d\n", -+ mmc_hostname(host->mmc), -+ (cmd == host->cmd) ? '>' : ' ', -+ label, cmd->opcode, cmd->arg, cmd->flags, -+ cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3], -+ cmd->error); -+} -+ -+static void bcm2835_sdhost_dumpregs(struct bcm2835_host *host) -+{ -+ bcm2835_sdhost_dumpcmd(host, host->mrq->sbc, "sbc"); -+ bcm2835_sdhost_dumpcmd(host, host->mrq->cmd, "cmd"); -+ if (host->mrq->data) -+ pr_err("%s: data blocks %x blksz %x - err %d\n", -+ mmc_hostname(host->mmc), -+ host->mrq->data->blocks, -+ host->mrq->data->blksz, -+ host->mrq->data->error); -+ bcm2835_sdhost_dumpcmd(host, host->mrq->stop, "stop"); -+ -+ pr_info("%s: =========== REGISTER DUMP ===========\n", -+ mmc_hostname(host->mmc)); -+ -+ pr_info("%s: SDCMD 0x%08x\n", -+ mmc_hostname(host->mmc), -+ bcm2835_sdhost_read(host, SDCMD)); -+ pr_info("%s: SDARG 0x%08x\n", -+ mmc_hostname(host->mmc), -+ bcm2835_sdhost_read(host, SDARG)); -+ pr_info("%s: SDTOUT 0x%08x\n", -+ mmc_hostname(host->mmc), -+ bcm2835_sdhost_read(host, SDTOUT)); -+ pr_info("%s: SDCDIV 0x%08x\n", -+ mmc_hostname(host->mmc), -+ bcm2835_sdhost_read(host, SDCDIV)); -+ pr_info("%s: SDRSP0 0x%08x\n", -+ mmc_hostname(host->mmc), -+ bcm2835_sdhost_read(host, SDRSP0)); -+ pr_info("%s: SDRSP1 0x%08x\n", -+ mmc_hostname(host->mmc), -+ bcm2835_sdhost_read(host, SDRSP1)); -+ pr_info("%s: SDRSP2 0x%08x\n", -+ mmc_hostname(host->mmc), -+ bcm2835_sdhost_read(host, SDRSP2)); -+ pr_info("%s: SDRSP3 0x%08x\n", -+ mmc_hostname(host->mmc), -+ bcm2835_sdhost_read(host, SDRSP3)); -+ pr_info("%s: SDHSTS 0x%08x\n", -+ mmc_hostname(host->mmc), -+ bcm2835_sdhost_read(host, SDHSTS)); -+ pr_info("%s: SDVDD 0x%08x\n", -+ mmc_hostname(host->mmc), -+ bcm2835_sdhost_read(host, SDVDD)); -+ pr_info("%s: SDEDM 0x%08x\n", -+ mmc_hostname(host->mmc), -+ bcm2835_sdhost_read(host, SDEDM)); -+ pr_info("%s: SDHCFG 0x%08x\n", -+ mmc_hostname(host->mmc), -+ bcm2835_sdhost_read(host, SDHCFG)); -+ pr_info("%s: SDHBCT 0x%08x\n", -+ mmc_hostname(host->mmc), -+ bcm2835_sdhost_read(host, SDHBCT)); -+ pr_info("%s: SDHBLC 0x%08x\n", -+ mmc_hostname(host->mmc), -+ bcm2835_sdhost_read(host, SDHBLC)); -+ -+ pr_info("%s: ===========================================\n", -+ mmc_hostname(host->mmc)); -+} -+ -+ -+static void bcm2835_sdhost_set_power(struct bcm2835_host *host, bool on) -+{ -+ bcm2835_sdhost_write(host, on ? 1 : 0, SDVDD); -+} -+ -+ -+static void bcm2835_sdhost_reset_internal(struct bcm2835_host *host) -+{ -+ u32 temp; -+ -+ bcm2835_sdhost_set_power(host, false); -+ -+ bcm2835_sdhost_write(host, 0, SDCMD); -+ bcm2835_sdhost_write(host, 0, SDARG); -+ bcm2835_sdhost_write(host, 0xf00000, SDTOUT); -+ bcm2835_sdhost_write(host, 0, SDCDIV); -+ bcm2835_sdhost_write(host, 0x7f8, SDHSTS); /* Write 1s to clear */ -+ bcm2835_sdhost_write(host, 0, SDHCFG); -+ bcm2835_sdhost_write(host, 0, SDHBCT); -+ bcm2835_sdhost_write(host, 0, SDHBLC); -+ -+ /* Limit fifo usage due to silicon bug */ -+ temp = bcm2835_sdhost_read(host, SDEDM); -+ temp &= ~((SDEDM_THRESHOLD_MASK<clock = 0; -+ bcm2835_sdhost_write(host, host->hcfg, SDHCFG); -+ bcm2835_sdhost_write(host, host->cdiv, SDCDIV); -+ mmiowb(); -+} -+ -+ -+static void bcm2835_sdhost_reset(struct mmc_host *mmc) -+{ -+ struct bcm2835_host *host = mmc_priv(mmc); -+ unsigned long flags; -+ if (host->debug) -+ pr_info("%s: reset\n", mmc_hostname(mmc)); -+ spin_lock_irqsave(&host->lock, flags); -+ -+ bcm2835_sdhost_reset_internal(host); -+ -+ spin_unlock_irqrestore(&host->lock, flags); -+} -+ -+static void bcm2835_sdhost_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); -+ -+static void bcm2835_sdhost_init(struct bcm2835_host *host, int soft) -+{ -+ pr_debug("bcm2835_sdhost_init(%d)\n", soft); -+ -+ /* Set interrupt enables */ -+ host->hcfg = SDHCFG_BUSY_IRPT_EN; -+ -+ bcm2835_sdhost_reset_internal(host); -+ -+ if (soft) { -+ /* force clock reconfiguration */ -+ host->clock = 0; -+ bcm2835_sdhost_set_ios(host->mmc, &host->mmc->ios); -+ } -+} -+ -+static bool bcm2835_sdhost_is_write_complete(struct bcm2835_host *host) -+{ -+ bool write_complete = ((bcm2835_sdhost_read(host, SDEDM) & 0xf) == 1); -+ -+ if (!write_complete) { -+ /* Request an IRQ for the last block */ -+ host->hcfg |= SDHCFG_BLOCK_IRPT_EN; -+ bcm2835_sdhost_write(host, host->hcfg, SDHCFG); -+ if ((bcm2835_sdhost_read(host, SDEDM) & 0xf) == 1) { -+ /* The write has now completed. Disable the interrupt -+ and clear the status flag */ -+ host->hcfg &= ~SDHCFG_BLOCK_IRPT_EN; -+ bcm2835_sdhost_write(host, host->hcfg, SDHCFG); -+ bcm2835_sdhost_write(host, SDHSTS_BLOCK_IRPT, SDHSTS); -+ write_complete = true; -+ } -+ } -+ -+ return write_complete; -+} -+ -+static void bcm2835_sdhost_wait_write_complete(struct bcm2835_host *host) -+{ -+ int timediff; -+#ifdef DEBUG -+ static struct timeval start_time; -+ static int max_stall_time = 0; -+ static int total_stall_time = 0; -+ struct timeval before, after; -+ -+ do_gettimeofday(&before); -+ if (max_stall_time == 0) -+ start_time = before; -+#endif -+ -+ timediff = 0; -+ -+ while (1) { -+ u32 edm = bcm2835_sdhost_read(host, SDEDM); -+ if ((edm & 0xf) == 1) -+ break; -+ timediff++; -+ if (timediff > 5000000) { -+#ifdef DEBUG -+ do_gettimeofday(&after); -+ timediff = (after.tv_sec - before.tv_sec)*1000000 + -+ (after.tv_usec - before.tv_usec); -+ -+ pr_err(" wait_write_complete - still waiting after %dus\n", -+ timediff); -+#else -+ pr_err(" wait_write_complete - still waiting after %d retries\n", -+ timediff); -+#endif -+ bcm2835_sdhost_dumpregs(host); -+ host->data->error = -ETIMEDOUT; -+ return; -+ } -+ } -+ -+#ifdef DEBUG -+ do_gettimeofday(&after); -+ timediff = (after.tv_sec - before.tv_sec)*1000000 + (after.tv_usec - before.tv_usec); -+ -+ total_stall_time += timediff; -+ if (timediff > max_stall_time) -+ max_stall_time = timediff; -+ -+ if ((after.tv_sec - start_time.tv_sec) > 10) { -+ pr_debug(" wait_write_complete - max wait %dus, total %dus\n", -+ max_stall_time, total_stall_time); -+ start_time = after; -+ max_stall_time = 0; -+ total_stall_time = 0; -+ } -+#endif -+} -+ -+static void bcm2835_sdhost_finish_data(struct bcm2835_host *host); -+ -+static void bcm2835_sdhost_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) { -+ bool write_complete; -+ if (USE_BLOCK_IRQ) -+ write_complete = bcm2835_sdhost_is_write_complete(host); -+ else { -+ bcm2835_sdhost_wait_write_complete(host); -+ write_complete = true; -+ } -+ pr_debug("dma_complete() - write_complete=%d\n", -+ write_complete); -+ -+ if (write_complete || (host->data->flags & MMC_DATA_READ)) -+ { -+ if (write_complete) { -+ dma_chan = host->dma_chan_tx; -+ dir_data = DMA_TO_DEVICE; -+ } else { -+ 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_sdhost_finish_data(host); -+ } -+ } -+ -+ spin_unlock_irqrestore(&host->lock, flags); -+} -+ -+static bool data_transfer_wait(struct bcm2835_host *host) -+{ -+ unsigned long timeout = 1000000; -+ while (timeout) -+ { -+ u32 sdhsts = bcm2835_sdhost_read(host, SDHSTS); -+ if (sdhsts & SDHSTS_DATA_FLAG) { -+ bcm2835_sdhost_write(host, SDHSTS_DATA_FLAG, SDHSTS); -+ break; -+ } -+ timeout--; -+ } -+ if (timeout == 0) { -+ pr_err("%s: Data %s timeout\n", -+ mmc_hostname(host->mmc), -+ (host->data->flags & MMC_DATA_READ) ? "read" : "write"); -+ bcm2835_sdhost_dumpregs(host); -+ host->data->error = -ETIMEDOUT; -+ return false; -+ } -+ return true; -+} -+ -+static void bcm2835_sdhost_read_block_pio(struct bcm2835_host *host) -+{ -+ unsigned long flags; -+ size_t blksize, len; -+ u32 *buf; -+ -+ blksize = host->data->blksz; -+ -+ local_irq_save(flags); -+ -+ while (blksize) { -+ if (!sg_miter_next(&host->sg_miter)) -+ BUG(); -+ -+ len = min(host->sg_miter.length, blksize); -+ BUG_ON(len % 4); -+ -+ blksize -= len; -+ host->sg_miter.consumed = len; -+ -+ buf = (u32 *)host->sg_miter.addr; -+ -+ while (len) { -+ if (!data_transfer_wait(host)) -+ break; -+ -+ *(buf++) = bcm2835_sdhost_read(host, SDDATA); -+ len -= 4; -+ } -+ -+ if (host->data->error) -+ break; -+ } -+ -+ sg_miter_stop(&host->sg_miter); -+ -+ local_irq_restore(flags); -+} -+ -+static void bcm2835_sdhost_write_block_pio(struct bcm2835_host *host) -+{ -+ unsigned long flags; -+ size_t blksize, len; -+ u32 *buf; -+ -+ blksize = host->data->blksz; -+ -+ local_irq_save(flags); -+ -+ while (blksize) { -+ if (!sg_miter_next(&host->sg_miter)) -+ BUG(); -+ -+ len = min(host->sg_miter.length, blksize); -+ BUG_ON(len % 4); -+ -+ blksize -= len; -+ host->sg_miter.consumed = len; -+ -+ buf = host->sg_miter.addr; -+ -+ while (len) { -+ if (!data_transfer_wait(host)) -+ break; -+ -+ bcm2835_sdhost_write(host, *(buf++), SDDATA); -+ len -= 4; -+ } -+ -+ if (host->data->error) -+ break; -+ } -+ -+ sg_miter_stop(&host->sg_miter); -+ -+ local_irq_restore(flags); -+} -+ -+ -+static void bcm2835_sdhost_transfer_pio(struct bcm2835_host *host) -+{ -+ u32 sdhsts; -+ bool is_read; -+ BUG_ON(!host->data); -+ -+ is_read = (host->data->flags & MMC_DATA_READ) != 0; -+ if (is_read) -+ bcm2835_sdhost_read_block_pio(host); -+ else -+ bcm2835_sdhost_write_block_pio(host); -+ -+ sdhsts = bcm2835_sdhost_read(host, SDHSTS); -+ if (sdhsts & (SDHSTS_CRC16_ERROR | -+ SDHSTS_CRC7_ERROR | -+ SDHSTS_FIFO_ERROR)) { -+ pr_err("%s: %s transfer error - HSTS %x\n", -+ mmc_hostname(host->mmc), -+ is_read ? "read" : "write", -+ sdhsts); -+ host->data->error = -EILSEQ; -+ } else if ((sdhsts & (SDHSTS_CMD_TIME_OUT | -+ SDHSTS_REW_TIME_OUT))) { -+ pr_err("%s: %s timeout error - HSTS %x\n", -+ mmc_hostname(host->mmc), -+ is_read ? "read" : "write", -+ sdhsts); -+ host->data->error = -ETIMEDOUT; -+ } else if (!is_read && !host->data->error) { -+ /* Start a timer in case a transfer error occurs because -+ there is no error interrupt */ -+ mod_timer(&host->pio_timer, jiffies + host->pio_timeout); -+ } -+} -+ -+ -+static void bcm2835_sdhost_transfer_dma(struct bcm2835_host *host) -+{ -+ u32 len, dir_data, dir_slave; -+ struct dma_async_tx_descriptor *desc = NULL; -+ struct dma_chan *dma_chan; -+ -+ pr_debug("bcm2835_sdhost_transfer_dma()\n"); -+ -+ WARN_ON(!host->data); -+ -+ if (!host->data) -+ 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) { -+ desc->callback = bcm2835_sdhost_dma_complete; -+ desc->callback_param = host; -+ dmaengine_submit(desc); -+ dma_async_issue_pending(dma_chan); -+ } -+ -+} -+ -+ -+static void bcm2835_sdhost_set_transfer_irqs(struct bcm2835_host *host) -+{ -+ u32 all_irqs = SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN | -+ SDHCFG_BUSY_IRPT_EN; -+ if (host->use_dma) -+ host->hcfg = (host->hcfg & ~all_irqs) | -+ SDHCFG_BUSY_IRPT_EN; -+ else -+ host->hcfg = (host->hcfg & ~all_irqs) | -+ SDHCFG_DATA_IRPT_EN | -+ SDHCFG_BUSY_IRPT_EN; -+ -+ bcm2835_sdhost_write(host, host->hcfg, SDHCFG); -+} -+ -+ -+static void bcm2835_sdhost_prepare_data(struct bcm2835_host *host, struct mmc_command *cmd) -+{ -+ struct mmc_data *data = cmd->data; -+ -+ WARN_ON(host->data); -+ -+ 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_complete = 0; -+ host->flush_fifo = 0; -+ host->data->bytes_xfered = 0; -+ -+ host->use_dma = host->have_dma && (data->blocks > host->pio_limit); -+ if (!host->use_dma) { -+ int flags; -+ -+ flags = SG_MITER_ATOMIC; -+ if (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; -+ } -+ -+ bcm2835_sdhost_set_transfer_irqs(host); -+ -+ bcm2835_sdhost_write(host, data->blksz, SDHBCT); -+ bcm2835_sdhost_write(host, host->use_dma ? data->blocks : 0, SDHBLC); -+ -+ BUG_ON(!host->data); -+} -+ -+ -+void bcm2835_sdhost_send_command(struct bcm2835_host *host, struct mmc_command *cmd) -+{ -+ u32 sdcmd, sdhsts; -+ unsigned long timeout; -+ int delay; -+ -+ WARN_ON(host->cmd); -+ -+ if (cmd->data) -+ pr_debug("%s: send_command %d 0x%x " -+ "(flags 0x%x) - %s %d*%d\n", -+ mmc_hostname(host->mmc), -+ cmd->opcode, cmd->arg, cmd->flags, -+ (cmd->data->flags & MMC_DATA_READ) ? -+ "read" : "write", cmd->data->blocks, -+ cmd->data->blksz); -+ else -+ pr_debug("%s: send_command %d 0x%x (flags 0x%x)\n", -+ mmc_hostname(host->mmc), -+ cmd->opcode, cmd->arg, cmd->flags); -+ -+ /* Wait max 100 ms */ -+ timeout = 10000; -+ -+ while (bcm2835_sdhost_read(host, SDCMD) & SDCMD_NEW_FLAG) { -+ if (timeout == 0) { -+ pr_err("%s: previous command never completed.\n", -+ mmc_hostname(host->mmc)); -+ bcm2835_sdhost_dumpregs(host); -+ cmd->error = -EIO; -+ tasklet_schedule(&host->finish_tasklet); -+ return; -+ } -+ timeout--; -+ udelay(10); -+ } -+ -+ delay = (10000 - timeout)/100; -+ if (delay > host->max_delay) { -+ host->max_delay = delay; -+ pr_warning("%s: controller hung for %d ms\n", -+ mmc_hostname(host->mmc), -+ host->max_delay); -+ } -+ -+ timeout = jiffies; -+ if (!cmd->data && cmd->busy_timeout > 9000) -+ timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ; -+ else -+ timeout += 10 * HZ; -+ mod_timer(&host->timer, timeout); -+ -+ host->cmd = cmd; -+ -+ /* Clear any error flags */ -+ sdhsts = bcm2835_sdhost_read(host, SDHSTS); -+ if (sdhsts & SDHSTS_ERROR_MASK) -+ bcm2835_sdhost_write(host, sdhsts, SDHSTS); -+ -+ bcm2835_sdhost_prepare_data(host, cmd); -+ -+ bcm2835_sdhost_write(host, cmd->arg, SDARG); -+ -+ 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; -+ } -+ -+ sdcmd = cmd->opcode & SDCMD_CMD_MASK; -+ -+ if (!(cmd->flags & MMC_RSP_PRESENT)) -+ sdcmd |= SDCMD_NO_RESPONSE; -+ else { -+ if (cmd->flags & MMC_RSP_136) -+ sdcmd |= SDCMD_LONG_RESPONSE; -+ if (cmd->flags & MMC_RSP_BUSY) { -+ sdcmd |= SDCMD_BUSYWAIT; -+ host->use_busy = 1; -+ } -+ } -+ -+ if (cmd->data) { -+ if (host->delay_after_stop) { -+ struct timeval now; -+ int time_since_stop; -+ do_gettimeofday(&now); -+ time_since_stop = (now.tv_sec - host->stop_time.tv_sec); -+ if (time_since_stop < 2) { -+ /* Possibly less than one second */ -+ time_since_stop = time_since_stop * 1000000 + -+ (now.tv_usec - host->stop_time.tv_usec); -+ if (time_since_stop < host->delay_after_stop) -+ udelay(host->delay_after_stop - -+ time_since_stop); -+ } -+ } -+ -+ if (cmd->data->flags & MMC_DATA_WRITE) -+ sdcmd |= SDCMD_WRITE_CMD; -+ if (cmd->data->flags & MMC_DATA_READ) -+ sdcmd |= SDCMD_READ_CMD; -+ } -+ -+ bcm2835_sdhost_write(host, sdcmd | SDCMD_NEW_FLAG, SDCMD); -+} -+ -+ -+static void bcm2835_sdhost_finish_command(struct bcm2835_host *host); -+static void bcm2835_sdhost_transfer_complete(struct bcm2835_host *host); -+ -+static void bcm2835_sdhost_finish_data(struct bcm2835_host *host) -+{ -+ struct mmc_data *data; -+ -+ data = host->data; -+ BUG_ON(!data); -+ -+ pr_debug("finish_data(error %d, stop %d, sbc %d)\n", -+ data->error, data->stop ? 1 : 0, -+ host->mrq->sbc ? 1 : 0); -+ -+ host->hcfg &= ~(SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN); -+ bcm2835_sdhost_write(host, host->hcfg, SDHCFG); -+ -+ if (data->error) { -+ data->bytes_xfered = 0; -+ } else -+ data->bytes_xfered = data->blksz * data->blocks; -+ -+ host->data_complete = 1; -+ -+ if (host->cmd) { -+ /* -+ * Data managed to finish before the -+ * command completed. Make sure we do -+ * things in the proper order. -+ */ -+ pr_debug("Finished early - HSTS %x\n", -+ bcm2835_sdhost_read(host, SDHSTS)); -+ } -+ else -+ bcm2835_sdhost_transfer_complete(host); -+} -+ -+ -+static void bcm2835_sdhost_transfer_complete(struct bcm2835_host *host) -+{ -+ struct mmc_data *data; -+ -+ BUG_ON(host->cmd); -+ BUG_ON(!host->data); -+ BUG_ON(!host->data_complete); -+ -+ data = host->data; -+ host->data = NULL; -+ -+ pr_debug("transfer_complete(error %d, stop %d)\n", -+ data->error, data->stop ? 1 : 0); -+ -+ /* -+ * 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)) { -+ host->flush_fifo = 1; -+ bcm2835_sdhost_send_command(host, data->stop); -+ if (host->delay_after_stop) -+ do_gettimeofday(&host->stop_time); -+ if (!host->use_busy) -+ bcm2835_sdhost_finish_command(host); -+ } else { -+ tasklet_schedule(&host->finish_tasklet); -+ } -+} -+ -+static void bcm2835_sdhost_finish_command(struct bcm2835_host *host) -+{ -+ u32 sdcmd; -+ unsigned long timeout; -+#ifdef DEBUG -+ struct timeval before, after; -+ int timediff = 0; -+#endif -+ -+ pr_debug("finish_command(%x)\n", bcm2835_sdhost_read(host, SDCMD)); -+ -+ BUG_ON(!host->cmd || !host->mrq); -+ -+#ifdef DEBUG -+ do_gettimeofday(&before); -+#endif -+ /* Wait max 100 ms */ -+ timeout = 10000; -+ for (sdcmd = bcm2835_sdhost_read(host, SDCMD); -+ (sdcmd & SDCMD_NEW_FLAG) && timeout; -+ timeout--) { -+ if (host->flush_fifo) { -+ while (bcm2835_sdhost_read(host, SDHSTS) & -+ SDHSTS_DATA_FLAG) -+ (void)bcm2835_sdhost_read(host, SDDATA); -+ } -+ udelay(10); -+ sdcmd = bcm2835_sdhost_read(host, SDCMD); -+ } -+#ifdef DEBUG -+ do_gettimeofday(&after); -+ timediff = (after.tv_sec - before.tv_sec)*1000000 + -+ (after.tv_usec - before.tv_usec); -+ -+ pr_debug(" finish_command - waited %dus\n", timediff); -+#endif -+ -+ if (timeout == 0) { -+ pr_err("%s: command never completed.\n", -+ mmc_hostname(host->mmc)); -+ bcm2835_sdhost_dumpregs(host); -+ host->cmd->error = -EIO; -+ tasklet_schedule(&host->finish_tasklet); -+ return; -+ } -+ -+ if (host->flush_fifo) { -+ for (timeout = 100; -+ (bcm2835_sdhost_read(host, SDHSTS) & SDHSTS_DATA_FLAG) && timeout; -+ timeout--) { -+ (void)bcm2835_sdhost_read(host, SDDATA); -+ } -+ host->flush_fifo = 0; -+ if (timeout == 0) { -+ pr_err("%s: FIFO never drained.\n", -+ mmc_hostname(host->mmc)); -+ bcm2835_sdhost_dumpregs(host); -+ host->cmd->error = -EIO; -+ tasklet_schedule(&host->finish_tasklet); -+ return; -+ } -+ } -+ -+ /* Check for errors */ -+ if (sdcmd & SDCMD_FAIL_FLAG) -+ { -+ u32 sdhsts = bcm2835_sdhost_read(host, SDHSTS); -+ -+ if (host->debug) -+ pr_info("%s: error detected - CMD %x, HSTS %03x, EDM %x\n", -+ mmc_hostname(host->mmc), sdcmd, sdhsts, -+ bcm2835_sdhost_read(host, SDEDM)); -+ -+ if ((sdhsts & SDHSTS_CRC7_ERROR) && -+ (host->cmd->opcode == 1)) { -+ if (host->debug) -+ pr_info("%s: ignoring CRC7 error for CMD1\n", -+ mmc_hostname(host->mmc)); -+ } else { -+ if (sdhsts & SDHSTS_CMD_TIME_OUT) { -+ if (host->debug) -+ pr_err("%s: command %d timeout\n", -+ mmc_hostname(host->mmc), -+ host->cmd->opcode); -+ host->cmd->error = -ETIMEDOUT; -+ } else { -+ pr_err("%s: unexpected command %d error\n", -+ mmc_hostname(host->mmc), -+ host->cmd->opcode); -+ bcm2835_sdhost_dumpregs(host); -+ host->cmd->error = -EIO; -+ } -+ tasklet_schedule(&host->finish_tasklet); -+ return; -+ } -+ } -+ -+ if (host->cmd->flags & MMC_RSP_PRESENT) { -+ if (host->cmd->flags & MMC_RSP_136) { -+ int i; -+ for (i = 0; i < 4; i++) -+ host->cmd->resp[3 - i] = bcm2835_sdhost_read(host, SDRSP0 + i*4); -+ pr_debug("%s: finish_command %08x %08x %08x %08x\n", -+ mmc_hostname(host->mmc), -+ host->cmd->resp[0], host->cmd->resp[1], host->cmd->resp[2], host->cmd->resp[3]); -+ } else { -+ host->cmd->resp[0] = bcm2835_sdhost_read(host, SDRSP0); -+ pr_debug("%s: finish_command %08x\n", -+ mmc_hostname(host->mmc), -+ host->cmd->resp[0]); -+ } -+ } -+ -+ host->cmd->error = 0; -+ -+ if (host->cmd == host->mrq->sbc) { -+ /* Finished CMD23, now send actual command. */ -+ host->cmd = NULL; -+ bcm2835_sdhost_send_command(host, host->mrq->cmd); -+ -+ if (host->cmd->data && host->use_dma) -+ /* DMA transfer starts now, PIO starts after irq */ -+ bcm2835_sdhost_transfer_dma(host); -+ -+ if (!host->use_busy) -+ bcm2835_sdhost_finish_command(host); -+ } else if (host->cmd == host->mrq->stop) -+ /* Finished CMD12 */ -+ tasklet_schedule(&host->finish_tasklet); -+ else { -+ /* Processed actual command. */ -+ host->cmd = NULL; -+ if (!host->data) -+ tasklet_schedule(&host->finish_tasklet); -+ else if (host->data_complete) -+ bcm2835_sdhost_transfer_complete(host); -+ } -+} -+ -+static void bcm2835_sdhost_timeout(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_sdhost_dumpregs(host); -+ -+ if (host->data) { -+ host->data->error = -ETIMEDOUT; -+ bcm2835_sdhost_finish_data(host); -+ } else { -+ if (host->cmd) -+ host->cmd->error = -ETIMEDOUT; -+ else -+ host->mrq->cmd->error = -ETIMEDOUT; -+ -+ pr_debug("timeout_timer tasklet_schedule\n"); -+ tasklet_schedule(&host->finish_tasklet); -+ } -+ } -+ -+ mmiowb(); -+ spin_unlock_irqrestore(&host->lock, flags); -+} -+ -+static void bcm2835_sdhost_pio_timeout(unsigned long data) -+{ -+ struct bcm2835_host *host; -+ unsigned long flags; -+ -+ host = (struct bcm2835_host *)data; -+ -+ spin_lock_irqsave(&host->lock, flags); -+ -+ if (host->data) { -+ u32 sdhsts = bcm2835_sdhost_read(host, SDHSTS); -+ -+ if (sdhsts & SDHSTS_REW_TIME_OUT) { -+ pr_err("%s: transfer timeout\n", -+ mmc_hostname(host->mmc)); -+ if (host->debug) -+ bcm2835_sdhost_dumpregs(host); -+ } else { -+ pr_err("%s: unexpected transfer timeout\n", -+ mmc_hostname(host->mmc)); -+ bcm2835_sdhost_dumpregs(host); -+ } -+ -+ bcm2835_sdhost_write(host, SDHSTS_TRANSFER_ERROR_MASK, -+ SDHSTS); -+ -+ host->data->error = -ETIMEDOUT; -+ -+ bcm2835_sdhost_finish_data(host); -+ } -+ -+ mmiowb(); -+ spin_unlock_irqrestore(&host->lock, flags); -+} -+ -+static void bcm2835_sdhost_enable_sdio_irq_nolock(struct bcm2835_host *host, int enable) -+{ -+ if (enable) -+ host->hcfg |= SDHCFG_SDIO_IRPT_EN; -+ else -+ host->hcfg &= ~SDHCFG_SDIO_IRPT_EN; -+ bcm2835_sdhost_write(host, host->hcfg, SDHCFG); -+ mmiowb(); -+} -+ -+static void bcm2835_sdhost_enable_sdio_irq(struct mmc_host *mmc, int enable) -+{ -+ struct bcm2835_host *host = mmc_priv(mmc); -+ unsigned long flags; -+ -+ pr_debug("%s: enable_sdio_irq(%d)\n", mmc_hostname(mmc), enable); -+ spin_lock_irqsave(&host->lock, flags); -+ bcm2835_sdhost_enable_sdio_irq_nolock(host, enable); -+ spin_unlock_irqrestore(&host->lock, flags); -+} -+ -+static u32 bcm2835_sdhost_busy_irq(struct bcm2835_host *host, u32 intmask) -+{ -+ const u32 handled = (SDHSTS_REW_TIME_OUT | SDHSTS_CMD_TIME_OUT | -+ SDHSTS_CRC16_ERROR | SDHSTS_CRC7_ERROR | -+ SDHSTS_FIFO_ERROR); -+ -+ if (!host->cmd) { -+ pr_err("%s: got command busy interrupt 0x%08x even " -+ "though no command operation was in progress.\n", -+ mmc_hostname(host->mmc), (unsigned)intmask); -+ bcm2835_sdhost_dumpregs(host); -+ return 0; -+ } -+ -+ if (!host->use_busy) { -+ pr_err("%s: got command busy interrupt 0x%08x even " -+ "though not expecting one.\n", -+ mmc_hostname(host->mmc), (unsigned)intmask); -+ bcm2835_sdhost_dumpregs(host); -+ return 0; -+ } -+ host->use_busy = 0; -+ -+ if (intmask & SDHSTS_ERROR_MASK) -+ { -+ pr_err("sdhost_busy_irq: intmask %x, data %p\n", intmask, host->mrq->data); -+ if (intmask & SDHSTS_CRC7_ERROR) -+ host->cmd->error = -EILSEQ; -+ else if (intmask & (SDHSTS_CRC16_ERROR | -+ SDHSTS_FIFO_ERROR)) { -+ if (host->mrq->data) -+ host->mrq->data->error = -EILSEQ; -+ else -+ host->cmd->error = -EILSEQ; -+ } else if (intmask & SDHSTS_REW_TIME_OUT) { -+ if (host->mrq->data) -+ host->mrq->data->error = -ETIMEDOUT; -+ else -+ host->cmd->error = -ETIMEDOUT; -+ } else if (intmask & SDHSTS_CMD_TIME_OUT) -+ host->cmd->error = -ETIMEDOUT; -+ -+ bcm2835_sdhost_dumpregs(host); -+ tasklet_schedule(&host->finish_tasklet); -+ } -+ else -+ bcm2835_sdhost_finish_command(host); -+ -+ return handled; -+} -+ -+static u32 bcm2835_sdhost_data_irq(struct bcm2835_host *host, u32 intmask) -+{ -+ const u32 handled = (SDHSTS_REW_TIME_OUT | -+ SDHSTS_CRC16_ERROR | -+ SDHSTS_FIFO_ERROR); -+ -+ /* There are no dedicated data/space available interrupt -+ status bits, so it is necessary to use the single shared -+ data/space available FIFO status bits. It is therefore not -+ an error to get here when there is no data transfer in -+ progress. */ -+ if (!host->data) -+ return 0; -+ -+ if (intmask & (SDHSTS_CRC16_ERROR | -+ SDHSTS_FIFO_ERROR | -+ SDHSTS_REW_TIME_OUT)) { -+ if (intmask & (SDHSTS_CRC16_ERROR | -+ SDHSTS_FIFO_ERROR)) -+ host->data->error = -EILSEQ; -+ else -+ host->data->error = -ETIMEDOUT; -+ -+ bcm2835_sdhost_dumpregs(host); -+ tasklet_schedule(&host->finish_tasklet); -+ return handled; -+ } -+ -+ /* Use the block interrupt for writes after the first block */ -+ if (host->data->flags & MMC_DATA_WRITE) { -+ host->hcfg &= ~(SDHCFG_DATA_IRPT_EN); -+ host->hcfg |= SDHCFG_BLOCK_IRPT_EN; -+ bcm2835_sdhost_write(host, host->hcfg, SDHCFG); -+ if (host->data->error) -+ bcm2835_sdhost_finish_data(host); -+ else -+ bcm2835_sdhost_transfer_pio(host); -+ } else { -+ if (!host->data->error) { -+ bcm2835_sdhost_transfer_pio(host); -+ host->blocks--; -+ } -+ if ((host->blocks == 0) || host->data->error) -+ bcm2835_sdhost_finish_data(host); -+ } -+ -+ return handled; -+} -+ -+static u32 bcm2835_sdhost_block_irq(struct bcm2835_host *host, u32 intmask) -+{ -+ struct dma_chan *dma_chan; -+ u32 dir_data; -+ const u32 handled = (SDHSTS_REW_TIME_OUT | -+ SDHSTS_CRC16_ERROR | -+ SDHSTS_FIFO_ERROR); -+ -+ if (!host->data) { -+ pr_err("%s: got block interrupt 0x%08x even " -+ "though no data operation was in progress.\n", -+ mmc_hostname(host->mmc), (unsigned)intmask); -+ bcm2835_sdhost_dumpregs(host); -+ return handled; -+ } -+ -+ if (intmask & (SDHSTS_CRC16_ERROR | -+ SDHSTS_FIFO_ERROR | -+ SDHSTS_REW_TIME_OUT)) { -+ if (intmask & (SDHSTS_CRC16_ERROR | -+ SDHSTS_FIFO_ERROR)) -+ host->data->error = -EILSEQ; -+ else -+ host->data->error = -ETIMEDOUT; -+ -+ if (host->debug) -+ bcm2835_sdhost_dumpregs(host); -+ tasklet_schedule(&host->finish_tasklet); -+ return handled; -+ } -+ -+ if (!host->use_dma) { -+ BUG_ON(!host->blocks); -+ host->blocks--; -+ if ((host->blocks == 0) || host->data->error) { -+ /* Cancel the timer */ -+ del_timer(&host->pio_timer); -+ -+ bcm2835_sdhost_finish_data(host); -+ } else { -+ bcm2835_sdhost_transfer_pio(host); -+ -+ /* Reset the timer */ -+ mod_timer(&host->pio_timer, -+ jiffies + host->pio_timeout); -+ } -+ } else if (host->data->flags & MMC_DATA_WRITE) { -+ 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_sdhost_finish_data(host); -+ } -+ -+ return handled; -+} -+ -+ -+static irqreturn_t bcm2835_sdhost_irq(int irq, void *dev_id) -+{ -+ irqreturn_t result = IRQ_NONE; -+ struct bcm2835_host *host = dev_id; -+ u32 unexpected = 0, early = 0; -+ int loops = 0; -+ -+ spin_lock(&host->lock); -+ -+ for (loops = 0; loops < 1; loops++) { -+ u32 intmask, handled; -+ -+ intmask = bcm2835_sdhost_read(host, SDHSTS); -+ handled = intmask & (SDHSTS_BUSY_IRPT | -+ SDHSTS_BLOCK_IRPT | -+ SDHSTS_SDIO_IRPT | -+ SDHSTS_DATA_FLAG); -+ if ((handled == SDHSTS_DATA_FLAG) && -+ (loops == 0) && !host->data) { -+ pr_err("%s: sdhost_irq data interrupt 0x%08x even " -+ "though no data operation was in progress.\n", -+ mmc_hostname(host->mmc), -+ (unsigned)intmask); -+ -+ bcm2835_sdhost_dumpregs(host); -+ } -+ -+ if (!handled) -+ break; -+ -+ if (loops) -+ early |= handled; -+ -+ result = IRQ_HANDLED; -+ -+ /* Clear all interrupts and notifications */ -+ bcm2835_sdhost_write(host, intmask, SDHSTS); -+ -+ if (intmask & SDHSTS_BUSY_IRPT) -+ handled |= bcm2835_sdhost_busy_irq(host, intmask); -+ -+ /* There is no true data interrupt status bit, so it is -+ necessary to qualify the data flag with the interrupt -+ enable bit */ -+ if ((intmask & SDHSTS_DATA_FLAG) && -+ (host->hcfg & SDHCFG_DATA_IRPT_EN)) -+ handled |= bcm2835_sdhost_data_irq(host, intmask); -+ -+ if (intmask & SDHSTS_BLOCK_IRPT) -+ handled |= bcm2835_sdhost_block_irq(host, intmask); -+ -+ if (intmask & SDHSTS_SDIO_IRPT) { -+ bcm2835_sdhost_enable_sdio_irq_nolock(host, false); -+ host->thread_isr |= SDHSTS_SDIO_IRPT; -+ result = IRQ_WAKE_THREAD; -+ } -+ -+ unexpected |= (intmask & ~handled); -+ } -+ -+ mmiowb(); -+ -+ spin_unlock(&host->lock); -+ -+ if (early) -+ pr_debug("%s: early %x (loops %d)\n", -+ mmc_hostname(host->mmc), early, loops); -+ -+ if (unexpected) { -+ pr_err("%s: unexpected interrupt 0x%08x.\n", -+ mmc_hostname(host->mmc), unexpected); -+ bcm2835_sdhost_dumpregs(host); -+ } -+ -+ return result; -+} -+ -+static irqreturn_t bcm2835_sdhost_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 & SDHSTS_SDIO_IRPT) { -+ sdio_run_irqs(host->mmc); -+ -+/* Is this necessary? Why re-enable an interrupt which is enabled? -+ spin_lock_irqsave(&host->lock, flags); -+ if (host->flags & SDHSTS_SDIO_IRPT_ENABLED) -+ bcm2835_sdhost_enable_sdio_irq_nolock(host, true); -+ spin_unlock_irqrestore(&host->lock, flags); -+*/ -+ } -+ -+ return isr ? IRQ_HANDLED : IRQ_NONE; -+} -+ -+ -+ -+void bcm2835_sdhost_set_clock(struct bcm2835_host *host, unsigned int clock) -+{ -+ int div = 0; /* Initialized for compiler warning */ -+ unsigned int input_clock = clock; -+ -+ if (host->debug) -+ pr_info("%s: set_clock(%d)\n", mmc_hostname(host->mmc), clock); -+ -+ if ((host->overclock_50 > 50) && -+ (clock == 50*MHZ)) { -+ clock = host->overclock_50 * MHZ + (MHZ - 1); -+ } -+ -+ /* The SDCDIV register has 11 bits, and holds (div - 2). -+ But in data mode the max is 50MHz wihout a minimum, and only the -+ bottom 3 bits are used. Since the switch over is automatic (unless -+ we have marked the card as slow...), chosen values have to make -+ sense in both modes. -+ Ident mode must be 100-400KHz, so can range check the requested -+ clock. CMD15 must be used to return to data mode, so this can be -+ monitored. -+ -+ clock 250MHz -> 0->125MHz, 1->83.3MHz, 2->62.5MHz, 3->50.0MHz -+ 4->41.7MHz, 5->35.7MHz, 6->31.3MHz, 7->27.8MHz -+ -+ 623->400KHz/27.8MHz -+ reset value (507)->491159/50MHz -+ -+ BUT, the 3-bit clock divisor in data mode is too small if the -+ core clock is higher than 250MHz, so instead use the SLOW_CARD -+ configuration bit to force the use of the ident clock divisor -+ at all times. -+ */ -+ -+ host->mmc->actual_clock = 0; -+ -+ if (clock < 100000) { -+ /* Can't stop the clock, but make it as slow as possible -+ * to show willing -+ */ -+ host->cdiv = SDCDIV_MAX_CDIV; -+ bcm2835_sdhost_write(host, host->cdiv, SDCDIV); -+ return; -+ } -+ -+ div = host->max_clk / clock; -+ if (div < 2) -+ div = 2; -+ if ((host->max_clk / div) > clock) -+ div++; -+ div -= 2; -+ -+ if (div > SDCDIV_MAX_CDIV) -+ div = SDCDIV_MAX_CDIV; -+ -+ clock = host->max_clk / (div + 2); -+ host->mmc->actual_clock = clock; -+ -+ if (clock > input_clock) { -+ /* Save the closest value, to make it easier -+ to reduce in the event of error */ -+ host->overclock_50 = (clock/MHZ); -+ -+ if (clock != host->overclock) { -+ pr_warn("%s: overclocking to %dHz\n", -+ mmc_hostname(host->mmc), clock); -+ host->overclock = clock; -+ } -+ } -+ else if (host->overclock) -+ { -+ host->overclock = 0; -+ if (clock == 50 * MHZ) -+ pr_warn("%s: cancelling overclock\n", -+ mmc_hostname(host->mmc)); -+ } -+ -+ host->cdiv = div; -+ bcm2835_sdhost_write(host, host->cdiv, SDCDIV); -+ -+ /* Set the timeout to 500ms */ -+ bcm2835_sdhost_write(host, host->mmc->actual_clock/2, SDTOUT); -+ -+ if (host->debug) -+ pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x (actual clock %d)\n", -+ mmc_hostname(host->mmc), input_clock, -+ host->max_clk, host->cdiv, host->mmc->actual_clock); -+} -+ -+static void bcm2835_sdhost_request(struct mmc_host *mmc, struct mmc_request *mrq) -+{ -+ struct bcm2835_host *host; -+ unsigned long flags; -+ -+ host = mmc_priv(mmc); -+ -+ if (host->debug) { -+ struct mmc_command *cmd = mrq->cmd; -+ BUG_ON(!cmd); -+ if (cmd->data) -+ pr_info("%s: cmd %d 0x%x (flags 0x%x) - %s %d*%d\n", -+ mmc_hostname(mmc), -+ cmd->opcode, cmd->arg, cmd->flags, -+ (cmd->data->flags & MMC_DATA_READ) ? -+ "read" : "write", cmd->data->blocks, -+ cmd->data->blksz); -+ else -+ pr_info("%s: cmd %d 0x%x (flags 0x%x)\n", -+ mmc_hostname(mmc), -+ cmd->opcode, cmd->arg, cmd->flags); -+ } -+ -+ /* Reset the error statuses in case this is a retry */ -+ if (mrq->cmd) -+ mrq->cmd->error = 0; -+ if (mrq->data) -+ mrq->data->error = 0; -+ if (mrq->stop) -+ mrq->stop->error = 0; -+ -+ if (mrq->data && !is_power_of_2(mrq->data->blksz)) { -+ pr_err("%s: unsupported block size (%d bytes)\n", -+ mmc_hostname(mmc), mrq->data->blksz); -+ mrq->cmd->error = -EINVAL; -+ mmc_request_done(mmc, mrq); -+ return; -+ } -+ -+ spin_lock_irqsave(&host->lock, flags); -+ -+ WARN_ON(host->mrq != NULL); -+ -+ host->mrq = mrq; -+ -+ if (mrq->sbc) -+ bcm2835_sdhost_send_command(host, mrq->sbc); -+ else -+ bcm2835_sdhost_send_command(host, mrq->cmd); -+ -+ mmiowb(); -+ spin_unlock_irqrestore(&host->lock, flags); -+ -+ if (!mrq->sbc && mrq->cmd->data && host->use_dma) -+ /* DMA transfer starts now, PIO starts after irq */ -+ bcm2835_sdhost_transfer_dma(host); -+ -+ if (!host->use_busy) -+ bcm2835_sdhost_finish_command(host); -+} -+ -+ -+static void bcm2835_sdhost_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -+{ -+ -+ struct bcm2835_host *host = mmc_priv(mmc); -+ unsigned long flags; -+ -+ if (host->debug) -+ pr_info("%s: ios clock %d, pwr %d, bus_width %d, " -+ "timing %d, vdd %d, drv_type %d\n", -+ mmc_hostname(mmc), -+ ios->clock, ios->power_mode, ios->bus_width, -+ ios->timing, ios->signal_voltage, ios->drv_type); -+ -+ spin_lock_irqsave(&host->lock, flags); -+ -+ if (!ios->clock || ios->clock != host->clock) { -+ bcm2835_sdhost_set_clock(host, ios->clock); -+ host->clock = ios->clock; -+ } -+ -+ /* set bus width */ -+ host->hcfg &= ~SDHCFG_WIDE_EXT_BUS; -+ if (ios->bus_width == MMC_BUS_WIDTH_4) -+ host->hcfg |= SDHCFG_WIDE_EXT_BUS; -+ -+ host->hcfg |= SDHCFG_WIDE_INT_BUS; -+ -+ /* Disable clever clock switching, to cope with fast core clocks */ -+ host->hcfg |= SDHCFG_SLOW_CARD; -+ -+ bcm2835_sdhost_write(host, host->hcfg, SDHCFG); -+ -+ mmiowb(); -+ -+ spin_unlock_irqrestore(&host->lock, flags); -+} -+ -+static int bcm2835_sdhost_multi_io_quirk(struct mmc_card *card, -+ unsigned int direction, -+ u32 blk_pos, int blk_size) -+{ -+ /* There is a bug in the host controller hardware that makes -+ reading the final sector of the card as part of a multiple read -+ problematic. Detect that case and shorten the read accordingly. -+ */ -+ /* csd.capacity is in weird units - convert to sectors */ -+ u32 card_sectors = (card->csd.capacity << (card->csd.read_blkbits - 9)); -+ -+ if ((direction == MMC_DATA_READ) && -+ ((blk_pos + blk_size) == card_sectors)) -+ blk_size--; -+ -+ return blk_size; -+} -+ -+ -+static struct mmc_host_ops bcm2835_sdhost_ops = { -+ .request = bcm2835_sdhost_request, -+ .set_ios = bcm2835_sdhost_set_ios, -+ .enable_sdio_irq = bcm2835_sdhost_enable_sdio_irq, -+ .hw_reset = bcm2835_sdhost_reset, -+ .multi_io_quirk = bcm2835_sdhost_multi_io_quirk, -+}; -+ -+ -+static void bcm2835_sdhost_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; -+ -+ /* Drop the overclock after any data corruption, or after any -+ error overclocked */ -+ if (host->overclock) { -+ if ((mrq->cmd && mrq->cmd->error) || -+ (mrq->data && mrq->data->error) || -+ (mrq->stop && mrq->stop->error)) { -+ host->overclock_50--; -+ pr_warn("%s: reducing overclock due to errors\n", -+ mmc_hostname(host->mmc)); -+ bcm2835_sdhost_set_clock(host,50*MHZ); -+ mrq->cmd->error = -EILSEQ; -+ mrq->cmd->retries = 1; -+ } -+ } -+ -+ host->mrq = NULL; -+ host->cmd = NULL; -+ host->data = NULL; -+ -+ mmiowb(); -+ -+ spin_unlock_irqrestore(&host->lock, flags); -+ mmc_request_done(host->mmc, mrq); -+} -+ -+ -+ -+int bcm2835_sdhost_add_host(struct bcm2835_host *host) -+{ -+ struct mmc_host *mmc; -+ struct dma_slave_config cfg; -+ char pio_limit_string[20]; -+ int ret; -+ -+ mmc = host->mmc; -+ -+ bcm2835_sdhost_reset_internal(host); -+ -+ mmc->f_max = host->max_clk; -+ mmc->f_min = host->max_clk / SDCDIV_MAX_CDIV; -+ -+ mmc->max_busy_timeout = (~(unsigned int)0)/(mmc->f_max/1000); -+ -+ pr_debug("f_max %d, f_min %d, max_busy_timeout %d\n", -+ mmc->f_max, mmc->f_min, mmc->max_busy_timeout); -+ -+ /* host controller capabilities */ -+ mmc->caps |= /* MMC_CAP_SDIO_IRQ |*/ MMC_CAP_4_BIT_DATA | -+ MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | -+ MMC_CAP_NEEDS_POLL | MMC_CAP_HW_RESET | MMC_CAP_ERASE | -+ (ALLOW_CMD23 * MMC_CAP_CMD23); -+ -+ spin_lock_init(&host->lock); -+ -+ if (host->allow_dma) { -+ if (IS_ERR_OR_NULL(host->dma_chan_tx) || -+ IS_ERR_OR_NULL(host->dma_chan_rx)) { -+ pr_err("%s: unable to initialise DMA channels. " -+ "Falling back to PIO\n", -+ mmc_hostname(mmc)); -+ host->have_dma = false; -+ } else { -+ 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 = 13; /* DREQ channel */ -+ -+ cfg.direction = DMA_MEM_TO_DEV; -+ cfg.src_addr = 0; -+ cfg.dst_addr = host->bus_addr + SDDATA; -+ ret = dmaengine_slave_config(host->dma_chan_tx, &cfg); -+ -+ cfg.direction = DMA_DEV_TO_MEM; -+ cfg.src_addr = host->bus_addr + SDDATA; -+ cfg.dst_addr = 0; -+ ret = dmaengine_slave_config(host->dma_chan_rx, &cfg); -+ } -+ } else { -+ host->have_dma = false; -+ } -+ -+ 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_sdhost_tasklet_finish, (unsigned long)host); -+ -+ setup_timer(&host->timer, bcm2835_sdhost_timeout, -+ (unsigned long)host); -+ -+ setup_timer(&host->pio_timer, bcm2835_sdhost_pio_timeout, -+ (unsigned long)host); -+ -+ bcm2835_sdhost_init(host, 0); -+ ret = request_threaded_irq(host->irq, bcm2835_sdhost_irq, -+ bcm2835_sdhost_thread_irq, -+ IRQF_SHARED, mmc_hostname(mmc), host); -+ 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); -+ -+ pio_limit_string[0] = '\0'; -+ if (host->have_dma && (host->pio_limit > 0)) -+ sprintf(pio_limit_string, " (>%d)", host->pio_limit); -+ pr_info("%s: %s loaded - DMA %s%s\n", -+ mmc_hostname(mmc), DRIVER_NAME, -+ host->have_dma ? "enabled" : "disabled", -+ pio_limit_string); -+ -+ return 0; -+ -+untasklet: -+ tasklet_kill(&host->finish_tasklet); -+ -+ return ret; -+} -+ -+static int bcm2835_sdhost_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; -+ const __be32 *addr; -+ int ret; -+ -+ pr_debug("bcm2835_sdhost_probe\n"); -+ mmc = mmc_alloc_host(sizeof(*host), dev); -+ if (!mmc) -+ return -ENOMEM; -+ -+ mmc->ops = &bcm2835_sdhost_ops; -+ host = mmc_priv(mmc); -+ host->mmc = mmc; -+ host->pio_timeout = msecs_to_jiffies(500); -+ host->max_delay = 1; /* Warn if over 1ms */ -+ 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; -+ } -+ -+ addr = of_get_address(node, 0, NULL, NULL); -+ if (!addr) { -+ dev_err(dev, "could not get DMA-register address\n"); -+ return -ENODEV; -+ } -+ host->bus_addr = be32_to_cpup(addr); -+ pr_debug(" - ioaddr %lx, iomem->start %lx, bus_addr %lx\n", -+ (unsigned long)host->ioaddr, -+ (unsigned long)iomem->start, -+ (unsigned long)host->bus_addr); -+ -+ host->allow_dma = ALLOW_DMA; -+ -+ if (node) { -+ /* Read any custom properties */ -+ of_property_read_u32(node, -+ "brcm,delay-after-stop", -+ &host->delay_after_stop); -+ of_property_read_u32(node, -+ "brcm,overclock-50", -+ &host->overclock_50); -+ of_property_read_u32(node, -+ "brcm,pio-limit", -+ &host->pio_limit); -+ host->allow_dma = ALLOW_DMA && -+ !of_property_read_bool(node, "brcm,force-pio"); -+ host->debug = of_property_read_bool(node, "brcm,debug"); -+ } -+ -+ if (host->allow_dma) { -+ if (node) { -+ host->dma_chan_tx = -+ dma_request_slave_channel(dev, "tx"); -+ host->dma_chan_rx = -+ dma_request_slave_channel(dev, "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); -+ } -+ } -+ -+ 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; -+ } -+ -+ pr_debug(" - max_clk %lx, irq %d\n", -+ (unsigned long)host->max_clk, -+ (int)host->irq); -+ -+ if (node) -+ mmc_of_parse(mmc); -+ else -+ mmc->caps |= MMC_CAP_4_BIT_DATA; -+ -+ ret = bcm2835_sdhost_add_host(host); -+ if (ret) -+ goto err; -+ -+ platform_set_drvdata(pdev, host); -+ -+ pr_debug("bcm2835_sdhost_probe -> OK\n"); -+ -+ return 0; -+ -+err: -+ pr_debug("bcm2835_sdhost_probe -> err %d\n", ret); -+ mmc_free_host(mmc); -+ -+ return ret; -+} -+ -+static int bcm2835_sdhost_remove(struct platform_device *pdev) -+{ -+ struct bcm2835_host *host = platform_get_drvdata(pdev); -+ -+ pr_debug("bcm2835_sdhost_remove\n"); -+ -+ mmc_remove_host(host->mmc); -+ -+ bcm2835_sdhost_set_power(host, false); -+ -+ 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); -+ -+ pr_debug("bcm2835_sdhost_remove - OK\n"); -+ return 0; -+} -+ -+ -+static const struct of_device_id bcm2835_sdhost_match[] = { -+ { .compatible = "brcm,bcm2835-sdhost" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, bcm2835_sdhost_match); -+ -+ -+ -+static struct platform_driver bcm2835_sdhost_driver = { -+ .probe = bcm2835_sdhost_probe, -+ .remove = bcm2835_sdhost_remove, -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ .of_match_table = bcm2835_sdhost_match, -+ }, -+}; -+module_platform_driver(bcm2835_sdhost_driver); -+ -+MODULE_ALIAS("platform:sdhost-bcm2835"); -+MODULE_DESCRIPTION("BCM2835 SDHost driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Phil Elwell"); diff --git a/target/linux/brcm2708/patches-4.4/0034-cma-Add-vc_cma-driver-to-enable-use-of-CMA.patch b/target/linux/brcm2708/patches-4.4/0034-cma-Add-vc_cma-driver-to-enable-use-of-CMA.patch new file mode 100644 index 0000000000..f39a2ab186 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0034-cma-Add-vc_cma-driver-to-enable-use-of-CMA.patch @@ -0,0 +1,1326 @@ +From 36d8db53953365eecdf56695944e9f9919634de0 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 3 Jul 2013 00:31:47 +0100 +Subject: [PATCH 034/232] cma: Add vc_cma driver to enable use of CMA +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: popcornmix + +vc_cma: Make the vc_cma area the default contiguous DMA area + +vc_cma: Provide empty functions when module is not built + +Providing empty functions saves the users from guarding the +function call with an #if clause. +Move __init markings from prototypes to functions. + +Signed-off-by: Noralf Trønnes +--- + 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 | 1193 +++++++++++++++++++++++++++++++++ + include/linux/broadcom/vc_cma.h | 36 + + 7 files changed, 1262 insertions(+) + create mode 100644 drivers/char/broadcom/Kconfig + create mode 100644 drivers/char/broadcom/Makefile + create mode 100644 drivers/char/broadcom/vc_cma/Makefile + create mode 100644 drivers/char/broadcom/vc_cma/vc_cma.c + create mode 100644 include/linux/broadcom/vc_cma.h + +--- a/drivers/char/Kconfig ++++ b/drivers/char/Kconfig +@@ -4,6 +4,8 @@ + + menu "Character devices" + ++source "drivers/char/broadcom/Kconfig" ++ + source "drivers/tty/Kconfig" + + config DEVMEM +--- a/drivers/char/Makefile ++++ b/drivers/char/Makefile +@@ -60,3 +60,4 @@ js-rtc-y = rtc.o + + obj-$(CONFIG_TILE_SROM) += tile-srom.o + obj-$(CONFIG_XILLYBUS) += xillybus/ ++obj-$(CONFIG_BRCM_CHAR_DRIVERS) += broadcom/ +--- /dev/null ++++ b/drivers/char/broadcom/Kconfig +@@ -0,0 +1,15 @@ ++# ++# Broadcom char driver config ++# ++ ++menuconfig BRCM_CHAR_DRIVERS ++ bool "Broadcom Char Drivers" ++ help ++ Broadcom's char drivers ++ ++config BCM_VC_CMA ++ bool "Videocore CMA" ++ depends on CMA && BRCM_CHAR_DRIVERS && BCM2708_VCHIQ ++ default n ++ help ++ Helper for videocore CMA access. +--- /dev/null ++++ b/drivers/char/broadcom/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_BCM_VC_CMA) += vc_cma/ +--- /dev/null ++++ b/drivers/char/broadcom/vc_cma/Makefile +@@ -0,0 +1,14 @@ ++ccflags-y += -Wall -Wstrict-prototypes -Wno-trigraphs ++ccflags-y += -Werror ++ccflags-y += -Iinclude/linux/broadcom ++ccflags-y += -Idrivers/misc/vc04_services ++ccflags-y += -Idrivers/misc/vc04_services/interface/vchi ++ccflags-y += -Idrivers/misc/vc04_services/interface/vchiq_arm ++ ++ccflags-y += -D__KERNEL__ ++ccflags-y += -D__linux__ ++ccflags-y += -Werror ++ ++obj-$(CONFIG_BCM_VC_CMA) += vc-cma.o ++ ++vc-cma-objs := vc_cma.o +--- /dev/null ++++ b/drivers/char/broadcom/vc_cma/vc_cma.c +@@ -0,0 +1,1193 @@ ++/** ++ * 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 "vc_cma.h" ++ ++#include "vchiq_util.h" ++#include "vchiq_connected.h" ++//#include "debug_sym.h" ++//#include "vc_mem.h" ++ ++#define DRIVER_NAME "vc-cma" ++ ++#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__) ++ ++#define VC_CMA_FOURCC VCHIQ_MAKE_FOURCC('C', 'M', 'A', ' ') ++#define VC_CMA_VERSION 2 ++ ++#define VC_CMA_CHUNK_ORDER 6 /* 256K */ ++#define VC_CMA_CHUNK_SIZE (4096 << VC_CMA_CHUNK_ORDER) ++#define VC_CMA_MAX_PARAMS_PER_MSG \ ++ ((VCHIQ_MAX_MSG_SIZE - sizeof(unsigned short))/sizeof(unsigned short)) ++#define VC_CMA_RESERVE_COUNT_MAX 16 ++ ++#define PAGES_PER_CHUNK (VC_CMA_CHUNK_SIZE / PAGE_SIZE) ++ ++#define VCADDR_TO_PHYSADDR(vcaddr) (mm_vc_mem_phys_addr + vcaddr) ++ ++#define loud_error(...) \ ++ LOG_ERR("===== " __VA_ARGS__) ++ ++enum { ++ VC_CMA_MSG_QUIT, ++ VC_CMA_MSG_OPEN, ++ VC_CMA_MSG_TICK, ++ VC_CMA_MSG_ALLOC, /* chunk count */ ++ VC_CMA_MSG_FREE, /* chunk, chunk, ... */ ++ VC_CMA_MSG_ALLOCATED, /* chunk, chunk, ... */ ++ VC_CMA_MSG_REQUEST_ALLOC, /* chunk count */ ++ VC_CMA_MSG_REQUEST_FREE, /* chunk count */ ++ VC_CMA_MSG_RESERVE, /* bytes lo, bytes hi */ ++ VC_CMA_MSG_UPDATE_RESERVE, ++ VC_CMA_MSG_MAX ++}; ++ ++struct cma_msg { ++ unsigned short type; ++ unsigned short params[VC_CMA_MAX_PARAMS_PER_MSG]; ++}; ++ ++struct vc_cma_reserve_user { ++ unsigned int pid; ++ unsigned int reserve; ++}; ++ ++/* Device (/dev) related variables */ ++static dev_t vc_cma_devnum; ++static struct class *vc_cma_class; ++static struct cdev vc_cma_cdev; ++static int vc_cma_inited; ++static int vc_cma_debug; ++ ++/* Proc entry */ ++static struct proc_dir_entry *vc_cma_proc_entry; ++ ++phys_addr_t vc_cma_base; ++struct page *vc_cma_base_page; ++unsigned int vc_cma_size; ++EXPORT_SYMBOL(vc_cma_size); ++unsigned int vc_cma_initial; ++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; ++unsigned int vc_cma_reserve_count; ++struct vc_cma_reserve_user vc_cma_reserve_users[VC_CMA_RESERVE_COUNT_MAX]; ++static DEFINE_SEMAPHORE(vc_cma_reserve_mutex); ++static DEFINE_SEMAPHORE(vc_cma_worker_queue_push_mutex); ++ ++static u64 vc_cma_dma_mask = DMA_BIT_MASK(32); ++static struct platform_device vc_cma_device = { ++ .name = "vc-cma", ++ .id = 0, ++ .dev = { ++ .dma_mask = &vc_cma_dma_mask, ++ .coherent_dma_mask = DMA_BIT_MASK(32), ++ }, ++}; ++ ++static VCHIQ_INSTANCE_T cma_instance; ++static VCHIQ_SERVICE_HANDLE_T cma_service; ++static VCHIU_QUEUE_T cma_msg_queue; ++static struct task_struct *cma_worker; ++ ++static int vc_cma_set_reserve(unsigned int reserve, unsigned int pid); ++static int vc_cma_alloc_chunks(int num_chunks, struct cma_msg *reply); ++static VCHIQ_STATUS_T cma_service_callback(VCHIQ_REASON_T reason, ++ VCHIQ_HEADER_T * header, ++ VCHIQ_SERVICE_HANDLE_T service, ++ void *bulk_userdata); ++static void send_vc_msg(unsigned short type, ++ unsigned short param1, unsigned short param2); ++static bool send_worker_msg(VCHIQ_HEADER_T * msg); ++ ++static int early_vc_cma_mem(char *p) ++{ ++ unsigned int new_size; ++ printk(KERN_NOTICE "early_vc_cma_mem(%s)", p); ++ vc_cma_size = memparse(p, &p); ++ vc_cma_initial = vc_cma_size; ++ if (*p == '/') ++ vc_cma_size = memparse(p + 1, &p); ++ if (*p == '@') ++ vc_cma_base = memparse(p + 1, &p); ++ ++ new_size = (vc_cma_size - ((-vc_cma_base) & (VC_CMA_CHUNK_SIZE - 1))) ++ & ~(VC_CMA_CHUNK_SIZE - 1); ++ if (new_size > vc_cma_size) ++ vc_cma_size = 0; ++ vc_cma_initial = (vc_cma_initial + VC_CMA_CHUNK_SIZE - 1) ++ & ~(VC_CMA_CHUNK_SIZE - 1); ++ if (vc_cma_initial > vc_cma_size) ++ vc_cma_initial = vc_cma_size; ++ vc_cma_base = (vc_cma_base + VC_CMA_CHUNK_SIZE - 1) ++ & ~(VC_CMA_CHUNK_SIZE - 1); ++ ++ printk(KERN_NOTICE " -> initial %x, size %x, base %x", vc_cma_initial, ++ vc_cma_size, (unsigned int)vc_cma_base); ++ ++ return 0; ++} ++ ++early_param("vc-cma-mem", early_vc_cma_mem); ++ ++void __init vc_cma_early_init(void) ++{ ++ LOG_DBG("vc_cma_early_init - vc_cma_chunks = %d", vc_cma_chunks); ++ if (vc_cma_size) { ++ int rc = platform_device_register(&vc_cma_device); ++ LOG_DBG("platform_device_register -> %d", rc); ++ } ++} ++ ++void __init vc_cma_reserve(void) ++{ ++ /* if vc_cma_size is set, then declare vc CMA area of the same ++ * size from the end of memory ++ */ ++ if (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); ++ vc_cma_size = 0; ++ } ++ } ++ vc_cma_chunks = vc_cma_size / VC_CMA_CHUNK_SIZE; ++} ++ ++/**************************************************************************** ++* ++* vc_cma_open ++* ++***************************************************************************/ ++ ++static int vc_cma_open(struct inode *inode, struct file *file) ++{ ++ (void)inode; ++ (void)file; ++ ++ return 0; ++} ++ ++/**************************************************************************** ++* ++* vc_cma_release ++* ++***************************************************************************/ ++ ++static int vc_cma_release(struct inode *inode, struct file *file) ++{ ++ (void)inode; ++ (void)file; ++ ++ vc_cma_set_reserve(0, current->tgid); ++ ++ return 0; ++} ++ ++/**************************************************************************** ++* ++* vc_cma_ioctl ++* ++***************************************************************************/ ++ ++static long vc_cma_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ int rc = 0; ++ ++ (void)cmd; ++ (void)arg; ++ ++ switch (cmd) { ++ case VC_CMA_IOC_RESERVE: ++ rc = vc_cma_set_reserve((unsigned int)arg, current->tgid); ++ if (rc >= 0) ++ rc = 0; ++ break; ++ default: ++ LOG_ERR("vc-cma: Unknown ioctl %x", cmd); ++ return -ENOTTY; ++ } ++ ++ return rc; ++} ++ ++/**************************************************************************** ++* ++* File Operations for the driver. ++* ++***************************************************************************/ ++ ++static const struct file_operations vc_cma_fops = { ++ .owner = THIS_MODULE, ++ .open = vc_cma_open, ++ .release = vc_cma_release, ++ .unlocked_ioctl = vc_cma_ioctl, ++}; ++ ++/**************************************************************************** ++* ++* vc_cma_proc_open ++* ++***************************************************************************/ ++ ++static int vc_cma_show_info(struct seq_file *m, void *v) ++{ ++ int i; ++ ++ seq_printf(m, "Videocore CMA:\n"); ++ seq_printf(m, " Base : %08x\n", (unsigned int)vc_cma_base); ++ seq_printf(m, " Length : %08x\n", vc_cma_size); ++ seq_printf(m, " Initial : %08x\n", vc_cma_initial); ++ seq_printf(m, " Chunk size : %08x\n", VC_CMA_CHUNK_SIZE); ++ seq_printf(m, " Chunks : %4d (%d bytes)\n", ++ (int)vc_cma_chunks, ++ (int)(vc_cma_chunks * VC_CMA_CHUNK_SIZE)); ++ seq_printf(m, " Used : %4d (%d bytes)\n", ++ (int)vc_cma_chunks_used, ++ (int)(vc_cma_chunks_used * VC_CMA_CHUNK_SIZE)); ++ seq_printf(m, " Reserved : %4d (%d bytes)\n", ++ (unsigned int)vc_cma_chunks_reserved, ++ (int)(vc_cma_chunks_reserved * VC_CMA_CHUNK_SIZE)); ++ ++ for (i = 0; i < vc_cma_reserve_count; i++) { ++ struct vc_cma_reserve_user *user = &vc_cma_reserve_users[i]; ++ 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"); ++ ++ return 0; ++} ++ ++static int vc_cma_proc_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, vc_cma_show_info, NULL); ++} ++ ++/**************************************************************************** ++* ++* vc_cma_proc_write ++* ++***************************************************************************/ ++ ++static int vc_cma_proc_write(struct file *file, ++ const char __user *buffer, ++ size_t size, loff_t *ppos) ++{ ++ int rc = -EFAULT; ++ char input_str[20]; ++ ++ memset(input_str, 0, sizeof(input_str)); ++ ++ if (size > sizeof(input_str)) { ++ LOG_ERR("%s: input string length too long", __func__); ++ goto out; ++ } ++ ++ if (copy_from_user(input_str, buffer, size - 1)) { ++ LOG_ERR("%s: failed to get input string", __func__); ++ goto out; ++ } ++#define ALLOC_STR "alloc" ++#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 alloc_size; ++ char *p = input_str + strlen(ALLOC_STR); ++ ++ while (*p == ' ') ++ p++; ++ 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, ++ 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 alloc_size; ++ char *p = input_str + strlen(FREE_STR); ++ ++ while (*p == ' ') ++ p++; ++ 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, ++ alloc_size / VC_CMA_CHUNK_SIZE, 0); ++ else ++ LOG_ERR("invalid size '%s'", p); ++ rc = size; ++ } else if (strncmp(input_str, DEBUG_STR, strlen(DEBUG_STR)) == 0) { ++ char *p = input_str + strlen(DEBUG_STR); ++ while (*p == ' ') ++ p++; ++ if ((strcmp(p, "on") == 0) || (strcmp(p, "1") == 0)) ++ vc_cma_debug = 1; ++ else if ((strcmp(p, "off") == 0) || (strcmp(p, "0") == 0)) ++ vc_cma_debug = 0; ++ 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 alloc_size; ++ int reserved; ++ char *p = input_str + strlen(RESERVE_STR); ++ while (*p == ' ') ++ p++; ++ alloc_size = memparse(p, NULL); ++ ++ 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: ++ return rc; ++} ++ ++/**************************************************************************** ++* ++* File Operations for /proc interface. ++* ++***************************************************************************/ ++ ++static const struct file_operations vc_cma_proc_fops = { ++ .open = vc_cma_proc_open, ++ .read = seq_read, ++ .write = vc_cma_proc_write, ++ .llseek = seq_lseek, ++ .release = single_release ++}; ++ ++static int vc_cma_set_reserve(unsigned int reserve, unsigned int pid) ++{ ++ struct vc_cma_reserve_user *user = NULL; ++ int delta = 0; ++ int i; ++ ++ if (down_interruptible(&vc_cma_reserve_mutex)) ++ return -ERESTARTSYS; ++ ++ for (i = 0; i < vc_cma_reserve_count; i++) { ++ if (pid == vc_cma_reserve_users[i].pid) { ++ user = &vc_cma_reserve_users[i]; ++ delta = reserve - user->reserve; ++ if (reserve) ++ user->reserve = reserve; ++ else { ++ /* Remove this entry by copying downwards */ ++ while ((i + 1) < vc_cma_reserve_count) { ++ user[0].pid = user[1].pid; ++ user[0].reserve = user[1].reserve; ++ user++; ++ i++; ++ } ++ vc_cma_reserve_count--; ++ user = NULL; ++ } ++ break; ++ } ++ } ++ ++ if (reserve && !user) { ++ if (vc_cma_reserve_count == VC_CMA_RESERVE_COUNT_MAX) { ++ LOG_ERR("vc-cma: Too many reservations - " ++ "increase CMA_RESERVE_COUNT_MAX"); ++ up(&vc_cma_reserve_mutex); ++ return -EBUSY; ++ } ++ user = &vc_cma_reserve_users[vc_cma_reserve_count]; ++ user->pid = pid; ++ user->reserve = reserve; ++ delta = reserve; ++ vc_cma_reserve_count++; ++ } ++ ++ vc_cma_reserve_total += delta; ++ ++ send_vc_msg(VC_CMA_MSG_RESERVE, ++ vc_cma_reserve_total & 0xffff, vc_cma_reserve_total >> 16); ++ ++ send_worker_msg((VCHIQ_HEADER_T *) VC_CMA_MSG_UPDATE_RESERVE); ++ ++ LOG_DBG("/proc/vc-cma: reserve %d (PID %d) - total %u", ++ reserve, pid, vc_cma_reserve_total); ++ ++ up(&vc_cma_reserve_mutex); ++ ++ return vc_cma_reserve_total; ++} ++ ++static VCHIQ_STATUS_T cma_service_callback(VCHIQ_REASON_T reason, ++ VCHIQ_HEADER_T * header, ++ VCHIQ_SERVICE_HANDLE_T service, ++ void *bulk_userdata) ++{ ++ switch (reason) { ++ case VCHIQ_MESSAGE_AVAILABLE: ++ if (!send_worker_msg(header)) ++ return VCHIQ_RETRY; ++ break; ++ case VCHIQ_SERVICE_CLOSED: ++ LOG_DBG("CMA service closed"); ++ break; ++ default: ++ LOG_ERR("Unexpected CMA callback reason %d", reason); ++ break; ++ } ++ return VCHIQ_SUCCESS; ++} ++ ++static void send_vc_msg(unsigned short type, ++ unsigned short param1, unsigned short param2) ++{ ++ unsigned short msg[] = { type, param1, param2 }; ++ VCHIQ_ELEMENT_T elem = { &msg, sizeof(msg) }; ++ VCHIQ_STATUS_T ret; ++ vchiq_use_service(cma_service); ++ ret = vchiq_queue_message(cma_service, &elem, 1); ++ vchiq_release_service(cma_service); ++ if (ret != VCHIQ_SUCCESS) ++ LOG_ERR("vchiq_queue_message returned %x", ret); ++} ++ ++static bool send_worker_msg(VCHIQ_HEADER_T * msg) ++{ ++ if (down_interruptible(&vc_cma_worker_queue_push_mutex)) ++ return false; ++ vchiu_queue_push(&cma_msg_queue, msg); ++ up(&vc_cma_worker_queue_push_mutex); ++ return true; ++} ++ ++static int vc_cma_alloc_chunks(int num_chunks, struct cma_msg *reply) ++{ ++ int i; ++ for (i = 0; i < num_chunks; i++) { ++ struct page *chunk; ++ unsigned int chunk_num; ++ uint8_t *chunk_addr; ++ size_t chunk_size = PAGES_PER_CHUNK << PAGE_SHIFT; ++ ++ chunk = dma_alloc_from_contiguous(&vc_cma_device.dev, ++ PAGES_PER_CHUNK, ++ VC_CMA_CHUNK_ORDER); ++ if (!chunk) ++ break; ++ ++ chunk_addr = page_address(chunk); ++ dmac_flush_range(chunk_addr, chunk_addr + chunk_size); ++ outer_inv_range(__pa(chunk_addr), __pa(chunk_addr) + ++ chunk_size); ++ ++ chunk_num = ++ (page_to_phys(chunk) - vc_cma_base) / VC_CMA_CHUNK_SIZE; ++ 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 %pa-%pa - " ++ "bad SPARSEMEM configuration?", ++ __func__, (unsigned int)page_to_phys(chunk), ++ &vc_cma_base, &_pa); ++ LOG_ERR("%s: dev->cma_area = %p", __func__, ++ (void*)0/*vc_cma_device.dev.cma_area*/); ++ LOG_ERR("%s: ===============================", ++ __func__); ++ break; ++ } ++ reply->params[i] = chunk_num; ++ vc_cma_chunks_used++; ++ } ++ ++ if (i < num_chunks) { ++ LOG_ERR("%s: dma_alloc_from_contiguous failed " ++ "for %x bytes (alloc %d of %d, %d free)", ++ __func__, VC_CMA_CHUNK_SIZE, i, ++ num_chunks, vc_cma_chunks - vc_cma_chunks_used); ++ num_chunks = i; ++ } ++ ++ LOG_DBG("CMA allocated %d chunks -> %d used", ++ num_chunks, vc_cma_chunks_used); ++ reply->type = VC_CMA_MSG_ALLOCATED; ++ ++ { ++ VCHIQ_ELEMENT_T elem = { ++ reply, ++ offsetof(struct cma_msg, params[0]) + ++ num_chunks * sizeof(reply->params[0]) ++ }; ++ VCHIQ_STATUS_T ret; ++ vchiq_use_service(cma_service); ++ ret = vchiq_queue_message(cma_service, &elem, 1); ++ vchiq_release_service(cma_service); ++ if (ret != VCHIQ_SUCCESS) ++ LOG_ERR("vchiq_queue_message return " "%x", ret); ++ } ++ ++ return num_chunks; ++} ++ ++static int cma_worker_proc(void *param) ++{ ++ static struct cma_msg reply; ++ (void)param; ++ ++ while (1) { ++ VCHIQ_HEADER_T *msg; ++ static struct cma_msg msg_copy; ++ struct cma_msg *cma_msg = &msg_copy; ++ int type, msg_size; ++ ++ msg = vchiu_queue_pop(&cma_msg_queue); ++ if ((unsigned int)msg >= VC_CMA_MSG_MAX) { ++ msg_size = msg->size; ++ memcpy(&msg_copy, msg->data, msg_size); ++ type = cma_msg->type; ++ vchiq_release_message(cma_service, msg); ++ } else { ++ msg_size = 0; ++ type = (int)msg; ++ if (type == VC_CMA_MSG_QUIT) ++ break; ++ else if (type == VC_CMA_MSG_UPDATE_RESERVE) { ++ msg = NULL; ++ cma_msg = NULL; ++ } else { ++ BUG(); ++ continue; ++ } ++ } ++ ++ switch (type) { ++ case VC_CMA_MSG_ALLOC:{ ++ int num_chunks, free_chunks; ++ num_chunks = cma_msg->params[0]; ++ free_chunks = ++ vc_cma_chunks - vc_cma_chunks_used; ++ LOG_DBG("CMA_MSG_ALLOC(%d chunks)", num_chunks); ++ if (num_chunks > VC_CMA_MAX_PARAMS_PER_MSG) { ++ LOG_ERR ++ ("CMA_MSG_ALLOC - chunk count (%d) " ++ "exceeds VC_CMA_MAX_PARAMS_PER_MSG (%d)", ++ num_chunks, ++ VC_CMA_MAX_PARAMS_PER_MSG); ++ num_chunks = VC_CMA_MAX_PARAMS_PER_MSG; ++ } ++ ++ if (num_chunks > free_chunks) { ++ LOG_ERR ++ ("CMA_MSG_ALLOC - chunk count (%d) " ++ "exceeds free chunks (%d)", ++ num_chunks, free_chunks); ++ num_chunks = free_chunks; ++ } ++ ++ vc_cma_alloc_chunks(num_chunks, &reply); ++ } ++ break; ++ ++ case VC_CMA_MSG_FREE:{ ++ int chunk_count = ++ (msg_size - ++ offsetof(struct cma_msg, ++ params)) / ++ sizeof(cma_msg->params[0]); ++ int i; ++ BUG_ON(chunk_count <= 0); ++ ++ LOG_DBG("CMA_MSG_FREE(%d chunks - %x, ...)", ++ chunk_count, cma_msg->params[0]); ++ for (i = 0; i < chunk_count; i++) { ++ int chunk_num = cma_msg->params[i]; ++ struct page *page = vc_cma_base_page + ++ chunk_num * PAGES_PER_CHUNK; ++ if (chunk_num >= vc_cma_chunks) { ++ LOG_ERR ++ ("CMA_MSG_FREE - chunk %d of %d" ++ " (value %x) exceeds maximum " ++ "(%x)", i, chunk_count, ++ chunk_num, ++ vc_cma_chunks - 1); ++ break; ++ } ++ ++ 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 %pa, " ++ "page %x)", chunk_num, ++ &_pa, ++ (unsigned int)page); ++ } ++ vc_cma_chunks_used--; ++ } ++ LOG_DBG("CMA released %d chunks -> %d used", ++ i, vc_cma_chunks_used); ++ } ++ break; ++ ++ case VC_CMA_MSG_UPDATE_RESERVE:{ ++ int chunks_needed = ++ ((vc_cma_reserve_total + VC_CMA_CHUNK_SIZE - ++ 1) ++ / VC_CMA_CHUNK_SIZE) - ++ vc_cma_chunks_reserved; ++ ++ LOG_DBG ++ ("CMA_MSG_UPDATE_RESERVE(%d chunks needed)", ++ chunks_needed); ++ ++ /* Cap the reservations to what is available */ ++ if (chunks_needed > 0) { ++ if (chunks_needed > ++ (vc_cma_chunks - ++ vc_cma_chunks_used)) ++ chunks_needed = ++ (vc_cma_chunks - ++ vc_cma_chunks_used); ++ ++ chunks_needed = ++ vc_cma_alloc_chunks(chunks_needed, ++ &reply); ++ } ++ ++ LOG_DBG ++ ("CMA_MSG_UPDATE_RESERVE(%d chunks allocated)", ++ chunks_needed); ++ vc_cma_chunks_reserved += chunks_needed; ++ } ++ break; ++ ++ default: ++ LOG_ERR("unexpected msg type %d", type); ++ break; ++ } ++ } ++ ++ LOG_DBG("quitting..."); ++ return 0; ++} ++ ++/**************************************************************************** ++* ++* vc_cma_connected_init ++* ++* This function is called once the videocore has been connected. ++* ++***************************************************************************/ ++ ++static void vc_cma_connected_init(void) ++{ ++ VCHIQ_SERVICE_PARAMS_T service_params; ++ ++ LOG_DBG("vc_cma_connected_init"); ++ ++ if (!vchiu_queue_init(&cma_msg_queue, 16)) { ++ LOG_ERR("could not create CMA msg queue"); ++ goto fail_queue; ++ } ++ ++ if (vchiq_initialise(&cma_instance) != VCHIQ_SUCCESS) ++ goto fail_vchiq_init; ++ ++ vchiq_connect(cma_instance); ++ ++ service_params.fourcc = VC_CMA_FOURCC; ++ service_params.callback = cma_service_callback; ++ service_params.userdata = NULL; ++ service_params.version = VC_CMA_VERSION; ++ service_params.version_min = VC_CMA_VERSION; ++ ++ if (vchiq_open_service(cma_instance, &service_params, ++ &cma_service) != VCHIQ_SUCCESS) { ++ LOG_ERR("failed to open service - already in use?"); ++ goto fail_vchiq_open; ++ } ++ ++ vchiq_release_service(cma_service); ++ ++ cma_worker = kthread_create(cma_worker_proc, NULL, "cma_worker"); ++ if (!cma_worker) { ++ LOG_ERR("could not create CMA worker thread"); ++ goto fail_worker; ++ } ++ set_user_nice(cma_worker, -20); ++ wake_up_process(cma_worker); ++ ++ return; ++ ++fail_worker: ++ vchiq_close_service(cma_service); ++fail_vchiq_open: ++ vchiq_shutdown(cma_instance); ++fail_vchiq_init: ++ vchiu_queue_delete(&cma_msg_queue); ++fail_queue: ++ return; ++} ++ ++void ++loud_error_header(void) ++{ ++ if (in_loud_error) ++ return; ++ ++ LOG_ERR("============================================================" ++ "================"); ++ LOG_ERR("============================================================" ++ "================"); ++ LOG_ERR("====="); ++ ++ in_loud_error = 1; ++} ++ ++void ++loud_error_footer(void) ++{ ++ if (!in_loud_error) ++ return; ++ ++ LOG_ERR("====="); ++ LOG_ERR("============================================================" ++ "================"); ++ LOG_ERR("============================================================" ++ "================"); ++ ++ in_loud_error = 0; ++} ++ ++#if 1 ++static int check_cma_config(void) { return 1; } ++#else ++static int ++read_vc_debug_var(VC_MEM_ACCESS_HANDLE_T handle, ++ const char *symbol, ++ void *buf, size_t bufsize) ++{ ++ VC_MEM_ADDR_T vcMemAddr; ++ size_t vcMemSize; ++ uint8_t *mapAddr; ++ off_t vcMapAddr; ++ ++ if (!LookupVideoCoreSymbol(handle, symbol, ++ &vcMemAddr, ++ &vcMemSize)) { ++ loud_error_header(); ++ loud_error( ++ "failed to find VC symbol \"%s\".", ++ symbol); ++ loud_error_footer(); ++ return 0; ++ } ++ ++ if (vcMemSize != bufsize) { ++ loud_error_header(); ++ loud_error( ++ "VC symbol \"%s\" is the wrong size.", ++ symbol); ++ loud_error_footer(); ++ return 0; ++ } ++ ++ vcMapAddr = (off_t)vcMemAddr & VC_MEM_TO_ARM_ADDR_MASK; ++ vcMapAddr += mm_vc_mem_phys_addr; ++ mapAddr = ioremap_nocache(vcMapAddr, vcMemSize); ++ if (mapAddr == 0) { ++ loud_error_header(); ++ loud_error( ++ "failed to ioremap \"%s\" @ 0x%x " ++ "(phys: 0x%x, size: %u).", ++ symbol, ++ (unsigned int)vcMapAddr, ++ (unsigned int)vcMemAddr, ++ (unsigned int)vcMemSize); ++ loud_error_footer(); ++ return 0; ++ } ++ ++ memcpy(buf, mapAddr, bufsize); ++ iounmap(mapAddr); ++ ++ return 1; ++} ++ ++ ++static int ++check_cma_config(void) ++{ ++ VC_MEM_ACCESS_HANDLE_T mem_hndl; ++ VC_MEM_ADDR_T mempool_start; ++ VC_MEM_ADDR_T mempool_end; ++ VC_MEM_ADDR_T mempool_offline_start; ++ VC_MEM_ADDR_T mempool_offline_end; ++ VC_MEM_ADDR_T cam_alloc_base; ++ VC_MEM_ADDR_T cam_alloc_size; ++ VC_MEM_ADDR_T cam_alloc_end; ++ int success = 0; ++ ++ if (OpenVideoCoreMemory(&mem_hndl) != 0) ++ goto out; ++ ++ /* Read the relevant VideoCore variables */ ++ if (!read_vc_debug_var(mem_hndl, "__MEMPOOL_START", ++ &mempool_start, ++ sizeof(mempool_start))) ++ goto close; ++ ++ if (!read_vc_debug_var(mem_hndl, "__MEMPOOL_END", ++ &mempool_end, ++ sizeof(mempool_end))) ++ goto close; ++ ++ if (!read_vc_debug_var(mem_hndl, "__MEMPOOL_OFFLINE_START", ++ &mempool_offline_start, ++ sizeof(mempool_offline_start))) ++ goto close; ++ ++ if (!read_vc_debug_var(mem_hndl, "__MEMPOOL_OFFLINE_END", ++ &mempool_offline_end, ++ sizeof(mempool_offline_end))) ++ goto close; ++ ++ if (!read_vc_debug_var(mem_hndl, "cam_alloc_base", ++ &cam_alloc_base, ++ sizeof(cam_alloc_base))) ++ goto close; ++ ++ if (!read_vc_debug_var(mem_hndl, "cam_alloc_size", ++ &cam_alloc_size, ++ sizeof(cam_alloc_size))) ++ goto close; ++ ++ cam_alloc_end = cam_alloc_base + cam_alloc_size; ++ ++ success = 1; ++ ++ /* Now the sanity checks */ ++ if (!mempool_offline_start) ++ mempool_offline_start = mempool_start; ++ if (!mempool_offline_end) ++ mempool_offline_end = mempool_end; ++ ++ if (VCADDR_TO_PHYSADDR(mempool_offline_start) != vc_cma_base) { ++ loud_error_header(); ++ loud_error( ++ "__MEMPOOL_OFFLINE_START(%x -> %lx) doesn't match " ++ "vc_cma_base(%x)", ++ mempool_offline_start, ++ VCADDR_TO_PHYSADDR(mempool_offline_start), ++ vc_cma_base); ++ success = 0; ++ } ++ ++ if (VCADDR_TO_PHYSADDR(mempool_offline_end) != ++ (vc_cma_base + vc_cma_size)) { ++ loud_error_header(); ++ loud_error( ++ "__MEMPOOL_OFFLINE_END(%x -> %lx) doesn't match " ++ "vc_cma_base(%x) + vc_cma_size(%x) = %x", ++ mempool_offline_start, ++ VCADDR_TO_PHYSADDR(mempool_offline_end), ++ vc_cma_base, vc_cma_size, vc_cma_base + vc_cma_size); ++ success = 0; ++ } ++ ++ if (mempool_end < mempool_start) { ++ loud_error_header(); ++ loud_error( ++ "__MEMPOOL_END(%x) must not be before " ++ "__MEMPOOL_START(%x)", ++ mempool_end, ++ mempool_start); ++ success = 0; ++ } ++ ++ if (mempool_offline_end < mempool_offline_start) { ++ loud_error_header(); ++ loud_error( ++ "__MEMPOOL_OFFLINE_END(%x) must not be before " ++ "__MEMPOOL_OFFLINE_START(%x)", ++ mempool_offline_end, ++ mempool_offline_start); ++ success = 0; ++ } ++ ++ if (mempool_offline_start < mempool_start) { ++ loud_error_header(); ++ loud_error( ++ "__MEMPOOL_OFFLINE_START(%x) must not be before " ++ "__MEMPOOL_START(%x)", ++ mempool_offline_start, ++ mempool_start); ++ success = 0; ++ } ++ ++ if (mempool_offline_end > mempool_end) { ++ loud_error_header(); ++ loud_error( ++ "__MEMPOOL_OFFLINE_END(%x) must not be after " ++ "__MEMPOOL_END(%x)", ++ mempool_offline_end, ++ mempool_end); ++ success = 0; ++ } ++ ++ if ((cam_alloc_base < mempool_end) && ++ (cam_alloc_end > mempool_start)) { ++ loud_error_header(); ++ loud_error( ++ "cam_alloc pool(%x-%x) overlaps " ++ "mempool(%x-%x)", ++ cam_alloc_base, cam_alloc_end, ++ mempool_start, mempool_end); ++ success = 0; ++ } ++ ++ loud_error_footer(); ++ ++close: ++ CloseVideoCoreMemory(mem_hndl); ++ ++out: ++ return success; ++} ++#endif ++ ++static int vc_cma_init(void) ++{ ++ int rc = -EFAULT; ++ struct device *dev; ++ ++ if (!check_cma_config()) ++ goto out_release; ++ ++ 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); ++ ++ if (vc_cma_chunks) { ++ int chunks_needed = vc_cma_initial / VC_CMA_CHUNK_SIZE; ++ ++ for (vc_cma_chunks_used = 0; ++ vc_cma_chunks_used < chunks_needed; vc_cma_chunks_used++) { ++ struct page *chunk; ++ chunk = dma_alloc_from_contiguous(&vc_cma_device.dev, ++ PAGES_PER_CHUNK, ++ VC_CMA_CHUNK_ORDER); ++ if (!chunk) ++ break; ++ BUG_ON(((page_to_phys(chunk) - vc_cma_base) % ++ VC_CMA_CHUNK_SIZE) != 0); ++ } ++ if (vc_cma_chunks_used != chunks_needed) { ++ LOG_ERR("%s: dma_alloc_from_contiguous failed (%d " ++ "bytes, allocation %d of %d)", ++ __func__, VC_CMA_CHUNK_SIZE, ++ vc_cma_chunks_used, chunks_needed); ++ goto out_release; ++ } ++ ++ vchiq_add_connected_callback(vc_cma_connected_init); ++ } ++ ++ rc = alloc_chrdev_region(&vc_cma_devnum, 0, 1, DRIVER_NAME); ++ if (rc < 0) { ++ LOG_ERR("%s: alloc_chrdev_region failed (rc=%d)", __func__, rc); ++ goto out_release; ++ } ++ ++ cdev_init(&vc_cma_cdev, &vc_cma_fops); ++ rc = cdev_add(&vc_cma_cdev, vc_cma_devnum, 1); ++ if (rc != 0) { ++ LOG_ERR("%s: cdev_add failed (rc=%d)", __func__, rc); ++ goto out_unregister; ++ } ++ ++ vc_cma_class = class_create(THIS_MODULE, DRIVER_NAME); ++ if (IS_ERR(vc_cma_class)) { ++ rc = PTR_ERR(vc_cma_class); ++ LOG_ERR("%s: class_create failed (rc=%d)", __func__, rc); ++ goto out_cdev_del; ++ } ++ ++ dev = device_create(vc_cma_class, NULL, vc_cma_devnum, NULL, ++ DRIVER_NAME); ++ if (IS_ERR(dev)) { ++ rc = PTR_ERR(dev); ++ LOG_ERR("%s: device_create failed (rc=%d)", __func__, rc); ++ goto out_class_destroy; ++ } ++ ++ vc_cma_proc_entry = proc_create(DRIVER_NAME, 0444, NULL, &vc_cma_proc_fops); ++ if (vc_cma_proc_entry == NULL) { ++ rc = -EFAULT; ++ LOG_ERR("%s: proc_create failed", __func__); ++ goto out_device_destroy; ++ } ++ ++ vc_cma_inited = 1; ++ return 0; ++ ++out_device_destroy: ++ device_destroy(vc_cma_class, vc_cma_devnum); ++ ++out_class_destroy: ++ class_destroy(vc_cma_class); ++ vc_cma_class = NULL; ++ ++out_cdev_del: ++ cdev_del(&vc_cma_cdev); ++ ++out_unregister: ++ unregister_chrdev_region(vc_cma_devnum, 1); ++ ++out_release: ++ /* It is tempting to try to clean up by calling ++ dma_release_from_contiguous for all allocated chunks, but it isn't ++ a very safe thing to do. If vc_cma_initial is non-zero it is because ++ VideoCore is already using that memory, so giving it back to Linux ++ is likely to be fatal. ++ */ ++ return -1; ++} ++ ++/**************************************************************************** ++* ++* vc_cma_exit ++* ++***************************************************************************/ ++ ++static void __exit vc_cma_exit(void) ++{ ++ LOG_DBG("%s: called", __func__); ++ ++ if (vc_cma_inited) { ++ remove_proc_entry(DRIVER_NAME, NULL); ++ device_destroy(vc_cma_class, vc_cma_devnum); ++ class_destroy(vc_cma_class); ++ cdev_del(&vc_cma_cdev); ++ unregister_chrdev_region(vc_cma_devnum, 1); ++ } ++} ++ ++module_init(vc_cma_init); ++module_exit(vc_cma_exit); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Broadcom Corporation"); +--- /dev/null ++++ b/include/linux/broadcom/vc_cma.h +@@ -0,0 +1,36 @@ ++/***************************************************************************** ++* Copyright 2012 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_CMA_H ) ++#define VC_CMA_H ++ ++#include ++ ++#define VC_CMA_IOC_MAGIC 0xc5 ++ ++#define VC_CMA_IOC_RESERVE _IO(VC_CMA_IOC_MAGIC, 0) ++ ++#ifdef __KERNEL__ ++ ++#ifdef CONFIG_BCM_VC_CMA ++void vc_cma_early_init(void); ++void vc_cma_reserve(void); ++#else ++static inline void vc_cma_early_init(void) { } ++static inline void vc_cma_reserve(void) { } ++#endif ++ ++#endif ++ ++#endif /* VC_CMA_H */ diff --git a/target/linux/brcm2708/patches-4.4/0035-bcm2708-alsa-sound-driver.patch b/target/linux/brcm2708/patches-4.4/0035-bcm2708-alsa-sound-driver.patch new file mode 100644 index 0000000000..2ee0ba3177 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0035-bcm2708-alsa-sound-driver.patch @@ -0,0 +1,2678 @@ +From 2dd634f97c4fb77d9e81be289a8da29c390a0309 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Mon, 26 Mar 2012 22:15:50 +0100 +Subject: [PATCH 035/232] bcm2708: alsa sound driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: popcornmix + +alsa: add mmap support and some cleanups to bcm2835 ALSA driver + +snd-bcm2835: Add support for spdif/hdmi passthrough + +This adds a dedicated subdevice which can be used for passthrough of non-audio +formats (ie encoded a52) through the hdmi audio link. In addition to this +driver extension an appropriate card config is required to make alsa-lib +support the AES parameters for this device. + +snd-bcm2708: Add mutex, improve logging + +Fix for ALSA driver crash + +Avoids an issue when closing and opening vchiq where a message can arrive before service handle has been written + +alsa: reduce severity of expected warning message + +snd-bcm2708: Fix dmesg spam for non-error case + +alsa: Ensure mutexes are released through error paths + +alsa: Make interrupted close paths quieter + +BCM270x: Add onboard sound device to Device Tree + +Add Device Tree support to alsa driver. +Add device to Device Tree. +Don't add platform devices when booting in DT mode. + +Signed-off-by: Noralf Trønnes +--- + sound/arm/Kconfig | 8 + + sound/arm/Makefile | 5 + + sound/arm/bcm2835-ctl.c | 323 +++++++++++++ + sound/arm/bcm2835-pcm.c | 557 +++++++++++++++++++++++ + sound/arm/bcm2835-vchiq.c | 902 +++++++++++++++++++++++++++++++++++++ + sound/arm/bcm2835.c | 511 +++++++++++++++++++++ + sound/arm/bcm2835.h | 167 +++++++ + sound/arm/vc_vchi_audioserv_defs.h | 116 +++++ + 8 files changed, 2589 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 + create mode 100644 sound/arm/bcm2835.c + create mode 100755 sound/arm/bcm2835.h + create mode 100644 sound/arm/vc_vchi_audioserv_defs.h + +--- a/sound/arm/Kconfig ++++ b/sound/arm/Kconfig +@@ -40,5 +40,13 @@ config SND_PXA2XX_AC97 + Say Y or M if you want to support any AC97 codec attached to + the PXA2xx AC97 interface. + ++config SND_BCM2835 ++ tristate "BCM2835 ALSA driver" ++ depends on (ARCH_BCM2708 || ARCH_BCM2709 || ARCH_BCM2835) \ ++ && BCM2708_VCHIQ && SND ++ select SND_PCM ++ help ++ Say Y or M if you want to support BCM2835 Alsa pcm card driver ++ + endif # SND_ARM + +--- a/sound/arm/Makefile ++++ b/sound/arm/Makefile +@@ -14,3 +14,8 @@ snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_A + + obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o + snd-pxa2xx-ac97-objs := pxa2xx-ac97.o ++ ++obj-$(CONFIG_SND_BCM2835) += snd-bcm2835.o ++snd-bcm2835-objs := bcm2835.o bcm2835-ctl.o bcm2835-pcm.o bcm2835-vchiq.o ++ ++ccflags-y += -Idrivers/misc/vc04_services -Idrivers/misc/vc04_services/interface/vcos/linuxkernel -D__VCCOREVER__=0x04000000 +--- /dev/null ++++ b/sound/arm/bcm2835-ctl.c +@@ -0,0 +1,323 @@ ++/***************************************************************************** ++* 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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "bcm2835.h" ++ ++/* volume maximum and minimum in terms of 0.01dB */ ++#define CTRL_VOL_MAX 400 ++#define CTRL_VOL_MIN -10239 /* originally -10240 */ ++ ++ ++static int snd_bcm2835_ctl_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ audio_info(" ... IN\n"); ++ if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) { ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ uinfo->value.integer.min = CTRL_VOL_MIN; ++ uinfo->value.integer.max = CTRL_VOL_MAX; /* 2303 */ ++ } else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) { ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; ++ uinfo->count = 1; ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = 1; ++ } else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) { ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = AUDIO_DEST_MAX-1; ++ } ++ audio_info(" ... OUT\n"); ++ return 0; ++} ++ ++/* toggles mute on or off depending on the value of nmute, and returns ++ * 1 if the mute value was changed, otherwise 0 ++ */ ++static int toggle_mute(struct bcm2835_chip *chip, int nmute) ++{ ++ /* if settings are ok, just return 0 */ ++ if(chip->mute == nmute) ++ return 0; ++ ++ /* if the sound is muted then we need to unmute */ ++ if(chip->mute == CTRL_VOL_MUTE) ++ { ++ chip->volume = chip->old_volume; /* copy the old volume back */ ++ audio_info("Unmuting, old_volume = %d, volume = %d ...\n", chip->old_volume, chip->volume); ++ } ++ else /* otherwise we mute */ ++ { ++ chip->old_volume = chip->volume; ++ chip->volume = 26214; /* set volume to minimum level AKA mute */ ++ audio_info("Muting, old_volume = %d, volume = %d ...\n", chip->old_volume, chip->volume); ++ } ++ ++ chip->mute = nmute; ++ return 1; ++} ++ ++static int snd_bcm2835_ctl_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); ++ ++ BUG_ON(!chip && !(chip->avail_substreams & AVAIL_SUBSTREAMS_MASK)); ++ ++ if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) ++ ucontrol->value.integer.value[0] = chip2alsa(chip->volume); ++ else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) ++ ucontrol->value.integer.value[0] = chip->mute; ++ else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) ++ ucontrol->value.integer.value[0] = chip->dest; ++ ++ return 0; ++} ++ ++static int snd_bcm2835_ctl_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); ++ int changed = 0; ++ ++ if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) { ++ audio_info("Volume change attempted.. volume = %d new_volume = %d\n", chip->volume, (int)ucontrol->value.integer.value[0]); ++ if (chip->mute == CTRL_VOL_MUTE) { ++ /* changed = toggle_mute(chip, CTRL_VOL_UNMUTE); */ ++ return 1; /* should return 0 to signify no change but the mixer takes this as the opposite sign (no idea why) */ ++ } ++ if (changed ++ || (ucontrol->value.integer.value[0] != chip2alsa(chip->volume))) { ++ ++ chip->volume = alsa2chip(ucontrol->value.integer.value[0]); ++ changed = 1; ++ } ++ ++ } else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) { ++ /* Now implemented */ ++ audio_info(" Mute attempted\n"); ++ changed = toggle_mute(chip, ucontrol->value.integer.value[0]); ++ ++ } else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) { ++ if (ucontrol->value.integer.value[0] != chip->dest) { ++ chip->dest = ucontrol->value.integer.value[0]; ++ changed = 1; ++ } ++ } ++ ++ if (changed) { ++ if (bcm2835_audio_set_ctls(chip)) ++ printk(KERN_ERR "Failed to set ALSA controls..\n"); ++ } ++ ++ return changed; ++} ++ ++static DECLARE_TLV_DB_SCALE(snd_bcm2835_db_scale, CTRL_VOL_MIN, 1, 1); ++ ++static struct snd_kcontrol_new snd_bcm2835_ctl[] = { ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "PCM Playback Volume", ++ .index = 0, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, ++ .private_value = PCM_PLAYBACK_VOLUME, ++ .info = snd_bcm2835_ctl_info, ++ .get = snd_bcm2835_ctl_get, ++ .put = snd_bcm2835_ctl_put, ++ .count = 1, ++ .tlv = {.p = snd_bcm2835_db_scale} ++ }, ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "PCM Playback Switch", ++ .index = 0, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, ++ .private_value = PCM_PLAYBACK_MUTE, ++ .info = snd_bcm2835_ctl_info, ++ .get = snd_bcm2835_ctl_get, ++ .put = snd_bcm2835_ctl_put, ++ .count = 1, ++ }, ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "PCM Playback Route", ++ .index = 0, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, ++ .private_value = PCM_PLAYBACK_DEVICE, ++ .info = snd_bcm2835_ctl_info, ++ .get = snd_bcm2835_ctl_get, ++ .put = snd_bcm2835_ctl_put, ++ .count = 1, ++ }, ++}; ++ ++static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; ++ uinfo->count = 1; ++ return 0; ++} ++ ++static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); ++ int i; ++ ++ for (i = 0; i < 4; i++) ++ ucontrol->value.iec958.status[i] = ++ (chip->spdif_status >> (i * 8)) && 0xff; ++ ++ return 0; ++} ++ ++static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); ++ unsigned int val = 0; ++ int i, change; ++ ++ for (i = 0; i < 4; i++) ++ val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); ++ ++ change = val != chip->spdif_status; ++ chip->spdif_status = val; ++ ++ return change; ++} ++ ++static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; ++ uinfo->count = 1; ++ return 0; ++} ++ ++static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ /* bcm2835 supports only consumer mode and sets all other format flags ++ * automatically. So the only thing left is signalling non-audio ++ * content */ ++ ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO; ++ return 0; ++} ++ ++static int snd_bcm2835_spdif_stream_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; ++ uinfo->count = 1; ++ return 0; ++} ++ ++static int snd_bcm2835_spdif_stream_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); ++ int i; ++ ++ for (i = 0; i < 4; i++) ++ ucontrol->value.iec958.status[i] = ++ (chip->spdif_status >> (i * 8)) & 0xff; ++ return 0; ++} ++ ++static int snd_bcm2835_spdif_stream_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); ++ unsigned int val = 0; ++ int i, change; ++ ++ for (i = 0; i < 4; i++) ++ val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); ++ change = val != chip->spdif_status; ++ chip->spdif_status = val; ++ ++ return change; ++} ++ ++static struct snd_kcontrol_new snd_bcm2835_spdif[] = { ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_PCM, ++ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), ++ .info = snd_bcm2835_spdif_default_info, ++ .get = snd_bcm2835_spdif_default_get, ++ .put = snd_bcm2835_spdif_default_put ++ }, ++ { ++ .access = SNDRV_CTL_ELEM_ACCESS_READ, ++ .iface = SNDRV_CTL_ELEM_IFACE_PCM, ++ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK), ++ .info = snd_bcm2835_spdif_mask_info, ++ .get = snd_bcm2835_spdif_mask_get, ++ }, ++ { ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | ++ SNDRV_CTL_ELEM_ACCESS_INACTIVE, ++ .iface = SNDRV_CTL_ELEM_IFACE_PCM, ++ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM), ++ .info = snd_bcm2835_spdif_stream_info, ++ .get = snd_bcm2835_spdif_stream_get, ++ .put = snd_bcm2835_spdif_stream_put, ++ }, ++}; ++ ++int snd_bcm2835_new_ctl(bcm2835_chip_t * chip) ++{ ++ int err; ++ unsigned int idx; ++ ++ strcpy(chip->card->mixername, "Broadcom Mixer"); ++ for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_ctl); idx++) { ++ err = ++ snd_ctl_add(chip->card, ++ snd_ctl_new1(&snd_bcm2835_ctl[idx], chip)); ++ if (err < 0) ++ return err; ++ } ++ for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_spdif); idx++) { ++ err = snd_ctl_add(chip->card, ++ snd_ctl_new1(&snd_bcm2835_spdif[idx], chip)); ++ if (err < 0) ++ return err; ++ } ++ return 0; ++} +--- /dev/null ++++ b/sound/arm/bcm2835-pcm.c +@@ -0,0 +1,557 @@ ++/***************************************************************************** ++* 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 "bcm2835.h" ++ ++/* hardware definition */ ++static struct snd_pcm_hardware snd_bcm2835_playback_hw = { ++ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), ++ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, ++ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, ++ .rate_min = 8000, ++ .rate_max = 48000, ++ .channels_min = 1, ++ .channels_max = 2, ++ .buffer_bytes_max = 128 * 1024, ++ .period_bytes_min = 1 * 1024, ++ .period_bytes_max = 128 * 1024, ++ .periods_min = 1, ++ .periods_max = 128, ++}; ++ ++static struct snd_pcm_hardware snd_bcm2835_playback_spdif_hw = { ++ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_44100 | ++ SNDRV_PCM_RATE_48000, ++ .rate_min = 44100, ++ .rate_max = 48000, ++ .channels_min = 2, ++ .channels_max = 2, ++ .buffer_bytes_max = 128 * 1024, ++ .period_bytes_min = 1 * 1024, ++ .period_bytes_max = 128 * 1024, ++ .periods_min = 1, ++ .periods_max = 128, ++}; ++ ++static void snd_bcm2835_playback_free(struct snd_pcm_runtime *runtime) ++{ ++ audio_info("Freeing up alsa stream here ..\n"); ++ if (runtime->private_data) ++ kfree(runtime->private_data); ++ runtime->private_data = NULL; ++} ++ ++static irqreturn_t bcm2835_playback_fifo_irq(int irq, void *dev_id) ++{ ++ bcm2835_alsa_stream_t *alsa_stream = (bcm2835_alsa_stream_t *) dev_id; ++ uint32_t consumed = 0; ++ int new_period = 0; ++ ++ audio_info(" .. IN\n"); ++ ++ audio_info("alsa_stream=%p substream=%p\n", alsa_stream, ++ alsa_stream ? alsa_stream->substream : 0); ++ ++ if (alsa_stream->open) ++ consumed = bcm2835_audio_retrieve_buffers(alsa_stream); ++ ++ /* We get called only if playback was triggered, So, the number of buffers we retrieve in ++ * each iteration are the buffers that have been played out already ++ */ ++ ++ if (alsa_stream->period_size) { ++ if ((alsa_stream->pos / alsa_stream->period_size) != ++ ((alsa_stream->pos + consumed) / alsa_stream->period_size)) ++ new_period = 1; ++ } ++ audio_debug("updating pos cur: %d + %d max:%d period_bytes:%d, hw_ptr: %d new_period:%d\n", ++ alsa_stream->pos, ++ consumed, ++ alsa_stream->buffer_size, ++ (int)(alsa_stream->period_size*alsa_stream->substream->runtime->periods), ++ frames_to_bytes(alsa_stream->substream->runtime, alsa_stream->substream->runtime->status->hw_ptr), ++ new_period); ++ if (alsa_stream->buffer_size) { ++ alsa_stream->pos += consumed &~ (1<<30); ++ alsa_stream->pos %= alsa_stream->buffer_size; ++ } ++ ++ if (alsa_stream->substream) { ++ if (new_period) ++ snd_pcm_period_elapsed(alsa_stream->substream); ++ } else { ++ audio_warning(" unexpected NULL substream\n"); ++ } ++ audio_info(" .. OUT\n"); ++ ++ return IRQ_HANDLED; ++} ++ ++/* open callback */ ++static int snd_bcm2835_playback_open_generic( ++ struct snd_pcm_substream *substream, int spdif) ++{ ++ bcm2835_chip_t *chip = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ bcm2835_alsa_stream_t *alsa_stream; ++ int idx; ++ int err; ++ ++ audio_info(" .. IN (%d)\n", substream->number); ++ ++ if(mutex_lock_interruptible(&chip->audio_mutex)) ++ { ++ audio_error("Interrupted whilst waiting for lock\n"); ++ return -EINTR; ++ } ++ audio_info("Alsa open (%d)\n", substream->number); ++ idx = substream->number; ++ ++ if (spdif && chip->opened != 0) { ++ err = -EBUSY; ++ goto out; ++ } ++ else if (!spdif && (chip->opened & (1 << idx))) { ++ err = -EBUSY; ++ goto out; ++ } ++ if (idx > MAX_SUBSTREAMS) { ++ audio_error ++ ("substream(%d) device doesn't exist max(%d) substreams allowed\n", ++ idx, MAX_SUBSTREAMS); ++ err = -ENODEV; ++ goto out; ++ } ++ ++ /* Check if we are ready */ ++ if (!(chip->avail_substreams & (1 << idx))) { ++ /* We are not ready yet */ ++ audio_error("substream(%d) device is not ready yet\n", idx); ++ err = -EAGAIN; ++ goto out; ++ } ++ ++ alsa_stream = kzalloc(sizeof(bcm2835_alsa_stream_t), GFP_KERNEL); ++ if (alsa_stream == NULL) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* Initialise alsa_stream */ ++ alsa_stream->chip = chip; ++ alsa_stream->substream = substream; ++ alsa_stream->idx = idx; ++ ++ sema_init(&alsa_stream->buffers_update_sem, 0); ++ sema_init(&alsa_stream->control_sem, 0); ++ spin_lock_init(&alsa_stream->lock); ++ ++ /* Enabled in start trigger, called on each "fifo irq" after that */ ++ alsa_stream->enable_fifo_irq = 0; ++ alsa_stream->fifo_irq_handler = bcm2835_playback_fifo_irq; ++ ++ err = bcm2835_audio_open(alsa_stream); ++ if (err != 0) { ++ kfree(alsa_stream); ++ goto out; ++ } ++ runtime->private_data = alsa_stream; ++ runtime->private_free = snd_bcm2835_playback_free; ++ if (spdif) { ++ runtime->hw = snd_bcm2835_playback_spdif_hw; ++ } else { ++ /* clear spdif status, as we are not in spdif mode */ ++ chip->spdif_status = 0; ++ runtime->hw = snd_bcm2835_playback_hw; ++ } ++ /* minimum 16 bytes alignment (for vchiq bulk transfers) */ ++ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, ++ 16); ++ ++ chip->alsa_stream[idx] = alsa_stream; ++ ++ chip->opened |= (1 << idx); ++ alsa_stream->open = 1; ++ alsa_stream->draining = 1; ++ ++out: ++ mutex_unlock(&chip->audio_mutex); ++ ++ audio_info(" .. OUT =%d\n", err); ++ ++ return err; ++} ++ ++static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream) ++{ ++ return snd_bcm2835_playback_open_generic(substream, 0); ++} ++ ++static int snd_bcm2835_playback_spdif_open(struct snd_pcm_substream *substream) ++{ ++ return snd_bcm2835_playback_open_generic(substream, 1); ++} ++ ++/* close callback */ ++static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream) ++{ ++ /* the hardware-specific codes will be here */ ++ ++ bcm2835_chip_t *chip; ++ struct snd_pcm_runtime *runtime; ++ bcm2835_alsa_stream_t *alsa_stream; ++ ++ audio_info(" .. IN\n"); ++ ++ chip = snd_pcm_substream_chip(substream); ++ if(mutex_lock_interruptible(&chip->audio_mutex)) ++ { ++ audio_error("Interrupted whilst waiting for lock\n"); ++ return -EINTR; ++ } ++ runtime = substream->runtime; ++ alsa_stream = runtime->private_data; ++ ++ audio_info("Alsa close\n"); ++ ++ /* ++ * Call stop if it's still running. This happens when app ++ * is force killed and we don't get a stop trigger. ++ */ ++ if (alsa_stream->running) { ++ int err; ++ err = bcm2835_audio_stop(alsa_stream); ++ alsa_stream->running = 0; ++ if (err != 0) ++ audio_error(" Failed to STOP alsa device\n"); ++ } ++ ++ alsa_stream->period_size = 0; ++ alsa_stream->buffer_size = 0; ++ ++ if (alsa_stream->open) { ++ alsa_stream->open = 0; ++ bcm2835_audio_close(alsa_stream); ++ } ++ if (alsa_stream->chip) ++ alsa_stream->chip->alsa_stream[alsa_stream->idx] = NULL; ++ /* ++ * Do not free up alsa_stream here, it will be freed up by ++ * runtime->private_free callback we registered in *_open above ++ */ ++ ++ chip->opened &= ~(1 << substream->number); ++ ++ mutex_unlock(&chip->audio_mutex); ++ audio_info(" .. OUT\n"); ++ ++ return 0; ++} ++ ++/* hw_params callback */ ++static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; ++ int err; ++ ++ audio_info(" .. IN\n"); ++ ++ err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); ++ if (err < 0) { ++ audio_error ++ (" pcm_lib_malloc failed to allocated pages for buffers\n"); ++ return err; ++ } ++ ++ alsa_stream->channels = params_channels(params); ++ alsa_stream->params_rate = params_rate(params); ++ alsa_stream->pcm_format_width = snd_pcm_format_width(params_format (params)); ++ audio_info(" .. OUT\n"); ++ ++ return err; ++} ++ ++/* hw_free callback */ ++static int snd_bcm2835_pcm_hw_free(struct snd_pcm_substream *substream) ++{ ++ audio_info(" .. IN\n"); ++ return snd_pcm_lib_free_pages(substream); ++} ++ ++/* prepare callback */ ++static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream) ++{ ++ bcm2835_chip_t *chip = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; ++ int channels; ++ int err; ++ ++ audio_info(" .. IN\n"); ++ ++ /* notify the vchiq that it should enter spdif passthrough mode by ++ * setting channels=0 (see ++ * https://github.com/raspberrypi/linux/issues/528) */ ++ if (chip->spdif_status & IEC958_AES0_NONAUDIO) ++ channels = 0; ++ else ++ channels = alsa_stream->channels; ++ ++ err = bcm2835_audio_set_params(alsa_stream, channels, ++ alsa_stream->params_rate, ++ alsa_stream->pcm_format_width); ++ if (err < 0) { ++ audio_error(" error setting hw params\n"); ++ } ++ ++ bcm2835_audio_setup(alsa_stream); ++ ++ /* in preparation of the stream, set the controls (volume level) of the stream */ ++ bcm2835_audio_set_ctls(alsa_stream->chip); ++ ++ ++ memset(&alsa_stream->pcm_indirect, 0, sizeof(alsa_stream->pcm_indirect)); ++ ++ alsa_stream->pcm_indirect.hw_buffer_size = ++ alsa_stream->pcm_indirect.sw_buffer_size = ++ snd_pcm_lib_buffer_bytes(substream); ++ ++ alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream); ++ alsa_stream->period_size = snd_pcm_lib_period_bytes(substream); ++ alsa_stream->pos = 0; ++ ++ audio_debug("buffer_size=%d, period_size=%d pos=%d frame_bits=%d\n", ++ alsa_stream->buffer_size, alsa_stream->period_size, ++ alsa_stream->pos, runtime->frame_bits); ++ ++ audio_info(" .. OUT\n"); ++ return 0; ++} ++ ++static void snd_bcm2835_pcm_transfer(struct snd_pcm_substream *substream, ++ struct snd_pcm_indirect *rec, size_t bytes) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; ++ void *src = (void *)(substream->runtime->dma_area + rec->sw_data); ++ int err; ++ ++ err = bcm2835_audio_write(alsa_stream, bytes, src); ++ if (err) ++ audio_error(" Failed to transfer to alsa device (%d)\n", err); ++ ++} ++ ++static int snd_bcm2835_pcm_ack(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; ++ struct snd_pcm_indirect *pcm_indirect = &alsa_stream->pcm_indirect; ++ ++ pcm_indirect->hw_queue_size = runtime->hw.buffer_bytes_max; ++ snd_pcm_indirect_playback_transfer(substream, pcm_indirect, ++ snd_bcm2835_pcm_transfer); ++ return 0; ++} ++ ++/* trigger callback */ ++static int snd_bcm2835_pcm_trigger(struct snd_pcm_substream *substream, int cmd) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; ++ int err = 0; ++ ++ audio_info(" .. IN\n"); ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ audio_debug("bcm2835_AUDIO_TRIGGER_START running=%d\n", ++ alsa_stream->running); ++ if (!alsa_stream->running) { ++ err = bcm2835_audio_start(alsa_stream); ++ if (err == 0) { ++ alsa_stream->pcm_indirect.hw_io = ++ alsa_stream->pcm_indirect.hw_data = ++ bytes_to_frames(runtime, ++ alsa_stream->pos); ++ substream->ops->ack(substream); ++ alsa_stream->running = 1; ++ alsa_stream->draining = 1; ++ } else { ++ audio_error(" Failed to START alsa device (%d)\n", err); ++ } ++ } ++ break; ++ case SNDRV_PCM_TRIGGER_STOP: ++ audio_debug ++ ("bcm2835_AUDIO_TRIGGER_STOP running=%d draining=%d\n", ++ alsa_stream->running, runtime->status->state == SNDRV_PCM_STATE_DRAINING); ++ if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { ++ audio_info("DRAINING\n"); ++ alsa_stream->draining = 1; ++ } else { ++ audio_info("DROPPING\n"); ++ alsa_stream->draining = 0; ++ } ++ if (alsa_stream->running) { ++ err = bcm2835_audio_stop(alsa_stream); ++ if (err != 0) ++ audio_error(" Failed to STOP alsa device (%d)\n", err); ++ alsa_stream->running = 0; ++ } ++ break; ++ default: ++ err = -EINVAL; ++ } ++ ++ audio_info(" .. OUT\n"); ++ return err; ++} ++ ++/* pointer callback */ ++static snd_pcm_uframes_t ++snd_bcm2835_pcm_pointer(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; ++ ++ audio_info(" .. IN\n"); ++ ++ audio_debug("pcm_pointer... (%d) hwptr=%d appl=%d pos=%d\n", 0, ++ frames_to_bytes(runtime, runtime->status->hw_ptr), ++ frames_to_bytes(runtime, runtime->control->appl_ptr), ++ alsa_stream->pos); ++ ++ audio_info(" .. OUT\n"); ++ return snd_pcm_indirect_playback_pointer(substream, ++ &alsa_stream->pcm_indirect, ++ alsa_stream->pos); ++} ++ ++static int snd_bcm2835_pcm_lib_ioctl(struct snd_pcm_substream *substream, ++ unsigned int cmd, void *arg) ++{ ++ int ret = snd_pcm_lib_ioctl(substream, cmd, arg); ++ audio_info(" .. substream=%p, cmd=%d, arg=%p (%x) ret=%d\n", substream, ++ cmd, arg, arg ? *(unsigned *)arg : 0, ret); ++ return ret; ++} ++ ++/* operators */ ++static struct snd_pcm_ops snd_bcm2835_playback_ops = { ++ .open = snd_bcm2835_playback_open, ++ .close = snd_bcm2835_playback_close, ++ .ioctl = snd_bcm2835_pcm_lib_ioctl, ++ .hw_params = snd_bcm2835_pcm_hw_params, ++ .hw_free = snd_bcm2835_pcm_hw_free, ++ .prepare = snd_bcm2835_pcm_prepare, ++ .trigger = snd_bcm2835_pcm_trigger, ++ .pointer = snd_bcm2835_pcm_pointer, ++ .ack = snd_bcm2835_pcm_ack, ++}; ++ ++static struct snd_pcm_ops snd_bcm2835_playback_spdif_ops = { ++ .open = snd_bcm2835_playback_spdif_open, ++ .close = snd_bcm2835_playback_close, ++ .ioctl = snd_bcm2835_pcm_lib_ioctl, ++ .hw_params = snd_bcm2835_pcm_hw_params, ++ .hw_free = snd_bcm2835_pcm_hw_free, ++ .prepare = snd_bcm2835_pcm_prepare, ++ .trigger = snd_bcm2835_pcm_trigger, ++ .pointer = snd_bcm2835_pcm_pointer, ++ .ack = snd_bcm2835_pcm_ack, ++}; ++ ++/* create a pcm device */ ++int snd_bcm2835_new_pcm(bcm2835_chip_t * chip) ++{ ++ struct snd_pcm *pcm; ++ int err; ++ ++ audio_info(" .. IN\n"); ++ mutex_init(&chip->audio_mutex); ++ if(mutex_lock_interruptible(&chip->audio_mutex)) ++ { ++ audio_error("Interrupted whilst waiting for lock\n"); ++ return -EINTR; ++ } ++ err = ++ snd_pcm_new(chip->card, "bcm2835 ALSA", 0, MAX_SUBSTREAMS, 0, &pcm); ++ if (err < 0) ++ goto out; ++ pcm->private_data = chip; ++ strcpy(pcm->name, "bcm2835 ALSA"); ++ chip->pcm = pcm; ++ chip->dest = AUDIO_DEST_AUTO; ++ chip->volume = alsa2chip(0); ++ chip->mute = CTRL_VOL_UNMUTE; /*disable mute on startup */ ++ /* set operators */ ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, ++ &snd_bcm2835_playback_ops); ++ ++ /* pre-allocation of buffers */ ++ /* NOTE: this may fail */ ++ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, ++ snd_dma_continuous_data ++ (GFP_KERNEL), 64 * 1024, ++ 64 * 1024); ++ ++out: ++ mutex_unlock(&chip->audio_mutex); ++ audio_info(" .. OUT\n"); ++ ++ return 0; ++} ++ ++int snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip) ++{ ++ struct snd_pcm *pcm; ++ int err; ++ ++ audio_info(" .. IN\n"); ++ if(mutex_lock_interruptible(&chip->audio_mutex)) ++ { ++ audio_error("Interrupted whilst waiting for lock\n"); ++ return -EINTR; ++ } ++ err = snd_pcm_new(chip->card, "bcm2835 ALSA", 1, 1, 0, &pcm); ++ if (err < 0) ++ goto out; ++ ++ pcm->private_data = chip; ++ strcpy(pcm->name, "bcm2835 IEC958/HDMI"); ++ chip->pcm_spdif = pcm; ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, ++ &snd_bcm2835_playback_spdif_ops); ++ ++ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, ++ snd_dma_continuous_data (GFP_KERNEL), ++ 64 * 1024, 64 * 1024); ++out: ++ mutex_unlock(&chip->audio_mutex); ++ audio_info(" .. OUT\n"); ++ ++ return 0; ++} +--- /dev/null ++++ b/sound/arm/bcm2835-vchiq.c +@@ -0,0 +1,902 @@ ++/***************************************************************************** ++* 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "bcm2835.h" ++ ++/* ---- Include Files -------------------------------------------------------- */ ++ ++#include "interface/vchi/vchi.h" ++#include "vc_vchi_audioserv_defs.h" ++ ++/* ---- Private Constants and Types ------------------------------------------ */ ++ ++#define BCM2835_AUDIO_STOP 0 ++#define BCM2835_AUDIO_START 1 ++#define BCM2835_AUDIO_WRITE 2 ++ ++/* Logging macros (for remapping to other logging mechanisms, i.e., printf) */ ++#ifdef AUDIO_DEBUG_ENABLE ++ #define LOG_ERR( fmt, arg... ) pr_err( "%s:%d " fmt, __func__, __LINE__, ##arg) ++ #define LOG_WARN( fmt, arg... ) pr_info( "%s:%d " fmt, __func__, __LINE__, ##arg) ++ #define LOG_INFO( fmt, arg... ) pr_info( "%s:%d " fmt, __func__, __LINE__, ##arg) ++ #define LOG_DBG( fmt, arg... ) pr_info( "%s:%d " fmt, __func__, __LINE__, ##arg) ++#else ++ #define LOG_ERR( fmt, arg... ) pr_err( "%s:%d " fmt, __func__, __LINE__, ##arg) ++ #define LOG_WARN( fmt, arg... ) ++ #define LOG_INFO( fmt, arg... ) ++ #define LOG_DBG( fmt, arg... ) ++#endif ++ ++typedef struct opaque_AUDIO_INSTANCE_T { ++ uint32_t num_connections; ++ VCHI_SERVICE_HANDLE_T vchi_handle[VCHI_MAX_NUM_CONNECTIONS]; ++ struct completion msg_avail_comp; ++ struct mutex vchi_mutex; ++ bcm2835_alsa_stream_t *alsa_stream; ++ int32_t result; ++ short peer_version; ++} AUDIO_INSTANCE_T; ++ ++bool force_bulk = false; ++ ++/* ---- Private Variables ---------------------------------------------------- */ ++ ++/* ---- Private Function Prototypes ------------------------------------------ */ ++ ++/* ---- Private Functions ---------------------------------------------------- */ ++ ++static int bcm2835_audio_stop_worker(bcm2835_alsa_stream_t * alsa_stream); ++static int bcm2835_audio_start_worker(bcm2835_alsa_stream_t * alsa_stream); ++static int bcm2835_audio_write_worker(bcm2835_alsa_stream_t *alsa_stream, ++ uint32_t count, void *src); ++ ++typedef struct { ++ struct work_struct my_work; ++ bcm2835_alsa_stream_t *alsa_stream; ++ int cmd; ++ void *src; ++ uint32_t count; ++} my_work_t; ++ ++static void my_wq_function(struct work_struct *work) ++{ ++ my_work_t *w = (my_work_t *) work; ++ int ret = -9; ++ LOG_DBG(" .. IN %p:%d\n", w->alsa_stream, w->cmd); ++ switch (w->cmd) { ++ case BCM2835_AUDIO_START: ++ ret = bcm2835_audio_start_worker(w->alsa_stream); ++ break; ++ case BCM2835_AUDIO_STOP: ++ ret = bcm2835_audio_stop_worker(w->alsa_stream); ++ break; ++ case BCM2835_AUDIO_WRITE: ++ ret = bcm2835_audio_write_worker(w->alsa_stream, w->count, ++ w->src); ++ break; ++ default: ++ LOG_ERR(" Unexpected work: %p:%d\n", w->alsa_stream, w->cmd); ++ break; ++ } ++ kfree((void *)work); ++ LOG_DBG(" .. OUT %d\n", ret); ++} ++ ++int bcm2835_audio_start(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ int ret = -1; ++ LOG_DBG(" .. IN\n"); ++ if (alsa_stream->my_wq) { ++ my_work_t *work = kmalloc(sizeof(my_work_t), GFP_ATOMIC); ++ /*--- Queue some work (item 1) ---*/ ++ if (work) { ++ INIT_WORK((struct work_struct *)work, my_wq_function); ++ work->alsa_stream = alsa_stream; ++ work->cmd = BCM2835_AUDIO_START; ++ if (queue_work ++ (alsa_stream->my_wq, (struct work_struct *)work)) ++ ret = 0; ++ } else ++ LOG_ERR(" .. Error: NULL work kmalloc\n"); ++ } ++ LOG_DBG(" .. OUT %d\n", ret); ++ return ret; ++} ++ ++int bcm2835_audio_stop(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ int ret = -1; ++ LOG_DBG(" .. IN\n"); ++ if (alsa_stream->my_wq) { ++ my_work_t *work = kmalloc(sizeof(my_work_t), GFP_ATOMIC); ++ /*--- Queue some work (item 1) ---*/ ++ if (work) { ++ INIT_WORK((struct work_struct *)work, my_wq_function); ++ work->alsa_stream = alsa_stream; ++ work->cmd = BCM2835_AUDIO_STOP; ++ if (queue_work ++ (alsa_stream->my_wq, (struct work_struct *)work)) ++ ret = 0; ++ } else ++ LOG_ERR(" .. Error: NULL work kmalloc\n"); ++ } ++ LOG_DBG(" .. OUT %d\n", ret); ++ return ret; ++} ++ ++int bcm2835_audio_write(bcm2835_alsa_stream_t *alsa_stream, ++ uint32_t count, void *src) ++{ ++ int ret = -1; ++ LOG_DBG(" .. IN\n"); ++ if (alsa_stream->my_wq) { ++ my_work_t *work = kmalloc(sizeof(my_work_t), GFP_ATOMIC); ++ /*--- Queue some work (item 1) ---*/ ++ if (work) { ++ INIT_WORK((struct work_struct *)work, my_wq_function); ++ work->alsa_stream = alsa_stream; ++ work->cmd = BCM2835_AUDIO_WRITE; ++ work->src = src; ++ work->count = count; ++ if (queue_work ++ (alsa_stream->my_wq, (struct work_struct *)work)) ++ ret = 0; ++ } else ++ LOG_ERR(" .. Error: NULL work kmalloc\n"); ++ } ++ LOG_DBG(" .. OUT %d\n", ret); ++ return ret; ++} ++ ++void my_workqueue_init(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ alsa_stream->my_wq = alloc_workqueue("my_queue", WQ_HIGHPRI, 1); ++ return; ++} ++ ++void my_workqueue_quit(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ if (alsa_stream->my_wq) { ++ flush_workqueue(alsa_stream->my_wq); ++ destroy_workqueue(alsa_stream->my_wq); ++ alsa_stream->my_wq = NULL; ++ } ++ return; ++} ++ ++static void audio_vchi_callback(void *param, ++ const VCHI_CALLBACK_REASON_T reason, ++ void *msg_handle) ++{ ++ AUDIO_INSTANCE_T *instance = (AUDIO_INSTANCE_T *) param; ++ int32_t status; ++ int32_t msg_len; ++ VC_AUDIO_MSG_T m; ++ LOG_DBG(" .. IN instance=%p, handle=%p, alsa=%p, reason=%d, handle=%p\n", ++ instance, instance ? instance->vchi_handle[0] : NULL, instance ? instance->alsa_stream : NULL, reason, msg_handle); ++ ++ if (reason != VCHI_CALLBACK_MSG_AVAILABLE) { ++ return; ++ } ++ if (!instance) { ++ LOG_ERR(" .. instance is null\n"); ++ BUG(); ++ return; ++ } ++ if (!instance->vchi_handle[0]) { ++ LOG_ERR(" .. instance->vchi_handle[0] is null\n"); ++ BUG(); ++ return; ++ } ++ status = vchi_msg_dequeue(instance->vchi_handle[0], ++ &m, sizeof m, &msg_len, VCHI_FLAGS_NONE); ++ if (m.type == VC_AUDIO_MSG_TYPE_RESULT) { ++ LOG_DBG ++ (" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_RESULT, success=%d\n", ++ instance, m.u.result.success); ++ instance->result = m.u.result.success; ++ complete(&instance->msg_avail_comp); ++ } else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) { ++ bcm2835_alsa_stream_t *alsa_stream = instance->alsa_stream; ++ irq_handler_t callback = (irq_handler_t) m.u.complete.callback; ++ LOG_DBG ++ (" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_COMPLETE, complete=%d\n", ++ instance, m.u.complete.count); ++ if (alsa_stream && callback) { ++ atomic_add(m.u.complete.count, &alsa_stream->retrieved); ++ callback(0, alsa_stream); ++ } else { ++ LOG_ERR(" .. unexpected alsa_stream=%p, callback=%p\n", ++ alsa_stream, callback); ++ } ++ } else { ++ LOG_ERR(" .. unexpected m.type=%d\n", m.type); ++ } ++ LOG_DBG(" .. OUT\n"); ++} ++ ++static AUDIO_INSTANCE_T *vc_vchi_audio_init(VCHI_INSTANCE_T vchi_instance, ++ VCHI_CONNECTION_T ** ++ vchi_connections, ++ uint32_t num_connections) ++{ ++ uint32_t i; ++ AUDIO_INSTANCE_T *instance; ++ int status; ++ ++ LOG_DBG("%s: start", __func__); ++ ++ if (num_connections > VCHI_MAX_NUM_CONNECTIONS) { ++ LOG_ERR("%s: unsupported number of connections %u (max=%u)\n", ++ __func__, num_connections, VCHI_MAX_NUM_CONNECTIONS); ++ ++ return NULL; ++ } ++ /* Allocate memory for this instance */ ++ instance = kmalloc(sizeof(*instance), GFP_KERNEL); ++ if (!instance) ++ return NULL; ++ ++ memset(instance, 0, sizeof(*instance)); ++ instance->num_connections = num_connections; ++ ++ /* Create a lock for exclusive, serialized VCHI connection access */ ++ mutex_init(&instance->vchi_mutex); ++ /* Open the VCHI service connections */ ++ for (i = 0; i < num_connections; i++) { ++ SERVICE_CREATION_T params = { ++ VCHI_VERSION_EX(VC_AUDIOSERV_VER, VC_AUDIOSERV_MIN_VER), ++ VC_AUDIO_SERVER_NAME, // 4cc service code ++ vchi_connections[i], // passed in fn pointers ++ 0, // rx fifo size (unused) ++ 0, // tx fifo size (unused) ++ audio_vchi_callback, // service callback ++ instance, // service callback parameter ++ 1, //TODO: remove VCOS_FALSE, // unaligned bulk recieves ++ 1, //TODO: remove VCOS_FALSE, // unaligned bulk transmits ++ 0 // want crc check on bulk transfers ++ }; ++ ++ LOG_DBG("%s: about to open %i\n", __func__, i); ++ status = vchi_service_open(vchi_instance, ¶ms, ++ &instance->vchi_handle[i]); ++ LOG_DBG("%s: opened %i: %p=%d\n", __func__, i, instance->vchi_handle[i], status); ++ if (status) { ++ LOG_ERR ++ ("%s: failed to open VCHI service connection (status=%d)\n", ++ __func__, status); ++ ++ goto err_close_services; ++ } ++ /* Finished with the service for now */ ++ vchi_service_release(instance->vchi_handle[i]); ++ } ++ ++ LOG_DBG("%s: okay\n", __func__); ++ return instance; ++ ++err_close_services: ++ for (i = 0; i < instance->num_connections; i++) { ++ LOG_ERR("%s: closing %i: %p\n", __func__, i, instance->vchi_handle[i]); ++ if (instance->vchi_handle[i]) ++ vchi_service_close(instance->vchi_handle[i]); ++ } ++ ++ kfree(instance); ++ LOG_ERR("%s: error\n", __func__); ++ ++ return NULL; ++} ++ ++static int32_t vc_vchi_audio_deinit(AUDIO_INSTANCE_T * instance) ++{ ++ uint32_t i; ++ ++ LOG_DBG(" .. IN\n"); ++ ++ if (instance == NULL) { ++ LOG_ERR("%s: invalid handle %p\n", __func__, instance); ++ ++ return -1; ++ } ++ ++ LOG_DBG(" .. about to lock (%d)\n", instance->num_connections); ++ if(mutex_lock_interruptible(&instance->vchi_mutex)) ++ { ++ LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); ++ return -EINTR; ++ } ++ ++ /* Close all VCHI service connections */ ++ for (i = 0; i < instance->num_connections; i++) { ++ int32_t success; ++ LOG_DBG(" .. %i:closing %p\n", i, instance->vchi_handle[i]); ++ vchi_service_use(instance->vchi_handle[i]); ++ ++ success = vchi_service_close(instance->vchi_handle[i]); ++ if (success != 0) { ++ LOG_DBG ++ ("%s: failed to close VCHI service connection (status=%d)\n", ++ __func__, success); ++ } ++ } ++ ++ mutex_unlock(&instance->vchi_mutex); ++ ++ kfree(instance); ++ ++ LOG_DBG(" .. OUT\n"); ++ ++ return 0; ++} ++ ++static int bcm2835_audio_open_connection(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ static VCHI_INSTANCE_T vchi_instance; ++ static VCHI_CONNECTION_T *vchi_connection; ++ static int initted; ++ AUDIO_INSTANCE_T *instance = alsa_stream->instance; ++ int ret; ++ LOG_DBG(" .. IN\n"); ++ ++ LOG_INFO("%s: start\n", __func__); ++ BUG_ON(instance); ++ if (instance) { ++ LOG_ERR("%s: VCHI instance already open (%p)\n", ++ __func__, instance); ++ instance->alsa_stream = alsa_stream; ++ alsa_stream->instance = instance; ++ ret = 0; // xxx todo -1; ++ goto err_free_mem; ++ } ++ ++ /* Initialize and create a VCHI connection */ ++ if (!initted) { ++ ret = vchi_initialise(&vchi_instance); ++ if (ret != 0) { ++ LOG_ERR("%s: failed to initialise VCHI instance (ret=%d)\n", ++ __func__, ret); ++ ++ ret = -EIO; ++ goto err_free_mem; ++ } ++ ret = vchi_connect(NULL, 0, vchi_instance); ++ if (ret != 0) { ++ LOG_ERR("%s: failed to connect VCHI instance (ret=%d)\n", ++ __func__, ret); ++ ++ ret = -EIO; ++ goto err_free_mem; ++ } ++ initted = 1; ++ } ++ ++ /* Initialize an instance of the audio service */ ++ instance = vc_vchi_audio_init(vchi_instance, &vchi_connection, 1); ++ ++ if (instance == NULL) { ++ LOG_ERR("%s: failed to initialize audio service\n", __func__); ++ ++ ret = -EPERM; ++ goto err_free_mem; ++ } ++ ++ instance->alsa_stream = alsa_stream; ++ alsa_stream->instance = instance; ++ ++ LOG_DBG(" success !\n"); ++err_free_mem: ++ LOG_DBG(" .. OUT\n"); ++ ++ return ret; ++} ++ ++int bcm2835_audio_open(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ AUDIO_INSTANCE_T *instance; ++ VC_AUDIO_MSG_T m; ++ int32_t success; ++ int ret; ++ LOG_DBG(" .. IN\n"); ++ ++ my_workqueue_init(alsa_stream); ++ ++ ret = bcm2835_audio_open_connection(alsa_stream); ++ if (ret != 0) { ++ ret = -1; ++ goto exit; ++ } ++ instance = alsa_stream->instance; ++ LOG_DBG(" instance (%p)\n", instance); ++ ++ if(mutex_lock_interruptible(&instance->vchi_mutex)) ++ { ++ LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); ++ return -EINTR; ++ } ++ vchi_service_use(instance->vchi_handle[0]); ++ ++ m.type = VC_AUDIO_MSG_TYPE_OPEN; ++ ++ /* Send the message to the videocore */ ++ success = vchi_msg_queue(instance->vchi_handle[0], ++ &m, sizeof m, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ if (success != 0) { ++ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", ++ __func__, success); ++ ++ ret = -1; ++ goto unlock; ++ } ++ ++ ret = 0; ++ ++unlock: ++ vchi_service_release(instance->vchi_handle[0]); ++ mutex_unlock(&instance->vchi_mutex); ++exit: ++ LOG_DBG(" .. OUT\n"); ++ return ret; ++} ++ ++static int bcm2835_audio_set_ctls_chan(bcm2835_alsa_stream_t * alsa_stream, ++ bcm2835_chip_t * chip) ++{ ++ VC_AUDIO_MSG_T m; ++ AUDIO_INSTANCE_T *instance = alsa_stream->instance; ++ int32_t success; ++ int ret; ++ LOG_DBG(" .. IN\n"); ++ ++ LOG_INFO ++ (" Setting ALSA dest(%d), volume(%d)\n", chip->dest, chip->volume); ++ ++ if(mutex_lock_interruptible(&instance->vchi_mutex)) ++ { ++ LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); ++ return -EINTR; ++ } ++ vchi_service_use(instance->vchi_handle[0]); ++ ++ instance->result = -1; ++ ++ m.type = VC_AUDIO_MSG_TYPE_CONTROL; ++ m.u.control.dest = chip->dest; ++ m.u.control.volume = chip->volume; ++ ++ /* Create the message available completion */ ++ init_completion(&instance->msg_avail_comp); ++ ++ /* Send the message to the videocore */ ++ success = vchi_msg_queue(instance->vchi_handle[0], ++ &m, sizeof m, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ if (success != 0) { ++ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", ++ __func__, success); ++ ++ ret = -1; ++ goto unlock; ++ } ++ ++ /* We are expecting a reply from the videocore */ ++ ret = wait_for_completion_interruptible(&instance->msg_avail_comp); ++ if (ret) { ++ LOG_DBG("%s: failed on waiting for event (status=%d)\n", ++ __func__, success); ++ goto unlock; ++ } ++ ++ if (instance->result != 0) { ++ LOG_ERR("%s: result=%d\n", __func__, instance->result); ++ ++ ret = -1; ++ goto unlock; ++ } ++ ++ ret = 0; ++ ++unlock: ++ vchi_service_release(instance->vchi_handle[0]); ++ mutex_unlock(&instance->vchi_mutex); ++ ++ LOG_DBG(" .. OUT\n"); ++ return ret; ++} ++ ++int bcm2835_audio_set_ctls(bcm2835_chip_t * chip) ++{ ++ int i; ++ int ret = 0; ++ LOG_DBG(" .. IN\n"); ++ LOG_DBG(" Setting ALSA dest(%d), volume(%d)\n", chip->dest, chip->volume); ++ ++ /* change ctls for all substreams */ ++ for (i = 0; i < MAX_SUBSTREAMS; i++) { ++ if (chip->avail_substreams & (1 << i)) { ++ if (!chip->alsa_stream[i]) ++ { ++ LOG_DBG(" No ALSA stream available?! %i:%p (%x)\n", i, chip->alsa_stream[i], chip->avail_substreams); ++ ret = 0; ++ } ++ else if (bcm2835_audio_set_ctls_chan /* returns 0 on success */ ++ (chip->alsa_stream[i], chip) != 0) ++ { ++ LOG_ERR("Couldn't set the controls for stream %d\n", i); ++ ret = -1; ++ } ++ else LOG_DBG(" Controls set for stream %d\n", i); ++ } ++ } ++ LOG_DBG(" .. OUT ret=%d\n", ret); ++ return ret; ++} ++ ++int bcm2835_audio_set_params(bcm2835_alsa_stream_t * alsa_stream, ++ uint32_t channels, uint32_t samplerate, ++ uint32_t bps) ++{ ++ VC_AUDIO_MSG_T m; ++ AUDIO_INSTANCE_T *instance = alsa_stream->instance; ++ int32_t success; ++ int ret; ++ LOG_DBG(" .. IN\n"); ++ ++ LOG_INFO ++ (" Setting ALSA channels(%d), samplerate(%d), bits-per-sample(%d)\n", ++ channels, samplerate, bps); ++ ++ /* resend ctls - alsa_stream may not have been open when first send */ ++ ret = bcm2835_audio_set_ctls_chan(alsa_stream, alsa_stream->chip); ++ if (ret != 0) { ++ LOG_ERR(" Alsa controls not supported\n"); ++ return -EINVAL; ++ } ++ ++ if(mutex_lock_interruptible(&instance->vchi_mutex)) ++ { ++ LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); ++ return -EINTR; ++ } ++ vchi_service_use(instance->vchi_handle[0]); ++ ++ instance->result = -1; ++ ++ m.type = VC_AUDIO_MSG_TYPE_CONFIG; ++ m.u.config.channels = channels; ++ m.u.config.samplerate = samplerate; ++ m.u.config.bps = bps; ++ ++ /* Create the message available completion */ ++ init_completion(&instance->msg_avail_comp); ++ ++ /* Send the message to the videocore */ ++ success = vchi_msg_queue(instance->vchi_handle[0], ++ &m, sizeof m, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ if (success != 0) { ++ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", ++ __func__, success); ++ ++ ret = -1; ++ goto unlock; ++ } ++ ++ /* We are expecting a reply from the videocore */ ++ ret = wait_for_completion_interruptible(&instance->msg_avail_comp); ++ if (ret) { ++ LOG_DBG("%s: failed on waiting for event (status=%d)\n", ++ __func__, success); ++ goto unlock; ++ } ++ ++ if (instance->result != 0) { ++ LOG_ERR("%s: result=%d", __func__, instance->result); ++ ++ ret = -1; ++ goto unlock; ++ } ++ ++ ret = 0; ++ ++unlock: ++ vchi_service_release(instance->vchi_handle[0]); ++ mutex_unlock(&instance->vchi_mutex); ++ ++ LOG_DBG(" .. OUT\n"); ++ return ret; ++} ++ ++int bcm2835_audio_setup(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ LOG_DBG(" .. IN\n"); ++ ++ LOG_DBG(" .. OUT\n"); ++ ++ return 0; ++} ++ ++static int bcm2835_audio_start_worker(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ VC_AUDIO_MSG_T m; ++ AUDIO_INSTANCE_T *instance = alsa_stream->instance; ++ int32_t success; ++ int ret; ++ LOG_DBG(" .. IN\n"); ++ ++ if(mutex_lock_interruptible(&instance->vchi_mutex)) ++ { ++ LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); ++ return -EINTR; ++ } ++ vchi_service_use(instance->vchi_handle[0]); ++ ++ m.type = VC_AUDIO_MSG_TYPE_START; ++ ++ /* Send the message to the videocore */ ++ success = vchi_msg_queue(instance->vchi_handle[0], ++ &m, sizeof m, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ if (success != 0) { ++ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", ++ __func__, success); ++ ++ ret = -1; ++ goto unlock; ++ } ++ ++ ret = 0; ++ ++unlock: ++ vchi_service_release(instance->vchi_handle[0]); ++ mutex_unlock(&instance->vchi_mutex); ++ LOG_DBG(" .. OUT\n"); ++ return ret; ++} ++ ++static int bcm2835_audio_stop_worker(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ VC_AUDIO_MSG_T m; ++ AUDIO_INSTANCE_T *instance = alsa_stream->instance; ++ int32_t success; ++ int ret; ++ LOG_DBG(" .. IN\n"); ++ ++ if(mutex_lock_interruptible(&instance->vchi_mutex)) ++ { ++ LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); ++ return -EINTR; ++ } ++ vchi_service_use(instance->vchi_handle[0]); ++ ++ m.type = VC_AUDIO_MSG_TYPE_STOP; ++ m.u.stop.draining = alsa_stream->draining; ++ ++ /* Send the message to the videocore */ ++ success = vchi_msg_queue(instance->vchi_handle[0], ++ &m, sizeof m, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ if (success != 0) { ++ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", ++ __func__, success); ++ ++ ret = -1; ++ goto unlock; ++ } ++ ++ ret = 0; ++ ++unlock: ++ vchi_service_release(instance->vchi_handle[0]); ++ mutex_unlock(&instance->vchi_mutex); ++ LOG_DBG(" .. OUT\n"); ++ return ret; ++} ++ ++int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ VC_AUDIO_MSG_T m; ++ AUDIO_INSTANCE_T *instance = alsa_stream->instance; ++ int32_t success; ++ int ret; ++ LOG_DBG(" .. IN\n"); ++ ++ my_workqueue_quit(alsa_stream); ++ ++ if(mutex_lock_interruptible(&instance->vchi_mutex)) ++ { ++ LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); ++ return -EINTR; ++ } ++ vchi_service_use(instance->vchi_handle[0]); ++ ++ m.type = VC_AUDIO_MSG_TYPE_CLOSE; ++ ++ /* Create the message available completion */ ++ init_completion(&instance->msg_avail_comp); ++ ++ /* Send the message to the videocore */ ++ success = vchi_msg_queue(instance->vchi_handle[0], ++ &m, sizeof m, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ if (success != 0) { ++ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", ++ __func__, success); ++ ret = -1; ++ goto unlock; ++ } ++ ++ ret = wait_for_completion_interruptible(&instance->msg_avail_comp); ++ if (ret) { ++ LOG_DBG("%s: failed on waiting for event (status=%d)\n", ++ __func__, success); ++ goto unlock; ++ } ++ if (instance->result != 0) { ++ LOG_ERR("%s: failed result (status=%d)\n", ++ __func__, instance->result); ++ ++ ret = -1; ++ goto unlock; ++ } ++ ++ ret = 0; ++ ++unlock: ++ vchi_service_release(instance->vchi_handle[0]); ++ mutex_unlock(&instance->vchi_mutex); ++ ++ /* Stop the audio service */ ++ if (instance) { ++ vc_vchi_audio_deinit(instance); ++ alsa_stream->instance = NULL; ++ } ++ LOG_DBG(" .. OUT\n"); ++ return ret; ++} ++ ++int bcm2835_audio_write_worker(bcm2835_alsa_stream_t *alsa_stream, ++ uint32_t count, void *src) ++{ ++ VC_AUDIO_MSG_T m; ++ AUDIO_INSTANCE_T *instance = alsa_stream->instance; ++ int32_t success; ++ int ret; ++ ++ LOG_DBG(" .. IN\n"); ++ ++ LOG_INFO(" Writing %d bytes from %p\n", count, src); ++ ++ if(mutex_lock_interruptible(&instance->vchi_mutex)) ++ { ++ LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); ++ return -EINTR; ++ } ++ vchi_service_use(instance->vchi_handle[0]); ++ ++ if ( instance->peer_version==0 && vchi_get_peer_version(instance->vchi_handle[0], &instance->peer_version) == 0 ) { ++ LOG_DBG("%s: client version %d connected\n", __func__, instance->peer_version); ++ } ++ m.type = VC_AUDIO_MSG_TYPE_WRITE; ++ m.u.write.count = count; ++ // old version uses bulk, new version uses control ++ m.u.write.max_packet = instance->peer_version < 2 || force_bulk ? 0:4000; ++ m.u.write.callback = alsa_stream->fifo_irq_handler; ++ m.u.write.cookie = alsa_stream; ++ m.u.write.silence = src == NULL; ++ ++ /* Send the message to the videocore */ ++ success = vchi_msg_queue(instance->vchi_handle[0], ++ &m, sizeof m, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ if (success != 0) { ++ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", ++ __func__, success); ++ ++ ret = -1; ++ goto unlock; ++ } ++ if (!m.u.write.silence) { ++ if (m.u.write.max_packet == 0) { ++ /* Send the message to the videocore */ ++ success = vchi_bulk_queue_transmit(instance->vchi_handle[0], ++ src, count, ++ 0 * ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED ++ + ++ 1 * ++ VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, ++ NULL); ++ } else { ++ while (count > 0) { ++ int bytes = min((int)m.u.write.max_packet, (int)count); ++ success = vchi_msg_queue(instance->vchi_handle[0], ++ src, bytes, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ src = (char *)src + bytes; ++ count -= bytes; ++ } ++ } ++ if (success != 0) { ++ LOG_ERR ++ ("%s: failed on vchi_bulk_queue_transmit (status=%d)\n", ++ __func__, success); ++ ++ ret = -1; ++ goto unlock; ++ } ++ } ++ ret = 0; ++ ++unlock: ++ vchi_service_release(instance->vchi_handle[0]); ++ mutex_unlock(&instance->vchi_mutex); ++ LOG_DBG(" .. OUT\n"); ++ return ret; ++} ++ ++/** ++ * Returns all buffers from arm->vc ++ */ ++void bcm2835_audio_flush_buffers(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ LOG_DBG(" .. IN\n"); ++ LOG_DBG(" .. OUT\n"); ++ return; ++} ++ ++/** ++ * Forces VC to flush(drop) its filled playback buffers and ++ * return them the us. (VC->ARM) ++ */ ++void bcm2835_audio_flush_playback_buffers(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ LOG_DBG(" .. IN\n"); ++ LOG_DBG(" .. OUT\n"); ++} ++ ++uint32_t bcm2835_audio_retrieve_buffers(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ uint32_t count = atomic_read(&alsa_stream->retrieved); ++ atomic_sub(count, &alsa_stream->retrieved); ++ return count; ++} ++ ++module_param(force_bulk, bool, 0444); ++MODULE_PARM_DESC(force_bulk, "Force use of vchiq bulk for audio"); +--- /dev/null ++++ b/sound/arm/bcm2835.c +@@ -0,0 +1,511 @@ ++/***************************************************************************** ++* 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 "bcm2835.h" ++ ++/* module parameters (see "Module Parameters") */ ++/* SNDRV_CARDS: maximum number of cards supported by this module */ ++static int index[MAX_SUBSTREAMS] = {[0 ... (MAX_SUBSTREAMS - 1)] = -1 }; ++static char *id[MAX_SUBSTREAMS] = {[0 ... (MAX_SUBSTREAMS - 1)] = NULL }; ++static int enable[MAX_SUBSTREAMS] = {[0 ... (MAX_SUBSTREAMS - 1)] = 1 }; ++ ++/* HACKY global pointers needed for successive probes to work : ssp ++ * But compared against the changes we will have to do in VC audio_ipc code ++ * to export 8 audio_ipc devices as a single IPC device and then monitor all ++ * four devices in a thread, this gets things done quickly and should be easier ++ * to debug if we run into issues ++ */ ++ ++static struct snd_card *g_card = NULL; ++static bcm2835_chip_t *g_chip = NULL; ++ ++static int snd_bcm2835_free(bcm2835_chip_t * chip) ++{ ++ kfree(chip); ++ return 0; ++} ++ ++/* component-destructor ++ * (see "Management of Cards and Components") ++ */ ++static int snd_bcm2835_dev_free(struct snd_device *device) ++{ ++ return snd_bcm2835_free(device->device_data); ++} ++ ++/* chip-specific constructor ++ * (see "Management of Cards and Components") ++ */ ++static int snd_bcm2835_create(struct snd_card *card, ++ struct platform_device *pdev, ++ bcm2835_chip_t ** rchip) ++{ ++ bcm2835_chip_t *chip; ++ int err; ++ static struct snd_device_ops ops = { ++ .dev_free = snd_bcm2835_dev_free, ++ }; ++ ++ *rchip = NULL; ++ ++ chip = kzalloc(sizeof(*chip), GFP_KERNEL); ++ if (chip == NULL) ++ return -ENOMEM; ++ ++ chip->card = card; ++ ++ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); ++ if (err < 0) { ++ snd_bcm2835_free(chip); ++ return err; ++ } ++ ++ *rchip = chip; ++ return 0; ++} ++ ++static int snd_bcm2835_alsa_probe_dt(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ bcm2835_chip_t *chip; ++ struct snd_card *card; ++ u32 numchans; ++ int err, i; ++ ++ err = of_property_read_u32(dev->of_node, "brcm,pwm-channels", ++ &numchans); ++ if (err) { ++ dev_err(dev, "Failed to get DT property 'brcm,pwm-channels'"); ++ return err; ++ } ++ ++ if (numchans == 0 || numchans > MAX_SUBSTREAMS) { ++ numchans = MAX_SUBSTREAMS; ++ dev_warn(dev, "Illegal 'brcm,pwm-channels' value, will use %u\n", ++ numchans); ++ } ++ ++ err = snd_card_new(NULL, -1, NULL, THIS_MODULE, 0, &card); ++ if (err) { ++ dev_err(dev, "Failed to create soundcard structure\n"); ++ return err; ++ } ++ ++ snd_card_set_dev(card, dev); ++ strcpy(card->driver, "bcm2835"); ++ strcpy(card->shortname, "bcm2835 ALSA"); ++ sprintf(card->longname, "%s", card->shortname); ++ ++ err = snd_bcm2835_create(card, pdev, &chip); ++ if (err < 0) { ++ dev_err(dev, "Failed to create bcm2835 chip\n"); ++ goto err_free; ++ } ++ ++ err = snd_bcm2835_new_pcm(chip); ++ if (err < 0) { ++ dev_err(dev, "Failed to create new bcm2835 pcm device\n"); ++ goto err_free; ++ } ++ ++ err = snd_bcm2835_new_spdif_pcm(chip); ++ if (err < 0) { ++ dev_err(dev, "Failed to create new bcm2835 spdif pcm device\n"); ++ goto err_free; ++ } ++ ++ err = snd_bcm2835_new_ctl(chip); ++ if (err < 0) { ++ dev_err(dev, "Failed to create new bcm2835 ctl\n"); ++ goto err_free; ++ } ++ ++ for (i = 0; i < numchans; i++) { ++ chip->avail_substreams |= (1 << i); ++ chip->pdev[i] = pdev; ++ } ++ ++ err = snd_card_register(card); ++ if (err) { ++ dev_err(dev, "Failed to register bcm2835 ALSA card \n"); ++ goto err_free; ++ } ++ ++ g_card = card; ++ g_chip = chip; ++ platform_set_drvdata(pdev, card); ++ audio_info("bcm2835 ALSA card created with %u channels\n", numchans); ++ ++ return 0; ++ ++err_free: ++ snd_card_free(card); ++ ++ return err; ++} ++ ++static int snd_bcm2835_alsa_probe(struct platform_device *pdev) ++{ ++ static int dev; ++ bcm2835_chip_t *chip; ++ struct snd_card *card; ++ int err; ++ ++ if (pdev->dev.of_node) ++ return snd_bcm2835_alsa_probe_dt(pdev); ++ ++ if (dev >= MAX_SUBSTREAMS) ++ return -ENODEV; ++ ++ if (!enable[dev]) { ++ dev++; ++ return -ENOENT; ++ } ++ ++ if (dev > 0) ++ goto add_register_map; ++ ++ err = snd_card_new(NULL, index[dev], id[dev], THIS_MODULE, 0, &g_card); ++ if (err < 0) ++ goto out; ++ ++ snd_card_set_dev(g_card, &pdev->dev); ++ strcpy(g_card->driver, "bcm2835"); ++ strcpy(g_card->shortname, "bcm2835 ALSA"); ++ sprintf(g_card->longname, "%s", g_card->shortname); ++ ++ err = snd_bcm2835_create(g_card, pdev, &chip); ++ if (err < 0) { ++ dev_err(&pdev->dev, "Failed to create bcm2835 chip\n"); ++ goto out_bcm2835_create; ++ } ++ ++ g_chip = chip; ++ err = snd_bcm2835_new_pcm(chip); ++ if (err < 0) { ++ dev_err(&pdev->dev, "Failed to create new BCM2835 pcm device\n"); ++ goto out_bcm2835_new_pcm; ++ } ++ ++ err = snd_bcm2835_new_spdif_pcm(chip); ++ if (err < 0) { ++ dev_err(&pdev->dev, "Failed to create new BCM2835 spdif pcm device\n"); ++ goto out_bcm2835_new_spdif; ++ } ++ ++ err = snd_bcm2835_new_ctl(chip); ++ if (err < 0) { ++ dev_err(&pdev->dev, "Failed to create new BCM2835 ctl\n"); ++ goto out_bcm2835_new_ctl; ++ } ++ ++add_register_map: ++ card = g_card; ++ chip = g_chip; ++ ++ BUG_ON(!(card && chip)); ++ ++ chip->avail_substreams |= (1 << dev); ++ chip->pdev[dev] = pdev; ++ ++ if (dev == 0) { ++ err = snd_card_register(card); ++ if (err < 0) { ++ dev_err(&pdev->dev, ++ "Failed to register bcm2835 ALSA card \n"); ++ goto out_card_register; ++ } ++ platform_set_drvdata(pdev, card); ++ audio_info("bcm2835 ALSA card created!\n"); ++ } else { ++ audio_info("bcm2835 ALSA chip created!\n"); ++ platform_set_drvdata(pdev, (void *)dev); ++ } ++ ++ dev++; ++ ++ return 0; ++ ++out_card_register: ++out_bcm2835_new_ctl: ++out_bcm2835_new_spdif: ++out_bcm2835_new_pcm: ++out_bcm2835_create: ++ BUG_ON(!g_card); ++ if (snd_card_free(g_card)) ++ dev_err(&pdev->dev, "Failed to free Registered alsa card\n"); ++ g_card = NULL; ++out: ++ dev = SNDRV_CARDS; /* stop more avail_substreams from being probed */ ++ dev_err(&pdev->dev, "BCM2835 ALSA Probe failed !!\n"); ++ return err; ++} ++ ++static int snd_bcm2835_alsa_remove(struct platform_device *pdev) ++{ ++ uint32_t idx; ++ void *drv_data; ++ ++ drv_data = platform_get_drvdata(pdev); ++ ++ if (drv_data == (void *)g_card) { ++ /* This is the card device */ ++ snd_card_free((struct snd_card *)drv_data); ++ g_card = NULL; ++ g_chip = NULL; ++ } else { ++ idx = (uint32_t) drv_data; ++ if (g_card != NULL) { ++ BUG_ON(!g_chip); ++ /* We pass chip device numbers in audio ipc devices ++ * other than the one we registered our card with ++ */ ++ idx = (uint32_t) drv_data; ++ BUG_ON(!idx || idx > MAX_SUBSTREAMS); ++ g_chip->avail_substreams &= ~(1 << idx); ++ /* There should be atleast one substream registered ++ * after we are done here, as it wil be removed when ++ * the *remove* is called for the card device ++ */ ++ BUG_ON(!g_chip->avail_substreams); ++ } ++ } ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int snd_bcm2835_alsa_suspend(struct platform_device *pdev, ++ pm_message_t state) ++{ ++ return 0; ++} ++ ++static int snd_bcm2835_alsa_resume(struct platform_device *pdev) ++{ ++ return 0; ++} ++ ++#endif ++ ++static const struct of_device_id snd_bcm2835_of_match_table[] = { ++ { .compatible = "brcm,bcm2835-audio", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, snd_bcm2835_of_match_table); ++ ++static struct platform_driver bcm2835_alsa0_driver = { ++ .probe = snd_bcm2835_alsa_probe, ++ .remove = snd_bcm2835_alsa_remove, ++#ifdef CONFIG_PM ++ .suspend = snd_bcm2835_alsa_suspend, ++ .resume = snd_bcm2835_alsa_resume, ++#endif ++ .driver = { ++ .name = "bcm2835_AUD0", ++ .owner = THIS_MODULE, ++ .of_match_table = snd_bcm2835_of_match_table, ++ }, ++}; ++ ++static struct platform_driver bcm2835_alsa1_driver = { ++ .probe = snd_bcm2835_alsa_probe, ++ .remove = snd_bcm2835_alsa_remove, ++#ifdef CONFIG_PM ++ .suspend = snd_bcm2835_alsa_suspend, ++ .resume = snd_bcm2835_alsa_resume, ++#endif ++ .driver = { ++ .name = "bcm2835_AUD1", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static struct platform_driver bcm2835_alsa2_driver = { ++ .probe = snd_bcm2835_alsa_probe, ++ .remove = snd_bcm2835_alsa_remove, ++#ifdef CONFIG_PM ++ .suspend = snd_bcm2835_alsa_suspend, ++ .resume = snd_bcm2835_alsa_resume, ++#endif ++ .driver = { ++ .name = "bcm2835_AUD2", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static struct platform_driver bcm2835_alsa3_driver = { ++ .probe = snd_bcm2835_alsa_probe, ++ .remove = snd_bcm2835_alsa_remove, ++#ifdef CONFIG_PM ++ .suspend = snd_bcm2835_alsa_suspend, ++ .resume = snd_bcm2835_alsa_resume, ++#endif ++ .driver = { ++ .name = "bcm2835_AUD3", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static struct platform_driver bcm2835_alsa4_driver = { ++ .probe = snd_bcm2835_alsa_probe, ++ .remove = snd_bcm2835_alsa_remove, ++#ifdef CONFIG_PM ++ .suspend = snd_bcm2835_alsa_suspend, ++ .resume = snd_bcm2835_alsa_resume, ++#endif ++ .driver = { ++ .name = "bcm2835_AUD4", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static struct platform_driver bcm2835_alsa5_driver = { ++ .probe = snd_bcm2835_alsa_probe, ++ .remove = snd_bcm2835_alsa_remove, ++#ifdef CONFIG_PM ++ .suspend = snd_bcm2835_alsa_suspend, ++ .resume = snd_bcm2835_alsa_resume, ++#endif ++ .driver = { ++ .name = "bcm2835_AUD5", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static struct platform_driver bcm2835_alsa6_driver = { ++ .probe = snd_bcm2835_alsa_probe, ++ .remove = snd_bcm2835_alsa_remove, ++#ifdef CONFIG_PM ++ .suspend = snd_bcm2835_alsa_suspend, ++ .resume = snd_bcm2835_alsa_resume, ++#endif ++ .driver = { ++ .name = "bcm2835_AUD6", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static struct platform_driver bcm2835_alsa7_driver = { ++ .probe = snd_bcm2835_alsa_probe, ++ .remove = snd_bcm2835_alsa_remove, ++#ifdef CONFIG_PM ++ .suspend = snd_bcm2835_alsa_suspend, ++ .resume = snd_bcm2835_alsa_resume, ++#endif ++ .driver = { ++ .name = "bcm2835_AUD7", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int bcm2835_alsa_device_init(void) ++{ ++ int err; ++ err = platform_driver_register(&bcm2835_alsa0_driver); ++ if (err) { ++ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); ++ goto out; ++ } ++ ++ err = platform_driver_register(&bcm2835_alsa1_driver); ++ if (err) { ++ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); ++ goto unregister_0; ++ } ++ ++ err = platform_driver_register(&bcm2835_alsa2_driver); ++ if (err) { ++ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); ++ goto unregister_1; ++ } ++ ++ err = platform_driver_register(&bcm2835_alsa3_driver); ++ if (err) { ++ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); ++ goto unregister_2; ++ } ++ ++ err = platform_driver_register(&bcm2835_alsa4_driver); ++ if (err) { ++ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); ++ goto unregister_3; ++ } ++ ++ err = platform_driver_register(&bcm2835_alsa5_driver); ++ if (err) { ++ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); ++ goto unregister_4; ++ } ++ ++ err = platform_driver_register(&bcm2835_alsa6_driver); ++ if (err) { ++ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); ++ goto unregister_5; ++ } ++ ++ err = platform_driver_register(&bcm2835_alsa7_driver); ++ if (err) { ++ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); ++ goto unregister_6; ++ } ++ ++ return 0; ++ ++unregister_6: ++ platform_driver_unregister(&bcm2835_alsa6_driver); ++unregister_5: ++ platform_driver_unregister(&bcm2835_alsa5_driver); ++unregister_4: ++ platform_driver_unregister(&bcm2835_alsa4_driver); ++unregister_3: ++ platform_driver_unregister(&bcm2835_alsa3_driver); ++unregister_2: ++ platform_driver_unregister(&bcm2835_alsa2_driver); ++unregister_1: ++ platform_driver_unregister(&bcm2835_alsa1_driver); ++unregister_0: ++ platform_driver_unregister(&bcm2835_alsa0_driver); ++out: ++ return err; ++} ++ ++static void bcm2835_alsa_device_exit(void) ++{ ++ platform_driver_unregister(&bcm2835_alsa0_driver); ++ platform_driver_unregister(&bcm2835_alsa1_driver); ++ platform_driver_unregister(&bcm2835_alsa2_driver); ++ platform_driver_unregister(&bcm2835_alsa3_driver); ++ platform_driver_unregister(&bcm2835_alsa4_driver); ++ platform_driver_unregister(&bcm2835_alsa5_driver); ++ platform_driver_unregister(&bcm2835_alsa6_driver); ++ platform_driver_unregister(&bcm2835_alsa7_driver); ++} ++ ++late_initcall(bcm2835_alsa_device_init); ++module_exit(bcm2835_alsa_device_exit); ++ ++MODULE_AUTHOR("Dom Cobley"); ++MODULE_DESCRIPTION("Alsa driver for BCM2835 chip"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:bcm2835_alsa"); +--- /dev/null ++++ b/sound/arm/bcm2835.h +@@ -0,0 +1,167 @@ ++/***************************************************************************** ++* 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. ++*****************************************************************************/ ++ ++#ifndef __SOUND_ARM_BCM2835_H ++#define __SOUND_ARM_BCM2835_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++#define AUDIO_DEBUG_ENABLE ++#define AUDIO_VERBOSE_DEBUG_ENABLE ++*/ ++ ++/* Debug macros */ ++ ++#ifdef AUDIO_DEBUG_ENABLE ++#ifdef AUDIO_VERBOSE_DEBUG_ENABLE ++ ++#define audio_debug(fmt, arg...) \ ++ printk(KERN_INFO"%s:%d " fmt, __func__, __LINE__, ##arg) ++ ++#define audio_info(fmt, arg...) \ ++ printk(KERN_INFO"%s:%d " fmt, __func__, __LINE__, ##arg) ++ ++#else ++ ++#define audio_debug(fmt, arg...) ++ ++#define audio_info(fmt, arg...) ++ ++#endif /* AUDIO_VERBOSE_DEBUG_ENABLE */ ++ ++#else ++ ++#define audio_debug(fmt, arg...) ++ ++#define audio_info(fmt, arg...) ++ ++#endif /* AUDIO_DEBUG_ENABLE */ ++ ++#define audio_error(fmt, arg...) \ ++ printk(KERN_ERR"%s:%d " fmt, __func__, __LINE__, ##arg) ++ ++#define audio_warning(fmt, arg...) \ ++ printk(KERN_WARNING"%s:%d " fmt, __func__, __LINE__, ##arg) ++ ++#define audio_alert(fmt, arg...) \ ++ printk(KERN_ALERT"%s:%d " fmt, __func__, __LINE__, ##arg) ++ ++#define MAX_SUBSTREAMS (8) ++#define AVAIL_SUBSTREAMS_MASK (0xff) ++enum { ++ CTRL_VOL_MUTE, ++ CTRL_VOL_UNMUTE ++}; ++ ++/* macros for alsa2chip and chip2alsa, instead of functions */ ++ ++#define alsa2chip(vol) (uint)(-((vol << 8) / 100)) /* convert alsa to chip volume (defined as macro rather than function call) */ ++#define chip2alsa(vol) -((vol * 100) >> 8) /* convert chip to alsa volume */ ++ ++/* Some constants for values .. */ ++typedef enum { ++ AUDIO_DEST_AUTO = 0, ++ AUDIO_DEST_HEADPHONES = 1, ++ AUDIO_DEST_HDMI = 2, ++ AUDIO_DEST_MAX, ++} SND_BCM2835_ROUTE_T; ++ ++typedef enum { ++ PCM_PLAYBACK_VOLUME, ++ PCM_PLAYBACK_MUTE, ++ PCM_PLAYBACK_DEVICE, ++} SND_BCM2835_CTRL_T; ++ ++/* definition of the chip-specific record */ ++typedef struct bcm2835_chip { ++ struct snd_card *card; ++ struct snd_pcm *pcm; ++ struct snd_pcm *pcm_spdif; ++ /* Bitmat for valid reg_base and irq numbers */ ++ uint32_t avail_substreams; ++ struct platform_device *pdev[MAX_SUBSTREAMS]; ++ struct bcm2835_alsa_stream *alsa_stream[MAX_SUBSTREAMS]; ++ ++ int volume; ++ int old_volume; /* stores the volume value whist muted */ ++ int dest; ++ int mute; ++ ++ unsigned int opened; ++ unsigned int spdif_status; ++ struct mutex audio_mutex; ++} bcm2835_chip_t; ++ ++typedef struct bcm2835_alsa_stream { ++ bcm2835_chip_t *chip; ++ struct snd_pcm_substream *substream; ++ struct snd_pcm_indirect pcm_indirect; ++ ++ struct semaphore buffers_update_sem; ++ struct semaphore control_sem; ++ spinlock_t lock; ++ volatile uint32_t control; ++ volatile uint32_t status; ++ ++ int open; ++ int running; ++ int draining; ++ ++ int channels; ++ int params_rate; ++ int pcm_format_width; ++ ++ unsigned int pos; ++ unsigned int buffer_size; ++ unsigned int period_size; ++ ++ uint32_t enable_fifo_irq; ++ irq_handler_t fifo_irq_handler; ++ ++ atomic_t retrieved; ++ struct opaque_AUDIO_INSTANCE_T *instance; ++ struct workqueue_struct *my_wq; ++ int idx; ++} bcm2835_alsa_stream_t; ++ ++int snd_bcm2835_new_ctl(bcm2835_chip_t * chip); ++int snd_bcm2835_new_pcm(bcm2835_chip_t * chip); ++int snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip); ++ ++int bcm2835_audio_open(bcm2835_alsa_stream_t * alsa_stream); ++int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream); ++int bcm2835_audio_set_params(bcm2835_alsa_stream_t * alsa_stream, ++ uint32_t channels, uint32_t samplerate, ++ uint32_t bps); ++int bcm2835_audio_setup(bcm2835_alsa_stream_t * alsa_stream); ++int bcm2835_audio_start(bcm2835_alsa_stream_t * alsa_stream); ++int bcm2835_audio_stop(bcm2835_alsa_stream_t * alsa_stream); ++int bcm2835_audio_set_ctls(bcm2835_chip_t * chip); ++int bcm2835_audio_write(bcm2835_alsa_stream_t * alsa_stream, uint32_t count, ++ void *src); ++uint32_t bcm2835_audio_retrieve_buffers(bcm2835_alsa_stream_t * alsa_stream); ++void bcm2835_audio_flush_buffers(bcm2835_alsa_stream_t * alsa_stream); ++void bcm2835_audio_flush_playback_buffers(bcm2835_alsa_stream_t * alsa_stream); ++ ++#endif /* __SOUND_ARM_BCM2835_H */ +--- /dev/null ++++ b/sound/arm/vc_vchi_audioserv_defs.h +@@ -0,0 +1,116 @@ ++/***************************************************************************** ++* 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. ++*****************************************************************************/ ++ ++#ifndef _VC_AUDIO_DEFS_H_ ++#define _VC_AUDIO_DEFS_H_ ++ ++#define VC_AUDIOSERV_MIN_VER 1 ++#define VC_AUDIOSERV_VER 2 ++ ++// FourCC code used for VCHI connection ++#define VC_AUDIO_SERVER_NAME MAKE_FOURCC("AUDS") ++ ++// Maximum message length ++#define VC_AUDIO_MAX_MSG_LEN (sizeof( VC_AUDIO_MSG_T )) ++ ++// List of screens that are currently supported ++// All message types supported for HOST->VC direction ++typedef enum { ++ VC_AUDIO_MSG_TYPE_RESULT, // Generic result ++ VC_AUDIO_MSG_TYPE_COMPLETE, // Generic result ++ VC_AUDIO_MSG_TYPE_CONFIG, // Configure audio ++ VC_AUDIO_MSG_TYPE_CONTROL, // Configure audio ++ VC_AUDIO_MSG_TYPE_OPEN, // Configure audio ++ VC_AUDIO_MSG_TYPE_CLOSE, // Configure audio ++ VC_AUDIO_MSG_TYPE_START, // Configure audio ++ VC_AUDIO_MSG_TYPE_STOP, // Configure audio ++ VC_AUDIO_MSG_TYPE_WRITE, // Configure audio ++ VC_AUDIO_MSG_TYPE_MAX ++} VC_AUDIO_MSG_TYPE; ++ ++// configure the audio ++typedef struct { ++ uint32_t channels; ++ uint32_t samplerate; ++ uint32_t bps; ++ ++} VC_AUDIO_CONFIG_T; ++ ++typedef struct { ++ uint32_t volume; ++ uint32_t dest; ++ ++} VC_AUDIO_CONTROL_T; ++ ++// audio ++typedef struct { ++ uint32_t dummy; ++ ++} VC_AUDIO_OPEN_T; ++ ++// audio ++typedef struct { ++ uint32_t dummy; ++ ++} VC_AUDIO_CLOSE_T; ++// audio ++typedef struct { ++ uint32_t dummy; ++ ++} VC_AUDIO_START_T; ++// audio ++typedef struct { ++ uint32_t draining; ++ ++} VC_AUDIO_STOP_T; ++ ++// configure the write audio samples ++typedef struct { ++ uint32_t count; // in bytes ++ void *callback; ++ void *cookie; ++ uint16_t silence; ++ uint16_t max_packet; ++} VC_AUDIO_WRITE_T; ++ ++// Generic result for a request (VC->HOST) ++typedef struct { ++ int32_t success; // Success value ++ ++} VC_AUDIO_RESULT_T; ++ ++// Generic result for a request (VC->HOST) ++typedef struct { ++ int32_t count; // Success value ++ void *callback; ++ void *cookie; ++} VC_AUDIO_COMPLETE_T; ++ ++// Message header for all messages in HOST->VC direction ++typedef struct { ++ int32_t type; // Message type (VC_AUDIO_MSG_TYPE) ++ union { ++ VC_AUDIO_CONFIG_T config; ++ VC_AUDIO_CONTROL_T control; ++ VC_AUDIO_OPEN_T open; ++ VC_AUDIO_CLOSE_T close; ++ VC_AUDIO_START_T start; ++ VC_AUDIO_STOP_T stop; ++ VC_AUDIO_WRITE_T write; ++ VC_AUDIO_RESULT_T result; ++ VC_AUDIO_COMPLETE_T complete; ++ } u; ++} VC_AUDIO_MSG_T; ++ ++#endif // _VC_AUDIO_DEFS_H_ diff --git a/target/linux/brcm2708/patches-4.4/0035-cma-Add-vc_cma-driver-to-enable-use-of-CMA.patch b/target/linux/brcm2708/patches-4.4/0035-cma-Add-vc_cma-driver-to-enable-use-of-CMA.patch deleted file mode 100644 index 781068f496..0000000000 --- a/target/linux/brcm2708/patches-4.4/0035-cma-Add-vc_cma-driver-to-enable-use-of-CMA.patch +++ /dev/null @@ -1,1326 +0,0 @@ -From 8dbb0430386b772f636f474ed45496076d135428 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 3 Jul 2013 00:31:47 +0100 -Subject: [PATCH 035/170] cma: Add vc_cma driver to enable use of CMA -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: popcornmix - -vc_cma: Make the vc_cma area the default contiguous DMA area - -vc_cma: Provide empty functions when module is not built - -Providing empty functions saves the users from guarding the -function call with an #if clause. -Move __init markings from prototypes to functions. - -Signed-off-by: Noralf Trønnes ---- - 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 | 1193 +++++++++++++++++++++++++++++++++ - include/linux/broadcom/vc_cma.h | 36 + - 7 files changed, 1262 insertions(+) - create mode 100644 drivers/char/broadcom/Kconfig - create mode 100644 drivers/char/broadcom/Makefile - create mode 100644 drivers/char/broadcom/vc_cma/Makefile - create mode 100644 drivers/char/broadcom/vc_cma/vc_cma.c - create mode 100644 include/linux/broadcom/vc_cma.h - ---- a/drivers/char/Kconfig -+++ b/drivers/char/Kconfig -@@ -4,6 +4,8 @@ - - menu "Character devices" - -+source "drivers/char/broadcom/Kconfig" -+ - source "drivers/tty/Kconfig" - - config DEVMEM ---- a/drivers/char/Makefile -+++ b/drivers/char/Makefile -@@ -60,3 +60,4 @@ js-rtc-y = rtc.o - - obj-$(CONFIG_TILE_SROM) += tile-srom.o - obj-$(CONFIG_XILLYBUS) += xillybus/ -+obj-$(CONFIG_BRCM_CHAR_DRIVERS) += broadcom/ ---- /dev/null -+++ b/drivers/char/broadcom/Kconfig -@@ -0,0 +1,15 @@ -+# -+# Broadcom char driver config -+# -+ -+menuconfig BRCM_CHAR_DRIVERS -+ bool "Broadcom Char Drivers" -+ help -+ Broadcom's char drivers -+ -+config BCM_VC_CMA -+ bool "Videocore CMA" -+ depends on CMA && BRCM_CHAR_DRIVERS && BCM2708_VCHIQ -+ default n -+ help -+ Helper for videocore CMA access. ---- /dev/null -+++ b/drivers/char/broadcom/Makefile -@@ -0,0 +1 @@ -+obj-$(CONFIG_BCM_VC_CMA) += vc_cma/ ---- /dev/null -+++ b/drivers/char/broadcom/vc_cma/Makefile -@@ -0,0 +1,14 @@ -+ccflags-y += -Wall -Wstrict-prototypes -Wno-trigraphs -+ccflags-y += -Werror -+ccflags-y += -Iinclude/linux/broadcom -+ccflags-y += -Idrivers/misc/vc04_services -+ccflags-y += -Idrivers/misc/vc04_services/interface/vchi -+ccflags-y += -Idrivers/misc/vc04_services/interface/vchiq_arm -+ -+ccflags-y += -D__KERNEL__ -+ccflags-y += -D__linux__ -+ccflags-y += -Werror -+ -+obj-$(CONFIG_BCM_VC_CMA) += vc-cma.o -+ -+vc-cma-objs := vc_cma.o ---- /dev/null -+++ b/drivers/char/broadcom/vc_cma/vc_cma.c -@@ -0,0 +1,1193 @@ -+/** -+ * 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 "vc_cma.h" -+ -+#include "vchiq_util.h" -+#include "vchiq_connected.h" -+//#include "debug_sym.h" -+//#include "vc_mem.h" -+ -+#define DRIVER_NAME "vc-cma" -+ -+#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__) -+ -+#define VC_CMA_FOURCC VCHIQ_MAKE_FOURCC('C', 'M', 'A', ' ') -+#define VC_CMA_VERSION 2 -+ -+#define VC_CMA_CHUNK_ORDER 6 /* 256K */ -+#define VC_CMA_CHUNK_SIZE (4096 << VC_CMA_CHUNK_ORDER) -+#define VC_CMA_MAX_PARAMS_PER_MSG \ -+ ((VCHIQ_MAX_MSG_SIZE - sizeof(unsigned short))/sizeof(unsigned short)) -+#define VC_CMA_RESERVE_COUNT_MAX 16 -+ -+#define PAGES_PER_CHUNK (VC_CMA_CHUNK_SIZE / PAGE_SIZE) -+ -+#define VCADDR_TO_PHYSADDR(vcaddr) (mm_vc_mem_phys_addr + vcaddr) -+ -+#define loud_error(...) \ -+ LOG_ERR("===== " __VA_ARGS__) -+ -+enum { -+ VC_CMA_MSG_QUIT, -+ VC_CMA_MSG_OPEN, -+ VC_CMA_MSG_TICK, -+ VC_CMA_MSG_ALLOC, /* chunk count */ -+ VC_CMA_MSG_FREE, /* chunk, chunk, ... */ -+ VC_CMA_MSG_ALLOCATED, /* chunk, chunk, ... */ -+ VC_CMA_MSG_REQUEST_ALLOC, /* chunk count */ -+ VC_CMA_MSG_REQUEST_FREE, /* chunk count */ -+ VC_CMA_MSG_RESERVE, /* bytes lo, bytes hi */ -+ VC_CMA_MSG_UPDATE_RESERVE, -+ VC_CMA_MSG_MAX -+}; -+ -+struct cma_msg { -+ unsigned short type; -+ unsigned short params[VC_CMA_MAX_PARAMS_PER_MSG]; -+}; -+ -+struct vc_cma_reserve_user { -+ unsigned int pid; -+ unsigned int reserve; -+}; -+ -+/* Device (/dev) related variables */ -+static dev_t vc_cma_devnum; -+static struct class *vc_cma_class; -+static struct cdev vc_cma_cdev; -+static int vc_cma_inited; -+static int vc_cma_debug; -+ -+/* Proc entry */ -+static struct proc_dir_entry *vc_cma_proc_entry; -+ -+phys_addr_t vc_cma_base; -+struct page *vc_cma_base_page; -+unsigned int vc_cma_size; -+EXPORT_SYMBOL(vc_cma_size); -+unsigned int vc_cma_initial; -+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; -+unsigned int vc_cma_reserve_count; -+struct vc_cma_reserve_user vc_cma_reserve_users[VC_CMA_RESERVE_COUNT_MAX]; -+static DEFINE_SEMAPHORE(vc_cma_reserve_mutex); -+static DEFINE_SEMAPHORE(vc_cma_worker_queue_push_mutex); -+ -+static u64 vc_cma_dma_mask = DMA_BIT_MASK(32); -+static struct platform_device vc_cma_device = { -+ .name = "vc-cma", -+ .id = 0, -+ .dev = { -+ .dma_mask = &vc_cma_dma_mask, -+ .coherent_dma_mask = DMA_BIT_MASK(32), -+ }, -+}; -+ -+static VCHIQ_INSTANCE_T cma_instance; -+static VCHIQ_SERVICE_HANDLE_T cma_service; -+static VCHIU_QUEUE_T cma_msg_queue; -+static struct task_struct *cma_worker; -+ -+static int vc_cma_set_reserve(unsigned int reserve, unsigned int pid); -+static int vc_cma_alloc_chunks(int num_chunks, struct cma_msg *reply); -+static VCHIQ_STATUS_T cma_service_callback(VCHIQ_REASON_T reason, -+ VCHIQ_HEADER_T * header, -+ VCHIQ_SERVICE_HANDLE_T service, -+ void *bulk_userdata); -+static void send_vc_msg(unsigned short type, -+ unsigned short param1, unsigned short param2); -+static bool send_worker_msg(VCHIQ_HEADER_T * msg); -+ -+static int early_vc_cma_mem(char *p) -+{ -+ unsigned int new_size; -+ printk(KERN_NOTICE "early_vc_cma_mem(%s)", p); -+ vc_cma_size = memparse(p, &p); -+ vc_cma_initial = vc_cma_size; -+ if (*p == '/') -+ vc_cma_size = memparse(p + 1, &p); -+ if (*p == '@') -+ vc_cma_base = memparse(p + 1, &p); -+ -+ new_size = (vc_cma_size - ((-vc_cma_base) & (VC_CMA_CHUNK_SIZE - 1))) -+ & ~(VC_CMA_CHUNK_SIZE - 1); -+ if (new_size > vc_cma_size) -+ vc_cma_size = 0; -+ vc_cma_initial = (vc_cma_initial + VC_CMA_CHUNK_SIZE - 1) -+ & ~(VC_CMA_CHUNK_SIZE - 1); -+ if (vc_cma_initial > vc_cma_size) -+ vc_cma_initial = vc_cma_size; -+ vc_cma_base = (vc_cma_base + VC_CMA_CHUNK_SIZE - 1) -+ & ~(VC_CMA_CHUNK_SIZE - 1); -+ -+ printk(KERN_NOTICE " -> initial %x, size %x, base %x", vc_cma_initial, -+ vc_cma_size, (unsigned int)vc_cma_base); -+ -+ return 0; -+} -+ -+early_param("vc-cma-mem", early_vc_cma_mem); -+ -+void __init vc_cma_early_init(void) -+{ -+ LOG_DBG("vc_cma_early_init - vc_cma_chunks = %d", vc_cma_chunks); -+ if (vc_cma_size) { -+ int rc = platform_device_register(&vc_cma_device); -+ LOG_DBG("platform_device_register -> %d", rc); -+ } -+} -+ -+void __init vc_cma_reserve(void) -+{ -+ /* if vc_cma_size is set, then declare vc CMA area of the same -+ * size from the end of memory -+ */ -+ if (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); -+ vc_cma_size = 0; -+ } -+ } -+ vc_cma_chunks = vc_cma_size / VC_CMA_CHUNK_SIZE; -+} -+ -+/**************************************************************************** -+* -+* vc_cma_open -+* -+***************************************************************************/ -+ -+static int vc_cma_open(struct inode *inode, struct file *file) -+{ -+ (void)inode; -+ (void)file; -+ -+ return 0; -+} -+ -+/**************************************************************************** -+* -+* vc_cma_release -+* -+***************************************************************************/ -+ -+static int vc_cma_release(struct inode *inode, struct file *file) -+{ -+ (void)inode; -+ (void)file; -+ -+ vc_cma_set_reserve(0, current->tgid); -+ -+ return 0; -+} -+ -+/**************************************************************************** -+* -+* vc_cma_ioctl -+* -+***************************************************************************/ -+ -+static long vc_cma_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ int rc = 0; -+ -+ (void)cmd; -+ (void)arg; -+ -+ switch (cmd) { -+ case VC_CMA_IOC_RESERVE: -+ rc = vc_cma_set_reserve((unsigned int)arg, current->tgid); -+ if (rc >= 0) -+ rc = 0; -+ break; -+ default: -+ LOG_ERR("vc-cma: Unknown ioctl %x", cmd); -+ return -ENOTTY; -+ } -+ -+ return rc; -+} -+ -+/**************************************************************************** -+* -+* File Operations for the driver. -+* -+***************************************************************************/ -+ -+static const struct file_operations vc_cma_fops = { -+ .owner = THIS_MODULE, -+ .open = vc_cma_open, -+ .release = vc_cma_release, -+ .unlocked_ioctl = vc_cma_ioctl, -+}; -+ -+/**************************************************************************** -+* -+* vc_cma_proc_open -+* -+***************************************************************************/ -+ -+static int vc_cma_show_info(struct seq_file *m, void *v) -+{ -+ int i; -+ -+ seq_printf(m, "Videocore CMA:\n"); -+ seq_printf(m, " Base : %08x\n", (unsigned int)vc_cma_base); -+ seq_printf(m, " Length : %08x\n", vc_cma_size); -+ seq_printf(m, " Initial : %08x\n", vc_cma_initial); -+ seq_printf(m, " Chunk size : %08x\n", VC_CMA_CHUNK_SIZE); -+ seq_printf(m, " Chunks : %4d (%d bytes)\n", -+ (int)vc_cma_chunks, -+ (int)(vc_cma_chunks * VC_CMA_CHUNK_SIZE)); -+ seq_printf(m, " Used : %4d (%d bytes)\n", -+ (int)vc_cma_chunks_used, -+ (int)(vc_cma_chunks_used * VC_CMA_CHUNK_SIZE)); -+ seq_printf(m, " Reserved : %4d (%d bytes)\n", -+ (unsigned int)vc_cma_chunks_reserved, -+ (int)(vc_cma_chunks_reserved * VC_CMA_CHUNK_SIZE)); -+ -+ for (i = 0; i < vc_cma_reserve_count; i++) { -+ struct vc_cma_reserve_user *user = &vc_cma_reserve_users[i]; -+ 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"); -+ -+ return 0; -+} -+ -+static int vc_cma_proc_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, vc_cma_show_info, NULL); -+} -+ -+/**************************************************************************** -+* -+* vc_cma_proc_write -+* -+***************************************************************************/ -+ -+static int vc_cma_proc_write(struct file *file, -+ const char __user *buffer, -+ size_t size, loff_t *ppos) -+{ -+ int rc = -EFAULT; -+ char input_str[20]; -+ -+ memset(input_str, 0, sizeof(input_str)); -+ -+ if (size > sizeof(input_str)) { -+ LOG_ERR("%s: input string length too long", __func__); -+ goto out; -+ } -+ -+ if (copy_from_user(input_str, buffer, size - 1)) { -+ LOG_ERR("%s: failed to get input string", __func__); -+ goto out; -+ } -+#define ALLOC_STR "alloc" -+#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 alloc_size; -+ char *p = input_str + strlen(ALLOC_STR); -+ -+ while (*p == ' ') -+ p++; -+ 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, -+ 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 alloc_size; -+ char *p = input_str + strlen(FREE_STR); -+ -+ while (*p == ' ') -+ p++; -+ 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, -+ alloc_size / VC_CMA_CHUNK_SIZE, 0); -+ else -+ LOG_ERR("invalid size '%s'", p); -+ rc = size; -+ } else if (strncmp(input_str, DEBUG_STR, strlen(DEBUG_STR)) == 0) { -+ char *p = input_str + strlen(DEBUG_STR); -+ while (*p == ' ') -+ p++; -+ if ((strcmp(p, "on") == 0) || (strcmp(p, "1") == 0)) -+ vc_cma_debug = 1; -+ else if ((strcmp(p, "off") == 0) || (strcmp(p, "0") == 0)) -+ vc_cma_debug = 0; -+ 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 alloc_size; -+ int reserved; -+ char *p = input_str + strlen(RESERVE_STR); -+ while (*p == ' ') -+ p++; -+ alloc_size = memparse(p, NULL); -+ -+ 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: -+ return rc; -+} -+ -+/**************************************************************************** -+* -+* File Operations for /proc interface. -+* -+***************************************************************************/ -+ -+static const struct file_operations vc_cma_proc_fops = { -+ .open = vc_cma_proc_open, -+ .read = seq_read, -+ .write = vc_cma_proc_write, -+ .llseek = seq_lseek, -+ .release = single_release -+}; -+ -+static int vc_cma_set_reserve(unsigned int reserve, unsigned int pid) -+{ -+ struct vc_cma_reserve_user *user = NULL; -+ int delta = 0; -+ int i; -+ -+ if (down_interruptible(&vc_cma_reserve_mutex)) -+ return -ERESTARTSYS; -+ -+ for (i = 0; i < vc_cma_reserve_count; i++) { -+ if (pid == vc_cma_reserve_users[i].pid) { -+ user = &vc_cma_reserve_users[i]; -+ delta = reserve - user->reserve; -+ if (reserve) -+ user->reserve = reserve; -+ else { -+ /* Remove this entry by copying downwards */ -+ while ((i + 1) < vc_cma_reserve_count) { -+ user[0].pid = user[1].pid; -+ user[0].reserve = user[1].reserve; -+ user++; -+ i++; -+ } -+ vc_cma_reserve_count--; -+ user = NULL; -+ } -+ break; -+ } -+ } -+ -+ if (reserve && !user) { -+ if (vc_cma_reserve_count == VC_CMA_RESERVE_COUNT_MAX) { -+ LOG_ERR("vc-cma: Too many reservations - " -+ "increase CMA_RESERVE_COUNT_MAX"); -+ up(&vc_cma_reserve_mutex); -+ return -EBUSY; -+ } -+ user = &vc_cma_reserve_users[vc_cma_reserve_count]; -+ user->pid = pid; -+ user->reserve = reserve; -+ delta = reserve; -+ vc_cma_reserve_count++; -+ } -+ -+ vc_cma_reserve_total += delta; -+ -+ send_vc_msg(VC_CMA_MSG_RESERVE, -+ vc_cma_reserve_total & 0xffff, vc_cma_reserve_total >> 16); -+ -+ send_worker_msg((VCHIQ_HEADER_T *) VC_CMA_MSG_UPDATE_RESERVE); -+ -+ LOG_DBG("/proc/vc-cma: reserve %d (PID %d) - total %u", -+ reserve, pid, vc_cma_reserve_total); -+ -+ up(&vc_cma_reserve_mutex); -+ -+ return vc_cma_reserve_total; -+} -+ -+static VCHIQ_STATUS_T cma_service_callback(VCHIQ_REASON_T reason, -+ VCHIQ_HEADER_T * header, -+ VCHIQ_SERVICE_HANDLE_T service, -+ void *bulk_userdata) -+{ -+ switch (reason) { -+ case VCHIQ_MESSAGE_AVAILABLE: -+ if (!send_worker_msg(header)) -+ return VCHIQ_RETRY; -+ break; -+ case VCHIQ_SERVICE_CLOSED: -+ LOG_DBG("CMA service closed"); -+ break; -+ default: -+ LOG_ERR("Unexpected CMA callback reason %d", reason); -+ break; -+ } -+ return VCHIQ_SUCCESS; -+} -+ -+static void send_vc_msg(unsigned short type, -+ unsigned short param1, unsigned short param2) -+{ -+ unsigned short msg[] = { type, param1, param2 }; -+ VCHIQ_ELEMENT_T elem = { &msg, sizeof(msg) }; -+ VCHIQ_STATUS_T ret; -+ vchiq_use_service(cma_service); -+ ret = vchiq_queue_message(cma_service, &elem, 1); -+ vchiq_release_service(cma_service); -+ if (ret != VCHIQ_SUCCESS) -+ LOG_ERR("vchiq_queue_message returned %x", ret); -+} -+ -+static bool send_worker_msg(VCHIQ_HEADER_T * msg) -+{ -+ if (down_interruptible(&vc_cma_worker_queue_push_mutex)) -+ return false; -+ vchiu_queue_push(&cma_msg_queue, msg); -+ up(&vc_cma_worker_queue_push_mutex); -+ return true; -+} -+ -+static int vc_cma_alloc_chunks(int num_chunks, struct cma_msg *reply) -+{ -+ int i; -+ for (i = 0; i < num_chunks; i++) { -+ struct page *chunk; -+ unsigned int chunk_num; -+ uint8_t *chunk_addr; -+ size_t chunk_size = PAGES_PER_CHUNK << PAGE_SHIFT; -+ -+ chunk = dma_alloc_from_contiguous(&vc_cma_device.dev, -+ PAGES_PER_CHUNK, -+ VC_CMA_CHUNK_ORDER); -+ if (!chunk) -+ break; -+ -+ chunk_addr = page_address(chunk); -+ dmac_flush_range(chunk_addr, chunk_addr + chunk_size); -+ outer_inv_range(__pa(chunk_addr), __pa(chunk_addr) + -+ chunk_size); -+ -+ chunk_num = -+ (page_to_phys(chunk) - vc_cma_base) / VC_CMA_CHUNK_SIZE; -+ 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 %pa-%pa - " -+ "bad SPARSEMEM configuration?", -+ __func__, (unsigned int)page_to_phys(chunk), -+ &vc_cma_base, &_pa); -+ LOG_ERR("%s: dev->cma_area = %p", __func__, -+ (void*)0/*vc_cma_device.dev.cma_area*/); -+ LOG_ERR("%s: ===============================", -+ __func__); -+ break; -+ } -+ reply->params[i] = chunk_num; -+ vc_cma_chunks_used++; -+ } -+ -+ if (i < num_chunks) { -+ LOG_ERR("%s: dma_alloc_from_contiguous failed " -+ "for %x bytes (alloc %d of %d, %d free)", -+ __func__, VC_CMA_CHUNK_SIZE, i, -+ num_chunks, vc_cma_chunks - vc_cma_chunks_used); -+ num_chunks = i; -+ } -+ -+ LOG_DBG("CMA allocated %d chunks -> %d used", -+ num_chunks, vc_cma_chunks_used); -+ reply->type = VC_CMA_MSG_ALLOCATED; -+ -+ { -+ VCHIQ_ELEMENT_T elem = { -+ reply, -+ offsetof(struct cma_msg, params[0]) + -+ num_chunks * sizeof(reply->params[0]) -+ }; -+ VCHIQ_STATUS_T ret; -+ vchiq_use_service(cma_service); -+ ret = vchiq_queue_message(cma_service, &elem, 1); -+ vchiq_release_service(cma_service); -+ if (ret != VCHIQ_SUCCESS) -+ LOG_ERR("vchiq_queue_message return " "%x", ret); -+ } -+ -+ return num_chunks; -+} -+ -+static int cma_worker_proc(void *param) -+{ -+ static struct cma_msg reply; -+ (void)param; -+ -+ while (1) { -+ VCHIQ_HEADER_T *msg; -+ static struct cma_msg msg_copy; -+ struct cma_msg *cma_msg = &msg_copy; -+ int type, msg_size; -+ -+ msg = vchiu_queue_pop(&cma_msg_queue); -+ if ((unsigned int)msg >= VC_CMA_MSG_MAX) { -+ msg_size = msg->size; -+ memcpy(&msg_copy, msg->data, msg_size); -+ type = cma_msg->type; -+ vchiq_release_message(cma_service, msg); -+ } else { -+ msg_size = 0; -+ type = (int)msg; -+ if (type == VC_CMA_MSG_QUIT) -+ break; -+ else if (type == VC_CMA_MSG_UPDATE_RESERVE) { -+ msg = NULL; -+ cma_msg = NULL; -+ } else { -+ BUG(); -+ continue; -+ } -+ } -+ -+ switch (type) { -+ case VC_CMA_MSG_ALLOC:{ -+ int num_chunks, free_chunks; -+ num_chunks = cma_msg->params[0]; -+ free_chunks = -+ vc_cma_chunks - vc_cma_chunks_used; -+ LOG_DBG("CMA_MSG_ALLOC(%d chunks)", num_chunks); -+ if (num_chunks > VC_CMA_MAX_PARAMS_PER_MSG) { -+ LOG_ERR -+ ("CMA_MSG_ALLOC - chunk count (%d) " -+ "exceeds VC_CMA_MAX_PARAMS_PER_MSG (%d)", -+ num_chunks, -+ VC_CMA_MAX_PARAMS_PER_MSG); -+ num_chunks = VC_CMA_MAX_PARAMS_PER_MSG; -+ } -+ -+ if (num_chunks > free_chunks) { -+ LOG_ERR -+ ("CMA_MSG_ALLOC - chunk count (%d) " -+ "exceeds free chunks (%d)", -+ num_chunks, free_chunks); -+ num_chunks = free_chunks; -+ } -+ -+ vc_cma_alloc_chunks(num_chunks, &reply); -+ } -+ break; -+ -+ case VC_CMA_MSG_FREE:{ -+ int chunk_count = -+ (msg_size - -+ offsetof(struct cma_msg, -+ params)) / -+ sizeof(cma_msg->params[0]); -+ int i; -+ BUG_ON(chunk_count <= 0); -+ -+ LOG_DBG("CMA_MSG_FREE(%d chunks - %x, ...)", -+ chunk_count, cma_msg->params[0]); -+ for (i = 0; i < chunk_count; i++) { -+ int chunk_num = cma_msg->params[i]; -+ struct page *page = vc_cma_base_page + -+ chunk_num * PAGES_PER_CHUNK; -+ if (chunk_num >= vc_cma_chunks) { -+ LOG_ERR -+ ("CMA_MSG_FREE - chunk %d of %d" -+ " (value %x) exceeds maximum " -+ "(%x)", i, chunk_count, -+ chunk_num, -+ vc_cma_chunks - 1); -+ break; -+ } -+ -+ 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 %pa, " -+ "page %x)", chunk_num, -+ &_pa, -+ (unsigned int)page); -+ } -+ vc_cma_chunks_used--; -+ } -+ LOG_DBG("CMA released %d chunks -> %d used", -+ i, vc_cma_chunks_used); -+ } -+ break; -+ -+ case VC_CMA_MSG_UPDATE_RESERVE:{ -+ int chunks_needed = -+ ((vc_cma_reserve_total + VC_CMA_CHUNK_SIZE - -+ 1) -+ / VC_CMA_CHUNK_SIZE) - -+ vc_cma_chunks_reserved; -+ -+ LOG_DBG -+ ("CMA_MSG_UPDATE_RESERVE(%d chunks needed)", -+ chunks_needed); -+ -+ /* Cap the reservations to what is available */ -+ if (chunks_needed > 0) { -+ if (chunks_needed > -+ (vc_cma_chunks - -+ vc_cma_chunks_used)) -+ chunks_needed = -+ (vc_cma_chunks - -+ vc_cma_chunks_used); -+ -+ chunks_needed = -+ vc_cma_alloc_chunks(chunks_needed, -+ &reply); -+ } -+ -+ LOG_DBG -+ ("CMA_MSG_UPDATE_RESERVE(%d chunks allocated)", -+ chunks_needed); -+ vc_cma_chunks_reserved += chunks_needed; -+ } -+ break; -+ -+ default: -+ LOG_ERR("unexpected msg type %d", type); -+ break; -+ } -+ } -+ -+ LOG_DBG("quitting..."); -+ return 0; -+} -+ -+/**************************************************************************** -+* -+* vc_cma_connected_init -+* -+* This function is called once the videocore has been connected. -+* -+***************************************************************************/ -+ -+static void vc_cma_connected_init(void) -+{ -+ VCHIQ_SERVICE_PARAMS_T service_params; -+ -+ LOG_DBG("vc_cma_connected_init"); -+ -+ if (!vchiu_queue_init(&cma_msg_queue, 16)) { -+ LOG_ERR("could not create CMA msg queue"); -+ goto fail_queue; -+ } -+ -+ if (vchiq_initialise(&cma_instance) != VCHIQ_SUCCESS) -+ goto fail_vchiq_init; -+ -+ vchiq_connect(cma_instance); -+ -+ service_params.fourcc = VC_CMA_FOURCC; -+ service_params.callback = cma_service_callback; -+ service_params.userdata = NULL; -+ service_params.version = VC_CMA_VERSION; -+ service_params.version_min = VC_CMA_VERSION; -+ -+ if (vchiq_open_service(cma_instance, &service_params, -+ &cma_service) != VCHIQ_SUCCESS) { -+ LOG_ERR("failed to open service - already in use?"); -+ goto fail_vchiq_open; -+ } -+ -+ vchiq_release_service(cma_service); -+ -+ cma_worker = kthread_create(cma_worker_proc, NULL, "cma_worker"); -+ if (!cma_worker) { -+ LOG_ERR("could not create CMA worker thread"); -+ goto fail_worker; -+ } -+ set_user_nice(cma_worker, -20); -+ wake_up_process(cma_worker); -+ -+ return; -+ -+fail_worker: -+ vchiq_close_service(cma_service); -+fail_vchiq_open: -+ vchiq_shutdown(cma_instance); -+fail_vchiq_init: -+ vchiu_queue_delete(&cma_msg_queue); -+fail_queue: -+ return; -+} -+ -+void -+loud_error_header(void) -+{ -+ if (in_loud_error) -+ return; -+ -+ LOG_ERR("============================================================" -+ "================"); -+ LOG_ERR("============================================================" -+ "================"); -+ LOG_ERR("====="); -+ -+ in_loud_error = 1; -+} -+ -+void -+loud_error_footer(void) -+{ -+ if (!in_loud_error) -+ return; -+ -+ LOG_ERR("====="); -+ LOG_ERR("============================================================" -+ "================"); -+ LOG_ERR("============================================================" -+ "================"); -+ -+ in_loud_error = 0; -+} -+ -+#if 1 -+static int check_cma_config(void) { return 1; } -+#else -+static int -+read_vc_debug_var(VC_MEM_ACCESS_HANDLE_T handle, -+ const char *symbol, -+ void *buf, size_t bufsize) -+{ -+ VC_MEM_ADDR_T vcMemAddr; -+ size_t vcMemSize; -+ uint8_t *mapAddr; -+ off_t vcMapAddr; -+ -+ if (!LookupVideoCoreSymbol(handle, symbol, -+ &vcMemAddr, -+ &vcMemSize)) { -+ loud_error_header(); -+ loud_error( -+ "failed to find VC symbol \"%s\".", -+ symbol); -+ loud_error_footer(); -+ return 0; -+ } -+ -+ if (vcMemSize != bufsize) { -+ loud_error_header(); -+ loud_error( -+ "VC symbol \"%s\" is the wrong size.", -+ symbol); -+ loud_error_footer(); -+ return 0; -+ } -+ -+ vcMapAddr = (off_t)vcMemAddr & VC_MEM_TO_ARM_ADDR_MASK; -+ vcMapAddr += mm_vc_mem_phys_addr; -+ mapAddr = ioremap_nocache(vcMapAddr, vcMemSize); -+ if (mapAddr == 0) { -+ loud_error_header(); -+ loud_error( -+ "failed to ioremap \"%s\" @ 0x%x " -+ "(phys: 0x%x, size: %u).", -+ symbol, -+ (unsigned int)vcMapAddr, -+ (unsigned int)vcMemAddr, -+ (unsigned int)vcMemSize); -+ loud_error_footer(); -+ return 0; -+ } -+ -+ memcpy(buf, mapAddr, bufsize); -+ iounmap(mapAddr); -+ -+ return 1; -+} -+ -+ -+static int -+check_cma_config(void) -+{ -+ VC_MEM_ACCESS_HANDLE_T mem_hndl; -+ VC_MEM_ADDR_T mempool_start; -+ VC_MEM_ADDR_T mempool_end; -+ VC_MEM_ADDR_T mempool_offline_start; -+ VC_MEM_ADDR_T mempool_offline_end; -+ VC_MEM_ADDR_T cam_alloc_base; -+ VC_MEM_ADDR_T cam_alloc_size; -+ VC_MEM_ADDR_T cam_alloc_end; -+ int success = 0; -+ -+ if (OpenVideoCoreMemory(&mem_hndl) != 0) -+ goto out; -+ -+ /* Read the relevant VideoCore variables */ -+ if (!read_vc_debug_var(mem_hndl, "__MEMPOOL_START", -+ &mempool_start, -+ sizeof(mempool_start))) -+ goto close; -+ -+ if (!read_vc_debug_var(mem_hndl, "__MEMPOOL_END", -+ &mempool_end, -+ sizeof(mempool_end))) -+ goto close; -+ -+ if (!read_vc_debug_var(mem_hndl, "__MEMPOOL_OFFLINE_START", -+ &mempool_offline_start, -+ sizeof(mempool_offline_start))) -+ goto close; -+ -+ if (!read_vc_debug_var(mem_hndl, "__MEMPOOL_OFFLINE_END", -+ &mempool_offline_end, -+ sizeof(mempool_offline_end))) -+ goto close; -+ -+ if (!read_vc_debug_var(mem_hndl, "cam_alloc_base", -+ &cam_alloc_base, -+ sizeof(cam_alloc_base))) -+ goto close; -+ -+ if (!read_vc_debug_var(mem_hndl, "cam_alloc_size", -+ &cam_alloc_size, -+ sizeof(cam_alloc_size))) -+ goto close; -+ -+ cam_alloc_end = cam_alloc_base + cam_alloc_size; -+ -+ success = 1; -+ -+ /* Now the sanity checks */ -+ if (!mempool_offline_start) -+ mempool_offline_start = mempool_start; -+ if (!mempool_offline_end) -+ mempool_offline_end = mempool_end; -+ -+ if (VCADDR_TO_PHYSADDR(mempool_offline_start) != vc_cma_base) { -+ loud_error_header(); -+ loud_error( -+ "__MEMPOOL_OFFLINE_START(%x -> %lx) doesn't match " -+ "vc_cma_base(%x)", -+ mempool_offline_start, -+ VCADDR_TO_PHYSADDR(mempool_offline_start), -+ vc_cma_base); -+ success = 0; -+ } -+ -+ if (VCADDR_TO_PHYSADDR(mempool_offline_end) != -+ (vc_cma_base + vc_cma_size)) { -+ loud_error_header(); -+ loud_error( -+ "__MEMPOOL_OFFLINE_END(%x -> %lx) doesn't match " -+ "vc_cma_base(%x) + vc_cma_size(%x) = %x", -+ mempool_offline_start, -+ VCADDR_TO_PHYSADDR(mempool_offline_end), -+ vc_cma_base, vc_cma_size, vc_cma_base + vc_cma_size); -+ success = 0; -+ } -+ -+ if (mempool_end < mempool_start) { -+ loud_error_header(); -+ loud_error( -+ "__MEMPOOL_END(%x) must not be before " -+ "__MEMPOOL_START(%x)", -+ mempool_end, -+ mempool_start); -+ success = 0; -+ } -+ -+ if (mempool_offline_end < mempool_offline_start) { -+ loud_error_header(); -+ loud_error( -+ "__MEMPOOL_OFFLINE_END(%x) must not be before " -+ "__MEMPOOL_OFFLINE_START(%x)", -+ mempool_offline_end, -+ mempool_offline_start); -+ success = 0; -+ } -+ -+ if (mempool_offline_start < mempool_start) { -+ loud_error_header(); -+ loud_error( -+ "__MEMPOOL_OFFLINE_START(%x) must not be before " -+ "__MEMPOOL_START(%x)", -+ mempool_offline_start, -+ mempool_start); -+ success = 0; -+ } -+ -+ if (mempool_offline_end > mempool_end) { -+ loud_error_header(); -+ loud_error( -+ "__MEMPOOL_OFFLINE_END(%x) must not be after " -+ "__MEMPOOL_END(%x)", -+ mempool_offline_end, -+ mempool_end); -+ success = 0; -+ } -+ -+ if ((cam_alloc_base < mempool_end) && -+ (cam_alloc_end > mempool_start)) { -+ loud_error_header(); -+ loud_error( -+ "cam_alloc pool(%x-%x) overlaps " -+ "mempool(%x-%x)", -+ cam_alloc_base, cam_alloc_end, -+ mempool_start, mempool_end); -+ success = 0; -+ } -+ -+ loud_error_footer(); -+ -+close: -+ CloseVideoCoreMemory(mem_hndl); -+ -+out: -+ return success; -+} -+#endif -+ -+static int vc_cma_init(void) -+{ -+ int rc = -EFAULT; -+ struct device *dev; -+ -+ if (!check_cma_config()) -+ goto out_release; -+ -+ 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); -+ -+ if (vc_cma_chunks) { -+ int chunks_needed = vc_cma_initial / VC_CMA_CHUNK_SIZE; -+ -+ for (vc_cma_chunks_used = 0; -+ vc_cma_chunks_used < chunks_needed; vc_cma_chunks_used++) { -+ struct page *chunk; -+ chunk = dma_alloc_from_contiguous(&vc_cma_device.dev, -+ PAGES_PER_CHUNK, -+ VC_CMA_CHUNK_ORDER); -+ if (!chunk) -+ break; -+ BUG_ON(((page_to_phys(chunk) - vc_cma_base) % -+ VC_CMA_CHUNK_SIZE) != 0); -+ } -+ if (vc_cma_chunks_used != chunks_needed) { -+ LOG_ERR("%s: dma_alloc_from_contiguous failed (%d " -+ "bytes, allocation %d of %d)", -+ __func__, VC_CMA_CHUNK_SIZE, -+ vc_cma_chunks_used, chunks_needed); -+ goto out_release; -+ } -+ -+ vchiq_add_connected_callback(vc_cma_connected_init); -+ } -+ -+ rc = alloc_chrdev_region(&vc_cma_devnum, 0, 1, DRIVER_NAME); -+ if (rc < 0) { -+ LOG_ERR("%s: alloc_chrdev_region failed (rc=%d)", __func__, rc); -+ goto out_release; -+ } -+ -+ cdev_init(&vc_cma_cdev, &vc_cma_fops); -+ rc = cdev_add(&vc_cma_cdev, vc_cma_devnum, 1); -+ if (rc != 0) { -+ LOG_ERR("%s: cdev_add failed (rc=%d)", __func__, rc); -+ goto out_unregister; -+ } -+ -+ vc_cma_class = class_create(THIS_MODULE, DRIVER_NAME); -+ if (IS_ERR(vc_cma_class)) { -+ rc = PTR_ERR(vc_cma_class); -+ LOG_ERR("%s: class_create failed (rc=%d)", __func__, rc); -+ goto out_cdev_del; -+ } -+ -+ dev = device_create(vc_cma_class, NULL, vc_cma_devnum, NULL, -+ DRIVER_NAME); -+ if (IS_ERR(dev)) { -+ rc = PTR_ERR(dev); -+ LOG_ERR("%s: device_create failed (rc=%d)", __func__, rc); -+ goto out_class_destroy; -+ } -+ -+ vc_cma_proc_entry = proc_create(DRIVER_NAME, 0444, NULL, &vc_cma_proc_fops); -+ if (vc_cma_proc_entry == NULL) { -+ rc = -EFAULT; -+ LOG_ERR("%s: proc_create failed", __func__); -+ goto out_device_destroy; -+ } -+ -+ vc_cma_inited = 1; -+ return 0; -+ -+out_device_destroy: -+ device_destroy(vc_cma_class, vc_cma_devnum); -+ -+out_class_destroy: -+ class_destroy(vc_cma_class); -+ vc_cma_class = NULL; -+ -+out_cdev_del: -+ cdev_del(&vc_cma_cdev); -+ -+out_unregister: -+ unregister_chrdev_region(vc_cma_devnum, 1); -+ -+out_release: -+ /* It is tempting to try to clean up by calling -+ dma_release_from_contiguous for all allocated chunks, but it isn't -+ a very safe thing to do. If vc_cma_initial is non-zero it is because -+ VideoCore is already using that memory, so giving it back to Linux -+ is likely to be fatal. -+ */ -+ return -1; -+} -+ -+/**************************************************************************** -+* -+* vc_cma_exit -+* -+***************************************************************************/ -+ -+static void __exit vc_cma_exit(void) -+{ -+ LOG_DBG("%s: called", __func__); -+ -+ if (vc_cma_inited) { -+ remove_proc_entry(DRIVER_NAME, NULL); -+ device_destroy(vc_cma_class, vc_cma_devnum); -+ class_destroy(vc_cma_class); -+ cdev_del(&vc_cma_cdev); -+ unregister_chrdev_region(vc_cma_devnum, 1); -+ } -+} -+ -+module_init(vc_cma_init); -+module_exit(vc_cma_exit); -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Broadcom Corporation"); ---- /dev/null -+++ b/include/linux/broadcom/vc_cma.h -@@ -0,0 +1,36 @@ -+/***************************************************************************** -+* Copyright 2012 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_CMA_H ) -+#define VC_CMA_H -+ -+#include -+ -+#define VC_CMA_IOC_MAGIC 0xc5 -+ -+#define VC_CMA_IOC_RESERVE _IO(VC_CMA_IOC_MAGIC, 0) -+ -+#ifdef __KERNEL__ -+ -+#ifdef CONFIG_BCM_VC_CMA -+void vc_cma_early_init(void); -+void vc_cma_reserve(void); -+#else -+static inline void vc_cma_early_init(void) { } -+static inline void vc_cma_reserve(void) { } -+#endif -+ -+#endif -+ -+#endif /* VC_CMA_H */ diff --git a/target/linux/brcm2708/patches-4.4/0036-bcm2708-alsa-sound-driver.patch b/target/linux/brcm2708/patches-4.4/0036-bcm2708-alsa-sound-driver.patch deleted file mode 100644 index 936c929e24..0000000000 --- a/target/linux/brcm2708/patches-4.4/0036-bcm2708-alsa-sound-driver.patch +++ /dev/null @@ -1,2678 +0,0 @@ -From 1e8eb30b259278ea7ab9d14c66d2fab5f580f73c Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Mon, 26 Mar 2012 22:15:50 +0100 -Subject: [PATCH 036/170] bcm2708: alsa sound driver -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: popcornmix - -alsa: add mmap support and some cleanups to bcm2835 ALSA driver - -snd-bcm2835: Add support for spdif/hdmi passthrough - -This adds a dedicated subdevice which can be used for passthrough of non-audio -formats (ie encoded a52) through the hdmi audio link. In addition to this -driver extension an appropriate card config is required to make alsa-lib -support the AES parameters for this device. - -snd-bcm2708: Add mutex, improve logging - -Fix for ALSA driver crash - -Avoids an issue when closing and opening vchiq where a message can arrive before service handle has been written - -alsa: reduce severity of expected warning message - -snd-bcm2708: Fix dmesg spam for non-error case - -alsa: Ensure mutexes are released through error paths - -alsa: Make interrupted close paths quieter - -BCM270x: Add onboard sound device to Device Tree - -Add Device Tree support to alsa driver. -Add device to Device Tree. -Don't add platform devices when booting in DT mode. - -Signed-off-by: Noralf Trønnes ---- - sound/arm/Kconfig | 8 + - sound/arm/Makefile | 5 + - sound/arm/bcm2835-ctl.c | 323 +++++++++++++ - sound/arm/bcm2835-pcm.c | 557 +++++++++++++++++++++++ - sound/arm/bcm2835-vchiq.c | 902 +++++++++++++++++++++++++++++++++++++ - sound/arm/bcm2835.c | 511 +++++++++++++++++++++ - sound/arm/bcm2835.h | 167 +++++++ - sound/arm/vc_vchi_audioserv_defs.h | 116 +++++ - 8 files changed, 2589 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 - create mode 100644 sound/arm/bcm2835.c - create mode 100755 sound/arm/bcm2835.h - create mode 100644 sound/arm/vc_vchi_audioserv_defs.h - ---- a/sound/arm/Kconfig -+++ b/sound/arm/Kconfig -@@ -40,5 +40,13 @@ config SND_PXA2XX_AC97 - Say Y or M if you want to support any AC97 codec attached to - the PXA2xx AC97 interface. - -+config SND_BCM2835 -+ tristate "BCM2835 ALSA driver" -+ depends on (ARCH_BCM2708 || ARCH_BCM2709 || ARCH_BCM2835) \ -+ && BCM2708_VCHIQ && SND -+ select SND_PCM -+ help -+ Say Y or M if you want to support BCM2835 Alsa pcm card driver -+ - endif # SND_ARM - ---- a/sound/arm/Makefile -+++ b/sound/arm/Makefile -@@ -14,3 +14,8 @@ snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_A - - obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o - snd-pxa2xx-ac97-objs := pxa2xx-ac97.o -+ -+obj-$(CONFIG_SND_BCM2835) += snd-bcm2835.o -+snd-bcm2835-objs := bcm2835.o bcm2835-ctl.o bcm2835-pcm.o bcm2835-vchiq.o -+ -+ccflags-y += -Idrivers/misc/vc04_services -Idrivers/misc/vc04_services/interface/vcos/linuxkernel -D__VCCOREVER__=0x04000000 ---- /dev/null -+++ b/sound/arm/bcm2835-ctl.c -@@ -0,0 +1,323 @@ -+/***************************************************************************** -+* 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 -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "bcm2835.h" -+ -+/* volume maximum and minimum in terms of 0.01dB */ -+#define CTRL_VOL_MAX 400 -+#define CTRL_VOL_MIN -10239 /* originally -10240 */ -+ -+ -+static int snd_bcm2835_ctl_info(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ audio_info(" ... IN\n"); -+ if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) { -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; -+ uinfo->count = 1; -+ uinfo->value.integer.min = CTRL_VOL_MIN; -+ uinfo->value.integer.max = CTRL_VOL_MAX; /* 2303 */ -+ } else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) { -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; -+ uinfo->count = 1; -+ uinfo->value.integer.min = 0; -+ uinfo->value.integer.max = 1; -+ } else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) { -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; -+ uinfo->count = 1; -+ uinfo->value.integer.min = 0; -+ uinfo->value.integer.max = AUDIO_DEST_MAX-1; -+ } -+ audio_info(" ... OUT\n"); -+ return 0; -+} -+ -+/* toggles mute on or off depending on the value of nmute, and returns -+ * 1 if the mute value was changed, otherwise 0 -+ */ -+static int toggle_mute(struct bcm2835_chip *chip, int nmute) -+{ -+ /* if settings are ok, just return 0 */ -+ if(chip->mute == nmute) -+ return 0; -+ -+ /* if the sound is muted then we need to unmute */ -+ if(chip->mute == CTRL_VOL_MUTE) -+ { -+ chip->volume = chip->old_volume; /* copy the old volume back */ -+ audio_info("Unmuting, old_volume = %d, volume = %d ...\n", chip->old_volume, chip->volume); -+ } -+ else /* otherwise we mute */ -+ { -+ chip->old_volume = chip->volume; -+ chip->volume = 26214; /* set volume to minimum level AKA mute */ -+ audio_info("Muting, old_volume = %d, volume = %d ...\n", chip->old_volume, chip->volume); -+ } -+ -+ chip->mute = nmute; -+ return 1; -+} -+ -+static int snd_bcm2835_ctl_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); -+ -+ BUG_ON(!chip && !(chip->avail_substreams & AVAIL_SUBSTREAMS_MASK)); -+ -+ if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) -+ ucontrol->value.integer.value[0] = chip2alsa(chip->volume); -+ else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) -+ ucontrol->value.integer.value[0] = chip->mute; -+ else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) -+ ucontrol->value.integer.value[0] = chip->dest; -+ -+ return 0; -+} -+ -+static int snd_bcm2835_ctl_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); -+ int changed = 0; -+ -+ if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) { -+ audio_info("Volume change attempted.. volume = %d new_volume = %d\n", chip->volume, (int)ucontrol->value.integer.value[0]); -+ if (chip->mute == CTRL_VOL_MUTE) { -+ /* changed = toggle_mute(chip, CTRL_VOL_UNMUTE); */ -+ return 1; /* should return 0 to signify no change but the mixer takes this as the opposite sign (no idea why) */ -+ } -+ if (changed -+ || (ucontrol->value.integer.value[0] != chip2alsa(chip->volume))) { -+ -+ chip->volume = alsa2chip(ucontrol->value.integer.value[0]); -+ changed = 1; -+ } -+ -+ } else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) { -+ /* Now implemented */ -+ audio_info(" Mute attempted\n"); -+ changed = toggle_mute(chip, ucontrol->value.integer.value[0]); -+ -+ } else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) { -+ if (ucontrol->value.integer.value[0] != chip->dest) { -+ chip->dest = ucontrol->value.integer.value[0]; -+ changed = 1; -+ } -+ } -+ -+ if (changed) { -+ if (bcm2835_audio_set_ctls(chip)) -+ printk(KERN_ERR "Failed to set ALSA controls..\n"); -+ } -+ -+ return changed; -+} -+ -+static DECLARE_TLV_DB_SCALE(snd_bcm2835_db_scale, CTRL_VOL_MIN, 1, 1); -+ -+static struct snd_kcontrol_new snd_bcm2835_ctl[] = { -+ { -+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, -+ .name = "PCM Playback Volume", -+ .index = 0, -+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, -+ .private_value = PCM_PLAYBACK_VOLUME, -+ .info = snd_bcm2835_ctl_info, -+ .get = snd_bcm2835_ctl_get, -+ .put = snd_bcm2835_ctl_put, -+ .count = 1, -+ .tlv = {.p = snd_bcm2835_db_scale} -+ }, -+ { -+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, -+ .name = "PCM Playback Switch", -+ .index = 0, -+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, -+ .private_value = PCM_PLAYBACK_MUTE, -+ .info = snd_bcm2835_ctl_info, -+ .get = snd_bcm2835_ctl_get, -+ .put = snd_bcm2835_ctl_put, -+ .count = 1, -+ }, -+ { -+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, -+ .name = "PCM Playback Route", -+ .index = 0, -+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, -+ .private_value = PCM_PLAYBACK_DEVICE, -+ .info = snd_bcm2835_ctl_info, -+ .get = snd_bcm2835_ctl_get, -+ .put = snd_bcm2835_ctl_put, -+ .count = 1, -+ }, -+}; -+ -+static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; -+ uinfo->count = 1; -+ return 0; -+} -+ -+static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); -+ int i; -+ -+ for (i = 0; i < 4; i++) -+ ucontrol->value.iec958.status[i] = -+ (chip->spdif_status >> (i * 8)) && 0xff; -+ -+ return 0; -+} -+ -+static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); -+ unsigned int val = 0; -+ int i, change; -+ -+ for (i = 0; i < 4; i++) -+ val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); -+ -+ change = val != chip->spdif_status; -+ chip->spdif_status = val; -+ -+ return change; -+} -+ -+static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; -+ uinfo->count = 1; -+ return 0; -+} -+ -+static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ /* bcm2835 supports only consumer mode and sets all other format flags -+ * automatically. So the only thing left is signalling non-audio -+ * content */ -+ ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO; -+ return 0; -+} -+ -+static int snd_bcm2835_spdif_stream_info(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; -+ uinfo->count = 1; -+ return 0; -+} -+ -+static int snd_bcm2835_spdif_stream_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); -+ int i; -+ -+ for (i = 0; i < 4; i++) -+ ucontrol->value.iec958.status[i] = -+ (chip->spdif_status >> (i * 8)) & 0xff; -+ return 0; -+} -+ -+static int snd_bcm2835_spdif_stream_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); -+ unsigned int val = 0; -+ int i, change; -+ -+ for (i = 0; i < 4; i++) -+ val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); -+ change = val != chip->spdif_status; -+ chip->spdif_status = val; -+ -+ return change; -+} -+ -+static struct snd_kcontrol_new snd_bcm2835_spdif[] = { -+ { -+ .iface = SNDRV_CTL_ELEM_IFACE_PCM, -+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), -+ .info = snd_bcm2835_spdif_default_info, -+ .get = snd_bcm2835_spdif_default_get, -+ .put = snd_bcm2835_spdif_default_put -+ }, -+ { -+ .access = SNDRV_CTL_ELEM_ACCESS_READ, -+ .iface = SNDRV_CTL_ELEM_IFACE_PCM, -+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK), -+ .info = snd_bcm2835_spdif_mask_info, -+ .get = snd_bcm2835_spdif_mask_get, -+ }, -+ { -+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | -+ SNDRV_CTL_ELEM_ACCESS_INACTIVE, -+ .iface = SNDRV_CTL_ELEM_IFACE_PCM, -+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM), -+ .info = snd_bcm2835_spdif_stream_info, -+ .get = snd_bcm2835_spdif_stream_get, -+ .put = snd_bcm2835_spdif_stream_put, -+ }, -+}; -+ -+int snd_bcm2835_new_ctl(bcm2835_chip_t * chip) -+{ -+ int err; -+ unsigned int idx; -+ -+ strcpy(chip->card->mixername, "Broadcom Mixer"); -+ for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_ctl); idx++) { -+ err = -+ snd_ctl_add(chip->card, -+ snd_ctl_new1(&snd_bcm2835_ctl[idx], chip)); -+ if (err < 0) -+ return err; -+ } -+ for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_spdif); idx++) { -+ err = snd_ctl_add(chip->card, -+ snd_ctl_new1(&snd_bcm2835_spdif[idx], chip)); -+ if (err < 0) -+ return err; -+ } -+ return 0; -+} ---- /dev/null -+++ b/sound/arm/bcm2835-pcm.c -@@ -0,0 +1,557 @@ -+/***************************************************************************** -+* 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 "bcm2835.h" -+ -+/* hardware definition */ -+static struct snd_pcm_hardware snd_bcm2835_playback_hw = { -+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | -+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), -+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, -+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, -+ .rate_min = 8000, -+ .rate_max = 48000, -+ .channels_min = 1, -+ .channels_max = 2, -+ .buffer_bytes_max = 128 * 1024, -+ .period_bytes_min = 1 * 1024, -+ .period_bytes_max = 128 * 1024, -+ .periods_min = 1, -+ .periods_max = 128, -+}; -+ -+static struct snd_pcm_hardware snd_bcm2835_playback_spdif_hw = { -+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | -+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), -+ .formats = SNDRV_PCM_FMTBIT_S16_LE, -+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_44100 | -+ SNDRV_PCM_RATE_48000, -+ .rate_min = 44100, -+ .rate_max = 48000, -+ .channels_min = 2, -+ .channels_max = 2, -+ .buffer_bytes_max = 128 * 1024, -+ .period_bytes_min = 1 * 1024, -+ .period_bytes_max = 128 * 1024, -+ .periods_min = 1, -+ .periods_max = 128, -+}; -+ -+static void snd_bcm2835_playback_free(struct snd_pcm_runtime *runtime) -+{ -+ audio_info("Freeing up alsa stream here ..\n"); -+ if (runtime->private_data) -+ kfree(runtime->private_data); -+ runtime->private_data = NULL; -+} -+ -+static irqreturn_t bcm2835_playback_fifo_irq(int irq, void *dev_id) -+{ -+ bcm2835_alsa_stream_t *alsa_stream = (bcm2835_alsa_stream_t *) dev_id; -+ uint32_t consumed = 0; -+ int new_period = 0; -+ -+ audio_info(" .. IN\n"); -+ -+ audio_info("alsa_stream=%p substream=%p\n", alsa_stream, -+ alsa_stream ? alsa_stream->substream : 0); -+ -+ if (alsa_stream->open) -+ consumed = bcm2835_audio_retrieve_buffers(alsa_stream); -+ -+ /* We get called only if playback was triggered, So, the number of buffers we retrieve in -+ * each iteration are the buffers that have been played out already -+ */ -+ -+ if (alsa_stream->period_size) { -+ if ((alsa_stream->pos / alsa_stream->period_size) != -+ ((alsa_stream->pos + consumed) / alsa_stream->period_size)) -+ new_period = 1; -+ } -+ audio_debug("updating pos cur: %d + %d max:%d period_bytes:%d, hw_ptr: %d new_period:%d\n", -+ alsa_stream->pos, -+ consumed, -+ alsa_stream->buffer_size, -+ (int)(alsa_stream->period_size*alsa_stream->substream->runtime->periods), -+ frames_to_bytes(alsa_stream->substream->runtime, alsa_stream->substream->runtime->status->hw_ptr), -+ new_period); -+ if (alsa_stream->buffer_size) { -+ alsa_stream->pos += consumed &~ (1<<30); -+ alsa_stream->pos %= alsa_stream->buffer_size; -+ } -+ -+ if (alsa_stream->substream) { -+ if (new_period) -+ snd_pcm_period_elapsed(alsa_stream->substream); -+ } else { -+ audio_warning(" unexpected NULL substream\n"); -+ } -+ audio_info(" .. OUT\n"); -+ -+ return IRQ_HANDLED; -+} -+ -+/* open callback */ -+static int snd_bcm2835_playback_open_generic( -+ struct snd_pcm_substream *substream, int spdif) -+{ -+ bcm2835_chip_t *chip = snd_pcm_substream_chip(substream); -+ struct snd_pcm_runtime *runtime = substream->runtime; -+ bcm2835_alsa_stream_t *alsa_stream; -+ int idx; -+ int err; -+ -+ audio_info(" .. IN (%d)\n", substream->number); -+ -+ if(mutex_lock_interruptible(&chip->audio_mutex)) -+ { -+ audio_error("Interrupted whilst waiting for lock\n"); -+ return -EINTR; -+ } -+ audio_info("Alsa open (%d)\n", substream->number); -+ idx = substream->number; -+ -+ if (spdif && chip->opened != 0) { -+ err = -EBUSY; -+ goto out; -+ } -+ else if (!spdif && (chip->opened & (1 << idx))) { -+ err = -EBUSY; -+ goto out; -+ } -+ if (idx > MAX_SUBSTREAMS) { -+ audio_error -+ ("substream(%d) device doesn't exist max(%d) substreams allowed\n", -+ idx, MAX_SUBSTREAMS); -+ err = -ENODEV; -+ goto out; -+ } -+ -+ /* Check if we are ready */ -+ if (!(chip->avail_substreams & (1 << idx))) { -+ /* We are not ready yet */ -+ audio_error("substream(%d) device is not ready yet\n", idx); -+ err = -EAGAIN; -+ goto out; -+ } -+ -+ alsa_stream = kzalloc(sizeof(bcm2835_alsa_stream_t), GFP_KERNEL); -+ if (alsa_stream == NULL) { -+ err = -ENOMEM; -+ goto out; -+ } -+ -+ /* Initialise alsa_stream */ -+ alsa_stream->chip = chip; -+ alsa_stream->substream = substream; -+ alsa_stream->idx = idx; -+ -+ sema_init(&alsa_stream->buffers_update_sem, 0); -+ sema_init(&alsa_stream->control_sem, 0); -+ spin_lock_init(&alsa_stream->lock); -+ -+ /* Enabled in start trigger, called on each "fifo irq" after that */ -+ alsa_stream->enable_fifo_irq = 0; -+ alsa_stream->fifo_irq_handler = bcm2835_playback_fifo_irq; -+ -+ err = bcm2835_audio_open(alsa_stream); -+ if (err != 0) { -+ kfree(alsa_stream); -+ goto out; -+ } -+ runtime->private_data = alsa_stream; -+ runtime->private_free = snd_bcm2835_playback_free; -+ if (spdif) { -+ runtime->hw = snd_bcm2835_playback_spdif_hw; -+ } else { -+ /* clear spdif status, as we are not in spdif mode */ -+ chip->spdif_status = 0; -+ runtime->hw = snd_bcm2835_playback_hw; -+ } -+ /* minimum 16 bytes alignment (for vchiq bulk transfers) */ -+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -+ 16); -+ -+ chip->alsa_stream[idx] = alsa_stream; -+ -+ chip->opened |= (1 << idx); -+ alsa_stream->open = 1; -+ alsa_stream->draining = 1; -+ -+out: -+ mutex_unlock(&chip->audio_mutex); -+ -+ audio_info(" .. OUT =%d\n", err); -+ -+ return err; -+} -+ -+static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream) -+{ -+ return snd_bcm2835_playback_open_generic(substream, 0); -+} -+ -+static int snd_bcm2835_playback_spdif_open(struct snd_pcm_substream *substream) -+{ -+ return snd_bcm2835_playback_open_generic(substream, 1); -+} -+ -+/* close callback */ -+static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream) -+{ -+ /* the hardware-specific codes will be here */ -+ -+ bcm2835_chip_t *chip; -+ struct snd_pcm_runtime *runtime; -+ bcm2835_alsa_stream_t *alsa_stream; -+ -+ audio_info(" .. IN\n"); -+ -+ chip = snd_pcm_substream_chip(substream); -+ if(mutex_lock_interruptible(&chip->audio_mutex)) -+ { -+ audio_error("Interrupted whilst waiting for lock\n"); -+ return -EINTR; -+ } -+ runtime = substream->runtime; -+ alsa_stream = runtime->private_data; -+ -+ audio_info("Alsa close\n"); -+ -+ /* -+ * Call stop if it's still running. This happens when app -+ * is force killed and we don't get a stop trigger. -+ */ -+ if (alsa_stream->running) { -+ int err; -+ err = bcm2835_audio_stop(alsa_stream); -+ alsa_stream->running = 0; -+ if (err != 0) -+ audio_error(" Failed to STOP alsa device\n"); -+ } -+ -+ alsa_stream->period_size = 0; -+ alsa_stream->buffer_size = 0; -+ -+ if (alsa_stream->open) { -+ alsa_stream->open = 0; -+ bcm2835_audio_close(alsa_stream); -+ } -+ if (alsa_stream->chip) -+ alsa_stream->chip->alsa_stream[alsa_stream->idx] = NULL; -+ /* -+ * Do not free up alsa_stream here, it will be freed up by -+ * runtime->private_free callback we registered in *_open above -+ */ -+ -+ chip->opened &= ~(1 << substream->number); -+ -+ mutex_unlock(&chip->audio_mutex); -+ audio_info(" .. OUT\n"); -+ -+ return 0; -+} -+ -+/* hw_params callback */ -+static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params) -+{ -+ struct snd_pcm_runtime *runtime = substream->runtime; -+ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; -+ int err; -+ -+ audio_info(" .. IN\n"); -+ -+ err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); -+ if (err < 0) { -+ audio_error -+ (" pcm_lib_malloc failed to allocated pages for buffers\n"); -+ return err; -+ } -+ -+ alsa_stream->channels = params_channels(params); -+ alsa_stream->params_rate = params_rate(params); -+ alsa_stream->pcm_format_width = snd_pcm_format_width(params_format (params)); -+ audio_info(" .. OUT\n"); -+ -+ return err; -+} -+ -+/* hw_free callback */ -+static int snd_bcm2835_pcm_hw_free(struct snd_pcm_substream *substream) -+{ -+ audio_info(" .. IN\n"); -+ return snd_pcm_lib_free_pages(substream); -+} -+ -+/* prepare callback */ -+static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream) -+{ -+ bcm2835_chip_t *chip = snd_pcm_substream_chip(substream); -+ struct snd_pcm_runtime *runtime = substream->runtime; -+ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; -+ int channels; -+ int err; -+ -+ audio_info(" .. IN\n"); -+ -+ /* notify the vchiq that it should enter spdif passthrough mode by -+ * setting channels=0 (see -+ * https://github.com/raspberrypi/linux/issues/528) */ -+ if (chip->spdif_status & IEC958_AES0_NONAUDIO) -+ channels = 0; -+ else -+ channels = alsa_stream->channels; -+ -+ err = bcm2835_audio_set_params(alsa_stream, channels, -+ alsa_stream->params_rate, -+ alsa_stream->pcm_format_width); -+ if (err < 0) { -+ audio_error(" error setting hw params\n"); -+ } -+ -+ bcm2835_audio_setup(alsa_stream); -+ -+ /* in preparation of the stream, set the controls (volume level) of the stream */ -+ bcm2835_audio_set_ctls(alsa_stream->chip); -+ -+ -+ memset(&alsa_stream->pcm_indirect, 0, sizeof(alsa_stream->pcm_indirect)); -+ -+ alsa_stream->pcm_indirect.hw_buffer_size = -+ alsa_stream->pcm_indirect.sw_buffer_size = -+ snd_pcm_lib_buffer_bytes(substream); -+ -+ alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream); -+ alsa_stream->period_size = snd_pcm_lib_period_bytes(substream); -+ alsa_stream->pos = 0; -+ -+ audio_debug("buffer_size=%d, period_size=%d pos=%d frame_bits=%d\n", -+ alsa_stream->buffer_size, alsa_stream->period_size, -+ alsa_stream->pos, runtime->frame_bits); -+ -+ audio_info(" .. OUT\n"); -+ return 0; -+} -+ -+static void snd_bcm2835_pcm_transfer(struct snd_pcm_substream *substream, -+ struct snd_pcm_indirect *rec, size_t bytes) -+{ -+ struct snd_pcm_runtime *runtime = substream->runtime; -+ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; -+ void *src = (void *)(substream->runtime->dma_area + rec->sw_data); -+ int err; -+ -+ err = bcm2835_audio_write(alsa_stream, bytes, src); -+ if (err) -+ audio_error(" Failed to transfer to alsa device (%d)\n", err); -+ -+} -+ -+static int snd_bcm2835_pcm_ack(struct snd_pcm_substream *substream) -+{ -+ struct snd_pcm_runtime *runtime = substream->runtime; -+ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; -+ struct snd_pcm_indirect *pcm_indirect = &alsa_stream->pcm_indirect; -+ -+ pcm_indirect->hw_queue_size = runtime->hw.buffer_bytes_max; -+ snd_pcm_indirect_playback_transfer(substream, pcm_indirect, -+ snd_bcm2835_pcm_transfer); -+ return 0; -+} -+ -+/* trigger callback */ -+static int snd_bcm2835_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -+{ -+ struct snd_pcm_runtime *runtime = substream->runtime; -+ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; -+ int err = 0; -+ -+ audio_info(" .. IN\n"); -+ -+ switch (cmd) { -+ case SNDRV_PCM_TRIGGER_START: -+ audio_debug("bcm2835_AUDIO_TRIGGER_START running=%d\n", -+ alsa_stream->running); -+ if (!alsa_stream->running) { -+ err = bcm2835_audio_start(alsa_stream); -+ if (err == 0) { -+ alsa_stream->pcm_indirect.hw_io = -+ alsa_stream->pcm_indirect.hw_data = -+ bytes_to_frames(runtime, -+ alsa_stream->pos); -+ substream->ops->ack(substream); -+ alsa_stream->running = 1; -+ alsa_stream->draining = 1; -+ } else { -+ audio_error(" Failed to START alsa device (%d)\n", err); -+ } -+ } -+ break; -+ case SNDRV_PCM_TRIGGER_STOP: -+ audio_debug -+ ("bcm2835_AUDIO_TRIGGER_STOP running=%d draining=%d\n", -+ alsa_stream->running, runtime->status->state == SNDRV_PCM_STATE_DRAINING); -+ if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { -+ audio_info("DRAINING\n"); -+ alsa_stream->draining = 1; -+ } else { -+ audio_info("DROPPING\n"); -+ alsa_stream->draining = 0; -+ } -+ if (alsa_stream->running) { -+ err = bcm2835_audio_stop(alsa_stream); -+ if (err != 0) -+ audio_error(" Failed to STOP alsa device (%d)\n", err); -+ alsa_stream->running = 0; -+ } -+ break; -+ default: -+ err = -EINVAL; -+ } -+ -+ audio_info(" .. OUT\n"); -+ return err; -+} -+ -+/* pointer callback */ -+static snd_pcm_uframes_t -+snd_bcm2835_pcm_pointer(struct snd_pcm_substream *substream) -+{ -+ struct snd_pcm_runtime *runtime = substream->runtime; -+ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; -+ -+ audio_info(" .. IN\n"); -+ -+ audio_debug("pcm_pointer... (%d) hwptr=%d appl=%d pos=%d\n", 0, -+ frames_to_bytes(runtime, runtime->status->hw_ptr), -+ frames_to_bytes(runtime, runtime->control->appl_ptr), -+ alsa_stream->pos); -+ -+ audio_info(" .. OUT\n"); -+ return snd_pcm_indirect_playback_pointer(substream, -+ &alsa_stream->pcm_indirect, -+ alsa_stream->pos); -+} -+ -+static int snd_bcm2835_pcm_lib_ioctl(struct snd_pcm_substream *substream, -+ unsigned int cmd, void *arg) -+{ -+ int ret = snd_pcm_lib_ioctl(substream, cmd, arg); -+ audio_info(" .. substream=%p, cmd=%d, arg=%p (%x) ret=%d\n", substream, -+ cmd, arg, arg ? *(unsigned *)arg : 0, ret); -+ return ret; -+} -+ -+/* operators */ -+static struct snd_pcm_ops snd_bcm2835_playback_ops = { -+ .open = snd_bcm2835_playback_open, -+ .close = snd_bcm2835_playback_close, -+ .ioctl = snd_bcm2835_pcm_lib_ioctl, -+ .hw_params = snd_bcm2835_pcm_hw_params, -+ .hw_free = snd_bcm2835_pcm_hw_free, -+ .prepare = snd_bcm2835_pcm_prepare, -+ .trigger = snd_bcm2835_pcm_trigger, -+ .pointer = snd_bcm2835_pcm_pointer, -+ .ack = snd_bcm2835_pcm_ack, -+}; -+ -+static struct snd_pcm_ops snd_bcm2835_playback_spdif_ops = { -+ .open = snd_bcm2835_playback_spdif_open, -+ .close = snd_bcm2835_playback_close, -+ .ioctl = snd_bcm2835_pcm_lib_ioctl, -+ .hw_params = snd_bcm2835_pcm_hw_params, -+ .hw_free = snd_bcm2835_pcm_hw_free, -+ .prepare = snd_bcm2835_pcm_prepare, -+ .trigger = snd_bcm2835_pcm_trigger, -+ .pointer = snd_bcm2835_pcm_pointer, -+ .ack = snd_bcm2835_pcm_ack, -+}; -+ -+/* create a pcm device */ -+int snd_bcm2835_new_pcm(bcm2835_chip_t * chip) -+{ -+ struct snd_pcm *pcm; -+ int err; -+ -+ audio_info(" .. IN\n"); -+ mutex_init(&chip->audio_mutex); -+ if(mutex_lock_interruptible(&chip->audio_mutex)) -+ { -+ audio_error("Interrupted whilst waiting for lock\n"); -+ return -EINTR; -+ } -+ err = -+ snd_pcm_new(chip->card, "bcm2835 ALSA", 0, MAX_SUBSTREAMS, 0, &pcm); -+ if (err < 0) -+ goto out; -+ pcm->private_data = chip; -+ strcpy(pcm->name, "bcm2835 ALSA"); -+ chip->pcm = pcm; -+ chip->dest = AUDIO_DEST_AUTO; -+ chip->volume = alsa2chip(0); -+ chip->mute = CTRL_VOL_UNMUTE; /*disable mute on startup */ -+ /* set operators */ -+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, -+ &snd_bcm2835_playback_ops); -+ -+ /* pre-allocation of buffers */ -+ /* NOTE: this may fail */ -+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, -+ snd_dma_continuous_data -+ (GFP_KERNEL), 64 * 1024, -+ 64 * 1024); -+ -+out: -+ mutex_unlock(&chip->audio_mutex); -+ audio_info(" .. OUT\n"); -+ -+ return 0; -+} -+ -+int snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip) -+{ -+ struct snd_pcm *pcm; -+ int err; -+ -+ audio_info(" .. IN\n"); -+ if(mutex_lock_interruptible(&chip->audio_mutex)) -+ { -+ audio_error("Interrupted whilst waiting for lock\n"); -+ return -EINTR; -+ } -+ err = snd_pcm_new(chip->card, "bcm2835 ALSA", 1, 1, 0, &pcm); -+ if (err < 0) -+ goto out; -+ -+ pcm->private_data = chip; -+ strcpy(pcm->name, "bcm2835 IEC958/HDMI"); -+ chip->pcm_spdif = pcm; -+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, -+ &snd_bcm2835_playback_spdif_ops); -+ -+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, -+ snd_dma_continuous_data (GFP_KERNEL), -+ 64 * 1024, 64 * 1024); -+out: -+ mutex_unlock(&chip->audio_mutex); -+ audio_info(" .. OUT\n"); -+ -+ return 0; -+} ---- /dev/null -+++ b/sound/arm/bcm2835-vchiq.c -@@ -0,0 +1,902 @@ -+/***************************************************************************** -+* 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 -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "bcm2835.h" -+ -+/* ---- Include Files -------------------------------------------------------- */ -+ -+#include "interface/vchi/vchi.h" -+#include "vc_vchi_audioserv_defs.h" -+ -+/* ---- Private Constants and Types ------------------------------------------ */ -+ -+#define BCM2835_AUDIO_STOP 0 -+#define BCM2835_AUDIO_START 1 -+#define BCM2835_AUDIO_WRITE 2 -+ -+/* Logging macros (for remapping to other logging mechanisms, i.e., printf) */ -+#ifdef AUDIO_DEBUG_ENABLE -+ #define LOG_ERR( fmt, arg... ) pr_err( "%s:%d " fmt, __func__, __LINE__, ##arg) -+ #define LOG_WARN( fmt, arg... ) pr_info( "%s:%d " fmt, __func__, __LINE__, ##arg) -+ #define LOG_INFO( fmt, arg... ) pr_info( "%s:%d " fmt, __func__, __LINE__, ##arg) -+ #define LOG_DBG( fmt, arg... ) pr_info( "%s:%d " fmt, __func__, __LINE__, ##arg) -+#else -+ #define LOG_ERR( fmt, arg... ) pr_err( "%s:%d " fmt, __func__, __LINE__, ##arg) -+ #define LOG_WARN( fmt, arg... ) -+ #define LOG_INFO( fmt, arg... ) -+ #define LOG_DBG( fmt, arg... ) -+#endif -+ -+typedef struct opaque_AUDIO_INSTANCE_T { -+ uint32_t num_connections; -+ VCHI_SERVICE_HANDLE_T vchi_handle[VCHI_MAX_NUM_CONNECTIONS]; -+ struct completion msg_avail_comp; -+ struct mutex vchi_mutex; -+ bcm2835_alsa_stream_t *alsa_stream; -+ int32_t result; -+ short peer_version; -+} AUDIO_INSTANCE_T; -+ -+bool force_bulk = false; -+ -+/* ---- Private Variables ---------------------------------------------------- */ -+ -+/* ---- Private Function Prototypes ------------------------------------------ */ -+ -+/* ---- Private Functions ---------------------------------------------------- */ -+ -+static int bcm2835_audio_stop_worker(bcm2835_alsa_stream_t * alsa_stream); -+static int bcm2835_audio_start_worker(bcm2835_alsa_stream_t * alsa_stream); -+static int bcm2835_audio_write_worker(bcm2835_alsa_stream_t *alsa_stream, -+ uint32_t count, void *src); -+ -+typedef struct { -+ struct work_struct my_work; -+ bcm2835_alsa_stream_t *alsa_stream; -+ int cmd; -+ void *src; -+ uint32_t count; -+} my_work_t; -+ -+static void my_wq_function(struct work_struct *work) -+{ -+ my_work_t *w = (my_work_t *) work; -+ int ret = -9; -+ LOG_DBG(" .. IN %p:%d\n", w->alsa_stream, w->cmd); -+ switch (w->cmd) { -+ case BCM2835_AUDIO_START: -+ ret = bcm2835_audio_start_worker(w->alsa_stream); -+ break; -+ case BCM2835_AUDIO_STOP: -+ ret = bcm2835_audio_stop_worker(w->alsa_stream); -+ break; -+ case BCM2835_AUDIO_WRITE: -+ ret = bcm2835_audio_write_worker(w->alsa_stream, w->count, -+ w->src); -+ break; -+ default: -+ LOG_ERR(" Unexpected work: %p:%d\n", w->alsa_stream, w->cmd); -+ break; -+ } -+ kfree((void *)work); -+ LOG_DBG(" .. OUT %d\n", ret); -+} -+ -+int bcm2835_audio_start(bcm2835_alsa_stream_t * alsa_stream) -+{ -+ int ret = -1; -+ LOG_DBG(" .. IN\n"); -+ if (alsa_stream->my_wq) { -+ my_work_t *work = kmalloc(sizeof(my_work_t), GFP_ATOMIC); -+ /*--- Queue some work (item 1) ---*/ -+ if (work) { -+ INIT_WORK((struct work_struct *)work, my_wq_function); -+ work->alsa_stream = alsa_stream; -+ work->cmd = BCM2835_AUDIO_START; -+ if (queue_work -+ (alsa_stream->my_wq, (struct work_struct *)work)) -+ ret = 0; -+ } else -+ LOG_ERR(" .. Error: NULL work kmalloc\n"); -+ } -+ LOG_DBG(" .. OUT %d\n", ret); -+ return ret; -+} -+ -+int bcm2835_audio_stop(bcm2835_alsa_stream_t * alsa_stream) -+{ -+ int ret = -1; -+ LOG_DBG(" .. IN\n"); -+ if (alsa_stream->my_wq) { -+ my_work_t *work = kmalloc(sizeof(my_work_t), GFP_ATOMIC); -+ /*--- Queue some work (item 1) ---*/ -+ if (work) { -+ INIT_WORK((struct work_struct *)work, my_wq_function); -+ work->alsa_stream = alsa_stream; -+ work->cmd = BCM2835_AUDIO_STOP; -+ if (queue_work -+ (alsa_stream->my_wq, (struct work_struct *)work)) -+ ret = 0; -+ } else -+ LOG_ERR(" .. Error: NULL work kmalloc\n"); -+ } -+ LOG_DBG(" .. OUT %d\n", ret); -+ return ret; -+} -+ -+int bcm2835_audio_write(bcm2835_alsa_stream_t *alsa_stream, -+ uint32_t count, void *src) -+{ -+ int ret = -1; -+ LOG_DBG(" .. IN\n"); -+ if (alsa_stream->my_wq) { -+ my_work_t *work = kmalloc(sizeof(my_work_t), GFP_ATOMIC); -+ /*--- Queue some work (item 1) ---*/ -+ if (work) { -+ INIT_WORK((struct work_struct *)work, my_wq_function); -+ work->alsa_stream = alsa_stream; -+ work->cmd = BCM2835_AUDIO_WRITE; -+ work->src = src; -+ work->count = count; -+ if (queue_work -+ (alsa_stream->my_wq, (struct work_struct *)work)) -+ ret = 0; -+ } else -+ LOG_ERR(" .. Error: NULL work kmalloc\n"); -+ } -+ LOG_DBG(" .. OUT %d\n", ret); -+ return ret; -+} -+ -+void my_workqueue_init(bcm2835_alsa_stream_t * alsa_stream) -+{ -+ alsa_stream->my_wq = alloc_workqueue("my_queue", WQ_HIGHPRI, 1); -+ return; -+} -+ -+void my_workqueue_quit(bcm2835_alsa_stream_t * alsa_stream) -+{ -+ if (alsa_stream->my_wq) { -+ flush_workqueue(alsa_stream->my_wq); -+ destroy_workqueue(alsa_stream->my_wq); -+ alsa_stream->my_wq = NULL; -+ } -+ return; -+} -+ -+static void audio_vchi_callback(void *param, -+ const VCHI_CALLBACK_REASON_T reason, -+ void *msg_handle) -+{ -+ AUDIO_INSTANCE_T *instance = (AUDIO_INSTANCE_T *) param; -+ int32_t status; -+ int32_t msg_len; -+ VC_AUDIO_MSG_T m; -+ LOG_DBG(" .. IN instance=%p, handle=%p, alsa=%p, reason=%d, handle=%p\n", -+ instance, instance ? instance->vchi_handle[0] : NULL, instance ? instance->alsa_stream : NULL, reason, msg_handle); -+ -+ if (reason != VCHI_CALLBACK_MSG_AVAILABLE) { -+ return; -+ } -+ if (!instance) { -+ LOG_ERR(" .. instance is null\n"); -+ BUG(); -+ return; -+ } -+ if (!instance->vchi_handle[0]) { -+ LOG_ERR(" .. instance->vchi_handle[0] is null\n"); -+ BUG(); -+ return; -+ } -+ status = vchi_msg_dequeue(instance->vchi_handle[0], -+ &m, sizeof m, &msg_len, VCHI_FLAGS_NONE); -+ if (m.type == VC_AUDIO_MSG_TYPE_RESULT) { -+ LOG_DBG -+ (" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_RESULT, success=%d\n", -+ instance, m.u.result.success); -+ instance->result = m.u.result.success; -+ complete(&instance->msg_avail_comp); -+ } else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) { -+ bcm2835_alsa_stream_t *alsa_stream = instance->alsa_stream; -+ irq_handler_t callback = (irq_handler_t) m.u.complete.callback; -+ LOG_DBG -+ (" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_COMPLETE, complete=%d\n", -+ instance, m.u.complete.count); -+ if (alsa_stream && callback) { -+ atomic_add(m.u.complete.count, &alsa_stream->retrieved); -+ callback(0, alsa_stream); -+ } else { -+ LOG_ERR(" .. unexpected alsa_stream=%p, callback=%p\n", -+ alsa_stream, callback); -+ } -+ } else { -+ LOG_ERR(" .. unexpected m.type=%d\n", m.type); -+ } -+ LOG_DBG(" .. OUT\n"); -+} -+ -+static AUDIO_INSTANCE_T *vc_vchi_audio_init(VCHI_INSTANCE_T vchi_instance, -+ VCHI_CONNECTION_T ** -+ vchi_connections, -+ uint32_t num_connections) -+{ -+ uint32_t i; -+ AUDIO_INSTANCE_T *instance; -+ int status; -+ -+ LOG_DBG("%s: start", __func__); -+ -+ if (num_connections > VCHI_MAX_NUM_CONNECTIONS) { -+ LOG_ERR("%s: unsupported number of connections %u (max=%u)\n", -+ __func__, num_connections, VCHI_MAX_NUM_CONNECTIONS); -+ -+ return NULL; -+ } -+ /* Allocate memory for this instance */ -+ instance = kmalloc(sizeof(*instance), GFP_KERNEL); -+ if (!instance) -+ return NULL; -+ -+ memset(instance, 0, sizeof(*instance)); -+ instance->num_connections = num_connections; -+ -+ /* Create a lock for exclusive, serialized VCHI connection access */ -+ mutex_init(&instance->vchi_mutex); -+ /* Open the VCHI service connections */ -+ for (i = 0; i < num_connections; i++) { -+ SERVICE_CREATION_T params = { -+ VCHI_VERSION_EX(VC_AUDIOSERV_VER, VC_AUDIOSERV_MIN_VER), -+ VC_AUDIO_SERVER_NAME, // 4cc service code -+ vchi_connections[i], // passed in fn pointers -+ 0, // rx fifo size (unused) -+ 0, // tx fifo size (unused) -+ audio_vchi_callback, // service callback -+ instance, // service callback parameter -+ 1, //TODO: remove VCOS_FALSE, // unaligned bulk recieves -+ 1, //TODO: remove VCOS_FALSE, // unaligned bulk transmits -+ 0 // want crc check on bulk transfers -+ }; -+ -+ LOG_DBG("%s: about to open %i\n", __func__, i); -+ status = vchi_service_open(vchi_instance, ¶ms, -+ &instance->vchi_handle[i]); -+ LOG_DBG("%s: opened %i: %p=%d\n", __func__, i, instance->vchi_handle[i], status); -+ if (status) { -+ LOG_ERR -+ ("%s: failed to open VCHI service connection (status=%d)\n", -+ __func__, status); -+ -+ goto err_close_services; -+ } -+ /* Finished with the service for now */ -+ vchi_service_release(instance->vchi_handle[i]); -+ } -+ -+ LOG_DBG("%s: okay\n", __func__); -+ return instance; -+ -+err_close_services: -+ for (i = 0; i < instance->num_connections; i++) { -+ LOG_ERR("%s: closing %i: %p\n", __func__, i, instance->vchi_handle[i]); -+ if (instance->vchi_handle[i]) -+ vchi_service_close(instance->vchi_handle[i]); -+ } -+ -+ kfree(instance); -+ LOG_ERR("%s: error\n", __func__); -+ -+ return NULL; -+} -+ -+static int32_t vc_vchi_audio_deinit(AUDIO_INSTANCE_T * instance) -+{ -+ uint32_t i; -+ -+ LOG_DBG(" .. IN\n"); -+ -+ if (instance == NULL) { -+ LOG_ERR("%s: invalid handle %p\n", __func__, instance); -+ -+ return -1; -+ } -+ -+ LOG_DBG(" .. about to lock (%d)\n", instance->num_connections); -+ if(mutex_lock_interruptible(&instance->vchi_mutex)) -+ { -+ LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); -+ return -EINTR; -+ } -+ -+ /* Close all VCHI service connections */ -+ for (i = 0; i < instance->num_connections; i++) { -+ int32_t success; -+ LOG_DBG(" .. %i:closing %p\n", i, instance->vchi_handle[i]); -+ vchi_service_use(instance->vchi_handle[i]); -+ -+ success = vchi_service_close(instance->vchi_handle[i]); -+ if (success != 0) { -+ LOG_DBG -+ ("%s: failed to close VCHI service connection (status=%d)\n", -+ __func__, success); -+ } -+ } -+ -+ mutex_unlock(&instance->vchi_mutex); -+ -+ kfree(instance); -+ -+ LOG_DBG(" .. OUT\n"); -+ -+ return 0; -+} -+ -+static int bcm2835_audio_open_connection(bcm2835_alsa_stream_t * alsa_stream) -+{ -+ static VCHI_INSTANCE_T vchi_instance; -+ static VCHI_CONNECTION_T *vchi_connection; -+ static int initted; -+ AUDIO_INSTANCE_T *instance = alsa_stream->instance; -+ int ret; -+ LOG_DBG(" .. IN\n"); -+ -+ LOG_INFO("%s: start\n", __func__); -+ BUG_ON(instance); -+ if (instance) { -+ LOG_ERR("%s: VCHI instance already open (%p)\n", -+ __func__, instance); -+ instance->alsa_stream = alsa_stream; -+ alsa_stream->instance = instance; -+ ret = 0; // xxx todo -1; -+ goto err_free_mem; -+ } -+ -+ /* Initialize and create a VCHI connection */ -+ if (!initted) { -+ ret = vchi_initialise(&vchi_instance); -+ if (ret != 0) { -+ LOG_ERR("%s: failed to initialise VCHI instance (ret=%d)\n", -+ __func__, ret); -+ -+ ret = -EIO; -+ goto err_free_mem; -+ } -+ ret = vchi_connect(NULL, 0, vchi_instance); -+ if (ret != 0) { -+ LOG_ERR("%s: failed to connect VCHI instance (ret=%d)\n", -+ __func__, ret); -+ -+ ret = -EIO; -+ goto err_free_mem; -+ } -+ initted = 1; -+ } -+ -+ /* Initialize an instance of the audio service */ -+ instance = vc_vchi_audio_init(vchi_instance, &vchi_connection, 1); -+ -+ if (instance == NULL) { -+ LOG_ERR("%s: failed to initialize audio service\n", __func__); -+ -+ ret = -EPERM; -+ goto err_free_mem; -+ } -+ -+ instance->alsa_stream = alsa_stream; -+ alsa_stream->instance = instance; -+ -+ LOG_DBG(" success !\n"); -+err_free_mem: -+ LOG_DBG(" .. OUT\n"); -+ -+ return ret; -+} -+ -+int bcm2835_audio_open(bcm2835_alsa_stream_t * alsa_stream) -+{ -+ AUDIO_INSTANCE_T *instance; -+ VC_AUDIO_MSG_T m; -+ int32_t success; -+ int ret; -+ LOG_DBG(" .. IN\n"); -+ -+ my_workqueue_init(alsa_stream); -+ -+ ret = bcm2835_audio_open_connection(alsa_stream); -+ if (ret != 0) { -+ ret = -1; -+ goto exit; -+ } -+ instance = alsa_stream->instance; -+ LOG_DBG(" instance (%p)\n", instance); -+ -+ if(mutex_lock_interruptible(&instance->vchi_mutex)) -+ { -+ LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); -+ return -EINTR; -+ } -+ vchi_service_use(instance->vchi_handle[0]); -+ -+ m.type = VC_AUDIO_MSG_TYPE_OPEN; -+ -+ /* Send the message to the videocore */ -+ success = vchi_msg_queue(instance->vchi_handle[0], -+ &m, sizeof m, -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); -+ -+ if (success != 0) { -+ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", -+ __func__, success); -+ -+ ret = -1; -+ goto unlock; -+ } -+ -+ ret = 0; -+ -+unlock: -+ vchi_service_release(instance->vchi_handle[0]); -+ mutex_unlock(&instance->vchi_mutex); -+exit: -+ LOG_DBG(" .. OUT\n"); -+ return ret; -+} -+ -+static int bcm2835_audio_set_ctls_chan(bcm2835_alsa_stream_t * alsa_stream, -+ bcm2835_chip_t * chip) -+{ -+ VC_AUDIO_MSG_T m; -+ AUDIO_INSTANCE_T *instance = alsa_stream->instance; -+ int32_t success; -+ int ret; -+ LOG_DBG(" .. IN\n"); -+ -+ LOG_INFO -+ (" Setting ALSA dest(%d), volume(%d)\n", chip->dest, chip->volume); -+ -+ if(mutex_lock_interruptible(&instance->vchi_mutex)) -+ { -+ LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); -+ return -EINTR; -+ } -+ vchi_service_use(instance->vchi_handle[0]); -+ -+ instance->result = -1; -+ -+ m.type = VC_AUDIO_MSG_TYPE_CONTROL; -+ m.u.control.dest = chip->dest; -+ m.u.control.volume = chip->volume; -+ -+ /* Create the message available completion */ -+ init_completion(&instance->msg_avail_comp); -+ -+ /* Send the message to the videocore */ -+ success = vchi_msg_queue(instance->vchi_handle[0], -+ &m, sizeof m, -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); -+ -+ if (success != 0) { -+ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", -+ __func__, success); -+ -+ ret = -1; -+ goto unlock; -+ } -+ -+ /* We are expecting a reply from the videocore */ -+ ret = wait_for_completion_interruptible(&instance->msg_avail_comp); -+ if (ret) { -+ LOG_DBG("%s: failed on waiting for event (status=%d)\n", -+ __func__, success); -+ goto unlock; -+ } -+ -+ if (instance->result != 0) { -+ LOG_ERR("%s: result=%d\n", __func__, instance->result); -+ -+ ret = -1; -+ goto unlock; -+ } -+ -+ ret = 0; -+ -+unlock: -+ vchi_service_release(instance->vchi_handle[0]); -+ mutex_unlock(&instance->vchi_mutex); -+ -+ LOG_DBG(" .. OUT\n"); -+ return ret; -+} -+ -+int bcm2835_audio_set_ctls(bcm2835_chip_t * chip) -+{ -+ int i; -+ int ret = 0; -+ LOG_DBG(" .. IN\n"); -+ LOG_DBG(" Setting ALSA dest(%d), volume(%d)\n", chip->dest, chip->volume); -+ -+ /* change ctls for all substreams */ -+ for (i = 0; i < MAX_SUBSTREAMS; i++) { -+ if (chip->avail_substreams & (1 << i)) { -+ if (!chip->alsa_stream[i]) -+ { -+ LOG_DBG(" No ALSA stream available?! %i:%p (%x)\n", i, chip->alsa_stream[i], chip->avail_substreams); -+ ret = 0; -+ } -+ else if (bcm2835_audio_set_ctls_chan /* returns 0 on success */ -+ (chip->alsa_stream[i], chip) != 0) -+ { -+ LOG_ERR("Couldn't set the controls for stream %d\n", i); -+ ret = -1; -+ } -+ else LOG_DBG(" Controls set for stream %d\n", i); -+ } -+ } -+ LOG_DBG(" .. OUT ret=%d\n", ret); -+ return ret; -+} -+ -+int bcm2835_audio_set_params(bcm2835_alsa_stream_t * alsa_stream, -+ uint32_t channels, uint32_t samplerate, -+ uint32_t bps) -+{ -+ VC_AUDIO_MSG_T m; -+ AUDIO_INSTANCE_T *instance = alsa_stream->instance; -+ int32_t success; -+ int ret; -+ LOG_DBG(" .. IN\n"); -+ -+ LOG_INFO -+ (" Setting ALSA channels(%d), samplerate(%d), bits-per-sample(%d)\n", -+ channels, samplerate, bps); -+ -+ /* resend ctls - alsa_stream may not have been open when first send */ -+ ret = bcm2835_audio_set_ctls_chan(alsa_stream, alsa_stream->chip); -+ if (ret != 0) { -+ LOG_ERR(" Alsa controls not supported\n"); -+ return -EINVAL; -+ } -+ -+ if(mutex_lock_interruptible(&instance->vchi_mutex)) -+ { -+ LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); -+ return -EINTR; -+ } -+ vchi_service_use(instance->vchi_handle[0]); -+ -+ instance->result = -1; -+ -+ m.type = VC_AUDIO_MSG_TYPE_CONFIG; -+ m.u.config.channels = channels; -+ m.u.config.samplerate = samplerate; -+ m.u.config.bps = bps; -+ -+ /* Create the message available completion */ -+ init_completion(&instance->msg_avail_comp); -+ -+ /* Send the message to the videocore */ -+ success = vchi_msg_queue(instance->vchi_handle[0], -+ &m, sizeof m, -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); -+ -+ if (success != 0) { -+ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", -+ __func__, success); -+ -+ ret = -1; -+ goto unlock; -+ } -+ -+ /* We are expecting a reply from the videocore */ -+ ret = wait_for_completion_interruptible(&instance->msg_avail_comp); -+ if (ret) { -+ LOG_DBG("%s: failed on waiting for event (status=%d)\n", -+ __func__, success); -+ goto unlock; -+ } -+ -+ if (instance->result != 0) { -+ LOG_ERR("%s: result=%d", __func__, instance->result); -+ -+ ret = -1; -+ goto unlock; -+ } -+ -+ ret = 0; -+ -+unlock: -+ vchi_service_release(instance->vchi_handle[0]); -+ mutex_unlock(&instance->vchi_mutex); -+ -+ LOG_DBG(" .. OUT\n"); -+ return ret; -+} -+ -+int bcm2835_audio_setup(bcm2835_alsa_stream_t * alsa_stream) -+{ -+ LOG_DBG(" .. IN\n"); -+ -+ LOG_DBG(" .. OUT\n"); -+ -+ return 0; -+} -+ -+static int bcm2835_audio_start_worker(bcm2835_alsa_stream_t * alsa_stream) -+{ -+ VC_AUDIO_MSG_T m; -+ AUDIO_INSTANCE_T *instance = alsa_stream->instance; -+ int32_t success; -+ int ret; -+ LOG_DBG(" .. IN\n"); -+ -+ if(mutex_lock_interruptible(&instance->vchi_mutex)) -+ { -+ LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); -+ return -EINTR; -+ } -+ vchi_service_use(instance->vchi_handle[0]); -+ -+ m.type = VC_AUDIO_MSG_TYPE_START; -+ -+ /* Send the message to the videocore */ -+ success = vchi_msg_queue(instance->vchi_handle[0], -+ &m, sizeof m, -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); -+ -+ if (success != 0) { -+ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", -+ __func__, success); -+ -+ ret = -1; -+ goto unlock; -+ } -+ -+ ret = 0; -+ -+unlock: -+ vchi_service_release(instance->vchi_handle[0]); -+ mutex_unlock(&instance->vchi_mutex); -+ LOG_DBG(" .. OUT\n"); -+ return ret; -+} -+ -+static int bcm2835_audio_stop_worker(bcm2835_alsa_stream_t * alsa_stream) -+{ -+ VC_AUDIO_MSG_T m; -+ AUDIO_INSTANCE_T *instance = alsa_stream->instance; -+ int32_t success; -+ int ret; -+ LOG_DBG(" .. IN\n"); -+ -+ if(mutex_lock_interruptible(&instance->vchi_mutex)) -+ { -+ LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); -+ return -EINTR; -+ } -+ vchi_service_use(instance->vchi_handle[0]); -+ -+ m.type = VC_AUDIO_MSG_TYPE_STOP; -+ m.u.stop.draining = alsa_stream->draining; -+ -+ /* Send the message to the videocore */ -+ success = vchi_msg_queue(instance->vchi_handle[0], -+ &m, sizeof m, -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); -+ -+ if (success != 0) { -+ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", -+ __func__, success); -+ -+ ret = -1; -+ goto unlock; -+ } -+ -+ ret = 0; -+ -+unlock: -+ vchi_service_release(instance->vchi_handle[0]); -+ mutex_unlock(&instance->vchi_mutex); -+ LOG_DBG(" .. OUT\n"); -+ return ret; -+} -+ -+int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream) -+{ -+ VC_AUDIO_MSG_T m; -+ AUDIO_INSTANCE_T *instance = alsa_stream->instance; -+ int32_t success; -+ int ret; -+ LOG_DBG(" .. IN\n"); -+ -+ my_workqueue_quit(alsa_stream); -+ -+ if(mutex_lock_interruptible(&instance->vchi_mutex)) -+ { -+ LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); -+ return -EINTR; -+ } -+ vchi_service_use(instance->vchi_handle[0]); -+ -+ m.type = VC_AUDIO_MSG_TYPE_CLOSE; -+ -+ /* Create the message available completion */ -+ init_completion(&instance->msg_avail_comp); -+ -+ /* Send the message to the videocore */ -+ success = vchi_msg_queue(instance->vchi_handle[0], -+ &m, sizeof m, -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); -+ -+ if (success != 0) { -+ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", -+ __func__, success); -+ ret = -1; -+ goto unlock; -+ } -+ -+ ret = wait_for_completion_interruptible(&instance->msg_avail_comp); -+ if (ret) { -+ LOG_DBG("%s: failed on waiting for event (status=%d)\n", -+ __func__, success); -+ goto unlock; -+ } -+ if (instance->result != 0) { -+ LOG_ERR("%s: failed result (status=%d)\n", -+ __func__, instance->result); -+ -+ ret = -1; -+ goto unlock; -+ } -+ -+ ret = 0; -+ -+unlock: -+ vchi_service_release(instance->vchi_handle[0]); -+ mutex_unlock(&instance->vchi_mutex); -+ -+ /* Stop the audio service */ -+ if (instance) { -+ vc_vchi_audio_deinit(instance); -+ alsa_stream->instance = NULL; -+ } -+ LOG_DBG(" .. OUT\n"); -+ return ret; -+} -+ -+int bcm2835_audio_write_worker(bcm2835_alsa_stream_t *alsa_stream, -+ uint32_t count, void *src) -+{ -+ VC_AUDIO_MSG_T m; -+ AUDIO_INSTANCE_T *instance = alsa_stream->instance; -+ int32_t success; -+ int ret; -+ -+ LOG_DBG(" .. IN\n"); -+ -+ LOG_INFO(" Writing %d bytes from %p\n", count, src); -+ -+ if(mutex_lock_interruptible(&instance->vchi_mutex)) -+ { -+ LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); -+ return -EINTR; -+ } -+ vchi_service_use(instance->vchi_handle[0]); -+ -+ if ( instance->peer_version==0 && vchi_get_peer_version(instance->vchi_handle[0], &instance->peer_version) == 0 ) { -+ LOG_DBG("%s: client version %d connected\n", __func__, instance->peer_version); -+ } -+ m.type = VC_AUDIO_MSG_TYPE_WRITE; -+ m.u.write.count = count; -+ // old version uses bulk, new version uses control -+ m.u.write.max_packet = instance->peer_version < 2 || force_bulk ? 0:4000; -+ m.u.write.callback = alsa_stream->fifo_irq_handler; -+ m.u.write.cookie = alsa_stream; -+ m.u.write.silence = src == NULL; -+ -+ /* Send the message to the videocore */ -+ success = vchi_msg_queue(instance->vchi_handle[0], -+ &m, sizeof m, -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); -+ -+ if (success != 0) { -+ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", -+ __func__, success); -+ -+ ret = -1; -+ goto unlock; -+ } -+ if (!m.u.write.silence) { -+ if (m.u.write.max_packet == 0) { -+ /* Send the message to the videocore */ -+ success = vchi_bulk_queue_transmit(instance->vchi_handle[0], -+ src, count, -+ 0 * -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED -+ + -+ 1 * -+ VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, -+ NULL); -+ } else { -+ while (count > 0) { -+ int bytes = min((int)m.u.write.max_packet, (int)count); -+ success = vchi_msg_queue(instance->vchi_handle[0], -+ src, bytes, -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); -+ src = (char *)src + bytes; -+ count -= bytes; -+ } -+ } -+ if (success != 0) { -+ LOG_ERR -+ ("%s: failed on vchi_bulk_queue_transmit (status=%d)\n", -+ __func__, success); -+ -+ ret = -1; -+ goto unlock; -+ } -+ } -+ ret = 0; -+ -+unlock: -+ vchi_service_release(instance->vchi_handle[0]); -+ mutex_unlock(&instance->vchi_mutex); -+ LOG_DBG(" .. OUT\n"); -+ return ret; -+} -+ -+/** -+ * Returns all buffers from arm->vc -+ */ -+void bcm2835_audio_flush_buffers(bcm2835_alsa_stream_t * alsa_stream) -+{ -+ LOG_DBG(" .. IN\n"); -+ LOG_DBG(" .. OUT\n"); -+ return; -+} -+ -+/** -+ * Forces VC to flush(drop) its filled playback buffers and -+ * return them the us. (VC->ARM) -+ */ -+void bcm2835_audio_flush_playback_buffers(bcm2835_alsa_stream_t * alsa_stream) -+{ -+ LOG_DBG(" .. IN\n"); -+ LOG_DBG(" .. OUT\n"); -+} -+ -+uint32_t bcm2835_audio_retrieve_buffers(bcm2835_alsa_stream_t * alsa_stream) -+{ -+ uint32_t count = atomic_read(&alsa_stream->retrieved); -+ atomic_sub(count, &alsa_stream->retrieved); -+ return count; -+} -+ -+module_param(force_bulk, bool, 0444); -+MODULE_PARM_DESC(force_bulk, "Force use of vchiq bulk for audio"); ---- /dev/null -+++ b/sound/arm/bcm2835.c -@@ -0,0 +1,511 @@ -+/***************************************************************************** -+* 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 "bcm2835.h" -+ -+/* module parameters (see "Module Parameters") */ -+/* SNDRV_CARDS: maximum number of cards supported by this module */ -+static int index[MAX_SUBSTREAMS] = {[0 ... (MAX_SUBSTREAMS - 1)] = -1 }; -+static char *id[MAX_SUBSTREAMS] = {[0 ... (MAX_SUBSTREAMS - 1)] = NULL }; -+static int enable[MAX_SUBSTREAMS] = {[0 ... (MAX_SUBSTREAMS - 1)] = 1 }; -+ -+/* HACKY global pointers needed for successive probes to work : ssp -+ * But compared against the changes we will have to do in VC audio_ipc code -+ * to export 8 audio_ipc devices as a single IPC device and then monitor all -+ * four devices in a thread, this gets things done quickly and should be easier -+ * to debug if we run into issues -+ */ -+ -+static struct snd_card *g_card = NULL; -+static bcm2835_chip_t *g_chip = NULL; -+ -+static int snd_bcm2835_free(bcm2835_chip_t * chip) -+{ -+ kfree(chip); -+ return 0; -+} -+ -+/* component-destructor -+ * (see "Management of Cards and Components") -+ */ -+static int snd_bcm2835_dev_free(struct snd_device *device) -+{ -+ return snd_bcm2835_free(device->device_data); -+} -+ -+/* chip-specific constructor -+ * (see "Management of Cards and Components") -+ */ -+static int snd_bcm2835_create(struct snd_card *card, -+ struct platform_device *pdev, -+ bcm2835_chip_t ** rchip) -+{ -+ bcm2835_chip_t *chip; -+ int err; -+ static struct snd_device_ops ops = { -+ .dev_free = snd_bcm2835_dev_free, -+ }; -+ -+ *rchip = NULL; -+ -+ chip = kzalloc(sizeof(*chip), GFP_KERNEL); -+ if (chip == NULL) -+ return -ENOMEM; -+ -+ chip->card = card; -+ -+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); -+ if (err < 0) { -+ snd_bcm2835_free(chip); -+ return err; -+ } -+ -+ *rchip = chip; -+ return 0; -+} -+ -+static int snd_bcm2835_alsa_probe_dt(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ bcm2835_chip_t *chip; -+ struct snd_card *card; -+ u32 numchans; -+ int err, i; -+ -+ err = of_property_read_u32(dev->of_node, "brcm,pwm-channels", -+ &numchans); -+ if (err) { -+ dev_err(dev, "Failed to get DT property 'brcm,pwm-channels'"); -+ return err; -+ } -+ -+ if (numchans == 0 || numchans > MAX_SUBSTREAMS) { -+ numchans = MAX_SUBSTREAMS; -+ dev_warn(dev, "Illegal 'brcm,pwm-channels' value, will use %u\n", -+ numchans); -+ } -+ -+ err = snd_card_new(NULL, -1, NULL, THIS_MODULE, 0, &card); -+ if (err) { -+ dev_err(dev, "Failed to create soundcard structure\n"); -+ return err; -+ } -+ -+ snd_card_set_dev(card, dev); -+ strcpy(card->driver, "bcm2835"); -+ strcpy(card->shortname, "bcm2835 ALSA"); -+ sprintf(card->longname, "%s", card->shortname); -+ -+ err = snd_bcm2835_create(card, pdev, &chip); -+ if (err < 0) { -+ dev_err(dev, "Failed to create bcm2835 chip\n"); -+ goto err_free; -+ } -+ -+ err = snd_bcm2835_new_pcm(chip); -+ if (err < 0) { -+ dev_err(dev, "Failed to create new bcm2835 pcm device\n"); -+ goto err_free; -+ } -+ -+ err = snd_bcm2835_new_spdif_pcm(chip); -+ if (err < 0) { -+ dev_err(dev, "Failed to create new bcm2835 spdif pcm device\n"); -+ goto err_free; -+ } -+ -+ err = snd_bcm2835_new_ctl(chip); -+ if (err < 0) { -+ dev_err(dev, "Failed to create new bcm2835 ctl\n"); -+ goto err_free; -+ } -+ -+ for (i = 0; i < numchans; i++) { -+ chip->avail_substreams |= (1 << i); -+ chip->pdev[i] = pdev; -+ } -+ -+ err = snd_card_register(card); -+ if (err) { -+ dev_err(dev, "Failed to register bcm2835 ALSA card \n"); -+ goto err_free; -+ } -+ -+ g_card = card; -+ g_chip = chip; -+ platform_set_drvdata(pdev, card); -+ audio_info("bcm2835 ALSA card created with %u channels\n", numchans); -+ -+ return 0; -+ -+err_free: -+ snd_card_free(card); -+ -+ return err; -+} -+ -+static int snd_bcm2835_alsa_probe(struct platform_device *pdev) -+{ -+ static int dev; -+ bcm2835_chip_t *chip; -+ struct snd_card *card; -+ int err; -+ -+ if (pdev->dev.of_node) -+ return snd_bcm2835_alsa_probe_dt(pdev); -+ -+ if (dev >= MAX_SUBSTREAMS) -+ return -ENODEV; -+ -+ if (!enable[dev]) { -+ dev++; -+ return -ENOENT; -+ } -+ -+ if (dev > 0) -+ goto add_register_map; -+ -+ err = snd_card_new(NULL, index[dev], id[dev], THIS_MODULE, 0, &g_card); -+ if (err < 0) -+ goto out; -+ -+ snd_card_set_dev(g_card, &pdev->dev); -+ strcpy(g_card->driver, "bcm2835"); -+ strcpy(g_card->shortname, "bcm2835 ALSA"); -+ sprintf(g_card->longname, "%s", g_card->shortname); -+ -+ err = snd_bcm2835_create(g_card, pdev, &chip); -+ if (err < 0) { -+ dev_err(&pdev->dev, "Failed to create bcm2835 chip\n"); -+ goto out_bcm2835_create; -+ } -+ -+ g_chip = chip; -+ err = snd_bcm2835_new_pcm(chip); -+ if (err < 0) { -+ dev_err(&pdev->dev, "Failed to create new BCM2835 pcm device\n"); -+ goto out_bcm2835_new_pcm; -+ } -+ -+ err = snd_bcm2835_new_spdif_pcm(chip); -+ if (err < 0) { -+ dev_err(&pdev->dev, "Failed to create new BCM2835 spdif pcm device\n"); -+ goto out_bcm2835_new_spdif; -+ } -+ -+ err = snd_bcm2835_new_ctl(chip); -+ if (err < 0) { -+ dev_err(&pdev->dev, "Failed to create new BCM2835 ctl\n"); -+ goto out_bcm2835_new_ctl; -+ } -+ -+add_register_map: -+ card = g_card; -+ chip = g_chip; -+ -+ BUG_ON(!(card && chip)); -+ -+ chip->avail_substreams |= (1 << dev); -+ chip->pdev[dev] = pdev; -+ -+ if (dev == 0) { -+ err = snd_card_register(card); -+ if (err < 0) { -+ dev_err(&pdev->dev, -+ "Failed to register bcm2835 ALSA card \n"); -+ goto out_card_register; -+ } -+ platform_set_drvdata(pdev, card); -+ audio_info("bcm2835 ALSA card created!\n"); -+ } else { -+ audio_info("bcm2835 ALSA chip created!\n"); -+ platform_set_drvdata(pdev, (void *)dev); -+ } -+ -+ dev++; -+ -+ return 0; -+ -+out_card_register: -+out_bcm2835_new_ctl: -+out_bcm2835_new_spdif: -+out_bcm2835_new_pcm: -+out_bcm2835_create: -+ BUG_ON(!g_card); -+ if (snd_card_free(g_card)) -+ dev_err(&pdev->dev, "Failed to free Registered alsa card\n"); -+ g_card = NULL; -+out: -+ dev = SNDRV_CARDS; /* stop more avail_substreams from being probed */ -+ dev_err(&pdev->dev, "BCM2835 ALSA Probe failed !!\n"); -+ return err; -+} -+ -+static int snd_bcm2835_alsa_remove(struct platform_device *pdev) -+{ -+ uint32_t idx; -+ void *drv_data; -+ -+ drv_data = platform_get_drvdata(pdev); -+ -+ if (drv_data == (void *)g_card) { -+ /* This is the card device */ -+ snd_card_free((struct snd_card *)drv_data); -+ g_card = NULL; -+ g_chip = NULL; -+ } else { -+ idx = (uint32_t) drv_data; -+ if (g_card != NULL) { -+ BUG_ON(!g_chip); -+ /* We pass chip device numbers in audio ipc devices -+ * other than the one we registered our card with -+ */ -+ idx = (uint32_t) drv_data; -+ BUG_ON(!idx || idx > MAX_SUBSTREAMS); -+ g_chip->avail_substreams &= ~(1 << idx); -+ /* There should be atleast one substream registered -+ * after we are done here, as it wil be removed when -+ * the *remove* is called for the card device -+ */ -+ BUG_ON(!g_chip->avail_substreams); -+ } -+ } -+ -+ platform_set_drvdata(pdev, NULL); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_PM -+static int snd_bcm2835_alsa_suspend(struct platform_device *pdev, -+ pm_message_t state) -+{ -+ return 0; -+} -+ -+static int snd_bcm2835_alsa_resume(struct platform_device *pdev) -+{ -+ return 0; -+} -+ -+#endif -+ -+static const struct of_device_id snd_bcm2835_of_match_table[] = { -+ { .compatible = "brcm,bcm2835-audio", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, snd_bcm2835_of_match_table); -+ -+static struct platform_driver bcm2835_alsa0_driver = { -+ .probe = snd_bcm2835_alsa_probe, -+ .remove = snd_bcm2835_alsa_remove, -+#ifdef CONFIG_PM -+ .suspend = snd_bcm2835_alsa_suspend, -+ .resume = snd_bcm2835_alsa_resume, -+#endif -+ .driver = { -+ .name = "bcm2835_AUD0", -+ .owner = THIS_MODULE, -+ .of_match_table = snd_bcm2835_of_match_table, -+ }, -+}; -+ -+static struct platform_driver bcm2835_alsa1_driver = { -+ .probe = snd_bcm2835_alsa_probe, -+ .remove = snd_bcm2835_alsa_remove, -+#ifdef CONFIG_PM -+ .suspend = snd_bcm2835_alsa_suspend, -+ .resume = snd_bcm2835_alsa_resume, -+#endif -+ .driver = { -+ .name = "bcm2835_AUD1", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static struct platform_driver bcm2835_alsa2_driver = { -+ .probe = snd_bcm2835_alsa_probe, -+ .remove = snd_bcm2835_alsa_remove, -+#ifdef CONFIG_PM -+ .suspend = snd_bcm2835_alsa_suspend, -+ .resume = snd_bcm2835_alsa_resume, -+#endif -+ .driver = { -+ .name = "bcm2835_AUD2", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static struct platform_driver bcm2835_alsa3_driver = { -+ .probe = snd_bcm2835_alsa_probe, -+ .remove = snd_bcm2835_alsa_remove, -+#ifdef CONFIG_PM -+ .suspend = snd_bcm2835_alsa_suspend, -+ .resume = snd_bcm2835_alsa_resume, -+#endif -+ .driver = { -+ .name = "bcm2835_AUD3", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static struct platform_driver bcm2835_alsa4_driver = { -+ .probe = snd_bcm2835_alsa_probe, -+ .remove = snd_bcm2835_alsa_remove, -+#ifdef CONFIG_PM -+ .suspend = snd_bcm2835_alsa_suspend, -+ .resume = snd_bcm2835_alsa_resume, -+#endif -+ .driver = { -+ .name = "bcm2835_AUD4", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static struct platform_driver bcm2835_alsa5_driver = { -+ .probe = snd_bcm2835_alsa_probe, -+ .remove = snd_bcm2835_alsa_remove, -+#ifdef CONFIG_PM -+ .suspend = snd_bcm2835_alsa_suspend, -+ .resume = snd_bcm2835_alsa_resume, -+#endif -+ .driver = { -+ .name = "bcm2835_AUD5", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static struct platform_driver bcm2835_alsa6_driver = { -+ .probe = snd_bcm2835_alsa_probe, -+ .remove = snd_bcm2835_alsa_remove, -+#ifdef CONFIG_PM -+ .suspend = snd_bcm2835_alsa_suspend, -+ .resume = snd_bcm2835_alsa_resume, -+#endif -+ .driver = { -+ .name = "bcm2835_AUD6", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static struct platform_driver bcm2835_alsa7_driver = { -+ .probe = snd_bcm2835_alsa_probe, -+ .remove = snd_bcm2835_alsa_remove, -+#ifdef CONFIG_PM -+ .suspend = snd_bcm2835_alsa_suspend, -+ .resume = snd_bcm2835_alsa_resume, -+#endif -+ .driver = { -+ .name = "bcm2835_AUD7", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int bcm2835_alsa_device_init(void) -+{ -+ int err; -+ err = platform_driver_register(&bcm2835_alsa0_driver); -+ if (err) { -+ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); -+ goto out; -+ } -+ -+ err = platform_driver_register(&bcm2835_alsa1_driver); -+ if (err) { -+ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); -+ goto unregister_0; -+ } -+ -+ err = platform_driver_register(&bcm2835_alsa2_driver); -+ if (err) { -+ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); -+ goto unregister_1; -+ } -+ -+ err = platform_driver_register(&bcm2835_alsa3_driver); -+ if (err) { -+ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); -+ goto unregister_2; -+ } -+ -+ err = platform_driver_register(&bcm2835_alsa4_driver); -+ if (err) { -+ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); -+ goto unregister_3; -+ } -+ -+ err = platform_driver_register(&bcm2835_alsa5_driver); -+ if (err) { -+ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); -+ goto unregister_4; -+ } -+ -+ err = platform_driver_register(&bcm2835_alsa6_driver); -+ if (err) { -+ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); -+ goto unregister_5; -+ } -+ -+ err = platform_driver_register(&bcm2835_alsa7_driver); -+ if (err) { -+ pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); -+ goto unregister_6; -+ } -+ -+ return 0; -+ -+unregister_6: -+ platform_driver_unregister(&bcm2835_alsa6_driver); -+unregister_5: -+ platform_driver_unregister(&bcm2835_alsa5_driver); -+unregister_4: -+ platform_driver_unregister(&bcm2835_alsa4_driver); -+unregister_3: -+ platform_driver_unregister(&bcm2835_alsa3_driver); -+unregister_2: -+ platform_driver_unregister(&bcm2835_alsa2_driver); -+unregister_1: -+ platform_driver_unregister(&bcm2835_alsa1_driver); -+unregister_0: -+ platform_driver_unregister(&bcm2835_alsa0_driver); -+out: -+ return err; -+} -+ -+static void bcm2835_alsa_device_exit(void) -+{ -+ platform_driver_unregister(&bcm2835_alsa0_driver); -+ platform_driver_unregister(&bcm2835_alsa1_driver); -+ platform_driver_unregister(&bcm2835_alsa2_driver); -+ platform_driver_unregister(&bcm2835_alsa3_driver); -+ platform_driver_unregister(&bcm2835_alsa4_driver); -+ platform_driver_unregister(&bcm2835_alsa5_driver); -+ platform_driver_unregister(&bcm2835_alsa6_driver); -+ platform_driver_unregister(&bcm2835_alsa7_driver); -+} -+ -+late_initcall(bcm2835_alsa_device_init); -+module_exit(bcm2835_alsa_device_exit); -+ -+MODULE_AUTHOR("Dom Cobley"); -+MODULE_DESCRIPTION("Alsa driver for BCM2835 chip"); -+MODULE_LICENSE("GPL"); -+MODULE_ALIAS("platform:bcm2835_alsa"); ---- /dev/null -+++ b/sound/arm/bcm2835.h -@@ -0,0 +1,167 @@ -+/***************************************************************************** -+* 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. -+*****************************************************************************/ -+ -+#ifndef __SOUND_ARM_BCM2835_H -+#define __SOUND_ARM_BCM2835_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+#define AUDIO_DEBUG_ENABLE -+#define AUDIO_VERBOSE_DEBUG_ENABLE -+*/ -+ -+/* Debug macros */ -+ -+#ifdef AUDIO_DEBUG_ENABLE -+#ifdef AUDIO_VERBOSE_DEBUG_ENABLE -+ -+#define audio_debug(fmt, arg...) \ -+ printk(KERN_INFO"%s:%d " fmt, __func__, __LINE__, ##arg) -+ -+#define audio_info(fmt, arg...) \ -+ printk(KERN_INFO"%s:%d " fmt, __func__, __LINE__, ##arg) -+ -+#else -+ -+#define audio_debug(fmt, arg...) -+ -+#define audio_info(fmt, arg...) -+ -+#endif /* AUDIO_VERBOSE_DEBUG_ENABLE */ -+ -+#else -+ -+#define audio_debug(fmt, arg...) -+ -+#define audio_info(fmt, arg...) -+ -+#endif /* AUDIO_DEBUG_ENABLE */ -+ -+#define audio_error(fmt, arg...) \ -+ printk(KERN_ERR"%s:%d " fmt, __func__, __LINE__, ##arg) -+ -+#define audio_warning(fmt, arg...) \ -+ printk(KERN_WARNING"%s:%d " fmt, __func__, __LINE__, ##arg) -+ -+#define audio_alert(fmt, arg...) \ -+ printk(KERN_ALERT"%s:%d " fmt, __func__, __LINE__, ##arg) -+ -+#define MAX_SUBSTREAMS (8) -+#define AVAIL_SUBSTREAMS_MASK (0xff) -+enum { -+ CTRL_VOL_MUTE, -+ CTRL_VOL_UNMUTE -+}; -+ -+/* macros for alsa2chip and chip2alsa, instead of functions */ -+ -+#define alsa2chip(vol) (uint)(-((vol << 8) / 100)) /* convert alsa to chip volume (defined as macro rather than function call) */ -+#define chip2alsa(vol) -((vol * 100) >> 8) /* convert chip to alsa volume */ -+ -+/* Some constants for values .. */ -+typedef enum { -+ AUDIO_DEST_AUTO = 0, -+ AUDIO_DEST_HEADPHONES = 1, -+ AUDIO_DEST_HDMI = 2, -+ AUDIO_DEST_MAX, -+} SND_BCM2835_ROUTE_T; -+ -+typedef enum { -+ PCM_PLAYBACK_VOLUME, -+ PCM_PLAYBACK_MUTE, -+ PCM_PLAYBACK_DEVICE, -+} SND_BCM2835_CTRL_T; -+ -+/* definition of the chip-specific record */ -+typedef struct bcm2835_chip { -+ struct snd_card *card; -+ struct snd_pcm *pcm; -+ struct snd_pcm *pcm_spdif; -+ /* Bitmat for valid reg_base and irq numbers */ -+ uint32_t avail_substreams; -+ struct platform_device *pdev[MAX_SUBSTREAMS]; -+ struct bcm2835_alsa_stream *alsa_stream[MAX_SUBSTREAMS]; -+ -+ int volume; -+ int old_volume; /* stores the volume value whist muted */ -+ int dest; -+ int mute; -+ -+ unsigned int opened; -+ unsigned int spdif_status; -+ struct mutex audio_mutex; -+} bcm2835_chip_t; -+ -+typedef struct bcm2835_alsa_stream { -+ bcm2835_chip_t *chip; -+ struct snd_pcm_substream *substream; -+ struct snd_pcm_indirect pcm_indirect; -+ -+ struct semaphore buffers_update_sem; -+ struct semaphore control_sem; -+ spinlock_t lock; -+ volatile uint32_t control; -+ volatile uint32_t status; -+ -+ int open; -+ int running; -+ int draining; -+ -+ int channels; -+ int params_rate; -+ int pcm_format_width; -+ -+ unsigned int pos; -+ unsigned int buffer_size; -+ unsigned int period_size; -+ -+ uint32_t enable_fifo_irq; -+ irq_handler_t fifo_irq_handler; -+ -+ atomic_t retrieved; -+ struct opaque_AUDIO_INSTANCE_T *instance; -+ struct workqueue_struct *my_wq; -+ int idx; -+} bcm2835_alsa_stream_t; -+ -+int snd_bcm2835_new_ctl(bcm2835_chip_t * chip); -+int snd_bcm2835_new_pcm(bcm2835_chip_t * chip); -+int snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip); -+ -+int bcm2835_audio_open(bcm2835_alsa_stream_t * alsa_stream); -+int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream); -+int bcm2835_audio_set_params(bcm2835_alsa_stream_t * alsa_stream, -+ uint32_t channels, uint32_t samplerate, -+ uint32_t bps); -+int bcm2835_audio_setup(bcm2835_alsa_stream_t * alsa_stream); -+int bcm2835_audio_start(bcm2835_alsa_stream_t * alsa_stream); -+int bcm2835_audio_stop(bcm2835_alsa_stream_t * alsa_stream); -+int bcm2835_audio_set_ctls(bcm2835_chip_t * chip); -+int bcm2835_audio_write(bcm2835_alsa_stream_t * alsa_stream, uint32_t count, -+ void *src); -+uint32_t bcm2835_audio_retrieve_buffers(bcm2835_alsa_stream_t * alsa_stream); -+void bcm2835_audio_flush_buffers(bcm2835_alsa_stream_t * alsa_stream); -+void bcm2835_audio_flush_playback_buffers(bcm2835_alsa_stream_t * alsa_stream); -+ -+#endif /* __SOUND_ARM_BCM2835_H */ ---- /dev/null -+++ b/sound/arm/vc_vchi_audioserv_defs.h -@@ -0,0 +1,116 @@ -+/***************************************************************************** -+* 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. -+*****************************************************************************/ -+ -+#ifndef _VC_AUDIO_DEFS_H_ -+#define _VC_AUDIO_DEFS_H_ -+ -+#define VC_AUDIOSERV_MIN_VER 1 -+#define VC_AUDIOSERV_VER 2 -+ -+// FourCC code used for VCHI connection -+#define VC_AUDIO_SERVER_NAME MAKE_FOURCC("AUDS") -+ -+// Maximum message length -+#define VC_AUDIO_MAX_MSG_LEN (sizeof( VC_AUDIO_MSG_T )) -+ -+// List of screens that are currently supported -+// All message types supported for HOST->VC direction -+typedef enum { -+ VC_AUDIO_MSG_TYPE_RESULT, // Generic result -+ VC_AUDIO_MSG_TYPE_COMPLETE, // Generic result -+ VC_AUDIO_MSG_TYPE_CONFIG, // Configure audio -+ VC_AUDIO_MSG_TYPE_CONTROL, // Configure audio -+ VC_AUDIO_MSG_TYPE_OPEN, // Configure audio -+ VC_AUDIO_MSG_TYPE_CLOSE, // Configure audio -+ VC_AUDIO_MSG_TYPE_START, // Configure audio -+ VC_AUDIO_MSG_TYPE_STOP, // Configure audio -+ VC_AUDIO_MSG_TYPE_WRITE, // Configure audio -+ VC_AUDIO_MSG_TYPE_MAX -+} VC_AUDIO_MSG_TYPE; -+ -+// configure the audio -+typedef struct { -+ uint32_t channels; -+ uint32_t samplerate; -+ uint32_t bps; -+ -+} VC_AUDIO_CONFIG_T; -+ -+typedef struct { -+ uint32_t volume; -+ uint32_t dest; -+ -+} VC_AUDIO_CONTROL_T; -+ -+// audio -+typedef struct { -+ uint32_t dummy; -+ -+} VC_AUDIO_OPEN_T; -+ -+// audio -+typedef struct { -+ uint32_t dummy; -+ -+} VC_AUDIO_CLOSE_T; -+// audio -+typedef struct { -+ uint32_t dummy; -+ -+} VC_AUDIO_START_T; -+// audio -+typedef struct { -+ uint32_t draining; -+ -+} VC_AUDIO_STOP_T; -+ -+// configure the write audio samples -+typedef struct { -+ uint32_t count; // in bytes -+ void *callback; -+ void *cookie; -+ uint16_t silence; -+ uint16_t max_packet; -+} VC_AUDIO_WRITE_T; -+ -+// Generic result for a request (VC->HOST) -+typedef struct { -+ int32_t success; // Success value -+ -+} VC_AUDIO_RESULT_T; -+ -+// Generic result for a request (VC->HOST) -+typedef struct { -+ int32_t count; // Success value -+ void *callback; -+ void *cookie; -+} VC_AUDIO_COMPLETE_T; -+ -+// Message header for all messages in HOST->VC direction -+typedef struct { -+ int32_t type; // Message type (VC_AUDIO_MSG_TYPE) -+ union { -+ VC_AUDIO_CONFIG_T config; -+ VC_AUDIO_CONTROL_T control; -+ VC_AUDIO_OPEN_T open; -+ VC_AUDIO_CLOSE_T close; -+ VC_AUDIO_START_T start; -+ VC_AUDIO_STOP_T stop; -+ VC_AUDIO_WRITE_T write; -+ VC_AUDIO_RESULT_T result; -+ VC_AUDIO_COMPLETE_T complete; -+ } u; -+} VC_AUDIO_MSG_T; -+ -+#endif // _VC_AUDIO_DEFS_H_ diff --git a/target/linux/brcm2708/patches-4.4/0036-bcm2708-vchiq-driver.patch b/target/linux/brcm2708/patches-4.4/0036-bcm2708-vchiq-driver.patch new file mode 100644 index 0000000000..4ec48e2192 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0036-bcm2708-vchiq-driver.patch @@ -0,0 +1,13200 @@ +From 112360f76c91c892f1a29825eb2807bbf86ac67d Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Tue, 2 Jul 2013 23:42:01 +0100 +Subject: [PATCH 036/232] bcm2708 vchiq driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +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 + +vchiq: Allocation does not need to be atomic + +vchiq: Fix wrong condition check + +The log level is checked from within the log call. Remove the check in the call. + +Signed-off-by: Pranith Kumar + +BCM270x: Add vchiq device to platform file and Device Tree + +Prepare to turn the vchiq module into a driver. + +Signed-off-by: Noralf Trønnes + +bcm2708: vchiq: Add Device Tree support + +Turn vchiq into a driver and stop hardcoding resources. +Use devm_* functions in probe path to simplify cleanup. +A global variable is used to hold the register address. This is done +to keep this patch as small as possible. +Also make available on ARCH_BCM2835. +Based on work by Lubomir Rintel. + +Signed-off-by: Noralf Trønnes + +vchiq: Change logging level for inbound data + +vchiq_arm: Two cacheing fixes + +1) Make fragment size vary with cache line size +Without this patch, non-cache-line-aligned transfers may corrupt +(or be corrupted by) adjacent data structures. + +Both ARM and VC need to be updated to enable this feature. This is +ensured by having the loader apply a new DT parameter - +cache-line-size. The existence of this parameter guarantees that the +kernel is capable, and the parameter will only be modified from the +safe default if the loader is capable. + +2) Flush/invalidate vmalloc'd memory, and invalidate after reads + +vchiq: fix NULL pointer dereference when closing driver + +The following code run as root will cause a null pointer dereference oops: + + int fd = open("/dev/vc-cma", O_RDONLY); + if (fd < 0) + err(1, "open failed"); + (void)close(fd); + +[ 1704.877721] Unable to handle kernel NULL pointer dereference at virtual address 00000000 +[ 1704.877725] pgd = b899c000 +[ 1704.877736] [00000000] *pgd=37fab831, *pte=00000000, *ppte=00000000 +[ 1704.877748] Internal error: Oops: 817 [#1] PREEMPT SMP ARM +[ 1704.877765] Modules linked in: evdev i2c_bcm2708 uio_pdrv_genirq uio +[ 1704.877774] CPU: 2 PID: 3656 Comm: stress-ng-fstat Not tainted 3.19.1-12-generic-bcm2709 #12-Ubuntu +[ 1704.877777] Hardware name: BCM2709 +[ 1704.877783] task: b8ab9b00 ti: b7e68000 task.ti: b7e68000 +[ 1704.877798] PC is at __down_interruptible+0x50/0xec +[ 1704.877806] LR is at down_interruptible+0x5c/0x68 +[ 1704.877813] pc : [<80630ee8>] lr : [<800704b0>] psr: 60080093 +sp : b7e69e50 ip : b7e69e88 fp : b7e69e84 +[ 1704.877817] r10: b88123c8 r9 : 00000010 r8 : 00000001 +[ 1704.877822] r7 : b8ab9b00 r6 : 7fffffff r5 : 80a1cc34 r4 : 80a1cc34 +[ 1704.877826] r3 : b7e69e50 r2 : 00000000 r1 : 00000000 r0 : 80a1cc34 +[ 1704.877833] Flags: nZCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment user +[ 1704.877838] Control: 10c5387d Table: 3899c06a DAC: 00000015 +[ 1704.877843] Process do-oops (pid: 3656, stack limit = 0xb7e68238) +[ 1704.877848] Stack: (0xb7e69e50 to 0xb7e6a000) +[ 1704.877856] 9e40: 80a1cc3c 00000000 00000010 b88123c8 +[ 1704.877865] 9e60: b7e69e84 80a1cc34 fff9fee9 ffffffff b7e68000 00000009 b7e69ea4 b7e69e88 +[ 1704.877874] 9e80: 800704b0 80630ea4 fff9fee9 60080013 80a1cc28 fff9fee9 b7e69edc b7e69ea8 +[ 1704.877884] 9ea0: 8040f558 80070460 fff9fee9 ffffffff 00000000 00000000 00000009 80a1cb7c +[ 1704.877893] 9ec0: 00000000 80a1cb7c 00000000 00000010 b7e69ef4 b7e69ee0 803e1ba4 8040f514 +[ 1704.877902] 9ee0: 00000e48 80a1cb7c b7e69f14 b7e69ef8 803e1c9c 803e1b74 b88123c0 b92acb18 +[ 1704.877911] 9f00: b8812790 b8d815d8 b7e69f24 b7e69f18 803e2250 803e1bc8 b7e69f5c b7e69f28 +[ 1704.877921] 9f20: 80167bac 803e222c 00000000 00000000 b7e69f54 b8ab9ffc 00000000 8098c794 +[ 1704.877930] 9f40: b8ab9b00 8000efc4 b7e68000 00000000 b7e69f6c b7e69f60 80167d6c 80167b28 +[ 1704.877939] 9f60: b7e69f8c b7e69f70 80047d38 80167d60 b7e68000 b7e68010 8000efc4 b7e69fb0 +[ 1704.877949] 9f80: b7e69fac b7e69f90 80012820 80047c84 01155490 011549a8 00000001 00000006 +[ 1704.877957] 9fa0: 00000000 b7e69fb0 8000ee5c 80012790 00000000 353d8c0f 7efc4308 00000000 +[ 1704.877966] 9fc0: 01155490 011549a8 00000001 00000006 00000000 00000000 76cf3ba0 00000003 +[ 1704.877975] 9fe0: 00000000 7efc42e4 0002272f 76e2ed66 60080030 00000003 00000000 00000000 +[ 1704.877998] [<80630ee8>] (__down_interruptible) from [<800704b0>] (down_interruptible+0x5c/0x68) +[ 1704.878015] [<800704b0>] (down_interruptible) from [<8040f558>] (vchiu_queue_push+0x50/0xd8) +[ 1704.878032] [<8040f558>] (vchiu_queue_push) from [<803e1ba4>] (send_worker_msg+0x3c/0x54) +[ 1704.878045] [<803e1ba4>] (send_worker_msg) from [<803e1c9c>] (vc_cma_set_reserve+0xe0/0x1c4) +[ 1704.878057] [<803e1c9c>] (vc_cma_set_reserve) from [<803e2250>] (vc_cma_release+0x30/0x38) +[ 1704.878069] [<803e2250>] (vc_cma_release) from [<80167bac>] (__fput+0x90/0x1e0) +[ 1704.878082] [<80167bac>] (__fput) from [<80167d6c>] (____fput+0x18/0x1c) +[ 1704.878094] [<80167d6c>] (____fput) from [<80047d38>] (task_work_run+0xc0/0xf8) +[ 1704.878109] [<80047d38>] (task_work_run) from [<80012820>] (do_work_pending+0x9c/0xc4) +[ 1704.878123] [<80012820>] (do_work_pending) from [<8000ee5c>] (work_pending+0xc/0x20) +[ 1704.878133] Code: e50b1034 e3a01000 e50b2030 e580300c (e5823000) + +..the fix is to ensure that we have actually initialized the queue before we attempt +to push any items onto it. This occurs if we do an open() followed by a close() without +any activity in between. + +Signed-off-by: Colin Ian King + +vchiq_arm: Sort out the vmalloc case + +See: https://github.com/raspberrypi/linux/issues/1055 + +vchiq: hack: Add include depecated dma include file +--- + arch/arm/mach-bcm2708/include/mach/platform.h | 2 + + arch/arm/mach-bcm2709/include/mach/platform.h | 2 + + 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 | 586 +++ + .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 2903 +++++++++++++++ + .../vc04_services/interface/vchiq_arm/vchiq_arm.h | 220 ++ + .../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 | 156 + + .../vc04_services/interface/vchiq_arm/vchiq_util.h | 82 + + .../interface/vchiq_arm/vchiq_version.c | 59 + + 37 files changed, 12819 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 + +--- a/arch/arm/mach-bcm2708/include/mach/platform.h ++++ b/arch/arm/mach-bcm2708/include/mach/platform.h +@@ -78,6 +78,8 @@ + #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 */ ++#define ARMCTRL_0_BELL_BASE (ARMCTRL_0_SBM_BASE + 0x40) /* User 0 (ARM)'s Doorbell */ ++#define ARMCTRL_0_MAIL0_BASE (ARMCTRL_0_SBM_BASE + 0x80) /* User 0 (ARM)'s Mailbox 0 */ + + /* + * Watchdog +--- a/arch/arm/mach-bcm2709/include/mach/platform.h ++++ b/arch/arm/mach-bcm2709/include/mach/platform.h +@@ -78,6 +78,8 @@ + #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 */ ++#define ARMCTRL_0_BELL_BASE (ARMCTRL_0_SBM_BASE + 0x40) /* User 0 (ARM)'s Doorbell */ ++#define ARMCTRL_0_MAIL0_BASE (ARMCTRL_0_SBM_BASE + 0x80) /* User 0 (ARM)'s Mailbox 0 */ + + /* + * Watchdog +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -533,6 +533,7 @@ source "drivers/misc/lis3lv02d/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" +--- 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/ +--- /dev/null ++++ b/drivers/misc/vc04_services/Kconfig +@@ -0,0 +1,9 @@ ++config BCM2708_VCHIQ ++ tristate "Videocore VCHIQ" ++ depends on RASPBERRYPI_FIRMWARE ++ 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. +--- /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 ++ +--- /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 **********************************/ +--- /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 ***********************************/ +--- /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 **********************************/ +--- /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 **********************************/ +--- /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_*/ +--- /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_ +--- /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 +--- /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 +--- /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 */ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +@@ -0,0 +1,586 @@ ++/** ++ * 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 ++ ++#define dmac_map_area __glue(_CACHE,_dma_map_area) ++#define dmac_unmap_area __glue(_CACHE,_dma_unmap_area) ++ ++extern void dmac_map_area(const void *, size_t, int); ++extern void dmac_unmap_area(const void *, size_t, int); ++ ++#define TOTAL_SLOTS (VCHIQ_SLOT_ZERO_SLOTS + 2 * 32) ++ ++#define VCHIQ_ARM_ADDRESS(x) ((void *)((char *)x + g_virt_to_bus_offset)) ++ ++#include "vchiq_arm.h" ++#include "vchiq_2835.h" ++#include "vchiq_connected.h" ++#include "vchiq_killable.h" ++ ++#define MAX_FRAGMENTS (VCHIQ_NUM_CURRENT_BULKS * 2) ++ ++#define BELL0 0x00 ++#define BELL2 0x08 ++ ++typedef struct vchiq_2835_state_struct { ++ int inited; ++ VCHIQ_ARM_STATE_T arm_state; ++} VCHIQ_2835_ARM_STATE_T; ++ ++static void __iomem *g_regs; ++static unsigned int g_cache_line_size = sizeof(CACHE_LINE_SIZE); ++static unsigned int g_fragments_size; ++static char *g_fragments_base; ++static char *g_free_fragments; ++static struct semaphore g_free_fragments_sema; ++static unsigned long g_virt_to_bus_offset; ++ ++extern int vchiq_arm_log_level; ++ ++static DEFINE_SEMAPHORE(g_free_fragments_mutex); ++ ++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 vchiq_platform_init(struct platform_device *pdev, VCHIQ_STATE_T *state) ++{ ++ struct device *dev = &pdev->dev; ++ struct rpi_firmware *fw = platform_get_drvdata(pdev); ++ VCHIQ_SLOT_ZERO_T *vchiq_slot_zero; ++ struct resource *res; ++ void *slot_mem; ++ dma_addr_t slot_phys; ++ u32 channelbase; ++ int slot_mem_size, frag_mem_size; ++ int err, irq, i; ++ ++ g_virt_to_bus_offset = virt_to_dma(dev, (void *)0); ++ ++ (void)of_property_read_u32(dev->of_node, "cache-line-size", ++ &g_cache_line_size); ++ g_fragments_size = 2 * g_cache_line_size; ++ ++ /* Allocate space for the channels in coherent memory */ ++ slot_mem_size = PAGE_ALIGN(TOTAL_SLOTS * VCHIQ_SLOT_SIZE); ++ frag_mem_size = PAGE_ALIGN(g_fragments_size * MAX_FRAGMENTS); ++ ++ slot_mem = dmam_alloc_coherent(dev, slot_mem_size + frag_mem_size, ++ &slot_phys, GFP_KERNEL); ++ if (!slot_mem) { ++ dev_err(dev, "could not allocate DMA memory\n"); ++ return -ENOMEM; ++ } ++ ++ WARN_ON(((int)slot_mem & (PAGE_SIZE - 1)) != 0); ++ ++ vchiq_slot_zero = vchiq_init_slots(slot_mem, slot_mem_size); ++ if (!vchiq_slot_zero) ++ return -EINVAL; ++ ++ vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_OFFSET_IDX] = ++ (int)slot_phys + slot_mem_size; ++ vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX] = ++ MAX_FRAGMENTS; ++ ++ g_fragments_base = (char *)slot_mem + slot_mem_size; ++ slot_mem_size += frag_mem_size; ++ ++ g_free_fragments = g_fragments_base; ++ for (i = 0; i < (MAX_FRAGMENTS - 1); i++) { ++ *(char **)&g_fragments_base[i*g_fragments_size] = ++ &g_fragments_base[(i + 1)*g_fragments_size]; ++ } ++ *(char **)&g_fragments_base[i * g_fragments_size] = NULL; ++ sema_init(&g_free_fragments_sema, MAX_FRAGMENTS); ++ ++ if (vchiq_init_state(state, vchiq_slot_zero, 0) != VCHIQ_SUCCESS) ++ return -EINVAL; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ g_regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(g_regs)) ++ return PTR_ERR(g_regs); ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq <= 0) { ++ dev_err(dev, "failed to get IRQ\n"); ++ return irq; ++ } ++ ++ err = devm_request_irq(dev, irq, vchiq_doorbell_irq, IRQF_IRQPOLL, ++ "VCHIQ doorbell", state); ++ if (err) { ++ dev_err(dev, "failed to register irq=%d\n", irq); ++ return err; ++ } ++ ++ /* Send the base address of the slots to VideoCore */ ++ channelbase = slot_phys; ++ err = rpi_firmware_property(fw, RPI_FIRMWARE_VCHIQ_INIT, ++ &channelbase, sizeof(channelbase)); ++ if (err || channelbase) { ++ dev_err(dev, "failed to set channelbase\n"); ++ return err ? : -ENXIO; ++ } ++ ++ vchiq_log_info(vchiq_arm_log_level, ++ "vchiq_init - done (slots %x, phys %pad)", ++ (unsigned int)vchiq_slot_zero, &slot_phys); ++ ++ vchiq_call_connected_callbacks(); ++ ++ return 0; ++} ++ ++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) ++ writel(0, g_regs + BELL2); /* trigger vc interrupt */ ++} ++ ++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 { ++ memcpy(dst, src, size); ++ return 0; ++ } ++} ++ ++VCHIQ_STATUS_T ++vchiq_prepare_bulk_data(VCHIQ_BULK_T *bulk, VCHI_MEM_HANDLE_T memhandle, ++ void *offset, int size, int dir) ++{ ++ 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; ++} ++ ++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, "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(g_regs + BELL0); ++ ++ if (status & 0x4) { /* Was the doorbell rung? */ ++ remote_event_pollall(state); ++ ret = IRQ_HANDLED; ++ } ++ ++ 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 dmac_map_area 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) ++{ ++ PAGELIST_T *pagelist; ++ struct page **pages; ++ unsigned long *addrs; ++ unsigned int num_pages, offset, i; ++ char *addr, *base_addr, *next_addr; ++ int run, addridx, actual_pages; ++ unsigned long *need_release; ++ ++ offset = (unsigned int)buf & (PAGE_SIZE - 1); ++ num_pages = (count + offset + PAGE_SIZE - 1) / PAGE_SIZE; ++ ++ *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)) { ++ int dir = (type == PAGELIST_WRITE) ? ++ DMA_TO_DEVICE : DMA_FROM_DEVICE; ++ unsigned long length = count; ++ unsigned int off = offset; ++ ++ for (actual_pages = 0; actual_pages < num_pages; ++ actual_pages++) { ++ struct page *pg = vmalloc_to_page(buf + (actual_pages * ++ PAGE_SIZE)); ++ size_t bytes = PAGE_SIZE - off; ++ ++ if (bytes > length) ++ bytes = length; ++ pages[actual_pages] = pg; ++ dmac_map_area(page_address(pg) + off, bytes, dir); ++ length -= bytes; ++ off = 0; ++ } ++ *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 & (g_cache_line_size - 1)) || ++ ((pagelist->offset + pagelist->length) & ++ (g_cache_line_size - 1)))) { ++ char *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 = g_free_fragments; ++ WARN_ON(fragments == NULL); ++ g_free_fragments = *(char **) g_free_fragments; ++ up(&g_free_fragments_mutex); ++ pagelist->type = PAGELIST_READ_WITH_FRAGMENTS + ++ (fragments - g_fragments_base) / g_fragments_size; ++ } ++ ++ dmac_flush_range(pagelist, addrs + num_pages); ++ ++ *ppagelist = pagelist; ++ ++ return 0; ++} ++ ++static void ++free_pagelist(PAGELIST_T *pagelist, int actual) ++{ ++ 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) { ++ char *fragments = g_fragments_base + ++ (pagelist->type - PAGELIST_READ_WITH_FRAGMENTS) * ++ g_fragments_size; ++ int head_bytes, tail_bytes; ++ head_bytes = (g_cache_line_size - pagelist->offset) & ++ (g_cache_line_size - 1); ++ tail_bytes = (pagelist->offset + actual) & ++ (g_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, ++ 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) & ~(g_cache_line_size - 1)), ++ fragments + g_cache_line_size, ++ tail_bytes); ++ } ++ ++ down(&g_free_fragments_mutex); ++ *(char **)fragments = g_free_fragments; ++ g_free_fragments = fragments; ++ up(&g_free_fragments_mutex); ++ up(&g_free_fragments_sema); ++ } ++ ++ if (*need_release) { ++ unsigned int length = pagelist->length; ++ unsigned int offset = pagelist->offset; ++ ++ for (i = 0; i < num_pages; i++) { ++ struct page *pg = pages[i]; ++ ++ if (pagelist->type != PAGELIST_WRITE) { ++ unsigned int bytes = PAGE_SIZE - offset; ++ ++ if (bytes > length) ++ bytes = length; ++ dmac_unmap_area(page_address(pg) + offset, ++ bytes, DMA_FROM_DEVICE); ++ length -= bytes; ++ offset = 0; ++ set_page_dirty(pg); ++ } ++ page_cache_release(pg); ++ } ++ } ++ ++ kfree(pagelist); ++} +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c +@@ -0,0 +1,2903 @@ ++/** ++ * 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 ++#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; ++} ++ ++/**************************************************************************** ++* ++* service_callback ++* ++***************************************************************************/ ++ ++static VCHIQ_STATUS_T ++service_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, ++ VCHIQ_SERVICE_HANDLE_T handle, void *bulk_userdata) ++{ ++ /* 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 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; ++ else ++ vchiq_log_error(vchiq_arm_log_level, ++ "vchiq: could not connect: %d", status); ++ break; ++ ++ 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 { ++ srvstate = ++ instance->connected ? ++ VCHIQ_SRVSTATE_LISTENING : ++ VCHIQ_SRVSTATE_HIDDEN; ++ } ++ ++ 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; ++ } ++ } ++ ++ 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; ++ } ++ 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. ++*/ ++ ++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); ++ } ++} ++ ++static int vchiq_probe(struct platform_device *pdev) ++{ ++ struct device_node *fw_node; ++ struct rpi_firmware *fw; ++ int err; ++ void *ptr_err; ++ ++ fw_node = of_parse_phandle(pdev->dev.of_node, "firmware", 0); ++/* Remove comment when booting without Device Tree is no longer supported ++ if (!fw_node) { ++ dev_err(&pdev->dev, "Missing firmware node\n"); ++ return -ENOENT; ++ } ++*/ ++ fw = rpi_firmware_get(fw_node); ++ if (!fw) ++ return -EPROBE_DEFER; ++ ++ platform_set_drvdata(pdev, fw); ++ ++ /* 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(pdev, &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; ++} ++ ++static int vchiq_remove(struct platform_device *pdev) ++{ ++ device_destroy(vchiq_class, vchiq_devid); ++ class_destroy(vchiq_class); ++ cdev_del(&vchiq_cdev); ++ unregister_chrdev_region(vchiq_devid, 1); ++ ++ return 0; ++} ++ ++static const struct of_device_id vchiq_of_match[] = { ++ { .compatible = "brcm,bcm2835-vchiq", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, vchiq_of_match); ++ ++static struct platform_driver vchiq_driver = { ++ .driver = { ++ .name = "bcm2835_vchiq", ++ .owner = THIS_MODULE, ++ .of_match_table = vchiq_of_match, ++ }, ++ .probe = vchiq_probe, ++ .remove = vchiq_remove, ++}; ++module_platform_driver(vchiq_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Broadcom Corporation"); +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h +@@ -0,0 +1,220 @@ ++/** ++ * 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 ++#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; ++ ++int vchiq_platform_init(struct platform_device *pdev, 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 */ +--- /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); +--- /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 */ +--- /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); +--- /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 */ +--- /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", ++ "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 flags) ++{ ++ 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 (!(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; ++ } ++ ++ 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, 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; ++ } ++ ++ 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); ++ 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); ++ } ++ ++ 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 (!(flags & QMFLAGS_NO_MUTEX_UNLOCK)) ++ 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) ++ }; ++ ++ 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; ++} ++ ++/* 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_info(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); ++ 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; ++ ++ 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, QMFLAGS_NO_MUTEX_UNLOCK) ++ == 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, ++ 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; ++ } ++ 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; ++ } ++ ++ ++ } ++ ++ 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); ++ 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 ++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 (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); ++ ++ return; ++ } ++ ++ 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, QMFLAGS_NO_MUTEX_UNLOCK); ++ ++ 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; ++ ++ /* 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 (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, 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); ++ } ++} ++ ++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; ++ } ++} +--- /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 ++ ++#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; ++ 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) ++{ ++ VCHIQ_STATE_T *state = vchiq_states[(handle / VCHIQ_MAX_SERVICES) & ++ (VCHIQ_MAX_STATES - 1)]; ++ if (!state) ++ return NULL; ++ ++ return state->services[handle & (VCHIQ_MAX_SERVICES - 1)]; ++} ++ ++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 +--- /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; ++ else ++ *levp = VCHIQ_LOG_DEFAULT; ++ ++ *ppos += count; ++ ++ return count; ++} ++ ++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 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; ++ ++ 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; ++ } ++ ++ vchiq_debugfs_log_entries[i].dir = dir; ++ } ++ return ret; ++} ++ ++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 '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 */ +--- /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 */ +--- /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 +--- /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 */ +--- /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 +--- /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; ++ } ++ } ++ ++failed: ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p): returning %d", __func__, instance, status); ++ ++ return status; ++} ++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) ++{ ++ 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); ++ ++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); ++ ++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; ++ ++ 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; ++ } ++ ++ 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 status; ++} ++EXPORT_SYMBOL(vchiq_bulk_receive); ++ ++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; ++ ++ 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; ++ } ++ } ++ 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); ++ } ++ } ++ } ++ ++ 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; ++} +--- /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 ++ ++ ++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 +--- /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" ++ ++/* ---- Constants and Types ---------------------------------------------- */ ++ ++typedef struct { ++ void *armSharedMemVirt; ++ dma_addr_t armSharedMemPhys; ++ size_t armSharedMemSize; ++ ++ void *vcSharedMemVirt; ++ dma_addr_t vcSharedMemPhys; ++ size_t vcSharedMemSize; ++} VCHIQ_SHARED_MEM_INFO_T; ++ ++/* ---- Variable Externs ------------------------------------------------- */ ++ ++/* ---- 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); ++ ++#endif +--- /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 */ +--- /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); ++ } ++ ++ return vchiq_status_to_vchi(status); ++} ++EXPORT_SYMBOL(vchi_msg_queue); ++ ++/*********************************************************** ++ * 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; ++ } ++ } ++ ++ 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); +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c +@@ -0,0 +1,156 @@ ++/** ++ * 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; ++ queue->initialized = 1; ++ ++ 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) ++{ ++ if (!queue->initialized) ++ return; ++ ++ 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; ++} +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h +@@ -0,0 +1,82 @@ ++/** ++ * 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; ++ int initialized; ++ ++ 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 +--- /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; ++} diff --git a/target/linux/brcm2708/patches-4.4/0037-bcm2708-vchiq-driver.patch b/target/linux/brcm2708/patches-4.4/0037-bcm2708-vchiq-driver.patch deleted file mode 100644 index 61e99d4bc9..0000000000 --- a/target/linux/brcm2708/patches-4.4/0037-bcm2708-vchiq-driver.patch +++ /dev/null @@ -1,13200 +0,0 @@ -From 20c0f57db9cca65943519bd404b3e346958f4544 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 2 Jul 2013 23:42:01 +0100 -Subject: [PATCH 037/170] bcm2708 vchiq driver -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -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 - -vchiq: Allocation does not need to be atomic - -vchiq: Fix wrong condition check - -The log level is checked from within the log call. Remove the check in the call. - -Signed-off-by: Pranith Kumar - -BCM270x: Add vchiq device to platform file and Device Tree - -Prepare to turn the vchiq module into a driver. - -Signed-off-by: Noralf Trønnes - -bcm2708: vchiq: Add Device Tree support - -Turn vchiq into a driver and stop hardcoding resources. -Use devm_* functions in probe path to simplify cleanup. -A global variable is used to hold the register address. This is done -to keep this patch as small as possible. -Also make available on ARCH_BCM2835. -Based on work by Lubomir Rintel. - -Signed-off-by: Noralf Trønnes - -vchiq: Change logging level for inbound data - -vchiq_arm: Two cacheing fixes - -1) Make fragment size vary with cache line size -Without this patch, non-cache-line-aligned transfers may corrupt -(or be corrupted by) adjacent data structures. - -Both ARM and VC need to be updated to enable this feature. This is -ensured by having the loader apply a new DT parameter - -cache-line-size. The existence of this parameter guarantees that the -kernel is capable, and the parameter will only be modified from the -safe default if the loader is capable. - -2) Flush/invalidate vmalloc'd memory, and invalidate after reads - -vchiq: fix NULL pointer dereference when closing driver - -The following code run as root will cause a null pointer dereference oops: - - int fd = open("/dev/vc-cma", O_RDONLY); - if (fd < 0) - err(1, "open failed"); - (void)close(fd); - -[ 1704.877721] Unable to handle kernel NULL pointer dereference at virtual address 00000000 -[ 1704.877725] pgd = b899c000 -[ 1704.877736] [00000000] *pgd=37fab831, *pte=00000000, *ppte=00000000 -[ 1704.877748] Internal error: Oops: 817 [#1] PREEMPT SMP ARM -[ 1704.877765] Modules linked in: evdev i2c_bcm2708 uio_pdrv_genirq uio -[ 1704.877774] CPU: 2 PID: 3656 Comm: stress-ng-fstat Not tainted 3.19.1-12-generic-bcm2709 #12-Ubuntu -[ 1704.877777] Hardware name: BCM2709 -[ 1704.877783] task: b8ab9b00 ti: b7e68000 task.ti: b7e68000 -[ 1704.877798] PC is at __down_interruptible+0x50/0xec -[ 1704.877806] LR is at down_interruptible+0x5c/0x68 -[ 1704.877813] pc : [<80630ee8>] lr : [<800704b0>] psr: 60080093 -sp : b7e69e50 ip : b7e69e88 fp : b7e69e84 -[ 1704.877817] r10: b88123c8 r9 : 00000010 r8 : 00000001 -[ 1704.877822] r7 : b8ab9b00 r6 : 7fffffff r5 : 80a1cc34 r4 : 80a1cc34 -[ 1704.877826] r3 : b7e69e50 r2 : 00000000 r1 : 00000000 r0 : 80a1cc34 -[ 1704.877833] Flags: nZCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment user -[ 1704.877838] Control: 10c5387d Table: 3899c06a DAC: 00000015 -[ 1704.877843] Process do-oops (pid: 3656, stack limit = 0xb7e68238) -[ 1704.877848] Stack: (0xb7e69e50 to 0xb7e6a000) -[ 1704.877856] 9e40: 80a1cc3c 00000000 00000010 b88123c8 -[ 1704.877865] 9e60: b7e69e84 80a1cc34 fff9fee9 ffffffff b7e68000 00000009 b7e69ea4 b7e69e88 -[ 1704.877874] 9e80: 800704b0 80630ea4 fff9fee9 60080013 80a1cc28 fff9fee9 b7e69edc b7e69ea8 -[ 1704.877884] 9ea0: 8040f558 80070460 fff9fee9 ffffffff 00000000 00000000 00000009 80a1cb7c -[ 1704.877893] 9ec0: 00000000 80a1cb7c 00000000 00000010 b7e69ef4 b7e69ee0 803e1ba4 8040f514 -[ 1704.877902] 9ee0: 00000e48 80a1cb7c b7e69f14 b7e69ef8 803e1c9c 803e1b74 b88123c0 b92acb18 -[ 1704.877911] 9f00: b8812790 b8d815d8 b7e69f24 b7e69f18 803e2250 803e1bc8 b7e69f5c b7e69f28 -[ 1704.877921] 9f20: 80167bac 803e222c 00000000 00000000 b7e69f54 b8ab9ffc 00000000 8098c794 -[ 1704.877930] 9f40: b8ab9b00 8000efc4 b7e68000 00000000 b7e69f6c b7e69f60 80167d6c 80167b28 -[ 1704.877939] 9f60: b7e69f8c b7e69f70 80047d38 80167d60 b7e68000 b7e68010 8000efc4 b7e69fb0 -[ 1704.877949] 9f80: b7e69fac b7e69f90 80012820 80047c84 01155490 011549a8 00000001 00000006 -[ 1704.877957] 9fa0: 00000000 b7e69fb0 8000ee5c 80012790 00000000 353d8c0f 7efc4308 00000000 -[ 1704.877966] 9fc0: 01155490 011549a8 00000001 00000006 00000000 00000000 76cf3ba0 00000003 -[ 1704.877975] 9fe0: 00000000 7efc42e4 0002272f 76e2ed66 60080030 00000003 00000000 00000000 -[ 1704.877998] [<80630ee8>] (__down_interruptible) from [<800704b0>] (down_interruptible+0x5c/0x68) -[ 1704.878015] [<800704b0>] (down_interruptible) from [<8040f558>] (vchiu_queue_push+0x50/0xd8) -[ 1704.878032] [<8040f558>] (vchiu_queue_push) from [<803e1ba4>] (send_worker_msg+0x3c/0x54) -[ 1704.878045] [<803e1ba4>] (send_worker_msg) from [<803e1c9c>] (vc_cma_set_reserve+0xe0/0x1c4) -[ 1704.878057] [<803e1c9c>] (vc_cma_set_reserve) from [<803e2250>] (vc_cma_release+0x30/0x38) -[ 1704.878069] [<803e2250>] (vc_cma_release) from [<80167bac>] (__fput+0x90/0x1e0) -[ 1704.878082] [<80167bac>] (__fput) from [<80167d6c>] (____fput+0x18/0x1c) -[ 1704.878094] [<80167d6c>] (____fput) from [<80047d38>] (task_work_run+0xc0/0xf8) -[ 1704.878109] [<80047d38>] (task_work_run) from [<80012820>] (do_work_pending+0x9c/0xc4) -[ 1704.878123] [<80012820>] (do_work_pending) from [<8000ee5c>] (work_pending+0xc/0x20) -[ 1704.878133] Code: e50b1034 e3a01000 e50b2030 e580300c (e5823000) - -..the fix is to ensure that we have actually initialized the queue before we attempt -to push any items onto it. This occurs if we do an open() followed by a close() without -any activity in between. - -Signed-off-by: Colin Ian King - -vchiq_arm: Sort out the vmalloc case - -See: https://github.com/raspberrypi/linux/issues/1055 - -vchiq: hack: Add include depecated dma include file ---- - arch/arm/mach-bcm2708/include/mach/platform.h | 2 + - arch/arm/mach-bcm2709/include/mach/platform.h | 2 + - 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 | 586 +++ - .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 2903 +++++++++++++++ - .../vc04_services/interface/vchiq_arm/vchiq_arm.h | 220 ++ - .../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 | 156 + - .../vc04_services/interface/vchiq_arm/vchiq_util.h | 82 + - .../interface/vchiq_arm/vchiq_version.c | 59 + - 37 files changed, 12819 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 - ---- a/arch/arm/mach-bcm2708/include/mach/platform.h -+++ b/arch/arm/mach-bcm2708/include/mach/platform.h -@@ -78,6 +78,8 @@ - #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 */ -+#define ARMCTRL_0_BELL_BASE (ARMCTRL_0_SBM_BASE + 0x40) /* User 0 (ARM)'s Doorbell */ -+#define ARMCTRL_0_MAIL0_BASE (ARMCTRL_0_SBM_BASE + 0x80) /* User 0 (ARM)'s Mailbox 0 */ - - /* - * Watchdog ---- a/arch/arm/mach-bcm2709/include/mach/platform.h -+++ b/arch/arm/mach-bcm2709/include/mach/platform.h -@@ -78,6 +78,8 @@ - #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 */ -+#define ARMCTRL_0_BELL_BASE (ARMCTRL_0_SBM_BASE + 0x40) /* User 0 (ARM)'s Doorbell */ -+#define ARMCTRL_0_MAIL0_BASE (ARMCTRL_0_SBM_BASE + 0x80) /* User 0 (ARM)'s Mailbox 0 */ - - /* - * Watchdog ---- a/drivers/misc/Kconfig -+++ b/drivers/misc/Kconfig -@@ -533,6 +533,7 @@ source "drivers/misc/lis3lv02d/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" ---- 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/ ---- /dev/null -+++ b/drivers/misc/vc04_services/Kconfig -@@ -0,0 +1,9 @@ -+config BCM2708_VCHIQ -+ tristate "Videocore VCHIQ" -+ depends on RASPBERRYPI_FIRMWARE -+ 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. ---- /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 -+ ---- /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 **********************************/ ---- /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 ***********************************/ ---- /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 **********************************/ ---- /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 **********************************/ ---- /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_*/ ---- /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_ ---- /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 ---- /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 ---- /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 */ ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c -@@ -0,0 +1,586 @@ -+/** -+ * 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 -+ -+#define dmac_map_area __glue(_CACHE,_dma_map_area) -+#define dmac_unmap_area __glue(_CACHE,_dma_unmap_area) -+ -+extern void dmac_map_area(const void *, size_t, int); -+extern void dmac_unmap_area(const void *, size_t, int); -+ -+#define TOTAL_SLOTS (VCHIQ_SLOT_ZERO_SLOTS + 2 * 32) -+ -+#define VCHIQ_ARM_ADDRESS(x) ((void *)((char *)x + g_virt_to_bus_offset)) -+ -+#include "vchiq_arm.h" -+#include "vchiq_2835.h" -+#include "vchiq_connected.h" -+#include "vchiq_killable.h" -+ -+#define MAX_FRAGMENTS (VCHIQ_NUM_CURRENT_BULKS * 2) -+ -+#define BELL0 0x00 -+#define BELL2 0x08 -+ -+typedef struct vchiq_2835_state_struct { -+ int inited; -+ VCHIQ_ARM_STATE_T arm_state; -+} VCHIQ_2835_ARM_STATE_T; -+ -+static void __iomem *g_regs; -+static unsigned int g_cache_line_size = sizeof(CACHE_LINE_SIZE); -+static unsigned int g_fragments_size; -+static char *g_fragments_base; -+static char *g_free_fragments; -+static struct semaphore g_free_fragments_sema; -+static unsigned long g_virt_to_bus_offset; -+ -+extern int vchiq_arm_log_level; -+ -+static DEFINE_SEMAPHORE(g_free_fragments_mutex); -+ -+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 vchiq_platform_init(struct platform_device *pdev, VCHIQ_STATE_T *state) -+{ -+ struct device *dev = &pdev->dev; -+ struct rpi_firmware *fw = platform_get_drvdata(pdev); -+ VCHIQ_SLOT_ZERO_T *vchiq_slot_zero; -+ struct resource *res; -+ void *slot_mem; -+ dma_addr_t slot_phys; -+ u32 channelbase; -+ int slot_mem_size, frag_mem_size; -+ int err, irq, i; -+ -+ g_virt_to_bus_offset = virt_to_dma(dev, (void *)0); -+ -+ (void)of_property_read_u32(dev->of_node, "cache-line-size", -+ &g_cache_line_size); -+ g_fragments_size = 2 * g_cache_line_size; -+ -+ /* Allocate space for the channels in coherent memory */ -+ slot_mem_size = PAGE_ALIGN(TOTAL_SLOTS * VCHIQ_SLOT_SIZE); -+ frag_mem_size = PAGE_ALIGN(g_fragments_size * MAX_FRAGMENTS); -+ -+ slot_mem = dmam_alloc_coherent(dev, slot_mem_size + frag_mem_size, -+ &slot_phys, GFP_KERNEL); -+ if (!slot_mem) { -+ dev_err(dev, "could not allocate DMA memory\n"); -+ return -ENOMEM; -+ } -+ -+ WARN_ON(((int)slot_mem & (PAGE_SIZE - 1)) != 0); -+ -+ vchiq_slot_zero = vchiq_init_slots(slot_mem, slot_mem_size); -+ if (!vchiq_slot_zero) -+ return -EINVAL; -+ -+ vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_OFFSET_IDX] = -+ (int)slot_phys + slot_mem_size; -+ vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX] = -+ MAX_FRAGMENTS; -+ -+ g_fragments_base = (char *)slot_mem + slot_mem_size; -+ slot_mem_size += frag_mem_size; -+ -+ g_free_fragments = g_fragments_base; -+ for (i = 0; i < (MAX_FRAGMENTS - 1); i++) { -+ *(char **)&g_fragments_base[i*g_fragments_size] = -+ &g_fragments_base[(i + 1)*g_fragments_size]; -+ } -+ *(char **)&g_fragments_base[i * g_fragments_size] = NULL; -+ sema_init(&g_free_fragments_sema, MAX_FRAGMENTS); -+ -+ if (vchiq_init_state(state, vchiq_slot_zero, 0) != VCHIQ_SUCCESS) -+ return -EINVAL; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ g_regs = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(g_regs)) -+ return PTR_ERR(g_regs); -+ -+ irq = platform_get_irq(pdev, 0); -+ if (irq <= 0) { -+ dev_err(dev, "failed to get IRQ\n"); -+ return irq; -+ } -+ -+ err = devm_request_irq(dev, irq, vchiq_doorbell_irq, IRQF_IRQPOLL, -+ "VCHIQ doorbell", state); -+ if (err) { -+ dev_err(dev, "failed to register irq=%d\n", irq); -+ return err; -+ } -+ -+ /* Send the base address of the slots to VideoCore */ -+ channelbase = slot_phys; -+ err = rpi_firmware_property(fw, RPI_FIRMWARE_VCHIQ_INIT, -+ &channelbase, sizeof(channelbase)); -+ if (err || channelbase) { -+ dev_err(dev, "failed to set channelbase\n"); -+ return err ? : -ENXIO; -+ } -+ -+ vchiq_log_info(vchiq_arm_log_level, -+ "vchiq_init - done (slots %x, phys %pad)", -+ (unsigned int)vchiq_slot_zero, &slot_phys); -+ -+ vchiq_call_connected_callbacks(); -+ -+ return 0; -+} -+ -+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) -+ writel(0, g_regs + BELL2); /* trigger vc interrupt */ -+} -+ -+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 { -+ memcpy(dst, src, size); -+ return 0; -+ } -+} -+ -+VCHIQ_STATUS_T -+vchiq_prepare_bulk_data(VCHIQ_BULK_T *bulk, VCHI_MEM_HANDLE_T memhandle, -+ void *offset, int size, int dir) -+{ -+ 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; -+} -+ -+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, "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(g_regs + BELL0); -+ -+ if (status & 0x4) { /* Was the doorbell rung? */ -+ remote_event_pollall(state); -+ ret = IRQ_HANDLED; -+ } -+ -+ 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 dmac_map_area 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) -+{ -+ PAGELIST_T *pagelist; -+ struct page **pages; -+ unsigned long *addrs; -+ unsigned int num_pages, offset, i; -+ char *addr, *base_addr, *next_addr; -+ int run, addridx, actual_pages; -+ unsigned long *need_release; -+ -+ offset = (unsigned int)buf & (PAGE_SIZE - 1); -+ num_pages = (count + offset + PAGE_SIZE - 1) / PAGE_SIZE; -+ -+ *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)) { -+ int dir = (type == PAGELIST_WRITE) ? -+ DMA_TO_DEVICE : DMA_FROM_DEVICE; -+ unsigned long length = count; -+ unsigned int off = offset; -+ -+ for (actual_pages = 0; actual_pages < num_pages; -+ actual_pages++) { -+ struct page *pg = vmalloc_to_page(buf + (actual_pages * -+ PAGE_SIZE)); -+ size_t bytes = PAGE_SIZE - off; -+ -+ if (bytes > length) -+ bytes = length; -+ pages[actual_pages] = pg; -+ dmac_map_area(page_address(pg) + off, bytes, dir); -+ length -= bytes; -+ off = 0; -+ } -+ *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 & (g_cache_line_size - 1)) || -+ ((pagelist->offset + pagelist->length) & -+ (g_cache_line_size - 1)))) { -+ char *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 = g_free_fragments; -+ WARN_ON(fragments == NULL); -+ g_free_fragments = *(char **) g_free_fragments; -+ up(&g_free_fragments_mutex); -+ pagelist->type = PAGELIST_READ_WITH_FRAGMENTS + -+ (fragments - g_fragments_base) / g_fragments_size; -+ } -+ -+ dmac_flush_range(pagelist, addrs + num_pages); -+ -+ *ppagelist = pagelist; -+ -+ return 0; -+} -+ -+static void -+free_pagelist(PAGELIST_T *pagelist, int actual) -+{ -+ 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) { -+ char *fragments = g_fragments_base + -+ (pagelist->type - PAGELIST_READ_WITH_FRAGMENTS) * -+ g_fragments_size; -+ int head_bytes, tail_bytes; -+ head_bytes = (g_cache_line_size - pagelist->offset) & -+ (g_cache_line_size - 1); -+ tail_bytes = (pagelist->offset + actual) & -+ (g_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, -+ 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) & ~(g_cache_line_size - 1)), -+ fragments + g_cache_line_size, -+ tail_bytes); -+ } -+ -+ down(&g_free_fragments_mutex); -+ *(char **)fragments = g_free_fragments; -+ g_free_fragments = fragments; -+ up(&g_free_fragments_mutex); -+ up(&g_free_fragments_sema); -+ } -+ -+ if (*need_release) { -+ unsigned int length = pagelist->length; -+ unsigned int offset = pagelist->offset; -+ -+ for (i = 0; i < num_pages; i++) { -+ struct page *pg = pages[i]; -+ -+ if (pagelist->type != PAGELIST_WRITE) { -+ unsigned int bytes = PAGE_SIZE - offset; -+ -+ if (bytes > length) -+ bytes = length; -+ dmac_unmap_area(page_address(pg) + offset, -+ bytes, DMA_FROM_DEVICE); -+ length -= bytes; -+ offset = 0; -+ set_page_dirty(pg); -+ } -+ page_cache_release(pg); -+ } -+ } -+ -+ kfree(pagelist); -+} ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c -@@ -0,0 +1,2903 @@ -+/** -+ * 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 -+#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; -+} -+ -+/**************************************************************************** -+* -+* service_callback -+* -+***************************************************************************/ -+ -+static VCHIQ_STATUS_T -+service_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, -+ VCHIQ_SERVICE_HANDLE_T handle, void *bulk_userdata) -+{ -+ /* 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 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; -+ else -+ vchiq_log_error(vchiq_arm_log_level, -+ "vchiq: could not connect: %d", status); -+ break; -+ -+ 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 { -+ srvstate = -+ instance->connected ? -+ VCHIQ_SRVSTATE_LISTENING : -+ VCHIQ_SRVSTATE_HIDDEN; -+ } -+ -+ 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; -+ } -+ } -+ -+ 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; -+ } -+ 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. -+*/ -+ -+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); -+ } -+} -+ -+static int vchiq_probe(struct platform_device *pdev) -+{ -+ struct device_node *fw_node; -+ struct rpi_firmware *fw; -+ int err; -+ void *ptr_err; -+ -+ fw_node = of_parse_phandle(pdev->dev.of_node, "firmware", 0); -+/* Remove comment when booting without Device Tree is no longer supported -+ if (!fw_node) { -+ dev_err(&pdev->dev, "Missing firmware node\n"); -+ return -ENOENT; -+ } -+*/ -+ fw = rpi_firmware_get(fw_node); -+ if (!fw) -+ return -EPROBE_DEFER; -+ -+ platform_set_drvdata(pdev, fw); -+ -+ /* 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(pdev, &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; -+} -+ -+static int vchiq_remove(struct platform_device *pdev) -+{ -+ device_destroy(vchiq_class, vchiq_devid); -+ class_destroy(vchiq_class); -+ cdev_del(&vchiq_cdev); -+ unregister_chrdev_region(vchiq_devid, 1); -+ -+ return 0; -+} -+ -+static const struct of_device_id vchiq_of_match[] = { -+ { .compatible = "brcm,bcm2835-vchiq", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, vchiq_of_match); -+ -+static struct platform_driver vchiq_driver = { -+ .driver = { -+ .name = "bcm2835_vchiq", -+ .owner = THIS_MODULE, -+ .of_match_table = vchiq_of_match, -+ }, -+ .probe = vchiq_probe, -+ .remove = vchiq_remove, -+}; -+module_platform_driver(vchiq_driver); -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Broadcom Corporation"); ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h -@@ -0,0 +1,220 @@ -+/** -+ * 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 -+#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; -+ -+int vchiq_platform_init(struct platform_device *pdev, 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 */ ---- /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); ---- /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 */ ---- /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); ---- /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 */ ---- /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", -+ "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 flags) -+{ -+ 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 (!(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; -+ } -+ -+ 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, 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; -+ } -+ -+ 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); -+ 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); -+ } -+ -+ 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 (!(flags & QMFLAGS_NO_MUTEX_UNLOCK)) -+ 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) -+ }; -+ -+ 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; -+} -+ -+/* 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_info(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); -+ 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; -+ -+ 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, QMFLAGS_NO_MUTEX_UNLOCK) -+ == 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, -+ 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; -+ } -+ 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; -+ } -+ -+ -+ } -+ -+ 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); -+ 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 -+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 (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); -+ -+ return; -+ } -+ -+ 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, QMFLAGS_NO_MUTEX_UNLOCK); -+ -+ 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; -+ -+ /* 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 (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, 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); -+ } -+} -+ -+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; -+ } -+} ---- /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 -+ -+#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; -+ 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) -+{ -+ VCHIQ_STATE_T *state = vchiq_states[(handle / VCHIQ_MAX_SERVICES) & -+ (VCHIQ_MAX_STATES - 1)]; -+ if (!state) -+ return NULL; -+ -+ return state->services[handle & (VCHIQ_MAX_SERVICES - 1)]; -+} -+ -+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 ---- /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; -+ else -+ *levp = VCHIQ_LOG_DEFAULT; -+ -+ *ppos += count; -+ -+ return count; -+} -+ -+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 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; -+ -+ 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; -+ } -+ -+ vchiq_debugfs_log_entries[i].dir = dir; -+ } -+ return ret; -+} -+ -+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 '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 */ ---- /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 */ ---- /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 ---- /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 */ ---- /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 ---- /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; -+ } -+ } -+ -+failed: -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p): returning %d", __func__, instance, status); -+ -+ return status; -+} -+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) -+{ -+ 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); -+ -+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); -+ -+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; -+ -+ 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; -+ } -+ -+ 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 status; -+} -+EXPORT_SYMBOL(vchiq_bulk_receive); -+ -+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; -+ -+ 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; -+ } -+ } -+ 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); -+ } -+ } -+ } -+ -+ 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; -+} ---- /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 -+ -+ -+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 ---- /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" -+ -+/* ---- Constants and Types ---------------------------------------------- */ -+ -+typedef struct { -+ void *armSharedMemVirt; -+ dma_addr_t armSharedMemPhys; -+ size_t armSharedMemSize; -+ -+ void *vcSharedMemVirt; -+ dma_addr_t vcSharedMemPhys; -+ size_t vcSharedMemSize; -+} VCHIQ_SHARED_MEM_INFO_T; -+ -+/* ---- Variable Externs ------------------------------------------------- */ -+ -+/* ---- 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); -+ -+#endif ---- /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 */ ---- /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); -+ } -+ -+ return vchiq_status_to_vchi(status); -+} -+EXPORT_SYMBOL(vchi_msg_queue); -+ -+/*********************************************************** -+ * 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; -+ } -+ } -+ -+ 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); ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c -@@ -0,0 +1,156 @@ -+/** -+ * 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; -+ queue->initialized = 1; -+ -+ 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) -+{ -+ if (!queue->initialized) -+ return; -+ -+ 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; -+} ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h -@@ -0,0 +1,82 @@ -+/** -+ * 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; -+ int initialized; -+ -+ 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 ---- /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; -+} diff --git a/target/linux/brcm2708/patches-4.4/0037-vc_mem-Add-vc_mem-driver.patch b/target/linux/brcm2708/patches-4.4/0037-vc_mem-Add-vc_mem-driver.patch new file mode 100644 index 0000000000..04593f3e12 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0037-vc_mem-Add-vc_mem-driver.patch @@ -0,0 +1,991 @@ +From b0acaf43055bacef7918c835517ff6ea7da26e83 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 17 Jun 2015 16:07:06 +0100 +Subject: [PATCH 037/232] vc_mem: Add vc_mem driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: popcornmix + +BCM270x: Move vc_mem + +Make the vc_mem module available for ARCH_BCM2835 by moving it. + +Signed-off-by: Noralf Trønnes +--- + arch/arm/mach-bcm2709/include/mach/vc_mem.h | 35 --- + arch/arm/mach-bcm2709/vc_mem.c | 431 ---------------------------- + drivers/char/broadcom/Kconfig | 12 +- + drivers/char/broadcom/Makefile | 1 + + drivers/char/broadcom/vc_mem.c | 422 +++++++++++++++++++++++++++ + include/linux/broadcom/vc_mem.h | 35 +++ + 6 files changed, 469 insertions(+), 467 deletions(-) + delete mode 100644 arch/arm/mach-bcm2709/include/mach/vc_mem.h + delete mode 100644 arch/arm/mach-bcm2709/vc_mem.c + create mode 100644 drivers/char/broadcom/vc_mem.c + create mode 100644 include/linux/broadcom/vc_mem.h + +--- a/arch/arm/mach-bcm2709/include/mach/vc_mem.h ++++ /dev/null +@@ -1,35 +0,0 @@ +-/***************************************************************************** +-* 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 */ +--- a/arch/arm/mach-bcm2709/vc_mem.c ++++ /dev/null +@@ -1,431 +0,0 @@ +-/***************************************************************************** +-* 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 +-#include +- +-#ifdef CONFIG_ARCH_KONA +-#include +-#elif defined(CONFIG_ARCH_BCM2708) || defined(CONFIG_ARCH_BCM2709) +-#else +-#include +-#endif +- +-#include "mach/vc_mem.h" +- +-#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); +--- a/drivers/char/broadcom/Kconfig ++++ b/drivers/char/broadcom/Kconfig +@@ -7,9 +7,19 @@ menuconfig BRCM_CHAR_DRIVERS + help + Broadcom's char drivers + ++if BRCM_CHAR_DRIVERS ++ + config BCM_VC_CMA + bool "Videocore CMA" +- depends on CMA && BRCM_CHAR_DRIVERS && BCM2708_VCHIQ ++ depends on CMA && BCM2708_VCHIQ + default n + help + Helper for videocore CMA access. ++ ++config BCM2708_VCMEM ++ bool "Videocore Memory" ++ default y ++ help ++ Helper for videocore memory access and total size allocation. ++ ++endif +--- a/drivers/char/broadcom/Makefile ++++ b/drivers/char/broadcom/Makefile +@@ -1 +1,2 @@ + obj-$(CONFIG_BCM_VC_CMA) += vc_cma/ ++obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o +--- /dev/null ++++ b/drivers/char/broadcom/vc_mem.c +@@ -0,0 +1,422 @@ ++/***************************************************************************** ++* 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 ++#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); +--- /dev/null ++++ b/include/linux/broadcom/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. ++*****************************************************************************/ ++ ++#ifndef _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/target/linux/brcm2708/patches-4.4/0038-vc_mem-Add-vc_mem-driver.patch b/target/linux/brcm2708/patches-4.4/0038-vc_mem-Add-vc_mem-driver.patch deleted file mode 100644 index c8778b5adb..0000000000 --- a/target/linux/brcm2708/patches-4.4/0038-vc_mem-Add-vc_mem-driver.patch +++ /dev/null @@ -1,991 +0,0 @@ -From 2cabfc660c80dd79f24a8323b3913b5060ebf750 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 17 Jun 2015 16:07:06 +0100 -Subject: [PATCH 038/170] vc_mem: Add vc_mem driver -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: popcornmix - -BCM270x: Move vc_mem - -Make the vc_mem module available for ARCH_BCM2835 by moving it. - -Signed-off-by: Noralf Trønnes ---- - arch/arm/mach-bcm2709/include/mach/vc_mem.h | 35 --- - arch/arm/mach-bcm2709/vc_mem.c | 431 ---------------------------- - drivers/char/broadcom/Kconfig | 12 +- - drivers/char/broadcom/Makefile | 1 + - drivers/char/broadcom/vc_mem.c | 422 +++++++++++++++++++++++++++ - include/linux/broadcom/vc_mem.h | 35 +++ - 6 files changed, 469 insertions(+), 467 deletions(-) - delete mode 100644 arch/arm/mach-bcm2709/include/mach/vc_mem.h - delete mode 100644 arch/arm/mach-bcm2709/vc_mem.c - create mode 100644 drivers/char/broadcom/vc_mem.c - create mode 100644 include/linux/broadcom/vc_mem.h - ---- a/arch/arm/mach-bcm2709/include/mach/vc_mem.h -+++ /dev/null -@@ -1,35 +0,0 @@ --/***************************************************************************** --* 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 */ ---- a/arch/arm/mach-bcm2709/vc_mem.c -+++ /dev/null -@@ -1,431 +0,0 @@ --/***************************************************************************** --* 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 --#include -- --#ifdef CONFIG_ARCH_KONA --#include --#elif defined(CONFIG_ARCH_BCM2708) || defined(CONFIG_ARCH_BCM2709) --#else --#include --#endif -- --#include "mach/vc_mem.h" -- --#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); ---- a/drivers/char/broadcom/Kconfig -+++ b/drivers/char/broadcom/Kconfig -@@ -7,9 +7,19 @@ menuconfig BRCM_CHAR_DRIVERS - help - Broadcom's char drivers - -+if BRCM_CHAR_DRIVERS -+ - config BCM_VC_CMA - bool "Videocore CMA" -- depends on CMA && BRCM_CHAR_DRIVERS && BCM2708_VCHIQ -+ depends on CMA && BCM2708_VCHIQ - default n - help - Helper for videocore CMA access. -+ -+config BCM2708_VCMEM -+ bool "Videocore Memory" -+ default y -+ help -+ Helper for videocore memory access and total size allocation. -+ -+endif ---- a/drivers/char/broadcom/Makefile -+++ b/drivers/char/broadcom/Makefile -@@ -1 +1,2 @@ - obj-$(CONFIG_BCM_VC_CMA) += vc_cma/ -+obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o ---- /dev/null -+++ b/drivers/char/broadcom/vc_mem.c -@@ -0,0 +1,422 @@ -+/***************************************************************************** -+* 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 -+#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); ---- /dev/null -+++ b/include/linux/broadcom/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. -+*****************************************************************************/ -+ -+#ifndef _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/target/linux/brcm2708/patches-4.4/0038-vcsm-VideoCore-shared-memory-service-for-BCM2835.patch b/target/linux/brcm2708/patches-4.4/0038-vcsm-VideoCore-shared-memory-service-for-BCM2835.patch new file mode 100644 index 0000000000..d23c7ece7b --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0038-vcsm-VideoCore-shared-memory-service-for-BCM2835.patch @@ -0,0 +1,4393 @@ +From 5f05accd033e4b032f64717a0276d7174f697919 Mon Sep 17 00:00:00 2001 +From: Tim Gover +Date: Tue, 22 Jul 2014 15:41:04 +0100 +Subject: [PATCH 038/232] vcsm: VideoCore shared memory service for BCM2835 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add experimental support for the VideoCore shared memory service. +This allows user processes to allocate memory from VideoCore's +GPU relocatable heap and mmap the buffers. Additionally, the memory +handles can passed to other VideoCore services such as MMAL, OpenMax +and DispmanX + +TODO +* This driver was originally released for BCM28155 which has a different + cache architecture to BCM2835. Consequently, in this release only + uncached mappings are supported. However, there's no fundamental + reason which cached mappings cannot be support or BCM2835 +* More refactoring is required to remove the typedefs. +* Re-enable the some of the commented out debug-fs statistics which were + disabled when migrating code from proc-fs. +* There's a lot of code to support sharing of VCSM in order to support + Android. This could probably done more cleanly or perhaps just + removed. + +Signed-off-by: Tim Gover + +config: Disable VC_SM for now to fix hang with cutdown kernel + +vcsm: Use boolean as it cannot be built as module + +On building the bcm_vc_sm as a module we get the following error: + +v7_dma_flush_range and do_munmap are undefined in vc-sm.ko. + +Fix by making it not an option to build as module + +vcsm: Add ioctl for custom cache flushing + +vc-sm: Move headers out of arch directory + +Signed-off-by: Noralf Trønnes +--- + drivers/char/broadcom/Kconfig | 9 + + drivers/char/broadcom/Makefile | 1 + + drivers/char/broadcom/vc_sm/Makefile | 20 + + drivers/char/broadcom/vc_sm/vc_sm_defs.h | 181 ++ + drivers/char/broadcom/vc_sm/vc_sm_knl.h | 55 + + drivers/char/broadcom/vc_sm/vc_vchi_sm.c | 492 +++++ + drivers/char/broadcom/vc_sm/vc_vchi_sm.h | 82 + + drivers/char/broadcom/vc_sm/vmcs_sm.c | 3211 ++++++++++++++++++++++++++++++ + include/linux/broadcom/vmcs_sm_ioctl.h | 248 +++ + 9 files changed, 4299 insertions(+) + create mode 100644 drivers/char/broadcom/vc_sm/Makefile + create mode 100644 drivers/char/broadcom/vc_sm/vc_sm_defs.h + create mode 100644 drivers/char/broadcom/vc_sm/vc_sm_knl.h + create mode 100644 drivers/char/broadcom/vc_sm/vc_vchi_sm.c + create mode 100644 drivers/char/broadcom/vc_sm/vc_vchi_sm.h + create mode 100644 drivers/char/broadcom/vc_sm/vmcs_sm.c + create mode 100644 include/linux/broadcom/vmcs_sm_ioctl.h + +--- a/drivers/char/broadcom/Kconfig ++++ b/drivers/char/broadcom/Kconfig +@@ -23,3 +23,12 @@ config BCM2708_VCMEM + Helper for videocore memory access and total size allocation. + + endif ++ ++config BCM_VC_SM ++ bool "VMCS Shared Memory" ++ depends on BCM2708_VCHIQ ++ select BCM2708_VCMEM ++ default n ++ help ++ Support for the VC shared memory on the Broadcom reference ++ design. Uses the VCHIQ stack. +--- a/drivers/char/broadcom/Makefile ++++ b/drivers/char/broadcom/Makefile +@@ -1,2 +1,3 @@ + obj-$(CONFIG_BCM_VC_CMA) += vc_cma/ + obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o ++obj-$(CONFIG_BCM_VC_SM) += vc_sm/ +--- /dev/null ++++ b/drivers/char/broadcom/vc_sm/Makefile +@@ -0,0 +1,20 @@ ++EXTRA_CFLAGS += -Wall -Wstrict-prototypes -Wno-trigraphs -O2 ++ ++EXTRA_CFLAGS += -I"drivers/misc/vc04_services" ++EXTRA_CFLAGS += -I"drivers/misc/vc04_services/interface/vchi" ++EXTRA_CFLAGS += -I"drivers/misc/vc04_services/interface/vchiq_arm" ++EXTRA_CFLAGS += -I"$(srctree)/fs/" ++ ++EXTRA_CFLAGS += -DOS_ASSERT_FAILURE ++EXTRA_CFLAGS += -D__STDC_VERSION=199901L ++EXTRA_CFLAGS += -D__STDC_VERSION__=199901L ++EXTRA_CFLAGS += -D__VCCOREVER__=0 ++EXTRA_CFLAGS += -D__KERNEL__ ++EXTRA_CFLAGS += -D__linux__ ++EXTRA_CFLAGS += -Werror ++ ++obj-$(CONFIG_BCM_VC_SM) := vc-sm.o ++ ++vc-sm-objs := \ ++ vmcs_sm.o \ ++ vc_vchi_sm.o +--- /dev/null ++++ b/drivers/char/broadcom/vc_sm/vc_sm_defs.h +@@ -0,0 +1,181 @@ ++/***************************************************************************** ++* 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. ++*****************************************************************************/ ++ ++#ifndef __VC_SM_DEFS_H__INCLUDED__ ++#define __VC_SM_DEFS_H__INCLUDED__ ++ ++/* FourCC code used for VCHI connection */ ++#define VC_SM_SERVER_NAME MAKE_FOURCC("SMEM") ++ ++/* Maximum message length */ ++#define VC_SM_MAX_MSG_LEN (sizeof(VC_SM_MSG_UNION_T) + \ ++ sizeof(VC_SM_MSG_HDR_T)) ++#define VC_SM_MAX_RSP_LEN (sizeof(VC_SM_MSG_UNION_T)) ++ ++/* Resource name maximum size */ ++#define VC_SM_RESOURCE_NAME 32 ++ ++/* All message types supported for HOST->VC direction */ ++typedef enum { ++ /* Allocate shared memory block */ ++ VC_SM_MSG_TYPE_ALLOC, ++ /* Lock allocated shared memory block */ ++ VC_SM_MSG_TYPE_LOCK, ++ /* Unlock allocated shared memory block */ ++ VC_SM_MSG_TYPE_UNLOCK, ++ /* Unlock allocated shared memory block, do not answer command */ ++ VC_SM_MSG_TYPE_UNLOCK_NOANS, ++ /* Free shared memory block */ ++ VC_SM_MSG_TYPE_FREE, ++ /* Resize a shared memory block */ ++ VC_SM_MSG_TYPE_RESIZE, ++ /* Walk the allocated shared memory block(s) */ ++ VC_SM_MSG_TYPE_WALK_ALLOC, ++ ++ /* A previously applied action will need to be reverted */ ++ VC_SM_MSG_TYPE_ACTION_CLEAN, ++ VC_SM_MSG_TYPE_MAX ++} VC_SM_MSG_TYPE; ++ ++/* Type of memory to be allocated */ ++typedef enum { ++ VC_SM_ALLOC_CACHED, ++ VC_SM_ALLOC_NON_CACHED, ++ ++} VC_SM_ALLOC_TYPE_T; ++ ++/* Message header for all messages in HOST->VC direction */ ++typedef struct { ++ int32_t type; ++ uint32_t trans_id; ++ uint8_t body[0]; ++ ++} VC_SM_MSG_HDR_T; ++ ++/* Request to allocate memory (HOST->VC) */ ++typedef struct { ++ /* type of memory to allocate */ ++ VC_SM_ALLOC_TYPE_T type; ++ /* byte amount of data to allocate per unit */ ++ uint32_t base_unit; ++ /* number of unit to allocate */ ++ uint32_t num_unit; ++ /* alignement to be applied on allocation */ ++ uint32_t alignement; ++ /* identity of who allocated this block */ ++ uint32_t allocator; ++ /* resource name (for easier tracking on vc side) */ ++ char name[VC_SM_RESOURCE_NAME]; ++ ++} VC_SM_ALLOC_T; ++ ++/* Result of a requested memory allocation (VC->HOST) */ ++typedef struct { ++ /* Transaction identifier */ ++ uint32_t trans_id; ++ ++ /* Resource handle */ ++ uint32_t res_handle; ++ /* Pointer to resource buffer */ ++ void *res_mem; ++ /* Resource base size (bytes) */ ++ uint32_t res_base_size; ++ /* Resource number */ ++ uint32_t res_num; ++ ++} VC_SM_ALLOC_RESULT_T; ++ ++/* Request to free a previously allocated memory (HOST->VC) */ ++typedef struct { ++ /* Resource handle (returned from alloc) */ ++ uint32_t res_handle; ++ /* Resource buffer (returned from alloc) */ ++ void *res_mem; ++ ++} VC_SM_FREE_T; ++ ++/* Request to lock a previously allocated memory (HOST->VC) */ ++typedef struct { ++ /* Resource handle (returned from alloc) */ ++ uint32_t res_handle; ++ /* Resource buffer (returned from alloc) */ ++ void *res_mem; ++ ++} VC_SM_LOCK_UNLOCK_T; ++ ++/* Request to resize a previously allocated memory (HOST->VC) */ ++typedef struct { ++ /* Resource handle (returned from alloc) */ ++ uint32_t res_handle; ++ /* Resource buffer (returned from alloc) */ ++ void *res_mem; ++ /* Resource *new* size requested (bytes) */ ++ uint32_t res_new_size; ++ ++} VC_SM_RESIZE_T; ++ ++/* Result of a requested memory lock (VC->HOST) */ ++typedef struct { ++ /* Transaction identifier */ ++ uint32_t trans_id; ++ ++ /* Resource handle */ ++ uint32_t res_handle; ++ /* Pointer to resource buffer */ ++ void *res_mem; ++ /* Pointer to former resource buffer if the memory ++ * was reallocated */ ++ void *res_old_mem; ++ ++} VC_SM_LOCK_RESULT_T; ++ ++/* Generic result for a request (VC->HOST) */ ++typedef struct { ++ /* Transaction identifier */ ++ uint32_t trans_id; ++ ++ int32_t success; ++ ++} VC_SM_RESULT_T; ++ ++/* Request to revert a previously applied action (HOST->VC) */ ++typedef struct { ++ /* Action of interest */ ++ VC_SM_MSG_TYPE res_action; ++ /* Transaction identifier for the action of interest */ ++ uint32_t action_trans_id; ++ ++} VC_SM_ACTION_CLEAN_T; ++ ++/* Request to remove all data associated with a given allocator (HOST->VC) */ ++typedef struct { ++ /* Allocator identifier */ ++ uint32_t allocator; ++ ++} VC_SM_FREE_ALL_T; ++ ++/* Union of ALL messages */ ++typedef union { ++ VC_SM_ALLOC_T alloc; ++ VC_SM_ALLOC_RESULT_T alloc_result; ++ VC_SM_FREE_T free; ++ VC_SM_ACTION_CLEAN_T action_clean; ++ VC_SM_RESIZE_T resize; ++ VC_SM_LOCK_RESULT_T lock_result; ++ VC_SM_RESULT_T result; ++ VC_SM_FREE_ALL_T free_all; ++ ++} VC_SM_MSG_UNION_T; ++ ++#endif /* __VC_SM_DEFS_H__INCLUDED__ */ +--- /dev/null ++++ b/drivers/char/broadcom/vc_sm/vc_sm_knl.h +@@ -0,0 +1,55 @@ ++/***************************************************************************** ++* 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. ++*****************************************************************************/ ++ ++#ifndef __VC_SM_KNL_H__INCLUDED__ ++#define __VC_SM_KNL_H__INCLUDED__ ++ ++#if !defined(__KERNEL__) ++#error "This interface is for kernel use only..." ++#endif ++ ++/* Type of memory to be locked (ie mapped) */ ++typedef enum { ++ VC_SM_LOCK_CACHED, ++ VC_SM_LOCK_NON_CACHED, ++ ++} VC_SM_LOCK_CACHE_MODE_T; ++ ++/* Allocate a shared memory handle and block. ++*/ ++int vc_sm_alloc(VC_SM_ALLOC_T *alloc, int *handle); ++ ++/* Free a previously allocated shared memory handle and block. ++*/ ++int vc_sm_free(int handle); ++ ++/* Lock a memory handle for use by kernel. ++*/ ++int vc_sm_lock(int handle, VC_SM_LOCK_CACHE_MODE_T mode, ++ long unsigned int *data); ++ ++/* Unlock a memory handle in use by kernel. ++*/ ++int vc_sm_unlock(int handle, int flush, int no_vc_unlock); ++ ++/* Get an internal resource handle mapped from the external one. ++*/ ++int vc_sm_int_handle(int handle); ++ ++/* Map a shared memory region for use by kernel. ++*/ ++int vc_sm_map(int handle, unsigned int sm_addr, VC_SM_LOCK_CACHE_MODE_T mode, ++ long unsigned int *data); ++ ++#endif /* __VC_SM_KNL_H__INCLUDED__ */ +--- /dev/null ++++ b/drivers/char/broadcom/vc_sm/vc_vchi_sm.c +@@ -0,0 +1,492 @@ ++/***************************************************************************** ++* Copyright 2011-2012 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 Files ----------------------------------------------------- */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "vc_vchi_sm.h" ++ ++#define VC_SM_VER 1 ++#define VC_SM_MIN_VER 0 ++ ++/* ---- Private Constants and Types -------------------------------------- */ ++ ++/* Command blocks come from a pool */ ++#define SM_MAX_NUM_CMD_RSP_BLKS 32 ++ ++struct sm_cmd_rsp_blk { ++ struct list_head head; /* To create lists */ ++ struct semaphore sema; /* To be signaled when the response is there */ ++ ++ uint16_t id; ++ uint16_t length; ++ ++ uint8_t msg[VC_SM_MAX_MSG_LEN]; ++ ++ uint32_t wait:1; ++ uint32_t sent:1; ++ uint32_t alloc:1; ++ ++}; ++ ++struct sm_instance { ++ uint32_t num_connections; ++ VCHI_SERVICE_HANDLE_T vchi_handle[VCHI_MAX_NUM_CONNECTIONS]; ++ struct task_struct *io_thread; ++ struct semaphore io_sema; ++ ++ uint32_t trans_id; ++ ++ struct mutex lock; ++ struct list_head cmd_list; ++ struct list_head rsp_list; ++ struct list_head dead_list; ++ ++ struct sm_cmd_rsp_blk free_blk[SM_MAX_NUM_CMD_RSP_BLKS]; ++ struct list_head free_list; ++ struct mutex free_lock; ++ struct semaphore free_sema; ++ ++}; ++ ++/* ---- Private Variables ------------------------------------------------ */ ++ ++/* ---- Private Function Prototypes -------------------------------------- */ ++ ++/* ---- Private Functions ------------------------------------------------ */ ++static struct ++sm_cmd_rsp_blk *vc_vchi_cmd_create(struct sm_instance *instance, ++ VC_SM_MSG_TYPE id, void *msg, ++ uint32_t size, int wait) ++{ ++ struct sm_cmd_rsp_blk *blk; ++ VC_SM_MSG_HDR_T *hdr; ++ ++ if (down_interruptible(&instance->free_sema)) { ++ blk = kmalloc(sizeof(*blk), GFP_KERNEL); ++ if (!blk) ++ return NULL; ++ ++ blk->alloc = 1; ++ sema_init(&blk->sema, 0); ++ } else { ++ mutex_lock(&instance->free_lock); ++ blk = ++ list_first_entry(&instance->free_list, ++ struct sm_cmd_rsp_blk, head); ++ list_del(&blk->head); ++ mutex_unlock(&instance->free_lock); ++ } ++ ++ blk->sent = 0; ++ blk->wait = wait; ++ blk->length = sizeof(*hdr) + size; ++ ++ hdr = (VC_SM_MSG_HDR_T *) blk->msg; ++ hdr->type = id; ++ mutex_lock(&instance->lock); ++ hdr->trans_id = blk->id = ++instance->trans_id; ++ mutex_unlock(&instance->lock); ++ ++ if (size) ++ memcpy(hdr->body, msg, size); ++ ++ return blk; ++} ++ ++static void ++vc_vchi_cmd_delete(struct sm_instance *instance, struct sm_cmd_rsp_blk *blk) ++{ ++ if (blk->alloc) { ++ kfree(blk); ++ return; ++ } ++ ++ mutex_lock(&instance->free_lock); ++ list_add(&blk->head, &instance->free_list); ++ mutex_unlock(&instance->free_lock); ++ up(&instance->free_sema); ++} ++ ++static int vc_vchi_sm_videocore_io(void *arg) ++{ ++ struct sm_instance *instance = arg; ++ struct sm_cmd_rsp_blk *cmd = NULL, *cmd_tmp; ++ VC_SM_RESULT_T *reply; ++ uint32_t reply_len; ++ int32_t status; ++ int svc_use = 1; ++ ++ while (1) { ++ if (svc_use) ++ vchi_service_release(instance->vchi_handle[0]); ++ svc_use = 0; ++ if (!down_interruptible(&instance->io_sema)) { ++ vchi_service_use(instance->vchi_handle[0]); ++ svc_use = 1; ++ ++ do { ++ unsigned int flags; ++ /* ++ * Get new command and move it to response list ++ */ ++ mutex_lock(&instance->lock); ++ if (list_empty(&instance->cmd_list)) { ++ /* no more commands to process */ ++ mutex_unlock(&instance->lock); ++ break; ++ } ++ cmd = ++ list_first_entry(&instance->cmd_list, ++ struct sm_cmd_rsp_blk, ++ head); ++ list_move(&cmd->head, &instance->rsp_list); ++ cmd->sent = 1; ++ mutex_unlock(&instance->lock); ++ ++ /* Send the command */ ++ flags = VCHI_FLAGS_BLOCK_UNTIL_QUEUED; ++ status = vchi_msg_queue( ++ instance->vchi_handle[0], ++ cmd->msg, cmd->length, ++ flags, NULL); ++ if (status) { ++ pr_err("%s: failed to queue message (%d)", ++ __func__, status); ++ } ++ ++ /* If no reply is needed then we're done */ ++ if (!cmd->wait) { ++ mutex_lock(&instance->lock); ++ list_del(&cmd->head); ++ mutex_unlock(&instance->lock); ++ vc_vchi_cmd_delete(instance, cmd); ++ continue; ++ } ++ ++ if (status) { ++ up(&cmd->sema); ++ continue; ++ } ++ ++ } while (1); ++ ++ while (!vchi_msg_peek ++ (instance->vchi_handle[0], (void **)&reply, ++ &reply_len, VCHI_FLAGS_NONE)) { ++ mutex_lock(&instance->lock); ++ list_for_each_entry(cmd, &instance->rsp_list, ++ head) { ++ if (cmd->id == reply->trans_id) ++ break; ++ } ++ mutex_unlock(&instance->lock); ++ ++ if (&cmd->head == &instance->rsp_list) { ++ pr_debug("%s: received response %u, throw away...", ++ __func__, reply->trans_id); ++ } else if (reply_len > sizeof(cmd->msg)) { ++ pr_err("%s: reply too big (%u) %u, throw away...", ++ __func__, reply_len, ++ reply->trans_id); ++ } else { ++ memcpy(cmd->msg, reply, reply_len); ++ up(&cmd->sema); ++ } ++ ++ vchi_msg_remove(instance->vchi_handle[0]); ++ } ++ ++ /* Go through the dead list and free them */ ++ mutex_lock(&instance->lock); ++ list_for_each_entry_safe(cmd, cmd_tmp, ++ &instance->dead_list, head) { ++ list_del(&cmd->head); ++ vc_vchi_cmd_delete(instance, cmd); ++ } ++ mutex_unlock(&instance->lock); ++ } ++ } ++ ++ return 0; ++} ++ ++static void vc_sm_vchi_callback(void *param, ++ const VCHI_CALLBACK_REASON_T reason, ++ void *msg_handle) ++{ ++ struct sm_instance *instance = param; ++ ++ (void)msg_handle; ++ ++ switch (reason) { ++ case VCHI_CALLBACK_MSG_AVAILABLE: ++ up(&instance->io_sema); ++ break; ++ ++ case VCHI_CALLBACK_SERVICE_CLOSED: ++ pr_info("%s: service CLOSED!!", __func__); ++ default: ++ break; ++ } ++} ++ ++VC_VCHI_SM_HANDLE_T vc_vchi_sm_init(VCHI_INSTANCE_T vchi_instance, ++ VCHI_CONNECTION_T **vchi_connections, ++ uint32_t num_connections) ++{ ++ uint32_t i; ++ struct sm_instance *instance; ++ int status; ++ ++ pr_debug("%s: start", __func__); ++ ++ if (num_connections > VCHI_MAX_NUM_CONNECTIONS) { ++ pr_err("%s: unsupported number of connections %u (max=%u)", ++ __func__, num_connections, VCHI_MAX_NUM_CONNECTIONS); ++ ++ goto err_null; ++ } ++ /* Allocate memory for this instance */ ++ instance = kzalloc(sizeof(*instance), GFP_KERNEL); ++ ++ /* Misc initialisations */ ++ mutex_init(&instance->lock); ++ sema_init(&instance->io_sema, 0); ++ INIT_LIST_HEAD(&instance->cmd_list); ++ INIT_LIST_HEAD(&instance->rsp_list); ++ INIT_LIST_HEAD(&instance->dead_list); ++ INIT_LIST_HEAD(&instance->free_list); ++ sema_init(&instance->free_sema, SM_MAX_NUM_CMD_RSP_BLKS); ++ mutex_init(&instance->free_lock); ++ for (i = 0; i < SM_MAX_NUM_CMD_RSP_BLKS; i++) { ++ sema_init(&instance->free_blk[i].sema, 0); ++ list_add(&instance->free_blk[i].head, &instance->free_list); ++ } ++ ++ /* Open the VCHI service connections */ ++ instance->num_connections = num_connections; ++ for (i = 0; i < num_connections; i++) { ++ SERVICE_CREATION_T params = { ++ VCHI_VERSION_EX(VC_SM_VER, VC_SM_MIN_VER), ++ VC_SM_SERVER_NAME, ++ vchi_connections[i], ++ 0, ++ 0, ++ vc_sm_vchi_callback, ++ instance, ++ 0, ++ 0, ++ 0, ++ }; ++ ++ status = vchi_service_open(vchi_instance, ++ ¶ms, &instance->vchi_handle[i]); ++ if (status) { ++ pr_err("%s: failed to open VCHI service (%d)", ++ __func__, status); ++ ++ goto err_close_services; ++ } ++ } ++ ++ /* Create the thread which takes care of all io to/from videoocore. */ ++ instance->io_thread = kthread_create(&vc_vchi_sm_videocore_io, ++ (void *)instance, "SMIO"); ++ if (instance->io_thread == NULL) { ++ pr_err("%s: failed to create SMIO thread", __func__); ++ ++ goto err_close_services; ++ } ++ set_user_nice(instance->io_thread, -10); ++ wake_up_process(instance->io_thread); ++ ++ pr_debug("%s: success - instance 0x%x", __func__, (unsigned)instance); ++ return instance; ++ ++err_close_services: ++ for (i = 0; i < instance->num_connections; i++) { ++ if (instance->vchi_handle[i] != NULL) ++ vchi_service_close(instance->vchi_handle[i]); ++ } ++ kfree(instance); ++err_null: ++ pr_debug("%s: FAILED", __func__); ++ return NULL; ++} ++ ++int vc_vchi_sm_stop(VC_VCHI_SM_HANDLE_T *handle) ++{ ++ struct sm_instance *instance; ++ uint32_t i; ++ ++ if (handle == NULL) { ++ pr_err("%s: invalid pointer to handle %p", __func__, handle); ++ goto lock; ++ } ++ ++ if (*handle == NULL) { ++ pr_err("%s: invalid handle %p", __func__, *handle); ++ goto lock; ++ } ++ ++ instance = *handle; ++ ++ /* Close all VCHI service connections */ ++ for (i = 0; i < instance->num_connections; i++) { ++ int32_t success; ++ vchi_service_use(instance->vchi_handle[i]); ++ ++ success = vchi_service_close(instance->vchi_handle[i]); ++ } ++ ++ kfree(instance); ++ ++ *handle = NULL; ++ return 0; ++ ++lock: ++ return -EINVAL; ++} ++ ++int vc_vchi_sm_send_msg(VC_VCHI_SM_HANDLE_T handle, ++ VC_SM_MSG_TYPE msg_id, ++ void *msg, uint32_t msg_size, ++ void *result, uint32_t result_size, ++ uint32_t *cur_trans_id, uint8_t wait_reply) ++{ ++ int status = 0; ++ struct sm_instance *instance = handle; ++ struct sm_cmd_rsp_blk *cmd_blk; ++ ++ if (handle == NULL) { ++ pr_err("%s: invalid handle", __func__); ++ return -EINVAL; ++ } ++ if (msg == NULL) { ++ pr_err("%s: invalid msg pointer", __func__); ++ return -EINVAL; ++ } ++ ++ cmd_blk = ++ vc_vchi_cmd_create(instance, msg_id, msg, msg_size, wait_reply); ++ if (cmd_blk == NULL) { ++ pr_err("[%s]: failed to allocate global tracking resource", ++ __func__); ++ return -ENOMEM; ++ } ++ ++ if (cur_trans_id != NULL) ++ *cur_trans_id = cmd_blk->id; ++ ++ mutex_lock(&instance->lock); ++ list_add_tail(&cmd_blk->head, &instance->cmd_list); ++ mutex_unlock(&instance->lock); ++ up(&instance->io_sema); ++ ++ if (!wait_reply) ++ /* We're done */ ++ return 0; ++ ++ /* Wait for the response */ ++ if (down_interruptible(&cmd_blk->sema)) { ++ mutex_lock(&instance->lock); ++ if (!cmd_blk->sent) { ++ list_del(&cmd_blk->head); ++ mutex_unlock(&instance->lock); ++ vc_vchi_cmd_delete(instance, cmd_blk); ++ return -ENXIO; ++ } ++ mutex_unlock(&instance->lock); ++ ++ mutex_lock(&instance->lock); ++ list_move(&cmd_blk->head, &instance->dead_list); ++ mutex_unlock(&instance->lock); ++ up(&instance->io_sema); ++ return -EINTR; /* We're done */ ++ } ++ ++ if (result && result_size) { ++ memcpy(result, cmd_blk->msg, result_size); ++ } else { ++ VC_SM_RESULT_T *res = (VC_SM_RESULT_T *) cmd_blk->msg; ++ status = (res->success == 0) ? 0 : -ENXIO; ++ } ++ ++ mutex_lock(&instance->lock); ++ list_del(&cmd_blk->head); ++ mutex_unlock(&instance->lock); ++ vc_vchi_cmd_delete(instance, cmd_blk); ++ return status; ++} ++ ++int vc_vchi_sm_alloc(VC_VCHI_SM_HANDLE_T handle, VC_SM_ALLOC_T *msg, ++ VC_SM_ALLOC_RESULT_T *result, uint32_t *cur_trans_id) ++{ ++ return vc_vchi_sm_send_msg(handle, VC_SM_MSG_TYPE_ALLOC, ++ msg, sizeof(*msg), result, sizeof(*result), ++ cur_trans_id, 1); ++} ++ ++int vc_vchi_sm_free(VC_VCHI_SM_HANDLE_T handle, ++ VC_SM_FREE_T *msg, uint32_t *cur_trans_id) ++{ ++ return vc_vchi_sm_send_msg(handle, VC_SM_MSG_TYPE_FREE, ++ msg, sizeof(*msg), 0, 0, cur_trans_id, 0); ++} ++ ++int vc_vchi_sm_lock(VC_VCHI_SM_HANDLE_T handle, ++ VC_SM_LOCK_UNLOCK_T *msg, ++ VC_SM_LOCK_RESULT_T *result, uint32_t *cur_trans_id) ++{ ++ return vc_vchi_sm_send_msg(handle, VC_SM_MSG_TYPE_LOCK, ++ msg, sizeof(*msg), result, sizeof(*result), ++ cur_trans_id, 1); ++} ++ ++int vc_vchi_sm_unlock(VC_VCHI_SM_HANDLE_T handle, ++ VC_SM_LOCK_UNLOCK_T *msg, ++ uint32_t *cur_trans_id, uint8_t wait_reply) ++{ ++ return vc_vchi_sm_send_msg(handle, wait_reply ? ++ VC_SM_MSG_TYPE_UNLOCK : ++ VC_SM_MSG_TYPE_UNLOCK_NOANS, msg, ++ sizeof(*msg), 0, 0, cur_trans_id, ++ wait_reply); ++} ++ ++int vc_vchi_sm_resize(VC_VCHI_SM_HANDLE_T handle, VC_SM_RESIZE_T *msg, ++ uint32_t *cur_trans_id) ++{ ++ return vc_vchi_sm_send_msg(handle, VC_SM_MSG_TYPE_RESIZE, ++ msg, sizeof(*msg), 0, 0, cur_trans_id, 1); ++} ++ ++int vc_vchi_sm_walk_alloc(VC_VCHI_SM_HANDLE_T handle) ++{ ++ return vc_vchi_sm_send_msg(handle, VC_SM_MSG_TYPE_WALK_ALLOC, ++ 0, 0, 0, 0, 0, 0); ++} ++ ++int vc_vchi_sm_clean_up(VC_VCHI_SM_HANDLE_T handle, VC_SM_ACTION_CLEAN_T *msg) ++{ ++ return vc_vchi_sm_send_msg(handle, VC_SM_MSG_TYPE_ACTION_CLEAN, ++ msg, sizeof(*msg), 0, 0, 0, 0); ++} +--- /dev/null ++++ b/drivers/char/broadcom/vc_sm/vc_vchi_sm.h +@@ -0,0 +1,82 @@ ++/***************************************************************************** ++* 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. ++*****************************************************************************/ ++ ++#ifndef __VC_VCHI_SM_H__INCLUDED__ ++#define __VC_VCHI_SM_H__INCLUDED__ ++ ++#include "interface/vchi/vchi.h" ++ ++#include "vc_sm_defs.h" ++ ++/* Forward declare. ++*/ ++typedef struct sm_instance *VC_VCHI_SM_HANDLE_T; ++ ++/* Initialize the shared memory service, opens up vchi connection to talk to it. ++*/ ++VC_VCHI_SM_HANDLE_T vc_vchi_sm_init(VCHI_INSTANCE_T vchi_instance, ++ VCHI_CONNECTION_T **vchi_connections, ++ uint32_t num_connections); ++ ++/* Terminates the shared memory service. ++*/ ++int vc_vchi_sm_stop(VC_VCHI_SM_HANDLE_T *handle); ++ ++/* Ask the shared memory service to allocate some memory on videocre and ++** return the result of this allocation (which upon success will be a pointer ++** to some memory in videocore space). ++*/ ++int vc_vchi_sm_alloc(VC_VCHI_SM_HANDLE_T handle, ++ VC_SM_ALLOC_T *alloc, ++ VC_SM_ALLOC_RESULT_T *alloc_result, uint32_t *trans_id); ++ ++/* Ask the shared memory service to free up some memory that was previously ++** allocated by the vc_vchi_sm_alloc function call. ++*/ ++int vc_vchi_sm_free(VC_VCHI_SM_HANDLE_T handle, ++ VC_SM_FREE_T *free, uint32_t *trans_id); ++ ++/* Ask the shared memory service to lock up some memory that was previously ++** allocated by the vc_vchi_sm_alloc function call. ++*/ ++int vc_vchi_sm_lock(VC_VCHI_SM_HANDLE_T handle, ++ VC_SM_LOCK_UNLOCK_T *lock_unlock, ++ VC_SM_LOCK_RESULT_T *lock_result, uint32_t *trans_id); ++ ++/* Ask the shared memory service to unlock some memory that was previously ++** allocated by the vc_vchi_sm_alloc function call. ++*/ ++int vc_vchi_sm_unlock(VC_VCHI_SM_HANDLE_T handle, ++ VC_SM_LOCK_UNLOCK_T *lock_unlock, ++ uint32_t *trans_id, uint8_t wait_reply); ++ ++/* Ask the shared memory service to resize some memory that was previously ++** allocated by the vc_vchi_sm_alloc function call. ++*/ ++int vc_vchi_sm_resize(VC_VCHI_SM_HANDLE_T handle, ++ VC_SM_RESIZE_T *resize, uint32_t *trans_id); ++ ++/* Walk the allocated resources on the videocore side, the allocation will ++** show up in the log. This is purely for debug/information and takes no ++** specific actions. ++*/ ++int vc_vchi_sm_walk_alloc(VC_VCHI_SM_HANDLE_T handle); ++ ++/* Clean up following a previously interrupted action which left the system ++** in a bad state of some sort. ++*/ ++int vc_vchi_sm_clean_up(VC_VCHI_SM_HANDLE_T handle, ++ VC_SM_ACTION_CLEAN_T *action_clean); ++ ++#endif /* __VC_VCHI_SM_H__INCLUDED__ */ +--- /dev/null ++++ b/drivers/char/broadcom/vc_sm/vmcs_sm.c +@@ -0,0 +1,3211 @@ ++/***************************************************************************** ++* Copyright 2011-2012 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 Files ----------------------------------------------------- */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "vchiq_connected.h" ++#include "vc_vchi_sm.h" ++ ++#include ++#include "vc_sm_knl.h" ++ ++/* ---- Private Constants and Types --------------------------------------- */ ++ ++#define DEVICE_NAME "vcsm" ++#define DEVICE_MINOR 0 ++ ++#define VC_SM_DIR_ROOT_NAME "vc-smem" ++#define VC_SM_DIR_ALLOC_NAME "alloc" ++#define VC_SM_STATE "state" ++#define VC_SM_STATS "statistics" ++#define VC_SM_RESOURCES "resources" ++#define VC_SM_DEBUG "debug" ++#define VC_SM_WRITE_BUF_SIZE 128 ++ ++/* Statistics tracked per resource and globally. ++*/ ++enum SM_STATS_T { ++ /* Attempt. */ ++ ALLOC, ++ FREE, ++ LOCK, ++ UNLOCK, ++ MAP, ++ FLUSH, ++ INVALID, ++ ++ END_ATTEMPT, ++ ++ /* Failure. */ ++ ALLOC_FAIL, ++ FREE_FAIL, ++ LOCK_FAIL, ++ UNLOCK_FAIL, ++ MAP_FAIL, ++ FLUSH_FAIL, ++ INVALID_FAIL, ++ ++ END_ALL, ++ ++}; ++ ++static const char *const sm_stats_human_read[] = { ++ "Alloc", ++ "Free", ++ "Lock", ++ "Unlock", ++ "Map", ++ "Cache Flush", ++ "Cache Invalidate", ++}; ++ ++typedef int (*VC_SM_SHOW) (struct seq_file *s, void *v); ++struct SM_PDE_T { ++ VC_SM_SHOW show; /* Debug fs function hookup. */ ++ struct dentry *dir_entry; /* Debug fs directory entry. */ ++ void *priv_data; /* Private data */ ++ ++}; ++ ++/* Single resource allocation tracked for all devices. ++*/ ++struct sm_mmap { ++ struct list_head map_list; /* Linked list of maps. */ ++ ++ struct SM_RESOURCE_T *resource; /* Pointer to the resource. */ ++ ++ pid_t res_pid; /* PID owning that resource. */ ++ unsigned int res_vc_hdl; /* Resource handle (videocore). */ ++ unsigned int res_usr_hdl; /* Resource handle (user). */ ++ ++ long unsigned int res_addr; /* Mapped virtual address. */ ++ struct vm_area_struct *vma; /* VM area for this mapping. */ ++ unsigned int ref_count; /* Reference count to this vma. */ ++ ++ /* Used to link maps associated with a resource. */ ++ struct list_head resource_map_list; ++}; ++ ++/* Single resource allocation tracked for each opened device. ++*/ ++struct SM_RESOURCE_T { ++ struct list_head resource_list; /* List of resources. */ ++ struct list_head global_resource_list; /* Global list of resources. */ ++ ++ pid_t pid; /* PID owning that resource. */ ++ uint32_t res_guid; /* Unique identifier. */ ++ uint32_t lock_count; /* Lock count for this resource. */ ++ uint32_t ref_count; /* Ref count for this resource. */ ++ ++ uint32_t res_handle; /* Resource allocation handle. */ ++ void *res_base_mem; /* Resource base memory address. */ ++ uint32_t res_size; /* Resource size allocated. */ ++ enum vmcs_sm_cache_e res_cached; /* Resource cache type. */ ++ struct SM_RESOURCE_T *res_shared; /* Shared resource */ ++ ++ enum SM_STATS_T res_stats[END_ALL]; /* Resource statistics. */ ++ ++ uint8_t map_count; /* Counter of mappings for this resource. */ ++ struct list_head map_list; /* Maps associated with a resource. */ ++ ++ struct SM_PRIV_DATA_T *private; ++}; ++ ++/* Private file data associated with each opened device. ++*/ ++struct SM_PRIV_DATA_T { ++ struct list_head resource_list; /* List of resources. */ ++ ++ pid_t pid; /* PID of creator. */ ++ ++ struct dentry *dir_pid; /* Debug fs entries root. */ ++ struct SM_PDE_T dir_stats; /* Debug fs entries statistics sub-tree. */ ++ struct SM_PDE_T dir_res; /* Debug fs resource sub-tree. */ ++ ++ int restart_sys; /* Tracks restart on interrupt. */ ++ VC_SM_MSG_TYPE int_action; /* Interrupted action. */ ++ uint32_t int_trans_id; /* Interrupted transaction. */ ++ ++}; ++ ++/* Global state information. ++*/ ++struct SM_STATE_T { ++ VC_VCHI_SM_HANDLE_T sm_handle; /* Handle for videocore service. */ ++ struct dentry *dir_root; /* Debug fs entries root. */ ++ struct dentry *dir_alloc; /* Debug fs entries allocations. */ ++ struct SM_PDE_T dir_stats; /* Debug fs entries statistics sub-tree. */ ++ struct SM_PDE_T dir_state; /* Debug fs entries state sub-tree. */ ++ struct dentry *debug; /* Debug fs entries debug. */ ++ ++ struct mutex map_lock; /* Global map lock. */ ++ struct list_head map_list; /* List of maps. */ ++ struct list_head resource_list; /* List of resources. */ ++ ++ enum SM_STATS_T deceased[END_ALL]; /* Natural termination stats. */ ++ enum SM_STATS_T terminated[END_ALL]; /* Forced termination stats. */ ++ uint32_t res_deceased_cnt; /* Natural termination counter. */ ++ uint32_t res_terminated_cnt; /* Forced termination counter. */ ++ ++ struct cdev sm_cdev; /* Device. */ ++ dev_t sm_devid; /* Device identifier. */ ++ struct class *sm_class; /* Class. */ ++ struct device *sm_dev; /* Device. */ ++ ++ struct SM_PRIV_DATA_T *data_knl; /* Kernel internal data tracking. */ ++ ++ struct mutex lock; /* Global lock. */ ++ uint32_t guid; /* GUID (next) tracker. */ ++ ++}; ++ ++/* ---- Private Variables ----------------------------------------------- */ ++ ++static struct SM_STATE_T *sm_state; ++static int sm_inited; ++ ++static const char *const sm_cache_map_vector[] = { ++ "(null)", ++ "host", ++ "videocore", ++ "host+videocore", ++}; ++ ++/* ---- Private Function Prototypes -------------------------------------- */ ++ ++/* ---- Private Functions ------------------------------------------------ */ ++ ++static inline unsigned vcaddr_to_pfn(unsigned long vc_addr) ++{ ++ unsigned long pfn = vc_addr & 0x3FFFFFFF; ++ pfn += mm_vc_mem_phys_addr; ++ pfn >>= PAGE_SHIFT; ++ return pfn; ++} ++ ++/* Carries over to the state statistics the statistics once owned by a deceased ++** resource. ++*/ ++static void vc_sm_resource_deceased(struct SM_RESOURCE_T *p_res, int terminated) ++{ ++ if (sm_state != NULL) { ++ if (p_res != NULL) { ++ int ix; ++ ++ if (terminated) ++ sm_state->res_terminated_cnt++; ++ else ++ sm_state->res_deceased_cnt++; ++ ++ for (ix = 0; ix < END_ALL; ix++) { ++ if (terminated) ++ sm_state->terminated[ix] += ++ p_res->res_stats[ix]; ++ else ++ sm_state->deceased[ix] += ++ p_res->res_stats[ix]; ++ } ++ } ++ } ++} ++ ++/* Fetch a videocore handle corresponding to a mapping of the pid+address ++** returns 0 (ie NULL) if no such handle exists in the global map. ++*/ ++static unsigned int vmcs_sm_vc_handle_from_pid_and_address(unsigned int pid, ++ unsigned int addr) ++{ ++ struct sm_mmap *map = NULL; ++ unsigned int handle = 0; ++ ++ if (!sm_state || addr == 0) ++ goto out; ++ ++ mutex_lock(&(sm_state->map_lock)); ++ ++ /* Lookup the resource. ++ */ ++ if (!list_empty(&sm_state->map_list)) { ++ list_for_each_entry(map, &sm_state->map_list, map_list) { ++ if (map->res_pid != pid || map->res_addr != addr) ++ continue; ++ ++ pr_debug("[%s]: global map %p (pid %u, addr %lx) -> vc-hdl %x (usr-hdl %x)\n", ++ __func__, map, map->res_pid, map->res_addr, ++ map->res_vc_hdl, map->res_usr_hdl); ++ ++ handle = map->res_vc_hdl; ++ break; ++ } ++ } ++ ++ mutex_unlock(&(sm_state->map_lock)); ++ ++out: ++ /* Use a debug log here as it may be a valid situation that we query ++ ** for something that is not mapped, we do not want a kernel log each ++ ** time around. ++ ** ++ ** There are other error log that would pop up accordingly if someone ++ ** subsequently tries to use something invalid after being told not to ++ ** use it... ++ */ ++ if (handle == 0) { ++ pr_debug("[%s]: not a valid map (pid %u, addr %x)\n", ++ __func__, pid, addr); ++ } ++ ++ return handle; ++} ++ ++/* Fetch a user handle corresponding to a mapping of the pid+address ++** returns 0 (ie NULL) if no such handle exists in the global map. ++*/ ++static unsigned int vmcs_sm_usr_handle_from_pid_and_address(unsigned int pid, ++ unsigned int addr) ++{ ++ struct sm_mmap *map = NULL; ++ unsigned int handle = 0; ++ ++ if (!sm_state || addr == 0) ++ goto out; ++ ++ mutex_lock(&(sm_state->map_lock)); ++ ++ /* Lookup the resource. ++ */ ++ if (!list_empty(&sm_state->map_list)) { ++ list_for_each_entry(map, &sm_state->map_list, map_list) { ++ if (map->res_pid != pid || map->res_addr != addr) ++ continue; ++ ++ pr_debug("[%s]: global map %p (pid %u, addr %lx) -> usr-hdl %x (vc-hdl %x)\n", ++ __func__, map, map->res_pid, map->res_addr, ++ map->res_usr_hdl, map->res_vc_hdl); ++ ++ handle = map->res_usr_hdl; ++ break; ++ } ++ } ++ ++ mutex_unlock(&(sm_state->map_lock)); ++ ++out: ++ /* Use a debug log here as it may be a valid situation that we query ++ * for something that is not mapped yet. ++ * ++ * There are other error log that would pop up accordingly if someone ++ * subsequently tries to use something invalid after being told not to ++ * use it... ++ */ ++ if (handle == 0) ++ pr_debug("[%s]: not a valid map (pid %u, addr %x)\n", ++ __func__, pid, addr); ++ ++ return handle; ++} ++ ++#if defined(DO_NOT_USE) ++/* Fetch an address corresponding to a mapping of the pid+handle ++** returns 0 (ie NULL) if no such address exists in the global map. ++*/ ++static unsigned int vmcs_sm_usr_address_from_pid_and_vc_handle(unsigned int pid, ++ unsigned int hdl) ++{ ++ struct sm_mmap *map = NULL; ++ unsigned int addr = 0; ++ ++ if (sm_state == NULL || hdl == 0) ++ goto out; ++ ++ mutex_lock(&(sm_state->map_lock)); ++ ++ /* Lookup the resource. ++ */ ++ if (!list_empty(&sm_state->map_list)) { ++ list_for_each_entry(map, &sm_state->map_list, map_list) { ++ if (map->res_pid != pid || map->res_vc_hdl != hdl) ++ continue; ++ ++ pr_debug("[%s]: global map %p (pid %u, vc-hdl %x, usr-hdl %x) -> addr %lx\n", ++ __func__, map, map->res_pid, map->res_vc_hdl, ++ map->res_usr_hdl, map->res_addr); ++ ++ addr = map->res_addr; ++ break; ++ } ++ } ++ ++ mutex_unlock(&(sm_state->map_lock)); ++ ++out: ++ /* Use a debug log here as it may be a valid situation that we query ++ ** for something that is not mapped, we do not want a kernel log each ++ ** time around. ++ ** ++ ** There are other error log that would pop up accordingly if someone ++ ** subsequently tries to use something invalid after being told not to ++ ** use it... ++ */ ++ if (addr == 0) ++ pr_debug("[%s]: not a valid map (pid %u, hdl %x)\n", ++ __func__, pid, hdl); ++ ++ return addr; ++} ++#endif ++ ++/* Fetch an address corresponding to a mapping of the pid+handle ++** returns 0 (ie NULL) if no such address exists in the global map. ++*/ ++static unsigned int vmcs_sm_usr_address_from_pid_and_usr_handle(unsigned int ++ pid, ++ unsigned int ++ hdl) ++{ ++ struct sm_mmap *map = NULL; ++ unsigned int addr = 0; ++ ++ if (sm_state == NULL || hdl == 0) ++ goto out; ++ ++ mutex_lock(&(sm_state->map_lock)); ++ ++ /* Lookup the resource. ++ */ ++ if (!list_empty(&sm_state->map_list)) { ++ list_for_each_entry(map, &sm_state->map_list, map_list) { ++ if (map->res_pid != pid || map->res_usr_hdl != hdl) ++ continue; ++ ++ pr_debug("[%s]: global map %p (pid %u, vc-hdl %x, usr-hdl %x) -> addr %lx\n", ++ __func__, map, map->res_pid, map->res_vc_hdl, ++ map->res_usr_hdl, map->res_addr); ++ ++ addr = map->res_addr; ++ break; ++ } ++ } ++ ++ mutex_unlock(&(sm_state->map_lock)); ++ ++out: ++ /* Use a debug log here as it may be a valid situation that we query ++ * for something that is not mapped, we do not want a kernel log each ++ * time around. ++ * ++ * There are other error log that would pop up accordingly if someone ++ * subsequently tries to use something invalid after being told not to ++ * use it... ++ */ ++ if (addr == 0) ++ pr_debug("[%s]: not a valid map (pid %u, hdl %x)\n", __func__, ++ pid, hdl); ++ ++ return addr; ++} ++ ++/* Adds a resource mapping to the global data list. ++*/ ++static void vmcs_sm_add_map(struct SM_STATE_T *state, ++ struct SM_RESOURCE_T *resource, struct sm_mmap *map) ++{ ++ mutex_lock(&(state->map_lock)); ++ ++ /* Add to the global list of mappings ++ */ ++ list_add(&map->map_list, &state->map_list); ++ ++ /* Add to the list of mappings for this resource ++ */ ++ list_add(&map->resource_map_list, &resource->map_list); ++ resource->map_count++; ++ ++ mutex_unlock(&(state->map_lock)); ++ ++ pr_debug("[%s]: added map %p (pid %u, vc-hdl %x, usr-hdl %x, addr %lx)\n", ++ __func__, map, map->res_pid, map->res_vc_hdl, ++ map->res_usr_hdl, map->res_addr); ++} ++ ++/* Removes a resource mapping from the global data list. ++*/ ++static void vmcs_sm_remove_map(struct SM_STATE_T *state, ++ struct SM_RESOURCE_T *resource, ++ struct sm_mmap *map) ++{ ++ mutex_lock(&(state->map_lock)); ++ ++ /* Remove from the global list of mappings ++ */ ++ list_del(&map->map_list); ++ ++ /* Remove from the list of mapping for this resource ++ */ ++ list_del(&map->resource_map_list); ++ if (resource->map_count > 0) ++ resource->map_count--; ++ ++ mutex_unlock(&(state->map_lock)); ++ ++ pr_debug("[%s]: removed map %p (pid %d, vc-hdl %x, usr-hdl %x, addr %lx)\n", ++ __func__, map, map->res_pid, map->res_vc_hdl, map->res_usr_hdl, ++ map->res_addr); ++ ++ kfree(map); ++} ++ ++/* Read callback for the global state proc entry. ++*/ ++static int vc_sm_global_state_show(struct seq_file *s, void *v) ++{ ++ struct sm_mmap *map = NULL; ++ int map_count = 0; ++ ++ if (sm_state == NULL) ++ return 0; ++ ++ seq_printf(s, "\nVC-ServiceHandle 0x%x\n", ++ (unsigned int)sm_state->sm_handle); ++ ++ /* Log all applicable mapping(s). ++ */ ++ ++ mutex_lock(&(sm_state->map_lock)); ++ ++ if (!list_empty(&sm_state->map_list)) { ++ list_for_each_entry(map, &sm_state->map_list, map_list) { ++ map_count++; ++ ++ seq_printf(s, "\nMapping 0x%x\n", ++ (unsigned int)map); ++ seq_printf(s, " TGID %u\n", ++ map->res_pid); ++ seq_printf(s, " VC-HDL 0x%x\n", ++ map->res_vc_hdl); ++ seq_printf(s, " USR-HDL 0x%x\n", ++ map->res_usr_hdl); ++ seq_printf(s, " USR-ADDR 0x%lx\n", ++ map->res_addr); ++ } ++ } ++ ++ mutex_unlock(&(sm_state->map_lock)); ++ seq_printf(s, "\n\nTotal map count: %d\n\n", map_count); ++ ++ return 0; ++} ++ ++static int vc_sm_global_statistics_show(struct seq_file *s, void *v) ++{ ++ int ix; ++ ++ /* Global state tracked statistics. ++ */ ++ if (sm_state != NULL) { ++ seq_puts(s, "\nDeceased Resources Statistics\n"); ++ ++ seq_printf(s, "\nNatural Cause (%u occurences)\n", ++ sm_state->res_deceased_cnt); ++ for (ix = 0; ix < END_ATTEMPT; ix++) { ++ if (sm_state->deceased[ix] > 0) { ++ seq_printf(s, " %u\t%s\n", ++ sm_state->deceased[ix], ++ sm_stats_human_read[ix]); ++ } ++ } ++ seq_puts(s, "\n"); ++ for (ix = 0; ix < END_ATTEMPT; ix++) { ++ if (sm_state->deceased[ix + END_ATTEMPT] > 0) { ++ seq_printf(s, " %u\tFAILED %s\n", ++ sm_state->deceased[ix + END_ATTEMPT], ++ sm_stats_human_read[ix]); ++ } ++ } ++ ++ seq_printf(s, "\nForcefull (%u occurences)\n", ++ sm_state->res_terminated_cnt); ++ for (ix = 0; ix < END_ATTEMPT; ix++) { ++ if (sm_state->terminated[ix] > 0) { ++ seq_printf(s, " %u\t%s\n", ++ sm_state->terminated[ix], ++ sm_stats_human_read[ix]); ++ } ++ } ++ seq_puts(s, "\n"); ++ for (ix = 0; ix < END_ATTEMPT; ix++) { ++ if (sm_state->terminated[ix + END_ATTEMPT] > 0) { ++ seq_printf(s, " %u\tFAILED %s\n", ++ sm_state->terminated[ix + ++ END_ATTEMPT], ++ sm_stats_human_read[ix]); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++#if 0 ++/* Read callback for the statistics proc entry. ++*/ ++static int vc_sm_statistics_show(struct seq_file *s, void *v) ++{ ++ int ix; ++ struct SM_PRIV_DATA_T *file_data; ++ struct SM_RESOURCE_T *resource; ++ int res_count = 0; ++ struct SM_PDE_T *p_pde; ++ ++ p_pde = (struct SM_PDE_T *)(s->private); ++ file_data = (struct SM_PRIV_DATA_T *)(p_pde->priv_data); ++ ++ if (file_data == NULL) ++ return 0; ++ ++ /* Per process statistics. ++ */ ++ ++ seq_printf(s, "\nStatistics for TGID %d\n", file_data->pid); ++ ++ mutex_lock(&(sm_state->map_lock)); ++ ++ if (!list_empty(&file_data->resource_list)) { ++ list_for_each_entry(resource, &file_data->resource_list, ++ resource_list) { ++ res_count++; ++ ++ seq_printf(s, "\nGUID: 0x%x\n\n", ++ resource->res_guid); ++ for (ix = 0; ix < END_ATTEMPT; ix++) { ++ if (resource->res_stats[ix] > 0) { ++ seq_printf(s, ++ " %u\t%s\n", ++ resource->res_stats[ix], ++ sm_stats_human_read[ix]); ++ } ++ } ++ seq_puts(s, "\n"); ++ for (ix = 0; ix < END_ATTEMPT; ix++) { ++ if (resource->res_stats[ix + END_ATTEMPT] > 0) { ++ seq_printf(s, ++ " %u\tFAILED %s\n", ++ resource->res_stats[ ++ ix + END_ATTEMPT], ++ sm_stats_human_read[ix]); ++ } ++ } ++ } ++ } ++ ++ mutex_unlock(&(sm_state->map_lock)); ++ ++ seq_printf(s, "\nResources Count %d\n", res_count); ++ ++ return 0; ++} ++#endif ++ ++#if 0 ++/* Read callback for the allocation proc entry. */ ++static int vc_sm_alloc_show(struct seq_file *s, void *v) ++{ ++ struct SM_PRIV_DATA_T *file_data; ++ struct SM_RESOURCE_T *resource; ++ int alloc_count = 0; ++ struct SM_PDE_T *p_pde; ++ ++ p_pde = (struct SM_PDE_T *)(s->private); ++ file_data = (struct SM_PRIV_DATA_T *)(p_pde->priv_data); ++ ++ if (!file_data) ++ return 0; ++ ++ /* Per process statistics. */ ++ seq_printf(s, "\nAllocation for TGID %d\n", file_data->pid); ++ ++ mutex_lock(&(sm_state->map_lock)); ++ ++ if (!list_empty(&file_data->resource_list)) { ++ list_for_each_entry(resource, &file_data->resource_list, ++ resource_list) { ++ alloc_count++; ++ ++ seq_printf(s, "\nGUID: 0x%x\n", ++ resource->res_guid); ++ seq_printf(s, "Lock Count: %u\n", ++ resource->lock_count); ++ seq_printf(s, "Mapped: %s\n", ++ (resource->map_count ? "yes" : "no")); ++ seq_printf(s, "VC-handle: 0x%x\n", ++ resource->res_handle); ++ seq_printf(s, "VC-address: 0x%p\n", ++ resource->res_base_mem); ++ seq_printf(s, "VC-size (bytes): %u\n", ++ resource->res_size); ++ seq_printf(s, "Cache: %s\n", ++ sm_cache_map_vector[resource->res_cached]); ++ } ++ } ++ ++ mutex_unlock(&(sm_state->map_lock)); ++ ++ seq_printf(s, "\n\nTotal allocation count: %d\n\n", alloc_count); ++ ++ return 0; ++} ++#endif ++ ++static int vc_sm_seq_file_show(struct seq_file *s, void *v) ++{ ++ struct SM_PDE_T *sm_pde; ++ ++ sm_pde = (struct SM_PDE_T *)(s->private); ++ ++ if (sm_pde && sm_pde->show) ++ sm_pde->show(s, v); ++ ++ return 0; ++} ++ ++static int vc_sm_single_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, vc_sm_seq_file_show, inode->i_private); ++} ++ ++static const struct file_operations vc_sm_debug_fs_fops = { ++ .open = vc_sm_single_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++/* Adds a resource to the private data list which tracks all the allocated ++** data. ++*/ ++static void vmcs_sm_add_resource(struct SM_PRIV_DATA_T *privdata, ++ struct SM_RESOURCE_T *resource) ++{ ++ mutex_lock(&(sm_state->map_lock)); ++ list_add(&resource->resource_list, &privdata->resource_list); ++ list_add(&resource->global_resource_list, &sm_state->resource_list); ++ mutex_unlock(&(sm_state->map_lock)); ++ ++ pr_debug("[%s]: added resource %p (base addr %p, hdl %x, size %u, cache %u)\n", ++ __func__, resource, resource->res_base_mem, ++ resource->res_handle, resource->res_size, resource->res_cached); ++} ++ ++/* Locates a resource and acquire a reference on it. ++** The resource won't be deleted while there is a reference on it. ++*/ ++static struct SM_RESOURCE_T *vmcs_sm_acquire_resource(struct SM_PRIV_DATA_T ++ *private, ++ unsigned int res_guid) ++{ ++ struct SM_RESOURCE_T *resource, *ret = NULL; ++ ++ mutex_lock(&(sm_state->map_lock)); ++ ++ list_for_each_entry(resource, &private->resource_list, resource_list) { ++ if (resource->res_guid != res_guid) ++ continue; ++ ++ pr_debug("[%s]: located resource %p (guid: %x, base addr %p, hdl %x, size %u, cache %u)\n", ++ __func__, resource, resource->res_guid, ++ resource->res_base_mem, resource->res_handle, ++ resource->res_size, resource->res_cached); ++ resource->ref_count++; ++ ret = resource; ++ break; ++ } ++ ++ mutex_unlock(&(sm_state->map_lock)); ++ ++ return ret; ++} ++ ++/* Locates a resource and acquire a reference on it. ++** The resource won't be deleted while there is a reference on it. ++*/ ++static struct SM_RESOURCE_T *vmcs_sm_acquire_first_resource( ++ struct SM_PRIV_DATA_T *private) ++{ ++ struct SM_RESOURCE_T *resource, *ret = NULL; ++ ++ mutex_lock(&(sm_state->map_lock)); ++ ++ list_for_each_entry(resource, &private->resource_list, resource_list) { ++ pr_debug("[%s]: located resource %p (guid: %x, base addr %p, hdl %x, size %u, cache %u)\n", ++ __func__, resource, resource->res_guid, ++ resource->res_base_mem, resource->res_handle, ++ resource->res_size, resource->res_cached); ++ resource->ref_count++; ++ ret = resource; ++ break; ++ } ++ ++ mutex_unlock(&(sm_state->map_lock)); ++ ++ return ret; ++} ++ ++/* Locates a resource and acquire a reference on it. ++** The resource won't be deleted while there is a reference on it. ++*/ ++static struct SM_RESOURCE_T *vmcs_sm_acquire_global_resource(unsigned int ++ res_guid) ++{ ++ struct SM_RESOURCE_T *resource, *ret = NULL; ++ ++ mutex_lock(&(sm_state->map_lock)); ++ ++ list_for_each_entry(resource, &sm_state->resource_list, ++ global_resource_list) { ++ if (resource->res_guid != res_guid) ++ continue; ++ ++ pr_debug("[%s]: located resource %p (guid: %x, base addr %p, hdl %x, size %u, cache %u)\n", ++ __func__, resource, resource->res_guid, ++ resource->res_base_mem, resource->res_handle, ++ resource->res_size, resource->res_cached); ++ resource->ref_count++; ++ ret = resource; ++ break; ++ } ++ ++ mutex_unlock(&(sm_state->map_lock)); ++ ++ return ret; ++} ++ ++/* Release a previously acquired resource. ++** The resource will be deleted when its refcount reaches 0. ++*/ ++static void vmcs_sm_release_resource(struct SM_RESOURCE_T *resource, int force) ++{ ++ struct SM_PRIV_DATA_T *private = resource->private; ++ struct sm_mmap *map, *map_tmp; ++ struct SM_RESOURCE_T *res_tmp; ++ int ret; ++ ++ mutex_lock(&(sm_state->map_lock)); ++ ++ if (--resource->ref_count) { ++ if (force) ++ pr_err("[%s]: resource %p in use\n", __func__, resource); ++ ++ mutex_unlock(&(sm_state->map_lock)); ++ return; ++ } ++ ++ /* Time to free the resource. Start by removing it from the list */ ++ list_del(&resource->resource_list); ++ list_del(&resource->global_resource_list); ++ ++ /* Walk the global resource list, find out if the resource is used ++ * somewhere else. In which case we don't want to delete it. ++ */ ++ list_for_each_entry(res_tmp, &sm_state->resource_list, ++ global_resource_list) { ++ if (res_tmp->res_handle == resource->res_handle) { ++ resource->res_handle = 0; ++ break; ++ } ++ } ++ ++ mutex_unlock(&(sm_state->map_lock)); ++ ++ pr_debug("[%s]: freeing data - guid %x, hdl %x, base address %p\n", ++ __func__, resource->res_guid, resource->res_handle, ++ resource->res_base_mem); ++ resource->res_stats[FREE]++; ++ ++ /* Make sure the resource we're removing is unmapped first */ ++ if (resource->map_count && !list_empty(&resource->map_list)) { ++ down_write(¤t->mm->mmap_sem); ++ list_for_each_entry_safe(map, map_tmp, &resource->map_list, ++ resource_map_list) { ++ ret = ++ do_munmap(current->mm, map->res_addr, ++ resource->res_size); ++ if (ret) { ++ pr_err("[%s]: could not unmap resource %p\n", ++ __func__, resource); ++ } ++ } ++ up_write(¤t->mm->mmap_sem); ++ } ++ ++ /* Free up the videocore allocated resource. ++ */ ++ if (resource->res_handle) { ++ VC_SM_FREE_T free = { ++ resource->res_handle, resource->res_base_mem ++ }; ++ int status = vc_vchi_sm_free(sm_state->sm_handle, &free, ++ &private->int_trans_id); ++ if (status != 0 && status != -EINTR) { ++ pr_err("[%s]: failed to free memory on videocore (status: %u, trans_id: %u)\n", ++ __func__, status, private->int_trans_id); ++ resource->res_stats[FREE_FAIL]++; ++ ret = -EPERM; ++ } ++ } ++ ++ /* Free up the shared resource. ++ */ ++ if (resource->res_shared) ++ vmcs_sm_release_resource(resource->res_shared, 0); ++ ++ /* Free up the local resource tracking this allocation. ++ */ ++ vc_sm_resource_deceased(resource, force); ++ kfree(resource); ++} ++ ++/* Dump the map table for the driver. If process is -1, dumps the whole table, ++** if process is a valid pid (non -1) dump only the entries associated with the ++** pid of interest. ++*/ ++static void vmcs_sm_host_walk_map_per_pid(int pid) ++{ ++ struct sm_mmap *map = NULL; ++ ++ /* Make sure the device was started properly. ++ */ ++ if (sm_state == NULL) { ++ pr_err("[%s]: invalid device\n", __func__); ++ return; ++ } ++ ++ mutex_lock(&(sm_state->map_lock)); ++ ++ /* Log all applicable mapping(s). ++ */ ++ if (!list_empty(&sm_state->map_list)) { ++ list_for_each_entry(map, &sm_state->map_list, map_list) { ++ if (pid == -1 || map->res_pid == pid) { ++ pr_info("[%s]: tgid: %u - vc-hdl: %x, usr-hdl: %x, usr-addr: %lx\n", ++ __func__, map->res_pid, map->res_vc_hdl, ++ map->res_usr_hdl, map->res_addr); ++ } ++ } ++ } ++ ++ mutex_unlock(&(sm_state->map_lock)); ++ ++ return; ++} ++ ++/* Dump the allocation table from host side point of view. This only dumps the ++** data allocated for this process/device referenced by the file_data. ++*/ ++static void vmcs_sm_host_walk_alloc(struct SM_PRIV_DATA_T *file_data) ++{ ++ struct SM_RESOURCE_T *resource = NULL; ++ ++ /* Make sure the device was started properly. ++ */ ++ if ((sm_state == NULL) || (file_data == NULL)) { ++ pr_err("[%s]: invalid device\n", __func__); ++ return; ++ } ++ ++ mutex_lock(&(sm_state->map_lock)); ++ ++ if (!list_empty(&file_data->resource_list)) { ++ list_for_each_entry(resource, &file_data->resource_list, ++ resource_list) { ++ pr_info("[%s]: guid: %x - hdl: %x, vc-mem: %p, size: %u, cache: %u\n", ++ __func__, resource->res_guid, resource->res_handle, ++ resource->res_base_mem, resource->res_size, ++ resource->res_cached); ++ } ++ } ++ ++ mutex_unlock(&(sm_state->map_lock)); ++ ++ return; ++} ++ ++/* Create support for private data tracking. ++*/ ++static struct SM_PRIV_DATA_T *vc_sm_create_priv_data(pid_t id) ++{ ++ char alloc_name[32]; ++ struct SM_PRIV_DATA_T *file_data = NULL; ++ ++ /* Allocate private structure. */ ++ file_data = kzalloc(sizeof(*file_data), GFP_KERNEL); ++ ++ if (!file_data) { ++ pr_err("[%s]: cannot allocate file data\n", __func__); ++ goto out; ++ } ++ ++ snprintf(alloc_name, sizeof(alloc_name), "%d", id); ++ ++ INIT_LIST_HEAD(&file_data->resource_list); ++ file_data->pid = id; ++ file_data->dir_pid = debugfs_create_dir(alloc_name, ++ sm_state->dir_alloc); ++#if 0 ++ /* TODO: fix this to support querying statistics per pid */ ++ ++ if (IS_ERR_OR_NULL(file_data->dir_pid)) { ++ file_data->dir_pid = NULL; ++ } else { ++ struct dentry *dir_entry; ++ ++ dir_entry = debugfs_create_file(VC_SM_RESOURCES, S_IRUGO, ++ file_data->dir_pid, file_data, ++ vc_sm_debug_fs_fops); ++ ++ file_data->dir_res.dir_entry = dir_entry; ++ file_data->dir_res.priv_data = file_data; ++ file_data->dir_res.show = &vc_sm_alloc_show; ++ ++ dir_entry = debugfs_create_file(VC_SM_STATS, S_IRUGO, ++ file_data->dir_pid, file_data, ++ vc_sm_debug_fs_fops); ++ ++ file_data->dir_res.dir_entry = dir_entry; ++ file_data->dir_res.priv_data = file_data; ++ file_data->dir_res.show = &vc_sm_statistics_show; ++ } ++ pr_debug("[%s]: private data allocated %p\n", __func__, file_data); ++ ++#endif ++out: ++ return file_data; ++} ++ ++/* Open the device. Creates a private state to help track all allocation ++** associated with this device. ++*/ ++static int vc_sm_open(struct inode *inode, struct file *file) ++{ ++ int ret = 0; ++ ++ /* Make sure the device was started properly. ++ */ ++ if (!sm_state) { ++ pr_err("[%s]: invalid device\n", __func__); ++ ret = -EPERM; ++ goto out; ++ } ++ ++ file->private_data = vc_sm_create_priv_data(current->tgid); ++ if (file->private_data == NULL) { ++ pr_err("[%s]: failed to create data tracker\n", __func__); ++ ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++out: ++ return ret; ++} ++ ++/* Close the device. Free up all resources still associated with this device ++** at the time. ++*/ ++static int vc_sm_release(struct inode *inode, struct file *file) ++{ ++ struct SM_PRIV_DATA_T *file_data = ++ (struct SM_PRIV_DATA_T *)file->private_data; ++ struct SM_RESOURCE_T *resource; ++ int ret = 0; ++ ++ /* Make sure the device was started properly. ++ */ ++ if (sm_state == NULL || file_data == NULL) { ++ pr_err("[%s]: invalid device\n", __func__); ++ ret = -EPERM; ++ goto out; ++ } ++ ++ pr_debug("[%s]: using private data %p\n", __func__, file_data); ++ ++ if (file_data->restart_sys == -EINTR) { ++ VC_SM_ACTION_CLEAN_T action_clean; ++ ++ pr_debug("[%s]: releasing following EINTR on %u (trans_id: %u) (likely due to signal)...\n", ++ __func__, file_data->int_action, ++ file_data->int_trans_id); ++ ++ action_clean.res_action = file_data->int_action; ++ action_clean.action_trans_id = file_data->int_trans_id; ++ ++ vc_vchi_sm_clean_up(sm_state->sm_handle, &action_clean); ++ } ++ ++ while ((resource = vmcs_sm_acquire_first_resource(file_data)) != NULL) { ++ vmcs_sm_release_resource(resource, 0); ++ vmcs_sm_release_resource(resource, 1); ++ } ++ ++ /* Remove the corresponding proc entry. */ ++ debugfs_remove_recursive(file_data->dir_pid); ++ ++ /* Terminate the private data. ++ */ ++ kfree(file_data); ++ ++out: ++ return ret; ++} ++ ++static void vcsm_vma_open(struct vm_area_struct *vma) ++{ ++ struct sm_mmap *map = (struct sm_mmap *)vma->vm_private_data; ++ ++ pr_debug("[%s]: virt %lx-%lx, pid %i, pfn %i\n", ++ __func__, vma->vm_start, vma->vm_end, (int)current->tgid, ++ (int)vma->vm_pgoff); ++ ++ map->ref_count++; ++} ++ ++static void vcsm_vma_close(struct vm_area_struct *vma) ++{ ++ struct sm_mmap *map = (struct sm_mmap *)vma->vm_private_data; ++ ++ pr_debug("[%s]: virt %lx-%lx, pid %i, pfn %i\n", ++ __func__, vma->vm_start, vma->vm_end, (int)current->tgid, ++ (int)vma->vm_pgoff); ++ ++ map->ref_count--; ++ ++ /* Remove from the map table. ++ */ ++ if (map->ref_count == 0) ++ vmcs_sm_remove_map(sm_state, map->resource, map); ++} ++ ++static int vcsm_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) ++{ ++ struct sm_mmap *map = (struct sm_mmap *)vma->vm_private_data; ++ struct SM_RESOURCE_T *resource = map->resource; ++ pgoff_t page_offset; ++ unsigned long pfn; ++ int ret = 0; ++ ++ /* Lock the resource if necessary. ++ */ ++ if (!resource->lock_count) { ++ VC_SM_LOCK_UNLOCK_T lock_unlock; ++ VC_SM_LOCK_RESULT_T lock_result; ++ int status; ++ ++ lock_unlock.res_handle = resource->res_handle; ++ lock_unlock.res_mem = resource->res_base_mem; ++ ++ pr_debug("[%s]: attempt to lock data - hdl %x, base address %p\n", ++ __func__, lock_unlock.res_handle, lock_unlock.res_mem); ++ ++ /* Lock the videocore allocated resource. ++ */ ++ status = vc_vchi_sm_lock(sm_state->sm_handle, ++ &lock_unlock, &lock_result, 0); ++ if ((status != 0) || ++ ((status == 0) && (lock_result.res_mem == NULL))) { ++ pr_err("[%s]: failed to lock memory on videocore (status: %u)\n", ++ __func__, status); ++ resource->res_stats[LOCK_FAIL]++; ++ return VM_FAULT_SIGBUS; ++ } ++ ++ pfn = vcaddr_to_pfn((unsigned long)resource->res_base_mem); ++ outer_inv_range(__pfn_to_phys(pfn), ++ __pfn_to_phys(pfn) + resource->res_size); ++ ++ resource->res_stats[LOCK]++; ++ resource->lock_count++; ++ ++ /* Keep track of the new base memory. ++ */ ++ if ((lock_result.res_mem != NULL) && ++ (lock_result.res_old_mem != NULL) && ++ (lock_result.res_mem != lock_result.res_old_mem)) { ++ resource->res_base_mem = lock_result.res_mem; ++ } ++ } ++ ++ /* We don't use vmf->pgoff since that has the fake offset */ ++ page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start); ++ pfn = (uint32_t)resource->res_base_mem & 0x3FFFFFFF; ++ pfn += mm_vc_mem_phys_addr; ++ pfn += page_offset; ++ pfn >>= PAGE_SHIFT; ++ ++ /* Finally, remap it */ ++ ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn); ++ ++ switch (ret) { ++ case 0: ++ case -ERESTARTSYS: ++ return VM_FAULT_NOPAGE; ++ case -ENOMEM: ++ case -EAGAIN: ++ return VM_FAULT_OOM; ++ default: ++ return VM_FAULT_SIGBUS; ++ } ++} ++ ++static struct vm_operations_struct vcsm_vm_ops = { ++ .open = vcsm_vma_open, ++ .close = vcsm_vma_close, ++ .fault = vcsm_vma_fault, ++}; ++ ++/* Walks a VMA and clean each valid page from the cache */ ++static void vcsm_vma_cache_clean_page_range(unsigned long addr, ++ unsigned long end) ++{ ++ pgd_t *pgd; ++ pud_t *pud; ++ pmd_t *pmd; ++ pte_t *pte; ++ unsigned long pgd_next, pud_next, pmd_next; ++ ++ if (addr >= end) ++ return; ++ ++ /* Walk PGD */ ++ pgd = pgd_offset(current->mm, addr); ++ do { ++ pgd_next = pgd_addr_end(addr, end); ++ ++ if (pgd_none(*pgd) || pgd_bad(*pgd)) ++ continue; ++ ++ /* Walk PUD */ ++ pud = pud_offset(pgd, addr); ++ do { ++ pud_next = pud_addr_end(addr, pgd_next); ++ if (pud_none(*pud) || pud_bad(*pud)) ++ continue; ++ ++ /* Walk PMD */ ++ pmd = pmd_offset(pud, addr); ++ do { ++ pmd_next = pmd_addr_end(addr, pud_next); ++ if (pmd_none(*pmd) || pmd_bad(*pmd)) ++ continue; ++ ++ /* Walk PTE */ ++ pte = pte_offset_map(pmd, addr); ++ do { ++ if (pte_none(*pte) ++ || !pte_present(*pte)) ++ continue; ++ ++ /* Clean + invalidate */ ++ dmac_flush_range((const void *) addr, ++ (const void *) ++ (addr + PAGE_SIZE)); ++ ++ } while (pte++, addr += ++ PAGE_SIZE, addr != pmd_next); ++ pte_unmap(pte); ++ ++ } while (pmd++, addr = pmd_next, addr != pud_next); ++ ++ } while (pud++, addr = pud_next, addr != pgd_next); ++ } while (pgd++, addr = pgd_next, addr != end); ++} ++ ++/* Map an allocated data into something that the user space. ++*/ ++static int vc_sm_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ int ret = 0; ++ struct SM_PRIV_DATA_T *file_data = ++ (struct SM_PRIV_DATA_T *)file->private_data; ++ struct SM_RESOURCE_T *resource = NULL; ++ struct sm_mmap *map = NULL; ++ ++ /* Make sure the device was started properly. ++ */ ++ if ((sm_state == NULL) || (file_data == NULL)) { ++ pr_err("[%s]: invalid device\n", __func__); ++ return -EPERM; ++ } ++ ++ pr_debug("[%s]: private data %p, guid %x\n", __func__, file_data, ++ ((unsigned int)vma->vm_pgoff << PAGE_SHIFT)); ++ ++ /* We lookup to make sure that the data we are being asked to mmap is ++ ** something that we allocated. ++ ** ++ ** We use the offset information as the key to tell us which resource ++ ** we are mapping. ++ */ ++ resource = vmcs_sm_acquire_resource(file_data, ++ ((unsigned int)vma->vm_pgoff << ++ PAGE_SHIFT)); ++ if (resource == NULL) { ++ pr_err("[%s]: failed to locate resource for guid %x\n", __func__, ++ ((unsigned int)vma->vm_pgoff << PAGE_SHIFT)); ++ return -ENOMEM; ++ } ++ ++ pr_debug("[%s]: guid %x, tgid %u, %u, %u\n", ++ __func__, resource->res_guid, current->tgid, resource->pid, ++ file_data->pid); ++ ++ /* Check permissions. ++ */ ++ if (resource->pid && (resource->pid != current->tgid)) { ++ pr_err("[%s]: current tgid %u != %u owner\n", ++ __func__, current->tgid, resource->pid); ++ ret = -EPERM; ++ goto error; ++ } ++ ++ /* Verify that what we are asked to mmap is proper. ++ */ ++ if (resource->res_size != (unsigned int)(vma->vm_end - vma->vm_start)) { ++ pr_err("[%s]: size inconsistency (resource: %u - mmap: %u)\n", ++ __func__, ++ resource->res_size, ++ (unsigned int)(vma->vm_end - vma->vm_start)); ++ ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ /* Keep track of the tuple in the global resource list such that one ++ * can do a mapping lookup for address/memory handle. ++ */ ++ map = kzalloc(sizeof(*map), GFP_KERNEL); ++ if (map == NULL) { ++ pr_err("[%s]: failed to allocate global tracking resource\n", ++ __func__); ++ ret = -ENOMEM; ++ goto error; ++ } ++ ++ map->res_pid = current->tgid; ++ map->res_vc_hdl = resource->res_handle; ++ map->res_usr_hdl = resource->res_guid; ++ map->res_addr = (long unsigned int)vma->vm_start; ++ map->resource = resource; ++ map->vma = vma; ++ vmcs_sm_add_map(sm_state, resource, map); ++ ++ /* We are not actually mapping the pages, we just provide a fault ++ ** handler to allow pages to be mapped when accessed ++ */ ++ vma->vm_flags |= ++ VM_IO | VM_PFNMAP | VM_DONTCOPY | VM_DONTEXPAND; ++ vma->vm_ops = &vcsm_vm_ops; ++ vma->vm_private_data = map; ++ ++ /* vm_pgoff is the first PFN of the mapped memory */ ++ vma->vm_pgoff = (unsigned long)resource->res_base_mem & 0x3FFFFFFF; ++ vma->vm_pgoff += mm_vc_mem_phys_addr; ++ vma->vm_pgoff >>= PAGE_SHIFT; ++ ++ if ((resource->res_cached == VMCS_SM_CACHE_NONE) || ++ (resource->res_cached == VMCS_SM_CACHE_VC)) { ++ /* Allocated non host cached memory, honour it. ++ */ ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ } ++ ++ pr_debug("[%s]: resource %p (guid %x) - cnt %u, base address %p, handle %x, size %u (%u), cache %u\n", ++ __func__, ++ resource, resource->res_guid, resource->lock_count, ++ resource->res_base_mem, resource->res_handle, ++ resource->res_size, (unsigned int)(vma->vm_end - vma->vm_start), ++ resource->res_cached); ++ ++ pr_debug("[%s]: resource %p (base address %p, handle %x) - map-count %d, usr-addr %x\n", ++ __func__, resource, resource->res_base_mem, ++ resource->res_handle, resource->map_count, ++ (unsigned int)vma->vm_start); ++ ++ vcsm_vma_open(vma); ++ resource->res_stats[MAP]++; ++ vmcs_sm_release_resource(resource, 0); ++ return 0; ++ ++error: ++ resource->res_stats[MAP_FAIL]++; ++ vmcs_sm_release_resource(resource, 0); ++ return ret; ++} ++ ++/* Allocate a shared memory handle and block. ++*/ ++int vc_sm_ioctl_alloc(struct SM_PRIV_DATA_T *private, ++ struct vmcs_sm_ioctl_alloc *ioparam) ++{ ++ int ret = 0; ++ int status; ++ struct SM_RESOURCE_T *resource; ++ VC_SM_ALLOC_T alloc = { 0 }; ++ VC_SM_ALLOC_RESULT_T result = { 0 }; ++ ++ /* Setup our allocation parameters */ ++ alloc.type = ((ioparam->cached == VMCS_SM_CACHE_VC) ++ || (ioparam->cached == ++ VMCS_SM_CACHE_BOTH)) ? VC_SM_ALLOC_CACHED : ++ VC_SM_ALLOC_NON_CACHED; ++ alloc.base_unit = ioparam->size; ++ alloc.num_unit = ioparam->num; ++ alloc.allocator = current->tgid; ++ /* Align to kernel page size */ ++ alloc.alignement = 4096; ++ /* Align the size to the kernel page size */ ++ alloc.base_unit = ++ (alloc.base_unit + alloc.alignement - 1) & ~(alloc.alignement - 1); ++ if (*ioparam->name) { ++ memcpy(alloc.name, ioparam->name, sizeof(alloc.name) - 1); ++ } else { ++ memcpy(alloc.name, VMCS_SM_RESOURCE_NAME_DEFAULT, ++ sizeof(VMCS_SM_RESOURCE_NAME_DEFAULT)); ++ } ++ ++ pr_debug("[%s]: attempt to allocate \"%s\" data - type %u, base %u (%u), num %u, alignement %u\n", ++ __func__, alloc.name, alloc.type, ioparam->size, ++ alloc.base_unit, alloc.num_unit, alloc.alignement); ++ ++ /* Allocate local resource to track this allocation. ++ */ ++ resource = kzalloc(sizeof(*resource), GFP_KERNEL); ++ if (!resource) { ++ ret = -ENOMEM; ++ goto error; ++ } ++ INIT_LIST_HEAD(&resource->map_list); ++ resource->ref_count++; ++ resource->pid = current->tgid; ++ ++ /* Allocate the videocore resource. ++ */ ++ status = vc_vchi_sm_alloc(sm_state->sm_handle, &alloc, &result, ++ &private->int_trans_id); ++ if (status == -EINTR) { ++ pr_debug("[%s]: requesting allocate memory action restart (trans_id: %u)\n", ++ __func__, private->int_trans_id); ++ ret = -ERESTARTSYS; ++ private->restart_sys = -EINTR; ++ private->int_action = VC_SM_MSG_TYPE_ALLOC; ++ goto error; ++ } else if (status != 0 || (status == 0 && result.res_mem == NULL)) { ++ pr_err("[%s]: failed to allocate memory on videocore (status: %u, trans_id: %u)\n", ++ __func__, status, private->int_trans_id); ++ ret = -ENOMEM; ++ resource->res_stats[ALLOC_FAIL]++; ++ goto error; ++ } ++ ++ /* Keep track of the resource we created. ++ */ ++ resource->private = private; ++ resource->res_handle = result.res_handle; ++ resource->res_base_mem = result.res_mem; ++ resource->res_size = alloc.base_unit * alloc.num_unit; ++ resource->res_cached = ioparam->cached; ++ ++ /* Kernel/user GUID. This global identifier is used for mmap'ing the ++ * allocated region from user space, it is passed as the mmap'ing ++ * offset, we use it to 'hide' the videocore handle/address. ++ */ ++ mutex_lock(&sm_state->lock); ++ resource->res_guid = ++sm_state->guid; ++ mutex_unlock(&sm_state->lock); ++ resource->res_guid <<= PAGE_SHIFT; ++ ++ vmcs_sm_add_resource(private, resource); ++ ++ pr_debug("[%s]: allocated data - guid %x, hdl %x, base address %p, size %d, cache %d\n", ++ __func__, resource->res_guid, resource->res_handle, ++ resource->res_base_mem, resource->res_size, ++ resource->res_cached); ++ ++ /* We're done */ ++ resource->res_stats[ALLOC]++; ++ ioparam->handle = resource->res_guid; ++ return 0; ++ ++error: ++ pr_err("[%s]: failed to allocate \"%s\" data (%i) - type %u, base %u (%u), num %u, alignment %u\n", ++ __func__, alloc.name, ret, alloc.type, ioparam->size, ++ alloc.base_unit, alloc.num_unit, alloc.alignement); ++ if (resource != NULL) { ++ vc_sm_resource_deceased(resource, 1); ++ kfree(resource); ++ } ++ return ret; ++} ++ ++/* Share an allocate memory handle and block. ++*/ ++int vc_sm_ioctl_alloc_share(struct SM_PRIV_DATA_T *private, ++ struct vmcs_sm_ioctl_alloc_share *ioparam) ++{ ++ struct SM_RESOURCE_T *resource, *shared_resource; ++ int ret = 0; ++ ++ pr_debug("[%s]: attempt to share resource %u\n", __func__, ++ ioparam->handle); ++ ++ shared_resource = vmcs_sm_acquire_global_resource(ioparam->handle); ++ if (shared_resource == NULL) { ++ ret = -ENOMEM; ++ goto error; ++ } ++ ++ /* Allocate local resource to track this allocation. ++ */ ++ resource = kzalloc(sizeof(*resource), GFP_KERNEL); ++ if (resource == NULL) { ++ pr_err("[%s]: failed to allocate local tracking resource\n", ++ __func__); ++ ret = -ENOMEM; ++ goto error; ++ } ++ INIT_LIST_HEAD(&resource->map_list); ++ resource->ref_count++; ++ resource->pid = current->tgid; ++ ++ /* Keep track of the resource we created. ++ */ ++ resource->private = private; ++ resource->res_handle = shared_resource->res_handle; ++ resource->res_base_mem = shared_resource->res_base_mem; ++ resource->res_size = shared_resource->res_size; ++ resource->res_cached = shared_resource->res_cached; ++ resource->res_shared = shared_resource; ++ ++ mutex_lock(&sm_state->lock); ++ resource->res_guid = ++sm_state->guid; ++ mutex_unlock(&sm_state->lock); ++ resource->res_guid <<= PAGE_SHIFT; ++ ++ vmcs_sm_add_resource(private, resource); ++ ++ pr_debug("[%s]: allocated data - guid %x, hdl %x, base address %p, size %d, cache %d\n", ++ __func__, resource->res_guid, resource->res_handle, ++ resource->res_base_mem, resource->res_size, ++ resource->res_cached); ++ ++ /* We're done */ ++ resource->res_stats[ALLOC]++; ++ ioparam->handle = resource->res_guid; ++ ioparam->size = resource->res_size; ++ return 0; ++ ++error: ++ pr_err("[%s]: failed to share %u\n", __func__, ioparam->handle); ++ if (shared_resource != NULL) ++ vmcs_sm_release_resource(shared_resource, 0); ++ ++ return ret; ++} ++ ++/* Free a previously allocated shared memory handle and block. ++*/ ++static int vc_sm_ioctl_free(struct SM_PRIV_DATA_T *private, ++ struct vmcs_sm_ioctl_free *ioparam) ++{ ++ struct SM_RESOURCE_T *resource = ++ vmcs_sm_acquire_resource(private, ioparam->handle); ++ ++ if (resource == NULL) { ++ pr_err("[%s]: resource for guid %u does not exist\n", __func__, ++ ioparam->handle); ++ return -EINVAL; ++ } ++ ++ /* Check permissions. ++ */ ++ if (resource->pid && (resource->pid != current->tgid)) { ++ pr_err("[%s]: current tgid %u != %u owner\n", ++ __func__, current->tgid, resource->pid); ++ vmcs_sm_release_resource(resource, 0); ++ return -EPERM; ++ } ++ ++ vmcs_sm_release_resource(resource, 0); ++ vmcs_sm_release_resource(resource, 0); ++ return 0; ++} ++ ++/* Resize a previously allocated shared memory handle and block. ++*/ ++static int vc_sm_ioctl_resize(struct SM_PRIV_DATA_T *private, ++ struct vmcs_sm_ioctl_resize *ioparam) ++{ ++ int ret = 0; ++ int status; ++ VC_SM_RESIZE_T resize; ++ struct SM_RESOURCE_T *resource; ++ ++ /* Locate resource from GUID. ++ */ ++ resource = vmcs_sm_acquire_resource(private, ioparam->handle); ++ if (!resource) { ++ pr_err("[%s]: failed resource - guid %x\n", ++ __func__, ioparam->handle); ++ ret = -EFAULT; ++ goto error; ++ } ++ ++ /* If the resource is locked, its reference count will be not NULL, ++ ** in which case we will not be allowed to resize it anyways, so ++ ** reject the attempt here. ++ */ ++ if (resource->lock_count != 0) { ++ pr_err("[%s]: cannot resize - guid %x, ref-cnt %d\n", ++ __func__, ioparam->handle, resource->lock_count); ++ ret = -EFAULT; ++ goto error; ++ } ++ ++ /* Check permissions. ++ */ ++ if (resource->pid && (resource->pid != current->tgid)) { ++ pr_err("[%s]: current tgid %u != %u owner\n", __func__, ++ current->tgid, resource->pid); ++ ret = -EPERM; ++ goto error; ++ } ++ ++ if (resource->map_count != 0) { ++ pr_err("[%s]: cannot resize - guid %x, ref-cnt %d\n", ++ __func__, ioparam->handle, resource->map_count); ++ ret = -EFAULT; ++ goto error; ++ } ++ ++ resize.res_handle = resource->res_handle; ++ resize.res_mem = resource->res_base_mem; ++ resize.res_new_size = ioparam->new_size; ++ ++ pr_debug("[%s]: attempt to resize data - guid %x, hdl %x, base address %p\n", ++ __func__, ioparam->handle, resize.res_handle, resize.res_mem); ++ ++ /* Resize the videocore allocated resource. ++ */ ++ status = vc_vchi_sm_resize(sm_state->sm_handle, &resize, ++ &private->int_trans_id); ++ if (status == -EINTR) { ++ pr_debug("[%s]: requesting resize memory action restart (trans_id: %u)\n", ++ __func__, private->int_trans_id); ++ ret = -ERESTARTSYS; ++ private->restart_sys = -EINTR; ++ private->int_action = VC_SM_MSG_TYPE_RESIZE; ++ goto error; ++ } else if (status != 0) { ++ pr_err("[%s]: failed to resize memory on videocore (status: %u, trans_id: %u)\n", ++ __func__, status, private->int_trans_id); ++ ret = -EPERM; ++ goto error; ++ } ++ ++ pr_debug("[%s]: success to resize data - hdl %x, size %d -> %d\n", ++ __func__, resize.res_handle, resource->res_size, ++ resize.res_new_size); ++ ++ /* Successfully resized, save the information and inform the user. ++ */ ++ ioparam->old_size = resource->res_size; ++ resource->res_size = resize.res_new_size; ++ ++error: ++ if (resource) ++ vmcs_sm_release_resource(resource, 0); ++ ++ return ret; ++} ++ ++/* Lock a previously allocated shared memory handle and block. ++*/ ++static int vc_sm_ioctl_lock(struct SM_PRIV_DATA_T *private, ++ struct vmcs_sm_ioctl_lock_unlock *ioparam, ++ int change_cache, enum vmcs_sm_cache_e cache_type, ++ unsigned int vc_addr) ++{ ++ int status; ++ VC_SM_LOCK_UNLOCK_T lock; ++ VC_SM_LOCK_RESULT_T result; ++ struct SM_RESOURCE_T *resource; ++ int ret = 0; ++ struct sm_mmap *map, *map_tmp; ++ long unsigned int phys_addr; ++ ++ map = NULL; ++ ++ /* Locate resource from GUID. ++ */ ++ resource = vmcs_sm_acquire_resource(private, ioparam->handle); ++ if (resource == NULL) { ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ /* Check permissions. ++ */ ++ if (resource->pid && (resource->pid != current->tgid)) { ++ pr_err("[%s]: current tgid %u != %u owner\n", __func__, ++ current->tgid, resource->pid); ++ ret = -EPERM; ++ goto error; ++ } ++ ++ lock.res_handle = resource->res_handle; ++ lock.res_mem = resource->res_base_mem; ++ ++ /* Take the lock and get the address to be mapped. ++ */ ++ if (vc_addr == 0) { ++ pr_debug("[%s]: attempt to lock data - guid %x, hdl %x, base address %p\n", ++ __func__, ioparam->handle, lock.res_handle, ++ lock.res_mem); ++ ++ /* Lock the videocore allocated resource. ++ */ ++ status = vc_vchi_sm_lock(sm_state->sm_handle, &lock, &result, ++ &private->int_trans_id); ++ if (status == -EINTR) { ++ pr_debug("[%s]: requesting lock memory action restart (trans_id: %u)\n", ++ __func__, private->int_trans_id); ++ ret = -ERESTARTSYS; ++ private->restart_sys = -EINTR; ++ private->int_action = VC_SM_MSG_TYPE_LOCK; ++ goto error; ++ } else if (status != 0 || ++ (status == 0 && result.res_mem == NULL)) { ++ pr_err("[%s]: failed to lock memory on videocore (status: %u, trans_id: %u)\n", ++ __func__, status, private->int_trans_id); ++ ret = -EPERM; ++ resource->res_stats[LOCK_FAIL]++; ++ goto error; ++ } ++ ++ pr_debug("[%s]: succeed to lock data - hdl %x, base address %p (%p), ref-cnt %d\n", ++ __func__, lock.res_handle, result.res_mem, ++ lock.res_mem, resource->lock_count); ++ } ++ /* Lock assumed taken already, address to be mapped is known. ++ */ ++ else ++ resource->res_base_mem = (void *)vc_addr; ++ ++ resource->res_stats[LOCK]++; ++ resource->lock_count++; ++ ++ /* Keep track of the new base memory allocation if it has changed. ++ */ ++ if ((vc_addr == 0) && ++ (result.res_mem != NULL) && ++ (result.res_old_mem != NULL) && ++ (result.res_mem != result.res_old_mem)) { ++ resource->res_base_mem = result.res_mem; ++ ++ /* Kernel allocated resources. ++ */ ++ if (resource->pid == 0) { ++ if (!list_empty(&resource->map_list)) { ++ list_for_each_entry_safe(map, map_tmp, ++ &resource->map_list, ++ resource_map_list) { ++ if (map->res_addr) { ++ iounmap((void *)map->res_addr); ++ map->res_addr = 0; ++ ++ vmcs_sm_remove_map(sm_state, ++ map->resource, ++ map); ++ break; ++ } ++ } ++ } ++ } ++ } ++ ++ if (change_cache) ++ resource->res_cached = cache_type; ++ ++ if (resource->map_count) { ++ ioparam->addr = ++ vmcs_sm_usr_address_from_pid_and_usr_handle( ++ current->tgid, ioparam->handle); ++ ++ pr_debug("[%s] map_count %d private->pid %d current->tgid %d hnd %x addr %u\n", ++ __func__, resource->map_count, private->pid, ++ current->tgid, ioparam->handle, ioparam->addr); ++ } else { ++ /* Kernel allocated resources. ++ */ ++ if (resource->pid == 0) { ++ pr_debug("[%s]: attempt mapping kernel resource - guid %x, hdl %x\n", ++ __func__, ioparam->handle, lock.res_handle); ++ ++ ioparam->addr = 0; ++ ++ map = kzalloc(sizeof(*map), GFP_KERNEL); ++ if (map == NULL) { ++ pr_err("[%s]: failed allocating tracker\n", ++ __func__); ++ ret = -ENOMEM; ++ goto error; ++ } else { ++ phys_addr = (uint32_t)resource->res_base_mem & ++ 0x3FFFFFFF; ++ phys_addr += mm_vc_mem_phys_addr; ++ if (resource->res_cached ++ == VMCS_SM_CACHE_HOST) { ++ ioparam->addr = (long unsigned int) ++ /* TODO - make cached work */ ++ ioremap_nocache(phys_addr, ++ resource->res_size); ++ ++ pr_debug("[%s]: mapping kernel - guid %x, hdl %x - cached mapping %u\n", ++ __func__, ioparam->handle, ++ lock.res_handle, ioparam->addr); ++ } else { ++ ioparam->addr = (long unsigned int) ++ ioremap_nocache(phys_addr, ++ resource->res_size); ++ ++ pr_debug("[%s]: mapping kernel- guid %x, hdl %x - non cached mapping %u\n", ++ __func__, ioparam->handle, ++ lock.res_handle, ioparam->addr); ++ } ++ ++ map->res_pid = 0; ++ map->res_vc_hdl = resource->res_handle; ++ map->res_usr_hdl = resource->res_guid; ++ map->res_addr = ioparam->addr; ++ map->resource = resource; ++ map->vma = NULL; ++ ++ vmcs_sm_add_map(sm_state, resource, map); ++ } ++ } else ++ ioparam->addr = 0; ++ } ++ ++error: ++ if (resource) ++ vmcs_sm_release_resource(resource, 0); ++ ++ return ret; ++} ++ ++/* Unlock a previously allocated shared memory handle and block. ++*/ ++static int vc_sm_ioctl_unlock(struct SM_PRIV_DATA_T *private, ++ struct vmcs_sm_ioctl_lock_unlock *ioparam, ++ int flush, int wait_reply, int no_vc_unlock) ++{ ++ int status; ++ VC_SM_LOCK_UNLOCK_T unlock; ++ struct sm_mmap *map, *map_tmp; ++ struct SM_RESOURCE_T *resource; ++ int ret = 0; ++ ++ map = NULL; ++ ++ /* Locate resource from GUID. ++ */ ++ resource = vmcs_sm_acquire_resource(private, ioparam->handle); ++ if (resource == NULL) { ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ /* Check permissions. ++ */ ++ if (resource->pid && (resource->pid != current->tgid)) { ++ pr_err("[%s]: current tgid %u != %u owner\n", ++ __func__, current->tgid, resource->pid); ++ ret = -EPERM; ++ goto error; ++ } ++ ++ unlock.res_handle = resource->res_handle; ++ unlock.res_mem = resource->res_base_mem; ++ ++ pr_debug("[%s]: attempt to unlock data - guid %x, hdl %x, base address %p\n", ++ __func__, ioparam->handle, unlock.res_handle, unlock.res_mem); ++ ++ /* User space allocated resources. ++ */ ++ if (resource->pid) { ++ /* Flush if requested */ ++ if (resource->res_cached && flush) { ++ dma_addr_t phys_addr = 0; ++ resource->res_stats[FLUSH]++; ++ ++ phys_addr = ++ (dma_addr_t)((uint32_t)resource->res_base_mem & ++ 0x3FFFFFFF); ++ phys_addr += (dma_addr_t)mm_vc_mem_phys_addr; ++ ++ /* L1 cache flush */ ++ down_read(¤t->mm->mmap_sem); ++ list_for_each_entry(map, &resource->map_list, ++ resource_map_list) { ++ if (map->vma) { ++ unsigned long start; ++ unsigned long end; ++ start = map->vma->vm_start; ++ end = map->vma->vm_end; ++ ++ vcsm_vma_cache_clean_page_range( ++ start, end); ++ } ++ } ++ up_read(¤t->mm->mmap_sem); ++ ++ /* L2 cache flush */ ++ outer_clean_range(phys_addr, ++ phys_addr + ++ (size_t) resource->res_size); ++ } ++ ++ /* We need to zap all the vmas associated with this resource */ ++ if (resource->lock_count == 1) { ++ down_read(¤t->mm->mmap_sem); ++ list_for_each_entry(map, &resource->map_list, ++ resource_map_list) { ++ if (map->vma) { ++ zap_vma_ptes(map->vma, ++ map->vma->vm_start, ++ map->vma->vm_end - ++ map->vma->vm_start); ++ } ++ } ++ up_read(¤t->mm->mmap_sem); ++ } ++ } ++ /* Kernel allocated resources. */ ++ else { ++ /* Global + Taken in this context */ ++ if (resource->ref_count == 2) { ++ if (!list_empty(&resource->map_list)) { ++ list_for_each_entry_safe(map, map_tmp, ++ &resource->map_list, ++ resource_map_list) { ++ if (map->res_addr) { ++ if (flush && ++ (resource->res_cached == ++ VMCS_SM_CACHE_HOST)) { ++ long unsigned int ++ phys_addr; ++ phys_addr = (uint32_t) ++ resource->res_base_mem & 0x3FFFFFFF; ++ phys_addr += ++ mm_vc_mem_phys_addr; ++ ++ /* L1 cache flush */ ++ dmac_flush_range((const ++ void ++ *) ++ map->res_addr, (const void *) ++ (map->res_addr + resource->res_size)); ++ ++ /* L2 cache flush */ ++ outer_clean_range ++ (phys_addr, ++ phys_addr + ++ (size_t) ++ resource->res_size); ++ } ++ ++ iounmap((void *)map->res_addr); ++ map->res_addr = 0; ++ ++ vmcs_sm_remove_map(sm_state, ++ map->resource, ++ map); ++ break; ++ } ++ } ++ } ++ } ++ } ++ ++ if (resource->lock_count) { ++ /* Bypass the videocore unlock. ++ */ ++ if (no_vc_unlock) ++ status = 0; ++ /* Unlock the videocore allocated resource. ++ */ ++ else { ++ status = ++ vc_vchi_sm_unlock(sm_state->sm_handle, &unlock, ++ &private->int_trans_id, ++ wait_reply); ++ if (status == -EINTR) { ++ pr_debug("[%s]: requesting unlock memory action restart (trans_id: %u)\n", ++ __func__, private->int_trans_id); ++ ++ ret = -ERESTARTSYS; ++ resource->res_stats[UNLOCK]--; ++ private->restart_sys = -EINTR; ++ private->int_action = VC_SM_MSG_TYPE_UNLOCK; ++ goto error; ++ } else if (status != 0) { ++ pr_err("[%s]: failed to unlock vc mem (status: %u, trans_id: %u)\n", ++ __func__, status, private->int_trans_id); ++ ++ ret = -EPERM; ++ resource->res_stats[UNLOCK_FAIL]++; ++ goto error; ++ } ++ } ++ ++ resource->res_stats[UNLOCK]++; ++ resource->lock_count--; ++ } ++ ++ pr_debug("[%s]: success to unlock data - hdl %x, base address %p, ref-cnt %d\n", ++ __func__, unlock.res_handle, unlock.res_mem, ++ resource->lock_count); ++ ++error: ++ if (resource) ++ vmcs_sm_release_resource(resource, 0); ++ ++ return ret; ++} ++ ++/* Handle control from host. */ ++static long vc_sm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ int ret = 0; ++ unsigned int cmdnr = _IOC_NR(cmd); ++ struct SM_PRIV_DATA_T *file_data = ++ (struct SM_PRIV_DATA_T *)file->private_data; ++ struct SM_RESOURCE_T *resource = NULL; ++ ++ /* Validate we can work with this device. */ ++ if ((sm_state == NULL) || (file_data == NULL)) { ++ pr_err("[%s]: invalid device\n", __func__); ++ ret = -EPERM; ++ goto out; ++ } ++ ++ pr_debug("[%s]: cmd %x tgid %u, owner %u\n", __func__, cmdnr, ++ current->tgid, file_data->pid); ++ ++ /* Action is a re-post of a previously interrupted action? */ ++ if (file_data->restart_sys == -EINTR) { ++ VC_SM_ACTION_CLEAN_T action_clean; ++ ++ pr_debug("[%s]: clean up of action %u (trans_id: %u) following EINTR\n", ++ __func__, file_data->int_action, ++ file_data->int_trans_id); ++ ++ action_clean.res_action = file_data->int_action; ++ action_clean.action_trans_id = file_data->int_trans_id; ++ ++ vc_vchi_sm_clean_up(sm_state->sm_handle, &action_clean); ++ ++ file_data->restart_sys = 0; ++ } ++ ++ /* Now process the command. ++ */ ++ switch (cmdnr) { ++ /* New memory allocation. ++ */ ++ case VMCS_SM_CMD_ALLOC: ++ { ++ struct vmcs_sm_ioctl_alloc ioparam; ++ ++ /* Get the parameter data. ++ */ ++ if (copy_from_user ++ (&ioparam, (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ ret = vc_sm_ioctl_alloc(file_data, &ioparam); ++ if (!ret && ++ (copy_to_user((void *)arg, ++ &ioparam, sizeof(ioparam)) != 0)) { ++ struct vmcs_sm_ioctl_free freeparam = { ++ ioparam.handle ++ }; ++ pr_err("[%s]: failed to copy-to-user for cmd %x\n", ++ __func__, cmdnr); ++ vc_sm_ioctl_free(file_data, &freeparam); ++ ret = -EFAULT; ++ } ++ ++ /* Done. ++ */ ++ goto out; ++ } ++ break; ++ ++ /* Share existing memory allocation. ++ */ ++ case VMCS_SM_CMD_ALLOC_SHARE: ++ { ++ struct vmcs_sm_ioctl_alloc_share ioparam; ++ ++ /* Get the parameter data. ++ */ ++ if (copy_from_user ++ (&ioparam, (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ ret = vc_sm_ioctl_alloc_share(file_data, &ioparam); ++ ++ /* Copy result back to user. ++ */ ++ if (!ret ++ && copy_to_user((void *)arg, &ioparam, ++ sizeof(ioparam)) != 0) { ++ struct vmcs_sm_ioctl_free freeparam = { ++ ioparam.handle ++ }; ++ pr_err("[%s]: failed to copy-to-user for cmd %x\n", ++ __func__, cmdnr); ++ vc_sm_ioctl_free(file_data, &freeparam); ++ ret = -EFAULT; ++ } ++ ++ /* Done. ++ */ ++ goto out; ++ } ++ break; ++ ++ /* Lock (attempt to) *and* register a cache behavior change. ++ */ ++ case VMCS_SM_CMD_LOCK_CACHE: ++ { ++ struct vmcs_sm_ioctl_lock_cache ioparam; ++ struct vmcs_sm_ioctl_lock_unlock lock; ++ ++ /* Get parameter data. ++ */ ++ if (copy_from_user ++ (&ioparam, (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ lock.handle = ioparam.handle; ++ ret = ++ vc_sm_ioctl_lock(file_data, &lock, 1, ++ ioparam.cached, 0); ++ ++ /* Done. ++ */ ++ goto out; ++ } ++ break; ++ ++ /* Lock (attempt to) existing memory allocation. ++ */ ++ case VMCS_SM_CMD_LOCK: ++ { ++ struct vmcs_sm_ioctl_lock_unlock ioparam; ++ ++ /* Get parameter data. ++ */ ++ if (copy_from_user ++ (&ioparam, (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ ret = vc_sm_ioctl_lock(file_data, &ioparam, 0, 0, 0); ++ ++ /* Copy result back to user. ++ */ ++ if (copy_to_user((void *)arg, &ioparam, sizeof(ioparam)) ++ != 0) { ++ pr_err("[%s]: failed to copy-to-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ } ++ ++ /* Done. ++ */ ++ goto out; ++ } ++ break; ++ ++ /* Unlock (attempt to) existing memory allocation. ++ */ ++ case VMCS_SM_CMD_UNLOCK: ++ { ++ struct vmcs_sm_ioctl_lock_unlock ioparam; ++ ++ /* Get parameter data. ++ */ ++ if (copy_from_user ++ (&ioparam, (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ ret = vc_sm_ioctl_unlock(file_data, &ioparam, 0, 1, 0); ++ ++ /* Done. ++ */ ++ goto out; ++ } ++ break; ++ ++ /* Resize (attempt to) existing memory allocation. ++ */ ++ case VMCS_SM_CMD_RESIZE: ++ { ++ struct vmcs_sm_ioctl_resize ioparam; ++ ++ /* Get parameter data. ++ */ ++ if (copy_from_user ++ (&ioparam, (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ ret = vc_sm_ioctl_resize(file_data, &ioparam); ++ ++ /* Copy result back to user. ++ */ ++ if (copy_to_user((void *)arg, &ioparam, sizeof(ioparam)) ++ != 0) { ++ pr_err("[%s]: failed to copy-to-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ } ++ ++ /* Done. ++ */ ++ goto out; ++ } ++ break; ++ ++ /* Terminate existing memory allocation. ++ */ ++ case VMCS_SM_CMD_FREE: ++ { ++ struct vmcs_sm_ioctl_free ioparam; ++ ++ /* Get parameter data. ++ */ ++ if (copy_from_user ++ (&ioparam, (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ ret = vc_sm_ioctl_free(file_data, &ioparam); ++ ++ /* Done. ++ */ ++ goto out; ++ } ++ break; ++ ++ /* Walk allocation on videocore, information shows up in the ++ ** videocore log. ++ */ ++ case VMCS_SM_CMD_VC_WALK_ALLOC: ++ { ++ pr_debug("[%s]: invoking walk alloc\n", __func__); ++ ++ if (vc_vchi_sm_walk_alloc(sm_state->sm_handle) != 0) ++ pr_err("[%s]: failed to walk-alloc on videocore\n", ++ __func__); ++ ++ /* Done. ++ */ ++ goto out; ++ } ++ break; ++/* Walk mapping table on host, information shows up in the ++ ** kernel log. ++ */ ++ case VMCS_SM_CMD_HOST_WALK_MAP: ++ { ++ /* Use pid of -1 to tell to walk the whole map. */ ++ vmcs_sm_host_walk_map_per_pid(-1); ++ ++ /* Done. */ ++ goto out; ++ } ++ break; ++ ++ /* Walk mapping table per process on host. */ ++ case VMCS_SM_CMD_HOST_WALK_PID_ALLOC: ++ { ++ struct vmcs_sm_ioctl_walk ioparam; ++ ++ /* Get parameter data. */ ++ if (copy_from_user(&ioparam, ++ (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ vmcs_sm_host_walk_alloc(file_data); ++ ++ /* Done. */ ++ goto out; ++ } ++ break; ++ ++ /* Walk allocation per process on host. */ ++ case VMCS_SM_CMD_HOST_WALK_PID_MAP: ++ { ++ struct vmcs_sm_ioctl_walk ioparam; ++ ++ /* Get parameter data. */ ++ if (copy_from_user(&ioparam, ++ (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ vmcs_sm_host_walk_map_per_pid(ioparam.pid); ++ ++ /* Done. */ ++ goto out; ++ } ++ break; ++ ++ /* Gets the size of the memory associated with a user handle. */ ++ case VMCS_SM_CMD_SIZE_USR_HANDLE: ++ { ++ struct vmcs_sm_ioctl_size ioparam; ++ ++ /* Get parameter data. */ ++ if (copy_from_user(&ioparam, ++ (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ /* Locate resource from GUID. */ ++ resource = ++ vmcs_sm_acquire_resource(file_data, ioparam.handle); ++ if (resource != NULL) { ++ ioparam.size = resource->res_size; ++ vmcs_sm_release_resource(resource, 0); ++ } else { ++ ioparam.size = 0; ++ } ++ ++ if (copy_to_user((void *)arg, ++ &ioparam, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-to-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ } ++ ++ /* Done. */ ++ goto out; ++ } ++ break; ++ ++ /* Verify we are dealing with a valid resource. */ ++ case VMCS_SM_CMD_CHK_USR_HANDLE: ++ { ++ struct vmcs_sm_ioctl_chk ioparam; ++ ++ /* Get parameter data. ++ */ ++ if (copy_from_user(&ioparam, ++ (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ /* Locate resource from GUID. */ ++ resource = ++ vmcs_sm_acquire_resource(file_data, ioparam.handle); ++ if (resource == NULL) ++ ret = -EINVAL; ++ /* If the resource is cacheable, return additional ++ * information that may be needed to flush the cache. ++ */ ++ else if ((resource->res_cached == VMCS_SM_CACHE_HOST) || ++ (resource->res_cached == VMCS_SM_CACHE_BOTH)) { ++ ioparam.addr = ++ vmcs_sm_usr_address_from_pid_and_usr_handle ++ (current->tgid, ioparam.handle); ++ ioparam.size = resource->res_size; ++ ioparam.cache = resource->res_cached; ++ } else { ++ ioparam.addr = 0; ++ ioparam.size = 0; ++ ioparam.cache = resource->res_cached; ++ } ++ ++ if (resource) ++ vmcs_sm_release_resource(resource, 0); ++ ++ if (copy_to_user((void *)arg, ++ &ioparam, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-to-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ } ++ ++ /* Done. ++ */ ++ goto out; ++ } ++ break; ++ ++ /* ++ * Maps a user handle given the process and the virtual address. ++ */ ++ case VMCS_SM_CMD_MAPPED_USR_HANDLE: ++ { ++ struct vmcs_sm_ioctl_map ioparam; ++ ++ /* Get parameter data. */ ++ if (copy_from_user(&ioparam, ++ (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ ioparam.handle = ++ vmcs_sm_usr_handle_from_pid_and_address( ++ ioparam.pid, ioparam.addr); ++ ++ resource = ++ vmcs_sm_acquire_resource(file_data, ioparam.handle); ++ if ((resource != NULL) ++ && ((resource->res_cached == VMCS_SM_CACHE_HOST) ++ || (resource->res_cached == ++ VMCS_SM_CACHE_BOTH))) { ++ ioparam.size = resource->res_size; ++ } else { ++ ioparam.size = 0; ++ } ++ ++ if (resource) ++ vmcs_sm_release_resource(resource, 0); ++ ++ if (copy_to_user((void *)arg, ++ &ioparam, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-to-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ } ++ ++ /* Done. */ ++ goto out; ++ } ++ break; ++ ++ /* ++ * Maps a videocore handle given process and virtual address. ++ */ ++ case VMCS_SM_CMD_MAPPED_VC_HDL_FROM_ADDR: ++ { ++ struct vmcs_sm_ioctl_map ioparam; ++ ++ /* Get parameter data. */ ++ if (copy_from_user(&ioparam, ++ (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ ioparam.handle = vmcs_sm_vc_handle_from_pid_and_address( ++ ioparam.pid, ioparam.addr); ++ ++ if (copy_to_user((void *)arg, ++ &ioparam, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-to-user for cmd %x\n", ++ __func__, cmdnr); ++ ++ ret = -EFAULT; ++ } ++ ++ /* Done. ++ */ ++ goto out; ++ } ++ break; ++ ++ /* Maps a videocore handle given process and user handle. */ ++ case VMCS_SM_CMD_MAPPED_VC_HDL_FROM_HDL: ++ { ++ struct vmcs_sm_ioctl_map ioparam; ++ ++ /* Get parameter data. */ ++ if (copy_from_user(&ioparam, ++ (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ /* Locate resource from GUID. */ ++ resource = ++ vmcs_sm_acquire_resource(file_data, ioparam.handle); ++ if (resource != NULL) { ++ ioparam.handle = resource->res_handle; ++ vmcs_sm_release_resource(resource, 0); ++ } else { ++ ioparam.handle = 0; ++ } ++ ++ if (copy_to_user((void *)arg, ++ &ioparam, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-to-user for cmd %x\n", ++ __func__, cmdnr); ++ ++ ret = -EFAULT; ++ } ++ ++ /* Done. */ ++ goto out; ++ } ++ break; ++ ++ /* ++ * Maps a videocore address given process and videocore handle. ++ */ ++ case VMCS_SM_CMD_MAPPED_VC_ADDR_FROM_HDL: ++ { ++ struct vmcs_sm_ioctl_map ioparam; ++ ++ /* Get parameter data. */ ++ if (copy_from_user(&ioparam, ++ (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ /* Locate resource from GUID. */ ++ resource = ++ vmcs_sm_acquire_resource(file_data, ioparam.handle); ++ if (resource != NULL) { ++ ioparam.addr = ++ (unsigned int)resource->res_base_mem; ++ vmcs_sm_release_resource(resource, 0); ++ } else { ++ ioparam.addr = 0; ++ } ++ ++ if (copy_to_user((void *)arg, ++ &ioparam, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-to-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ } ++ ++ /* Done. */ ++ goto out; ++ } ++ break; ++ ++ /* Maps a user address given process and vc handle. ++ */ ++ case VMCS_SM_CMD_MAPPED_USR_ADDRESS: ++ { ++ struct vmcs_sm_ioctl_map ioparam; ++ ++ /* Get parameter data. */ ++ if (copy_from_user(&ioparam, ++ (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ /* ++ * Return the address information from the mapping, ++ * 0 (ie NULL) if it cannot locate the actual mapping. ++ */ ++ ioparam.addr = ++ vmcs_sm_usr_address_from_pid_and_usr_handle ++ (ioparam.pid, ioparam.handle); ++ ++ if (copy_to_user((void *)arg, ++ &ioparam, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-to-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ } ++ ++ /* Done. */ ++ goto out; ++ } ++ break; ++ ++ /* Flush the cache for a given mapping. */ ++ case VMCS_SM_CMD_FLUSH: ++ { ++ struct vmcs_sm_ioctl_cache ioparam; ++ ++ /* Get parameter data. */ ++ if (copy_from_user(&ioparam, ++ (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ /* Locate resource from GUID. */ ++ resource = ++ vmcs_sm_acquire_resource(file_data, ioparam.handle); ++ ++ if ((resource != NULL) && resource->res_cached) { ++ dma_addr_t phys_addr = 0; ++ ++ resource->res_stats[FLUSH]++; ++ ++ phys_addr = ++ (dma_addr_t)((uint32_t) ++ resource->res_base_mem & ++ 0x3FFFFFFF); ++ phys_addr += (dma_addr_t)mm_vc_mem_phys_addr; ++ ++ /* L1 cache flush */ ++ down_read(¤t->mm->mmap_sem); ++ vcsm_vma_cache_clean_page_range((unsigned long) ++ ioparam.addr, ++ (unsigned long) ++ ioparam.addr + ++ ioparam.size); ++ up_read(¤t->mm->mmap_sem); ++ ++ /* L2 cache flush */ ++ outer_clean_range(phys_addr, ++ phys_addr + ++ (size_t) ioparam.size); ++ } else if (resource == NULL) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (resource) ++ vmcs_sm_release_resource(resource, 0); ++ ++ /* Done. */ ++ goto out; ++ } ++ break; ++ ++ /* Invalidate the cache for a given mapping. */ ++ case VMCS_SM_CMD_INVALID: ++ { ++ struct vmcs_sm_ioctl_cache ioparam; ++ ++ /* Get parameter data. */ ++ if (copy_from_user(&ioparam, ++ (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ /* Locate resource from GUID. ++ */ ++ resource = ++ vmcs_sm_acquire_resource(file_data, ioparam.handle); ++ ++ if ((resource != NULL) && resource->res_cached) { ++ dma_addr_t phys_addr = 0; ++ ++ resource->res_stats[INVALID]++; ++ ++ phys_addr = ++ (dma_addr_t)((uint32_t) ++ resource->res_base_mem & ++ 0x3FFFFFFF); ++ phys_addr += (dma_addr_t)mm_vc_mem_phys_addr; ++ ++ /* L2 cache invalidate */ ++ outer_inv_range(phys_addr, ++ phys_addr + ++ (size_t) ioparam.size); ++ ++ /* L1 cache invalidate */ ++ down_read(¤t->mm->mmap_sem); ++ vcsm_vma_cache_clean_page_range((unsigned long) ++ ioparam.addr, ++ (unsigned long) ++ ioparam.addr + ++ ioparam.size); ++ up_read(¤t->mm->mmap_sem); ++ } else if (resource == NULL) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (resource) ++ vmcs_sm_release_resource(resource, 0); ++ ++ /* Done. ++ */ ++ goto out; ++ } ++ break; ++ ++ /* Flush/Invalidate the cache for a given mapping. */ ++ case VMCS_SM_CMD_CLEAN_INVALID: ++ { ++ int i; ++ struct vmcs_sm_ioctl_clean_invalid ioparam; ++ ++ /* Get parameter data. */ ++ if (copy_from_user(&ioparam, ++ (void *)arg, sizeof(ioparam)) != 0) { ++ pr_err("[%s]: failed to copy-from-user for cmd %x\n", ++ __func__, cmdnr); ++ ret = -EFAULT; ++ goto out; ++ } ++ for (i=0; ires_cached) { ++ unsigned long base = ioparam.s[i].addr & ~(PAGE_SIZE-1); ++ unsigned long end = (ioparam.s[i].addr + ioparam.s[i].size + PAGE_SIZE-1) & ~(PAGE_SIZE-1); ++ resource->res_stats[ioparam.s[i].cmd == 1 ? INVALID:FLUSH]++; ++ ++ /* L1/L2 cache flush */ ++ down_read(¤t->mm->mmap_sem); ++ vcsm_vma_cache_clean_page_range(base, end); ++ up_read(¤t->mm->mmap_sem); ++ } else if (resource == NULL) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (resource) ++ vmcs_sm_release_resource(resource, 0); ++ } ++ break; ++ } ++ } ++ } ++ break; ++ ++ default: ++ { ++ ret = -EINVAL; ++ goto out; ++ } ++ break; ++ } ++ ++out: ++ return ret; ++} ++ ++/* Device operations that we managed in this driver. ++*/ ++static const struct file_operations vmcs_sm_ops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = vc_sm_ioctl, ++ .open = vc_sm_open, ++ .release = vc_sm_release, ++ .mmap = vc_sm_mmap, ++}; ++ ++/* Creation of device. ++*/ ++static int vc_sm_create_sharedmemory(void) ++{ ++ int ret; ++ ++ if (sm_state == NULL) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ /* Create a device class for creating dev nodes. ++ */ ++ sm_state->sm_class = class_create(THIS_MODULE, "vc-sm"); ++ if (IS_ERR(sm_state->sm_class)) { ++ pr_err("[%s]: unable to create device class\n", __func__); ++ ret = PTR_ERR(sm_state->sm_class); ++ goto out; ++ } ++ ++ /* Create a character driver. ++ */ ++ ret = alloc_chrdev_region(&sm_state->sm_devid, ++ DEVICE_MINOR, 1, DEVICE_NAME); ++ if (ret != 0) { ++ pr_err("[%s]: unable to allocate device number\n", __func__); ++ goto out_dev_class_destroy; ++ } ++ ++ cdev_init(&sm_state->sm_cdev, &vmcs_sm_ops); ++ ret = cdev_add(&sm_state->sm_cdev, sm_state->sm_devid, 1); ++ if (ret != 0) { ++ pr_err("[%s]: unable to register device\n", __func__); ++ goto out_chrdev_unreg; ++ } ++ ++ /* Create a device node. ++ */ ++ sm_state->sm_dev = device_create(sm_state->sm_class, ++ NULL, ++ MKDEV(MAJOR(sm_state->sm_devid), ++ DEVICE_MINOR), NULL, ++ DEVICE_NAME); ++ if (IS_ERR(sm_state->sm_dev)) { ++ pr_err("[%s]: unable to create device node\n", __func__); ++ ret = PTR_ERR(sm_state->sm_dev); ++ goto out_chrdev_del; ++ } ++ ++ goto out; ++ ++out_chrdev_del: ++ cdev_del(&sm_state->sm_cdev); ++out_chrdev_unreg: ++ unregister_chrdev_region(sm_state->sm_devid, 1); ++out_dev_class_destroy: ++ class_destroy(sm_state->sm_class); ++ sm_state->sm_class = NULL; ++out: ++ return ret; ++} ++ ++/* Termination of the device. ++*/ ++static int vc_sm_remove_sharedmemory(void) ++{ ++ int ret; ++ ++ if (sm_state == NULL) { ++ /* Nothing to do. ++ */ ++ ret = 0; ++ goto out; ++ } ++ ++ /* Remove the sharedmemory character driver. ++ */ ++ cdev_del(&sm_state->sm_cdev); ++ ++ /* Unregister region. ++ */ ++ unregister_chrdev_region(sm_state->sm_devid, 1); ++ ++ ret = 0; ++ goto out; ++ ++out: ++ return ret; ++} ++ ++/* Videocore connected. */ ++static void vc_sm_connected_init(void) ++{ ++ int ret; ++ VCHI_INSTANCE_T vchi_instance; ++ VCHI_CONNECTION_T *vchi_connection = NULL; ++ ++ pr_info("[%s]: start\n", __func__); ++ ++ /* Allocate memory for the state structure. ++ */ ++ sm_state = kzalloc(sizeof(struct SM_STATE_T), GFP_KERNEL); ++ if (sm_state == NULL) { ++ pr_err("[%s]: failed to allocate memory\n", __func__); ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ mutex_init(&sm_state->lock); ++ mutex_init(&sm_state->map_lock); ++ ++ /* Initialize and create a VCHI connection for the shared memory service ++ ** running on videocore. ++ */ ++ ret = vchi_initialise(&vchi_instance); ++ if (ret != 0) { ++ pr_err("[%s]: failed to initialise VCHI instance (ret=%d)\n", ++ __func__, ret); ++ ++ ret = -EIO; ++ goto err_free_mem; ++ } ++ ++ ret = vchi_connect(NULL, 0, vchi_instance); ++ if (ret != 0) { ++ pr_err("[%s]: failed to connect VCHI instance (ret=%d)\n", ++ __func__, ret); ++ ++ ret = -EIO; ++ goto err_free_mem; ++ } ++ ++ /* Initialize an instance of the shared memory service. */ ++ sm_state->sm_handle = ++ vc_vchi_sm_init(vchi_instance, &vchi_connection, 1); ++ if (sm_state->sm_handle == NULL) { ++ pr_err("[%s]: failed to initialize shared memory service\n", ++ __func__); ++ ++ ret = -EPERM; ++ goto err_free_mem; ++ } ++ ++ /* Create a debug fs directory entry (root). */ ++ sm_state->dir_root = debugfs_create_dir(VC_SM_DIR_ROOT_NAME, NULL); ++ if (!sm_state->dir_root) { ++ pr_err("[%s]: failed to create \'%s\' directory entry\n", ++ __func__, VC_SM_DIR_ROOT_NAME); ++ ++ ret = -EPERM; ++ goto err_stop_sm_service; ++ } ++ ++ sm_state->dir_state.show = &vc_sm_global_state_show; ++ sm_state->dir_state.dir_entry = debugfs_create_file(VC_SM_STATE, ++ S_IRUGO, sm_state->dir_root, &sm_state->dir_state, ++ &vc_sm_debug_fs_fops); ++ ++ sm_state->dir_stats.show = &vc_sm_global_statistics_show; ++ sm_state->dir_stats.dir_entry = debugfs_create_file(VC_SM_STATS, ++ S_IRUGO, sm_state->dir_root, &sm_state->dir_stats, ++ &vc_sm_debug_fs_fops); ++ ++ /* Create the proc entry children. */ ++ sm_state->dir_alloc = debugfs_create_dir(VC_SM_DIR_ALLOC_NAME, ++ sm_state->dir_root); ++ ++ /* Create a shared memory device. */ ++ ret = vc_sm_create_sharedmemory(); ++ if (ret != 0) { ++ pr_err("[%s]: failed to create shared memory device\n", ++ __func__); ++ goto err_remove_debugfs; ++ } ++ ++ INIT_LIST_HEAD(&sm_state->map_list); ++ INIT_LIST_HEAD(&sm_state->resource_list); ++ ++ sm_state->data_knl = vc_sm_create_priv_data(0); ++ if (sm_state->data_knl == NULL) { ++ pr_err("[%s]: failed to create kernel private data tracker\n", ++ __func__); ++ goto err_remove_shared_memory; ++ } ++ ++ /* Done! ++ */ ++ sm_inited = 1; ++ goto out; ++ ++err_remove_shared_memory: ++ vc_sm_remove_sharedmemory(); ++err_remove_debugfs: ++ debugfs_remove_recursive(sm_state->dir_root); ++err_stop_sm_service: ++ vc_vchi_sm_stop(&sm_state->sm_handle); ++err_free_mem: ++ kfree(sm_state); ++out: ++ pr_info("[%s]: end - returning %d\n", __func__, ret); ++} ++ ++/* Driver loading. */ ++static int __init vc_sm_init(void) ++{ ++ pr_info("vc-sm: Videocore shared memory driver\n"); ++ vchiq_add_connected_callback(vc_sm_connected_init); ++ return 0; ++} ++ ++/* Driver unloading. */ ++static void __exit vc_sm_exit(void) ++{ ++ pr_debug("[%s]: start\n", __func__); ++ if (sm_inited) { ++ /* Remove shared memory device. ++ */ ++ vc_sm_remove_sharedmemory(); ++ ++ /* Remove all proc entries. ++ */ ++ debugfs_remove_recursive(sm_state->dir_root); ++ ++ /* Stop the videocore shared memory service. ++ */ ++ vc_vchi_sm_stop(&sm_state->sm_handle); ++ ++ /* Free the memory for the state structure. ++ */ ++ mutex_destroy(&(sm_state->map_lock)); ++ kfree(sm_state); ++ } ++ ++ pr_debug("[%s]: end\n", __func__); ++} ++ ++#if defined(__KERNEL__) ++/* Allocate a shared memory handle and block. */ ++int vc_sm_alloc(VC_SM_ALLOC_T *alloc, int *handle) ++{ ++ struct vmcs_sm_ioctl_alloc ioparam = { 0 }; ++ int ret; ++ struct SM_RESOURCE_T *resource; ++ ++ /* Validate we can work with this device. ++ */ ++ if (sm_state == NULL || alloc == NULL || handle == NULL) { ++ pr_err("[%s]: invalid input\n", __func__); ++ return -EPERM; ++ } ++ ++ ioparam.size = alloc->base_unit; ++ ioparam.num = alloc->num_unit; ++ ioparam.cached = ++ alloc->type == VC_SM_ALLOC_CACHED ? VMCS_SM_CACHE_VC : 0; ++ ++ ret = vc_sm_ioctl_alloc(sm_state->data_knl, &ioparam); ++ ++ if (ret == 0) { ++ resource = ++ vmcs_sm_acquire_resource(sm_state->data_knl, ++ ioparam.handle); ++ if (resource) { ++ resource->pid = 0; ++ vmcs_sm_release_resource(resource, 0); ++ ++ /* Assign valid handle at this time. ++ */ ++ *handle = ioparam.handle; ++ } else { ++ ret = -ENOMEM; ++ } ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(vc_sm_alloc); ++ ++/* Get an internal resource handle mapped from the external one. ++*/ ++int vc_sm_int_handle(int handle) ++{ ++ struct SM_RESOURCE_T *resource; ++ int ret = 0; ++ ++ /* Validate we can work with this device. ++ */ ++ if (sm_state == NULL || handle == 0) { ++ pr_err("[%s]: invalid input\n", __func__); ++ return 0; ++ } ++ ++ /* Locate resource from GUID. ++ */ ++ resource = vmcs_sm_acquire_resource(sm_state->data_knl, handle); ++ if (resource) { ++ ret = resource->res_handle; ++ vmcs_sm_release_resource(resource, 0); ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(vc_sm_int_handle); ++ ++/* Free a previously allocated shared memory handle and block. ++*/ ++int vc_sm_free(int handle) ++{ ++ struct vmcs_sm_ioctl_free ioparam = { handle }; ++ ++ /* Validate we can work with this device. ++ */ ++ if (sm_state == NULL || handle == 0) { ++ pr_err("[%s]: invalid input\n", __func__); ++ return -EPERM; ++ } ++ ++ return vc_sm_ioctl_free(sm_state->data_knl, &ioparam); ++} ++EXPORT_SYMBOL_GPL(vc_sm_free); ++ ++/* Lock a memory handle for use by kernel. ++*/ ++int vc_sm_lock(int handle, VC_SM_LOCK_CACHE_MODE_T mode, ++ long unsigned int *data) ++{ ++ struct vmcs_sm_ioctl_lock_unlock ioparam; ++ int ret; ++ ++ /* Validate we can work with this device. ++ */ ++ if (sm_state == NULL || handle == 0 || data == NULL) { ++ pr_err("[%s]: invalid input\n", __func__); ++ return -EPERM; ++ } ++ ++ *data = 0; ++ ++ ioparam.handle = handle; ++ ret = vc_sm_ioctl_lock(sm_state->data_knl, ++ &ioparam, ++ 1, ++ ((mode == ++ VC_SM_LOCK_CACHED) ? VMCS_SM_CACHE_HOST : ++ VMCS_SM_CACHE_NONE), 0); ++ ++ *data = ioparam.addr; ++ return ret; ++} ++EXPORT_SYMBOL_GPL(vc_sm_lock); ++ ++/* Unlock a memory handle in use by kernel. ++*/ ++int vc_sm_unlock(int handle, int flush, int no_vc_unlock) ++{ ++ struct vmcs_sm_ioctl_lock_unlock ioparam; ++ ++ /* Validate we can work with this device. ++ */ ++ if (sm_state == NULL || handle == 0) { ++ pr_err("[%s]: invalid input\n", __func__); ++ return -EPERM; ++ } ++ ++ ioparam.handle = handle; ++ return vc_sm_ioctl_unlock(sm_state->data_knl, ++ &ioparam, flush, 0, no_vc_unlock); ++} ++EXPORT_SYMBOL_GPL(vc_sm_unlock); ++ ++/* Map a shared memory region for use by kernel. ++*/ ++int vc_sm_map(int handle, unsigned int sm_addr, VC_SM_LOCK_CACHE_MODE_T mode, ++ long unsigned int *data) ++{ ++ struct vmcs_sm_ioctl_lock_unlock ioparam; ++ int ret; ++ ++ /* Validate we can work with this device. ++ */ ++ if (sm_state == NULL || handle == 0 || data == NULL || sm_addr == 0) { ++ pr_err("[%s]: invalid input\n", __func__); ++ return -EPERM; ++ } ++ ++ *data = 0; ++ ++ ioparam.handle = handle; ++ ret = vc_sm_ioctl_lock(sm_state->data_knl, ++ &ioparam, ++ 1, ++ ((mode == ++ VC_SM_LOCK_CACHED) ? VMCS_SM_CACHE_HOST : ++ VMCS_SM_CACHE_NONE), sm_addr); ++ ++ *data = ioparam.addr; ++ return ret; ++} ++EXPORT_SYMBOL_GPL(vc_sm_map); ++#endif ++ ++late_initcall(vc_sm_init); ++module_exit(vc_sm_exit); ++ ++MODULE_AUTHOR("Broadcom"); ++MODULE_DESCRIPTION("VideoCore SharedMemory Driver"); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/include/linux/broadcom/vmcs_sm_ioctl.h +@@ -0,0 +1,248 @@ ++/***************************************************************************** ++* 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. ++* ++*****************************************************************************/ ++ ++#if !defined(__VMCS_SM_IOCTL_H__INCLUDED__) ++#define __VMCS_SM_IOCTL_H__INCLUDED__ ++ ++/* ---- Include Files ---------------------------------------------------- */ ++ ++#if defined(__KERNEL__) ++#include /* Needed for standard types */ ++#else ++#include ++#endif ++ ++#include ++ ++/* ---- Constants and Types ---------------------------------------------- */ ++ ++#define VMCS_SM_RESOURCE_NAME 32 ++#define VMCS_SM_RESOURCE_NAME_DEFAULT "sm-host-resource" ++ ++/* Type define used to create unique IOCTL number */ ++#define VMCS_SM_MAGIC_TYPE 'I' ++ ++/* IOCTL commands */ ++enum vmcs_sm_cmd_e { ++ VMCS_SM_CMD_ALLOC = 0x5A, /* Start at 0x5A arbitrarily */ ++ VMCS_SM_CMD_ALLOC_SHARE, ++ VMCS_SM_CMD_LOCK, ++ VMCS_SM_CMD_LOCK_CACHE, ++ VMCS_SM_CMD_UNLOCK, ++ VMCS_SM_CMD_RESIZE, ++ VMCS_SM_CMD_UNMAP, ++ VMCS_SM_CMD_FREE, ++ VMCS_SM_CMD_FLUSH, ++ VMCS_SM_CMD_INVALID, ++ ++ VMCS_SM_CMD_SIZE_USR_HANDLE, ++ VMCS_SM_CMD_CHK_USR_HANDLE, ++ ++ VMCS_SM_CMD_MAPPED_USR_HANDLE, ++ VMCS_SM_CMD_MAPPED_USR_ADDRESS, ++ VMCS_SM_CMD_MAPPED_VC_HDL_FROM_ADDR, ++ VMCS_SM_CMD_MAPPED_VC_HDL_FROM_HDL, ++ VMCS_SM_CMD_MAPPED_VC_ADDR_FROM_HDL, ++ ++ VMCS_SM_CMD_VC_WALK_ALLOC, ++ VMCS_SM_CMD_HOST_WALK_MAP, ++ VMCS_SM_CMD_HOST_WALK_PID_ALLOC, ++ VMCS_SM_CMD_HOST_WALK_PID_MAP, ++ ++ VMCS_SM_CMD_CLEAN_INVALID, ++ ++ VMCS_SM_CMD_LAST /* Do no delete */ ++}; ++ ++/* Cache type supported, conveniently matches the user space definition in ++** user-vcsm.h. ++*/ ++enum vmcs_sm_cache_e { ++ VMCS_SM_CACHE_NONE, ++ VMCS_SM_CACHE_HOST, ++ VMCS_SM_CACHE_VC, ++ VMCS_SM_CACHE_BOTH, ++}; ++ ++/* IOCTL Data structures */ ++struct vmcs_sm_ioctl_alloc { ++ /* user -> kernel */ ++ unsigned int size; ++ unsigned int num; ++ enum vmcs_sm_cache_e cached; ++ char name[VMCS_SM_RESOURCE_NAME]; ++ ++ /* kernel -> user */ ++ unsigned int handle; ++ /* unsigned int base_addr; */ ++}; ++ ++struct vmcs_sm_ioctl_alloc_share { ++ /* user -> kernel */ ++ unsigned int handle; ++ unsigned int size; ++}; ++ ++struct vmcs_sm_ioctl_free { ++ /* user -> kernel */ ++ unsigned int handle; ++ /* unsigned int base_addr; */ ++}; ++ ++struct vmcs_sm_ioctl_lock_unlock { ++ /* user -> kernel */ ++ unsigned int handle; ++ ++ /* kernel -> user */ ++ unsigned int addr; ++}; ++ ++struct vmcs_sm_ioctl_lock_cache { ++ /* user -> kernel */ ++ unsigned int handle; ++ enum vmcs_sm_cache_e cached; ++}; ++ ++struct vmcs_sm_ioctl_resize { ++ /* user -> kernel */ ++ unsigned int handle; ++ unsigned int new_size; ++ ++ /* kernel -> user */ ++ unsigned int old_size; ++}; ++ ++struct vmcs_sm_ioctl_map { ++ /* user -> kernel */ ++ /* and kernel -> user */ ++ unsigned int pid; ++ unsigned int handle; ++ unsigned int addr; ++ ++ /* kernel -> user */ ++ unsigned int size; ++}; ++ ++struct vmcs_sm_ioctl_walk { ++ /* user -> kernel */ ++ unsigned int pid; ++}; ++ ++struct vmcs_sm_ioctl_chk { ++ /* user -> kernel */ ++ unsigned int handle; ++ ++ /* kernel -> user */ ++ unsigned int addr; ++ unsigned int size; ++ enum vmcs_sm_cache_e cache; ++}; ++ ++struct vmcs_sm_ioctl_size { ++ /* user -> kernel */ ++ unsigned int handle; ++ ++ /* kernel -> user */ ++ unsigned int size; ++}; ++ ++struct vmcs_sm_ioctl_cache { ++ /* user -> kernel */ ++ unsigned int handle; ++ unsigned int addr; ++ unsigned int size; ++}; ++ ++struct vmcs_sm_ioctl_clean_invalid { ++ /* user -> kernel */ ++ struct { ++ unsigned int cmd; ++ unsigned int handle; ++ unsigned int addr; ++ unsigned int size; ++ } s[8]; ++}; ++ ++/* IOCTL numbers */ ++#define VMCS_SM_IOCTL_MEM_ALLOC\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_ALLOC,\ ++ struct vmcs_sm_ioctl_alloc) ++#define VMCS_SM_IOCTL_MEM_ALLOC_SHARE\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_ALLOC_SHARE,\ ++ struct vmcs_sm_ioctl_alloc_share) ++#define VMCS_SM_IOCTL_MEM_LOCK\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_LOCK,\ ++ struct vmcs_sm_ioctl_lock_unlock) ++#define VMCS_SM_IOCTL_MEM_LOCK_CACHE\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_LOCK_CACHE,\ ++ struct vmcs_sm_ioctl_lock_cache) ++#define VMCS_SM_IOCTL_MEM_UNLOCK\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_UNLOCK,\ ++ struct vmcs_sm_ioctl_lock_unlock) ++#define VMCS_SM_IOCTL_MEM_RESIZE\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_RESIZE,\ ++ struct vmcs_sm_ioctl_resize) ++#define VMCS_SM_IOCTL_MEM_FREE\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_FREE,\ ++ struct vmcs_sm_ioctl_free) ++#define VMCS_SM_IOCTL_MEM_FLUSH\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_FLUSH,\ ++ struct vmcs_sm_ioctl_cache) ++#define VMCS_SM_IOCTL_MEM_INVALID\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_INVALID,\ ++ struct vmcs_sm_ioctl_cache) ++#define VMCS_SM_IOCTL_MEM_CLEAN_INVALID\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_CLEAN_INVALID,\ ++ struct vmcs_sm_ioctl_clean_invalid) ++ ++#define VMCS_SM_IOCTL_SIZE_USR_HDL\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_SIZE_USR_HANDLE,\ ++ struct vmcs_sm_ioctl_size) ++#define VMCS_SM_IOCTL_CHK_USR_HDL\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_CHK_USR_HANDLE,\ ++ struct vmcs_sm_ioctl_chk) ++ ++#define VMCS_SM_IOCTL_MAP_USR_HDL\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_MAPPED_USR_HANDLE,\ ++ struct vmcs_sm_ioctl_map) ++#define VMCS_SM_IOCTL_MAP_USR_ADDRESS\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_MAPPED_USR_ADDRESS,\ ++ struct vmcs_sm_ioctl_map) ++#define VMCS_SM_IOCTL_MAP_VC_HDL_FR_ADDR\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_MAPPED_VC_HDL_FROM_ADDR,\ ++ struct vmcs_sm_ioctl_map) ++#define VMCS_SM_IOCTL_MAP_VC_HDL_FR_HDL\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_MAPPED_VC_HDL_FROM_HDL,\ ++ struct vmcs_sm_ioctl_map) ++#define VMCS_SM_IOCTL_MAP_VC_ADDR_FR_HDL\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_MAPPED_VC_ADDR_FROM_HDL,\ ++ struct vmcs_sm_ioctl_map) ++ ++#define VMCS_SM_IOCTL_VC_WALK_ALLOC\ ++ _IO(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_VC_WALK_ALLOC) ++#define VMCS_SM_IOCTL_HOST_WALK_MAP\ ++ _IO(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_HOST_WALK_MAP) ++#define VMCS_SM_IOCTL_HOST_WALK_PID_ALLOC\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_HOST_WALK_PID_ALLOC,\ ++ struct vmcs_sm_ioctl_walk) ++#define VMCS_SM_IOCTL_HOST_WALK_PID_MAP\ ++ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_HOST_WALK_PID_MAP,\ ++ struct vmcs_sm_ioctl_walk) ++ ++/* ---- Variable Externs ------------------------------------------------- */ ++ ++/* ---- Function Prototypes ---------------------------------------------- */ ++ ++#endif /* __VMCS_SM_IOCTL_H__INCLUDED__ */ diff --git a/target/linux/brcm2708/patches-4.4/0039-Add-dev-gpiomem-device-for-rootless-user-GPIO-access.patch b/target/linux/brcm2708/patches-4.4/0039-Add-dev-gpiomem-device-for-rootless-user-GPIO-access.patch new file mode 100644 index 0000000000..f4bd017b26 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0039-Add-dev-gpiomem-device-for-rootless-user-GPIO-access.patch @@ -0,0 +1,306 @@ +From 1ee3b49a62efe1416a6b40fb5c6032288f3d2b08 Mon Sep 17 00:00:00 2001 +From: Luke Wren +Date: Fri, 21 Aug 2015 23:14:48 +0100 +Subject: [PATCH 039/232] Add /dev/gpiomem device for rootless user GPIO access + +Signed-off-by: Luke Wren + +bcm2835-gpiomem: Fix for ARCH_BCM2835 builds + +Build on ARCH_BCM2835, and fail to probe if no IO resource. + +See: https://github.com/raspberrypi/linux/issues/1154 +--- + drivers/char/broadcom/Kconfig | 9 ++ + drivers/char/broadcom/Makefile | 3 + + drivers/char/broadcom/bcm2835-gpiomem.c | 260 ++++++++++++++++++++++++++++++++ + 3 files changed, 272 insertions(+) + create mode 100644 drivers/char/broadcom/bcm2835-gpiomem.c + +--- a/drivers/char/broadcom/Kconfig ++++ b/drivers/char/broadcom/Kconfig +@@ -32,3 +32,12 @@ config BCM_VC_SM + help + Support for the VC shared memory on the Broadcom reference + design. Uses the VCHIQ stack. ++ ++config BCM2835_DEVGPIOMEM ++ tristate "/dev/gpiomem rootless GPIO access via mmap() on the BCM2835" ++ default m ++ help ++ Provides users with root-free access to the GPIO registers ++ on the 2835. Calling mmap(/dev/gpiomem) will map the GPIO ++ register page to the user's pointer. ++ +--- a/drivers/char/broadcom/Makefile ++++ b/drivers/char/broadcom/Makefile +@@ -1,3 +1,6 @@ + obj-$(CONFIG_BCM_VC_CMA) += vc_cma/ + obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o + obj-$(CONFIG_BCM_VC_SM) += vc_sm/ ++ ++obj-$(CONFIG_BCM2835_DEVGPIOMEM)+= bcm2835-gpiomem.o ++ +--- /dev/null ++++ b/drivers/char/broadcom/bcm2835-gpiomem.c +@@ -0,0 +1,260 @@ ++/** ++ * GPIO memory device driver ++ * ++ * Creates a chardev /dev/gpiomem which will provide user access to ++ * the BCM2835's GPIO registers when it is mmap()'d. ++ * No longer need root for user GPIO access, but without relaxing permissions ++ * on /dev/mem. ++ * ++ * Written by Luke Wren ++ * Copyright (c) 2015, 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DEVICE_NAME "bcm2835-gpiomem" ++#define DRIVER_NAME "gpiomem-bcm2835" ++#define DEVICE_MINOR 0 ++ ++struct bcm2835_gpiomem_instance { ++ unsigned long gpio_regs_phys; ++ struct device *dev; ++}; ++ ++static struct cdev bcm2835_gpiomem_cdev; ++static dev_t bcm2835_gpiomem_devid; ++static struct class *bcm2835_gpiomem_class; ++static struct device *bcm2835_gpiomem_dev; ++static struct bcm2835_gpiomem_instance *inst; ++ ++ ++/**************************************************************************** ++* ++* GPIO mem chardev file ops ++* ++***************************************************************************/ ++ ++static int bcm2835_gpiomem_open(struct inode *inode, struct file *file) ++{ ++ int dev = iminor(inode); ++ int ret = 0; ++ ++ dev_info(inst->dev, "gpiomem device opened."); ++ ++ if (dev != DEVICE_MINOR) { ++ dev_err(inst->dev, "Unknown minor device: %d", dev); ++ ret = -ENXIO; ++ } ++ return ret; ++} ++ ++static int bcm2835_gpiomem_release(struct inode *inode, struct file *file) ++{ ++ int dev = iminor(inode); ++ int ret = 0; ++ ++ if (dev != DEVICE_MINOR) { ++ dev_err(inst->dev, "Unknown minor device %d", dev); ++ ret = -ENXIO; ++ } ++ return ret; ++} ++ ++static const struct vm_operations_struct bcm2835_gpiomem_vm_ops = { ++#ifdef CONFIG_HAVE_IOREMAP_PROT ++ .access = generic_access_phys ++#endif ++}; ++ ++static int bcm2835_gpiomem_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ /* Ignore what the user says - they're getting the GPIO regs ++ whether they like it or not! */ ++ unsigned long gpio_page = inst->gpio_regs_phys >> PAGE_SHIFT; ++ ++ vma->vm_page_prot = phys_mem_access_prot(file, gpio_page, ++ PAGE_SIZE, ++ vma->vm_page_prot); ++ vma->vm_ops = &bcm2835_gpiomem_vm_ops; ++ if (remap_pfn_range(vma, vma->vm_start, ++ gpio_page, ++ PAGE_SIZE, ++ vma->vm_page_prot)) { ++ return -EAGAIN; ++ } ++ return 0; ++} ++ ++static const struct file_operations ++bcm2835_gpiomem_fops = { ++ .owner = THIS_MODULE, ++ .open = bcm2835_gpiomem_open, ++ .release = bcm2835_gpiomem_release, ++ .mmap = bcm2835_gpiomem_mmap, ++}; ++ ++ ++ /**************************************************************************** ++* ++* Probe and remove functions ++* ++***************************************************************************/ ++ ++ ++static int bcm2835_gpiomem_probe(struct platform_device *pdev) ++{ ++ int err; ++ void *ptr_err; ++ struct device *dev = &pdev->dev; ++ struct resource *ioresource; ++ ++ /* Allocate buffers and instance data */ ++ ++ inst = kzalloc(sizeof(struct bcm2835_gpiomem_instance), GFP_KERNEL); ++ ++ if (!inst) { ++ err = -ENOMEM; ++ goto failed_inst_alloc; ++ } ++ ++ inst->dev = dev; ++ ++ ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (ioresource) { ++ inst->gpio_regs_phys = ioresource->start; ++ } else { ++ dev_err(inst->dev, "failed to get IO resource"); ++ err = -ENOENT; ++ goto failed_get_resource; ++ } ++ ++ /* Create character device entries */ ++ ++ err = alloc_chrdev_region(&bcm2835_gpiomem_devid, ++ DEVICE_MINOR, 1, DEVICE_NAME); ++ if (err != 0) { ++ dev_err(inst->dev, "unable to allocate device number"); ++ goto failed_alloc_chrdev; ++ } ++ cdev_init(&bcm2835_gpiomem_cdev, &bcm2835_gpiomem_fops); ++ bcm2835_gpiomem_cdev.owner = THIS_MODULE; ++ err = cdev_add(&bcm2835_gpiomem_cdev, bcm2835_gpiomem_devid, 1); ++ if (err != 0) { ++ dev_err(inst->dev, "unable to register device"); ++ goto failed_cdev_add; ++ } ++ ++ /* Create sysfs entries */ ++ ++ bcm2835_gpiomem_class = class_create(THIS_MODULE, DEVICE_NAME); ++ ptr_err = bcm2835_gpiomem_class; ++ if (IS_ERR(ptr_err)) ++ goto failed_class_create; ++ ++ bcm2835_gpiomem_dev = device_create(bcm2835_gpiomem_class, NULL, ++ bcm2835_gpiomem_devid, NULL, ++ "gpiomem"); ++ ptr_err = bcm2835_gpiomem_dev; ++ if (IS_ERR(ptr_err)) ++ goto failed_device_create; ++ ++ dev_info(inst->dev, "Initialised: Registers at 0x%08lx", ++ inst->gpio_regs_phys); ++ ++ return 0; ++ ++failed_device_create: ++ class_destroy(bcm2835_gpiomem_class); ++failed_class_create: ++ cdev_del(&bcm2835_gpiomem_cdev); ++ err = PTR_ERR(ptr_err); ++failed_cdev_add: ++ unregister_chrdev_region(bcm2835_gpiomem_devid, 1); ++failed_alloc_chrdev: ++failed_get_resource: ++ kfree(inst); ++failed_inst_alloc: ++ dev_err(inst->dev, "could not load bcm2835_gpiomem"); ++ return err; ++} ++ ++static int bcm2835_gpiomem_remove(struct platform_device *pdev) ++{ ++ struct device *dev = inst->dev; ++ ++ kfree(inst); ++ device_destroy(bcm2835_gpiomem_class, bcm2835_gpiomem_devid); ++ class_destroy(bcm2835_gpiomem_class); ++ cdev_del(&bcm2835_gpiomem_cdev); ++ unregister_chrdev_region(bcm2835_gpiomem_devid, 1); ++ ++ dev_info(dev, "GPIO mem driver removed - OK"); ++ return 0; ++} ++ ++ /**************************************************************************** ++* ++* Register the driver with device tree ++* ++***************************************************************************/ ++ ++static const struct of_device_id bcm2835_gpiomem_of_match[] = { ++ {.compatible = "brcm,bcm2835-gpiomem",}, ++ { /* sentinel */ }, ++}; ++ ++MODULE_DEVICE_TABLE(of, bcm2835_gpiomem_of_match); ++ ++static struct platform_driver bcm2835_gpiomem_driver = { ++ .probe = bcm2835_gpiomem_probe, ++ .remove = bcm2835_gpiomem_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = bcm2835_gpiomem_of_match, ++ }, ++}; ++ ++module_platform_driver(bcm2835_gpiomem_driver); ++ ++MODULE_ALIAS("platform:gpiomem-bcm2835"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("gpiomem driver for accessing GPIO from userspace"); ++MODULE_AUTHOR("Luke Wren "); diff --git a/target/linux/brcm2708/patches-4.4/0039-vcsm-VideoCore-shared-memory-service-for-BCM2835.patch b/target/linux/brcm2708/patches-4.4/0039-vcsm-VideoCore-shared-memory-service-for-BCM2835.patch deleted file mode 100644 index 8940443a0b..0000000000 --- a/target/linux/brcm2708/patches-4.4/0039-vcsm-VideoCore-shared-memory-service-for-BCM2835.patch +++ /dev/null @@ -1,4393 +0,0 @@ -From 01574b9e0f1086399e7f796cc1dc8fcadabf4a46 Mon Sep 17 00:00:00 2001 -From: Tim Gover -Date: Tue, 22 Jul 2014 15:41:04 +0100 -Subject: [PATCH 039/170] vcsm: VideoCore shared memory service for BCM2835 -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add experimental support for the VideoCore shared memory service. -This allows user processes to allocate memory from VideoCore's -GPU relocatable heap and mmap the buffers. Additionally, the memory -handles can passed to other VideoCore services such as MMAL, OpenMax -and DispmanX - -TODO -* This driver was originally released for BCM28155 which has a different - cache architecture to BCM2835. Consequently, in this release only - uncached mappings are supported. However, there's no fundamental - reason which cached mappings cannot be support or BCM2835 -* More refactoring is required to remove the typedefs. -* Re-enable the some of the commented out debug-fs statistics which were - disabled when migrating code from proc-fs. -* There's a lot of code to support sharing of VCSM in order to support - Android. This could probably done more cleanly or perhaps just - removed. - -Signed-off-by: Tim Gover - -config: Disable VC_SM for now to fix hang with cutdown kernel - -vcsm: Use boolean as it cannot be built as module - -On building the bcm_vc_sm as a module we get the following error: - -v7_dma_flush_range and do_munmap are undefined in vc-sm.ko. - -Fix by making it not an option to build as module - -vcsm: Add ioctl for custom cache flushing - -vc-sm: Move headers out of arch directory - -Signed-off-by: Noralf Trønnes ---- - drivers/char/broadcom/Kconfig | 9 + - drivers/char/broadcom/Makefile | 1 + - drivers/char/broadcom/vc_sm/Makefile | 20 + - drivers/char/broadcom/vc_sm/vc_sm_defs.h | 181 ++ - drivers/char/broadcom/vc_sm/vc_sm_knl.h | 55 + - drivers/char/broadcom/vc_sm/vc_vchi_sm.c | 492 +++++ - drivers/char/broadcom/vc_sm/vc_vchi_sm.h | 82 + - drivers/char/broadcom/vc_sm/vmcs_sm.c | 3211 ++++++++++++++++++++++++++++++ - include/linux/broadcom/vmcs_sm_ioctl.h | 248 +++ - 9 files changed, 4299 insertions(+) - create mode 100644 drivers/char/broadcom/vc_sm/Makefile - create mode 100644 drivers/char/broadcom/vc_sm/vc_sm_defs.h - create mode 100644 drivers/char/broadcom/vc_sm/vc_sm_knl.h - create mode 100644 drivers/char/broadcom/vc_sm/vc_vchi_sm.c - create mode 100644 drivers/char/broadcom/vc_sm/vc_vchi_sm.h - create mode 100644 drivers/char/broadcom/vc_sm/vmcs_sm.c - create mode 100644 include/linux/broadcom/vmcs_sm_ioctl.h - ---- a/drivers/char/broadcom/Kconfig -+++ b/drivers/char/broadcom/Kconfig -@@ -23,3 +23,12 @@ config BCM2708_VCMEM - Helper for videocore memory access and total size allocation. - - endif -+ -+config BCM_VC_SM -+ bool "VMCS Shared Memory" -+ depends on BCM2708_VCHIQ -+ select BCM2708_VCMEM -+ default n -+ help -+ Support for the VC shared memory on the Broadcom reference -+ design. Uses the VCHIQ stack. ---- a/drivers/char/broadcom/Makefile -+++ b/drivers/char/broadcom/Makefile -@@ -1,2 +1,3 @@ - obj-$(CONFIG_BCM_VC_CMA) += vc_cma/ - obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o -+obj-$(CONFIG_BCM_VC_SM) += vc_sm/ ---- /dev/null -+++ b/drivers/char/broadcom/vc_sm/Makefile -@@ -0,0 +1,20 @@ -+EXTRA_CFLAGS += -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -+ -+EXTRA_CFLAGS += -I"drivers/misc/vc04_services" -+EXTRA_CFLAGS += -I"drivers/misc/vc04_services/interface/vchi" -+EXTRA_CFLAGS += -I"drivers/misc/vc04_services/interface/vchiq_arm" -+EXTRA_CFLAGS += -I"$(srctree)/fs/" -+ -+EXTRA_CFLAGS += -DOS_ASSERT_FAILURE -+EXTRA_CFLAGS += -D__STDC_VERSION=199901L -+EXTRA_CFLAGS += -D__STDC_VERSION__=199901L -+EXTRA_CFLAGS += -D__VCCOREVER__=0 -+EXTRA_CFLAGS += -D__KERNEL__ -+EXTRA_CFLAGS += -D__linux__ -+EXTRA_CFLAGS += -Werror -+ -+obj-$(CONFIG_BCM_VC_SM) := vc-sm.o -+ -+vc-sm-objs := \ -+ vmcs_sm.o \ -+ vc_vchi_sm.o ---- /dev/null -+++ b/drivers/char/broadcom/vc_sm/vc_sm_defs.h -@@ -0,0 +1,181 @@ -+/***************************************************************************** -+* 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. -+*****************************************************************************/ -+ -+#ifndef __VC_SM_DEFS_H__INCLUDED__ -+#define __VC_SM_DEFS_H__INCLUDED__ -+ -+/* FourCC code used for VCHI connection */ -+#define VC_SM_SERVER_NAME MAKE_FOURCC("SMEM") -+ -+/* Maximum message length */ -+#define VC_SM_MAX_MSG_LEN (sizeof(VC_SM_MSG_UNION_T) + \ -+ sizeof(VC_SM_MSG_HDR_T)) -+#define VC_SM_MAX_RSP_LEN (sizeof(VC_SM_MSG_UNION_T)) -+ -+/* Resource name maximum size */ -+#define VC_SM_RESOURCE_NAME 32 -+ -+/* All message types supported for HOST->VC direction */ -+typedef enum { -+ /* Allocate shared memory block */ -+ VC_SM_MSG_TYPE_ALLOC, -+ /* Lock allocated shared memory block */ -+ VC_SM_MSG_TYPE_LOCK, -+ /* Unlock allocated shared memory block */ -+ VC_SM_MSG_TYPE_UNLOCK, -+ /* Unlock allocated shared memory block, do not answer command */ -+ VC_SM_MSG_TYPE_UNLOCK_NOANS, -+ /* Free shared memory block */ -+ VC_SM_MSG_TYPE_FREE, -+ /* Resize a shared memory block */ -+ VC_SM_MSG_TYPE_RESIZE, -+ /* Walk the allocated shared memory block(s) */ -+ VC_SM_MSG_TYPE_WALK_ALLOC, -+ -+ /* A previously applied action will need to be reverted */ -+ VC_SM_MSG_TYPE_ACTION_CLEAN, -+ VC_SM_MSG_TYPE_MAX -+} VC_SM_MSG_TYPE; -+ -+/* Type of memory to be allocated */ -+typedef enum { -+ VC_SM_ALLOC_CACHED, -+ VC_SM_ALLOC_NON_CACHED, -+ -+} VC_SM_ALLOC_TYPE_T; -+ -+/* Message header for all messages in HOST->VC direction */ -+typedef struct { -+ int32_t type; -+ uint32_t trans_id; -+ uint8_t body[0]; -+ -+} VC_SM_MSG_HDR_T; -+ -+/* Request to allocate memory (HOST->VC) */ -+typedef struct { -+ /* type of memory to allocate */ -+ VC_SM_ALLOC_TYPE_T type; -+ /* byte amount of data to allocate per unit */ -+ uint32_t base_unit; -+ /* number of unit to allocate */ -+ uint32_t num_unit; -+ /* alignement to be applied on allocation */ -+ uint32_t alignement; -+ /* identity of who allocated this block */ -+ uint32_t allocator; -+ /* resource name (for easier tracking on vc side) */ -+ char name[VC_SM_RESOURCE_NAME]; -+ -+} VC_SM_ALLOC_T; -+ -+/* Result of a requested memory allocation (VC->HOST) */ -+typedef struct { -+ /* Transaction identifier */ -+ uint32_t trans_id; -+ -+ /* Resource handle */ -+ uint32_t res_handle; -+ /* Pointer to resource buffer */ -+ void *res_mem; -+ /* Resource base size (bytes) */ -+ uint32_t res_base_size; -+ /* Resource number */ -+ uint32_t res_num; -+ -+} VC_SM_ALLOC_RESULT_T; -+ -+/* Request to free a previously allocated memory (HOST->VC) */ -+typedef struct { -+ /* Resource handle (returned from alloc) */ -+ uint32_t res_handle; -+ /* Resource buffer (returned from alloc) */ -+ void *res_mem; -+ -+} VC_SM_FREE_T; -+ -+/* Request to lock a previously allocated memory (HOST->VC) */ -+typedef struct { -+ /* Resource handle (returned from alloc) */ -+ uint32_t res_handle; -+ /* Resource buffer (returned from alloc) */ -+ void *res_mem; -+ -+} VC_SM_LOCK_UNLOCK_T; -+ -+/* Request to resize a previously allocated memory (HOST->VC) */ -+typedef struct { -+ /* Resource handle (returned from alloc) */ -+ uint32_t res_handle; -+ /* Resource buffer (returned from alloc) */ -+ void *res_mem; -+ /* Resource *new* size requested (bytes) */ -+ uint32_t res_new_size; -+ -+} VC_SM_RESIZE_T; -+ -+/* Result of a requested memory lock (VC->HOST) */ -+typedef struct { -+ /* Transaction identifier */ -+ uint32_t trans_id; -+ -+ /* Resource handle */ -+ uint32_t res_handle; -+ /* Pointer to resource buffer */ -+ void *res_mem; -+ /* Pointer to former resource buffer if the memory -+ * was reallocated */ -+ void *res_old_mem; -+ -+} VC_SM_LOCK_RESULT_T; -+ -+/* Generic result for a request (VC->HOST) */ -+typedef struct { -+ /* Transaction identifier */ -+ uint32_t trans_id; -+ -+ int32_t success; -+ -+} VC_SM_RESULT_T; -+ -+/* Request to revert a previously applied action (HOST->VC) */ -+typedef struct { -+ /* Action of interest */ -+ VC_SM_MSG_TYPE res_action; -+ /* Transaction identifier for the action of interest */ -+ uint32_t action_trans_id; -+ -+} VC_SM_ACTION_CLEAN_T; -+ -+/* Request to remove all data associated with a given allocator (HOST->VC) */ -+typedef struct { -+ /* Allocator identifier */ -+ uint32_t allocator; -+ -+} VC_SM_FREE_ALL_T; -+ -+/* Union of ALL messages */ -+typedef union { -+ VC_SM_ALLOC_T alloc; -+ VC_SM_ALLOC_RESULT_T alloc_result; -+ VC_SM_FREE_T free; -+ VC_SM_ACTION_CLEAN_T action_clean; -+ VC_SM_RESIZE_T resize; -+ VC_SM_LOCK_RESULT_T lock_result; -+ VC_SM_RESULT_T result; -+ VC_SM_FREE_ALL_T free_all; -+ -+} VC_SM_MSG_UNION_T; -+ -+#endif /* __VC_SM_DEFS_H__INCLUDED__ */ ---- /dev/null -+++ b/drivers/char/broadcom/vc_sm/vc_sm_knl.h -@@ -0,0 +1,55 @@ -+/***************************************************************************** -+* 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. -+*****************************************************************************/ -+ -+#ifndef __VC_SM_KNL_H__INCLUDED__ -+#define __VC_SM_KNL_H__INCLUDED__ -+ -+#if !defined(__KERNEL__) -+#error "This interface is for kernel use only..." -+#endif -+ -+/* Type of memory to be locked (ie mapped) */ -+typedef enum { -+ VC_SM_LOCK_CACHED, -+ VC_SM_LOCK_NON_CACHED, -+ -+} VC_SM_LOCK_CACHE_MODE_T; -+ -+/* Allocate a shared memory handle and block. -+*/ -+int vc_sm_alloc(VC_SM_ALLOC_T *alloc, int *handle); -+ -+/* Free a previously allocated shared memory handle and block. -+*/ -+int vc_sm_free(int handle); -+ -+/* Lock a memory handle for use by kernel. -+*/ -+int vc_sm_lock(int handle, VC_SM_LOCK_CACHE_MODE_T mode, -+ long unsigned int *data); -+ -+/* Unlock a memory handle in use by kernel. -+*/ -+int vc_sm_unlock(int handle, int flush, int no_vc_unlock); -+ -+/* Get an internal resource handle mapped from the external one. -+*/ -+int vc_sm_int_handle(int handle); -+ -+/* Map a shared memory region for use by kernel. -+*/ -+int vc_sm_map(int handle, unsigned int sm_addr, VC_SM_LOCK_CACHE_MODE_T mode, -+ long unsigned int *data); -+ -+#endif /* __VC_SM_KNL_H__INCLUDED__ */ ---- /dev/null -+++ b/drivers/char/broadcom/vc_sm/vc_vchi_sm.c -@@ -0,0 +1,492 @@ -+/***************************************************************************** -+* Copyright 2011-2012 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 Files ----------------------------------------------------- */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "vc_vchi_sm.h" -+ -+#define VC_SM_VER 1 -+#define VC_SM_MIN_VER 0 -+ -+/* ---- Private Constants and Types -------------------------------------- */ -+ -+/* Command blocks come from a pool */ -+#define SM_MAX_NUM_CMD_RSP_BLKS 32 -+ -+struct sm_cmd_rsp_blk { -+ struct list_head head; /* To create lists */ -+ struct semaphore sema; /* To be signaled when the response is there */ -+ -+ uint16_t id; -+ uint16_t length; -+ -+ uint8_t msg[VC_SM_MAX_MSG_LEN]; -+ -+ uint32_t wait:1; -+ uint32_t sent:1; -+ uint32_t alloc:1; -+ -+}; -+ -+struct sm_instance { -+ uint32_t num_connections; -+ VCHI_SERVICE_HANDLE_T vchi_handle[VCHI_MAX_NUM_CONNECTIONS]; -+ struct task_struct *io_thread; -+ struct semaphore io_sema; -+ -+ uint32_t trans_id; -+ -+ struct mutex lock; -+ struct list_head cmd_list; -+ struct list_head rsp_list; -+ struct list_head dead_list; -+ -+ struct sm_cmd_rsp_blk free_blk[SM_MAX_NUM_CMD_RSP_BLKS]; -+ struct list_head free_list; -+ struct mutex free_lock; -+ struct semaphore free_sema; -+ -+}; -+ -+/* ---- Private Variables ------------------------------------------------ */ -+ -+/* ---- Private Function Prototypes -------------------------------------- */ -+ -+/* ---- Private Functions ------------------------------------------------ */ -+static struct -+sm_cmd_rsp_blk *vc_vchi_cmd_create(struct sm_instance *instance, -+ VC_SM_MSG_TYPE id, void *msg, -+ uint32_t size, int wait) -+{ -+ struct sm_cmd_rsp_blk *blk; -+ VC_SM_MSG_HDR_T *hdr; -+ -+ if (down_interruptible(&instance->free_sema)) { -+ blk = kmalloc(sizeof(*blk), GFP_KERNEL); -+ if (!blk) -+ return NULL; -+ -+ blk->alloc = 1; -+ sema_init(&blk->sema, 0); -+ } else { -+ mutex_lock(&instance->free_lock); -+ blk = -+ list_first_entry(&instance->free_list, -+ struct sm_cmd_rsp_blk, head); -+ list_del(&blk->head); -+ mutex_unlock(&instance->free_lock); -+ } -+ -+ blk->sent = 0; -+ blk->wait = wait; -+ blk->length = sizeof(*hdr) + size; -+ -+ hdr = (VC_SM_MSG_HDR_T *) blk->msg; -+ hdr->type = id; -+ mutex_lock(&instance->lock); -+ hdr->trans_id = blk->id = ++instance->trans_id; -+ mutex_unlock(&instance->lock); -+ -+ if (size) -+ memcpy(hdr->body, msg, size); -+ -+ return blk; -+} -+ -+static void -+vc_vchi_cmd_delete(struct sm_instance *instance, struct sm_cmd_rsp_blk *blk) -+{ -+ if (blk->alloc) { -+ kfree(blk); -+ return; -+ } -+ -+ mutex_lock(&instance->free_lock); -+ list_add(&blk->head, &instance->free_list); -+ mutex_unlock(&instance->free_lock); -+ up(&instance->free_sema); -+} -+ -+static int vc_vchi_sm_videocore_io(void *arg) -+{ -+ struct sm_instance *instance = arg; -+ struct sm_cmd_rsp_blk *cmd = NULL, *cmd_tmp; -+ VC_SM_RESULT_T *reply; -+ uint32_t reply_len; -+ int32_t status; -+ int svc_use = 1; -+ -+ while (1) { -+ if (svc_use) -+ vchi_service_release(instance->vchi_handle[0]); -+ svc_use = 0; -+ if (!down_interruptible(&instance->io_sema)) { -+ vchi_service_use(instance->vchi_handle[0]); -+ svc_use = 1; -+ -+ do { -+ unsigned int flags; -+ /* -+ * Get new command and move it to response list -+ */ -+ mutex_lock(&instance->lock); -+ if (list_empty(&instance->cmd_list)) { -+ /* no more commands to process */ -+ mutex_unlock(&instance->lock); -+ break; -+ } -+ cmd = -+ list_first_entry(&instance->cmd_list, -+ struct sm_cmd_rsp_blk, -+ head); -+ list_move(&cmd->head, &instance->rsp_list); -+ cmd->sent = 1; -+ mutex_unlock(&instance->lock); -+ -+ /* Send the command */ -+ flags = VCHI_FLAGS_BLOCK_UNTIL_QUEUED; -+ status = vchi_msg_queue( -+ instance->vchi_handle[0], -+ cmd->msg, cmd->length, -+ flags, NULL); -+ if (status) { -+ pr_err("%s: failed to queue message (%d)", -+ __func__, status); -+ } -+ -+ /* If no reply is needed then we're done */ -+ if (!cmd->wait) { -+ mutex_lock(&instance->lock); -+ list_del(&cmd->head); -+ mutex_unlock(&instance->lock); -+ vc_vchi_cmd_delete(instance, cmd); -+ continue; -+ } -+ -+ if (status) { -+ up(&cmd->sema); -+ continue; -+ } -+ -+ } while (1); -+ -+ while (!vchi_msg_peek -+ (instance->vchi_handle[0], (void **)&reply, -+ &reply_len, VCHI_FLAGS_NONE)) { -+ mutex_lock(&instance->lock); -+ list_for_each_entry(cmd, &instance->rsp_list, -+ head) { -+ if (cmd->id == reply->trans_id) -+ break; -+ } -+ mutex_unlock(&instance->lock); -+ -+ if (&cmd->head == &instance->rsp_list) { -+ pr_debug("%s: received response %u, throw away...", -+ __func__, reply->trans_id); -+ } else if (reply_len > sizeof(cmd->msg)) { -+ pr_err("%s: reply too big (%u) %u, throw away...", -+ __func__, reply_len, -+ reply->trans_id); -+ } else { -+ memcpy(cmd->msg, reply, reply_len); -+ up(&cmd->sema); -+ } -+ -+ vchi_msg_remove(instance->vchi_handle[0]); -+ } -+ -+ /* Go through the dead list and free them */ -+ mutex_lock(&instance->lock); -+ list_for_each_entry_safe(cmd, cmd_tmp, -+ &instance->dead_list, head) { -+ list_del(&cmd->head); -+ vc_vchi_cmd_delete(instance, cmd); -+ } -+ mutex_unlock(&instance->lock); -+ } -+ } -+ -+ return 0; -+} -+ -+static void vc_sm_vchi_callback(void *param, -+ const VCHI_CALLBACK_REASON_T reason, -+ void *msg_handle) -+{ -+ struct sm_instance *instance = param; -+ -+ (void)msg_handle; -+ -+ switch (reason) { -+ case VCHI_CALLBACK_MSG_AVAILABLE: -+ up(&instance->io_sema); -+ break; -+ -+ case VCHI_CALLBACK_SERVICE_CLOSED: -+ pr_info("%s: service CLOSED!!", __func__); -+ default: -+ break; -+ } -+} -+ -+VC_VCHI_SM_HANDLE_T vc_vchi_sm_init(VCHI_INSTANCE_T vchi_instance, -+ VCHI_CONNECTION_T **vchi_connections, -+ uint32_t num_connections) -+{ -+ uint32_t i; -+ struct sm_instance *instance; -+ int status; -+ -+ pr_debug("%s: start", __func__); -+ -+ if (num_connections > VCHI_MAX_NUM_CONNECTIONS) { -+ pr_err("%s: unsupported number of connections %u (max=%u)", -+ __func__, num_connections, VCHI_MAX_NUM_CONNECTIONS); -+ -+ goto err_null; -+ } -+ /* Allocate memory for this instance */ -+ instance = kzalloc(sizeof(*instance), GFP_KERNEL); -+ -+ /* Misc initialisations */ -+ mutex_init(&instance->lock); -+ sema_init(&instance->io_sema, 0); -+ INIT_LIST_HEAD(&instance->cmd_list); -+ INIT_LIST_HEAD(&instance->rsp_list); -+ INIT_LIST_HEAD(&instance->dead_list); -+ INIT_LIST_HEAD(&instance->free_list); -+ sema_init(&instance->free_sema, SM_MAX_NUM_CMD_RSP_BLKS); -+ mutex_init(&instance->free_lock); -+ for (i = 0; i < SM_MAX_NUM_CMD_RSP_BLKS; i++) { -+ sema_init(&instance->free_blk[i].sema, 0); -+ list_add(&instance->free_blk[i].head, &instance->free_list); -+ } -+ -+ /* Open the VCHI service connections */ -+ instance->num_connections = num_connections; -+ for (i = 0; i < num_connections; i++) { -+ SERVICE_CREATION_T params = { -+ VCHI_VERSION_EX(VC_SM_VER, VC_SM_MIN_VER), -+ VC_SM_SERVER_NAME, -+ vchi_connections[i], -+ 0, -+ 0, -+ vc_sm_vchi_callback, -+ instance, -+ 0, -+ 0, -+ 0, -+ }; -+ -+ status = vchi_service_open(vchi_instance, -+ ¶ms, &instance->vchi_handle[i]); -+ if (status) { -+ pr_err("%s: failed to open VCHI service (%d)", -+ __func__, status); -+ -+ goto err_close_services; -+ } -+ } -+ -+ /* Create the thread which takes care of all io to/from videoocore. */ -+ instance->io_thread = kthread_create(&vc_vchi_sm_videocore_io, -+ (void *)instance, "SMIO"); -+ if (instance->io_thread == NULL) { -+ pr_err("%s: failed to create SMIO thread", __func__); -+ -+ goto err_close_services; -+ } -+ set_user_nice(instance->io_thread, -10); -+ wake_up_process(instance->io_thread); -+ -+ pr_debug("%s: success - instance 0x%x", __func__, (unsigned)instance); -+ return instance; -+ -+err_close_services: -+ for (i = 0; i < instance->num_connections; i++) { -+ if (instance->vchi_handle[i] != NULL) -+ vchi_service_close(instance->vchi_handle[i]); -+ } -+ kfree(instance); -+err_null: -+ pr_debug("%s: FAILED", __func__); -+ return NULL; -+} -+ -+int vc_vchi_sm_stop(VC_VCHI_SM_HANDLE_T *handle) -+{ -+ struct sm_instance *instance; -+ uint32_t i; -+ -+ if (handle == NULL) { -+ pr_err("%s: invalid pointer to handle %p", __func__, handle); -+ goto lock; -+ } -+ -+ if (*handle == NULL) { -+ pr_err("%s: invalid handle %p", __func__, *handle); -+ goto lock; -+ } -+ -+ instance = *handle; -+ -+ /* Close all VCHI service connections */ -+ for (i = 0; i < instance->num_connections; i++) { -+ int32_t success; -+ vchi_service_use(instance->vchi_handle[i]); -+ -+ success = vchi_service_close(instance->vchi_handle[i]); -+ } -+ -+ kfree(instance); -+ -+ *handle = NULL; -+ return 0; -+ -+lock: -+ return -EINVAL; -+} -+ -+int vc_vchi_sm_send_msg(VC_VCHI_SM_HANDLE_T handle, -+ VC_SM_MSG_TYPE msg_id, -+ void *msg, uint32_t msg_size, -+ void *result, uint32_t result_size, -+ uint32_t *cur_trans_id, uint8_t wait_reply) -+{ -+ int status = 0; -+ struct sm_instance *instance = handle; -+ struct sm_cmd_rsp_blk *cmd_blk; -+ -+ if (handle == NULL) { -+ pr_err("%s: invalid handle", __func__); -+ return -EINVAL; -+ } -+ if (msg == NULL) { -+ pr_err("%s: invalid msg pointer", __func__); -+ return -EINVAL; -+ } -+ -+ cmd_blk = -+ vc_vchi_cmd_create(instance, msg_id, msg, msg_size, wait_reply); -+ if (cmd_blk == NULL) { -+ pr_err("[%s]: failed to allocate global tracking resource", -+ __func__); -+ return -ENOMEM; -+ } -+ -+ if (cur_trans_id != NULL) -+ *cur_trans_id = cmd_blk->id; -+ -+ mutex_lock(&instance->lock); -+ list_add_tail(&cmd_blk->head, &instance->cmd_list); -+ mutex_unlock(&instance->lock); -+ up(&instance->io_sema); -+ -+ if (!wait_reply) -+ /* We're done */ -+ return 0; -+ -+ /* Wait for the response */ -+ if (down_interruptible(&cmd_blk->sema)) { -+ mutex_lock(&instance->lock); -+ if (!cmd_blk->sent) { -+ list_del(&cmd_blk->head); -+ mutex_unlock(&instance->lock); -+ vc_vchi_cmd_delete(instance, cmd_blk); -+ return -ENXIO; -+ } -+ mutex_unlock(&instance->lock); -+ -+ mutex_lock(&instance->lock); -+ list_move(&cmd_blk->head, &instance->dead_list); -+ mutex_unlock(&instance->lock); -+ up(&instance->io_sema); -+ return -EINTR; /* We're done */ -+ } -+ -+ if (result && result_size) { -+ memcpy(result, cmd_blk->msg, result_size); -+ } else { -+ VC_SM_RESULT_T *res = (VC_SM_RESULT_T *) cmd_blk->msg; -+ status = (res->success == 0) ? 0 : -ENXIO; -+ } -+ -+ mutex_lock(&instance->lock); -+ list_del(&cmd_blk->head); -+ mutex_unlock(&instance->lock); -+ vc_vchi_cmd_delete(instance, cmd_blk); -+ return status; -+} -+ -+int vc_vchi_sm_alloc(VC_VCHI_SM_HANDLE_T handle, VC_SM_ALLOC_T *msg, -+ VC_SM_ALLOC_RESULT_T *result, uint32_t *cur_trans_id) -+{ -+ return vc_vchi_sm_send_msg(handle, VC_SM_MSG_TYPE_ALLOC, -+ msg, sizeof(*msg), result, sizeof(*result), -+ cur_trans_id, 1); -+} -+ -+int vc_vchi_sm_free(VC_VCHI_SM_HANDLE_T handle, -+ VC_SM_FREE_T *msg, uint32_t *cur_trans_id) -+{ -+ return vc_vchi_sm_send_msg(handle, VC_SM_MSG_TYPE_FREE, -+ msg, sizeof(*msg), 0, 0, cur_trans_id, 0); -+} -+ -+int vc_vchi_sm_lock(VC_VCHI_SM_HANDLE_T handle, -+ VC_SM_LOCK_UNLOCK_T *msg, -+ VC_SM_LOCK_RESULT_T *result, uint32_t *cur_trans_id) -+{ -+ return vc_vchi_sm_send_msg(handle, VC_SM_MSG_TYPE_LOCK, -+ msg, sizeof(*msg), result, sizeof(*result), -+ cur_trans_id, 1); -+} -+ -+int vc_vchi_sm_unlock(VC_VCHI_SM_HANDLE_T handle, -+ VC_SM_LOCK_UNLOCK_T *msg, -+ uint32_t *cur_trans_id, uint8_t wait_reply) -+{ -+ return vc_vchi_sm_send_msg(handle, wait_reply ? -+ VC_SM_MSG_TYPE_UNLOCK : -+ VC_SM_MSG_TYPE_UNLOCK_NOANS, msg, -+ sizeof(*msg), 0, 0, cur_trans_id, -+ wait_reply); -+} -+ -+int vc_vchi_sm_resize(VC_VCHI_SM_HANDLE_T handle, VC_SM_RESIZE_T *msg, -+ uint32_t *cur_trans_id) -+{ -+ return vc_vchi_sm_send_msg(handle, VC_SM_MSG_TYPE_RESIZE, -+ msg, sizeof(*msg), 0, 0, cur_trans_id, 1); -+} -+ -+int vc_vchi_sm_walk_alloc(VC_VCHI_SM_HANDLE_T handle) -+{ -+ return vc_vchi_sm_send_msg(handle, VC_SM_MSG_TYPE_WALK_ALLOC, -+ 0, 0, 0, 0, 0, 0); -+} -+ -+int vc_vchi_sm_clean_up(VC_VCHI_SM_HANDLE_T handle, VC_SM_ACTION_CLEAN_T *msg) -+{ -+ return vc_vchi_sm_send_msg(handle, VC_SM_MSG_TYPE_ACTION_CLEAN, -+ msg, sizeof(*msg), 0, 0, 0, 0); -+} ---- /dev/null -+++ b/drivers/char/broadcom/vc_sm/vc_vchi_sm.h -@@ -0,0 +1,82 @@ -+/***************************************************************************** -+* 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. -+*****************************************************************************/ -+ -+#ifndef __VC_VCHI_SM_H__INCLUDED__ -+#define __VC_VCHI_SM_H__INCLUDED__ -+ -+#include "interface/vchi/vchi.h" -+ -+#include "vc_sm_defs.h" -+ -+/* Forward declare. -+*/ -+typedef struct sm_instance *VC_VCHI_SM_HANDLE_T; -+ -+/* Initialize the shared memory service, opens up vchi connection to talk to it. -+*/ -+VC_VCHI_SM_HANDLE_T vc_vchi_sm_init(VCHI_INSTANCE_T vchi_instance, -+ VCHI_CONNECTION_T **vchi_connections, -+ uint32_t num_connections); -+ -+/* Terminates the shared memory service. -+*/ -+int vc_vchi_sm_stop(VC_VCHI_SM_HANDLE_T *handle); -+ -+/* Ask the shared memory service to allocate some memory on videocre and -+** return the result of this allocation (which upon success will be a pointer -+** to some memory in videocore space). -+*/ -+int vc_vchi_sm_alloc(VC_VCHI_SM_HANDLE_T handle, -+ VC_SM_ALLOC_T *alloc, -+ VC_SM_ALLOC_RESULT_T *alloc_result, uint32_t *trans_id); -+ -+/* Ask the shared memory service to free up some memory that was previously -+** allocated by the vc_vchi_sm_alloc function call. -+*/ -+int vc_vchi_sm_free(VC_VCHI_SM_HANDLE_T handle, -+ VC_SM_FREE_T *free, uint32_t *trans_id); -+ -+/* Ask the shared memory service to lock up some memory that was previously -+** allocated by the vc_vchi_sm_alloc function call. -+*/ -+int vc_vchi_sm_lock(VC_VCHI_SM_HANDLE_T handle, -+ VC_SM_LOCK_UNLOCK_T *lock_unlock, -+ VC_SM_LOCK_RESULT_T *lock_result, uint32_t *trans_id); -+ -+/* Ask the shared memory service to unlock some memory that was previously -+** allocated by the vc_vchi_sm_alloc function call. -+*/ -+int vc_vchi_sm_unlock(VC_VCHI_SM_HANDLE_T handle, -+ VC_SM_LOCK_UNLOCK_T *lock_unlock, -+ uint32_t *trans_id, uint8_t wait_reply); -+ -+/* Ask the shared memory service to resize some memory that was previously -+** allocated by the vc_vchi_sm_alloc function call. -+*/ -+int vc_vchi_sm_resize(VC_VCHI_SM_HANDLE_T handle, -+ VC_SM_RESIZE_T *resize, uint32_t *trans_id); -+ -+/* Walk the allocated resources on the videocore side, the allocation will -+** show up in the log. This is purely for debug/information and takes no -+** specific actions. -+*/ -+int vc_vchi_sm_walk_alloc(VC_VCHI_SM_HANDLE_T handle); -+ -+/* Clean up following a previously interrupted action which left the system -+** in a bad state of some sort. -+*/ -+int vc_vchi_sm_clean_up(VC_VCHI_SM_HANDLE_T handle, -+ VC_SM_ACTION_CLEAN_T *action_clean); -+ -+#endif /* __VC_VCHI_SM_H__INCLUDED__ */ ---- /dev/null -+++ b/drivers/char/broadcom/vc_sm/vmcs_sm.c -@@ -0,0 +1,3211 @@ -+/***************************************************************************** -+* Copyright 2011-2012 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 Files ----------------------------------------------------- */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "vchiq_connected.h" -+#include "vc_vchi_sm.h" -+ -+#include -+#include "vc_sm_knl.h" -+ -+/* ---- Private Constants and Types --------------------------------------- */ -+ -+#define DEVICE_NAME "vcsm" -+#define DEVICE_MINOR 0 -+ -+#define VC_SM_DIR_ROOT_NAME "vc-smem" -+#define VC_SM_DIR_ALLOC_NAME "alloc" -+#define VC_SM_STATE "state" -+#define VC_SM_STATS "statistics" -+#define VC_SM_RESOURCES "resources" -+#define VC_SM_DEBUG "debug" -+#define VC_SM_WRITE_BUF_SIZE 128 -+ -+/* Statistics tracked per resource and globally. -+*/ -+enum SM_STATS_T { -+ /* Attempt. */ -+ ALLOC, -+ FREE, -+ LOCK, -+ UNLOCK, -+ MAP, -+ FLUSH, -+ INVALID, -+ -+ END_ATTEMPT, -+ -+ /* Failure. */ -+ ALLOC_FAIL, -+ FREE_FAIL, -+ LOCK_FAIL, -+ UNLOCK_FAIL, -+ MAP_FAIL, -+ FLUSH_FAIL, -+ INVALID_FAIL, -+ -+ END_ALL, -+ -+}; -+ -+static const char *const sm_stats_human_read[] = { -+ "Alloc", -+ "Free", -+ "Lock", -+ "Unlock", -+ "Map", -+ "Cache Flush", -+ "Cache Invalidate", -+}; -+ -+typedef int (*VC_SM_SHOW) (struct seq_file *s, void *v); -+struct SM_PDE_T { -+ VC_SM_SHOW show; /* Debug fs function hookup. */ -+ struct dentry *dir_entry; /* Debug fs directory entry. */ -+ void *priv_data; /* Private data */ -+ -+}; -+ -+/* Single resource allocation tracked for all devices. -+*/ -+struct sm_mmap { -+ struct list_head map_list; /* Linked list of maps. */ -+ -+ struct SM_RESOURCE_T *resource; /* Pointer to the resource. */ -+ -+ pid_t res_pid; /* PID owning that resource. */ -+ unsigned int res_vc_hdl; /* Resource handle (videocore). */ -+ unsigned int res_usr_hdl; /* Resource handle (user). */ -+ -+ long unsigned int res_addr; /* Mapped virtual address. */ -+ struct vm_area_struct *vma; /* VM area for this mapping. */ -+ unsigned int ref_count; /* Reference count to this vma. */ -+ -+ /* Used to link maps associated with a resource. */ -+ struct list_head resource_map_list; -+}; -+ -+/* Single resource allocation tracked for each opened device. -+*/ -+struct SM_RESOURCE_T { -+ struct list_head resource_list; /* List of resources. */ -+ struct list_head global_resource_list; /* Global list of resources. */ -+ -+ pid_t pid; /* PID owning that resource. */ -+ uint32_t res_guid; /* Unique identifier. */ -+ uint32_t lock_count; /* Lock count for this resource. */ -+ uint32_t ref_count; /* Ref count for this resource. */ -+ -+ uint32_t res_handle; /* Resource allocation handle. */ -+ void *res_base_mem; /* Resource base memory address. */ -+ uint32_t res_size; /* Resource size allocated. */ -+ enum vmcs_sm_cache_e res_cached; /* Resource cache type. */ -+ struct SM_RESOURCE_T *res_shared; /* Shared resource */ -+ -+ enum SM_STATS_T res_stats[END_ALL]; /* Resource statistics. */ -+ -+ uint8_t map_count; /* Counter of mappings for this resource. */ -+ struct list_head map_list; /* Maps associated with a resource. */ -+ -+ struct SM_PRIV_DATA_T *private; -+}; -+ -+/* Private file data associated with each opened device. -+*/ -+struct SM_PRIV_DATA_T { -+ struct list_head resource_list; /* List of resources. */ -+ -+ pid_t pid; /* PID of creator. */ -+ -+ struct dentry *dir_pid; /* Debug fs entries root. */ -+ struct SM_PDE_T dir_stats; /* Debug fs entries statistics sub-tree. */ -+ struct SM_PDE_T dir_res; /* Debug fs resource sub-tree. */ -+ -+ int restart_sys; /* Tracks restart on interrupt. */ -+ VC_SM_MSG_TYPE int_action; /* Interrupted action. */ -+ uint32_t int_trans_id; /* Interrupted transaction. */ -+ -+}; -+ -+/* Global state information. -+*/ -+struct SM_STATE_T { -+ VC_VCHI_SM_HANDLE_T sm_handle; /* Handle for videocore service. */ -+ struct dentry *dir_root; /* Debug fs entries root. */ -+ struct dentry *dir_alloc; /* Debug fs entries allocations. */ -+ struct SM_PDE_T dir_stats; /* Debug fs entries statistics sub-tree. */ -+ struct SM_PDE_T dir_state; /* Debug fs entries state sub-tree. */ -+ struct dentry *debug; /* Debug fs entries debug. */ -+ -+ struct mutex map_lock; /* Global map lock. */ -+ struct list_head map_list; /* List of maps. */ -+ struct list_head resource_list; /* List of resources. */ -+ -+ enum SM_STATS_T deceased[END_ALL]; /* Natural termination stats. */ -+ enum SM_STATS_T terminated[END_ALL]; /* Forced termination stats. */ -+ uint32_t res_deceased_cnt; /* Natural termination counter. */ -+ uint32_t res_terminated_cnt; /* Forced termination counter. */ -+ -+ struct cdev sm_cdev; /* Device. */ -+ dev_t sm_devid; /* Device identifier. */ -+ struct class *sm_class; /* Class. */ -+ struct device *sm_dev; /* Device. */ -+ -+ struct SM_PRIV_DATA_T *data_knl; /* Kernel internal data tracking. */ -+ -+ struct mutex lock; /* Global lock. */ -+ uint32_t guid; /* GUID (next) tracker. */ -+ -+}; -+ -+/* ---- Private Variables ----------------------------------------------- */ -+ -+static struct SM_STATE_T *sm_state; -+static int sm_inited; -+ -+static const char *const sm_cache_map_vector[] = { -+ "(null)", -+ "host", -+ "videocore", -+ "host+videocore", -+}; -+ -+/* ---- Private Function Prototypes -------------------------------------- */ -+ -+/* ---- Private Functions ------------------------------------------------ */ -+ -+static inline unsigned vcaddr_to_pfn(unsigned long vc_addr) -+{ -+ unsigned long pfn = vc_addr & 0x3FFFFFFF; -+ pfn += mm_vc_mem_phys_addr; -+ pfn >>= PAGE_SHIFT; -+ return pfn; -+} -+ -+/* Carries over to the state statistics the statistics once owned by a deceased -+** resource. -+*/ -+static void vc_sm_resource_deceased(struct SM_RESOURCE_T *p_res, int terminated) -+{ -+ if (sm_state != NULL) { -+ if (p_res != NULL) { -+ int ix; -+ -+ if (terminated) -+ sm_state->res_terminated_cnt++; -+ else -+ sm_state->res_deceased_cnt++; -+ -+ for (ix = 0; ix < END_ALL; ix++) { -+ if (terminated) -+ sm_state->terminated[ix] += -+ p_res->res_stats[ix]; -+ else -+ sm_state->deceased[ix] += -+ p_res->res_stats[ix]; -+ } -+ } -+ } -+} -+ -+/* Fetch a videocore handle corresponding to a mapping of the pid+address -+** returns 0 (ie NULL) if no such handle exists in the global map. -+*/ -+static unsigned int vmcs_sm_vc_handle_from_pid_and_address(unsigned int pid, -+ unsigned int addr) -+{ -+ struct sm_mmap *map = NULL; -+ unsigned int handle = 0; -+ -+ if (!sm_state || addr == 0) -+ goto out; -+ -+ mutex_lock(&(sm_state->map_lock)); -+ -+ /* Lookup the resource. -+ */ -+ if (!list_empty(&sm_state->map_list)) { -+ list_for_each_entry(map, &sm_state->map_list, map_list) { -+ if (map->res_pid != pid || map->res_addr != addr) -+ continue; -+ -+ pr_debug("[%s]: global map %p (pid %u, addr %lx) -> vc-hdl %x (usr-hdl %x)\n", -+ __func__, map, map->res_pid, map->res_addr, -+ map->res_vc_hdl, map->res_usr_hdl); -+ -+ handle = map->res_vc_hdl; -+ break; -+ } -+ } -+ -+ mutex_unlock(&(sm_state->map_lock)); -+ -+out: -+ /* Use a debug log here as it may be a valid situation that we query -+ ** for something that is not mapped, we do not want a kernel log each -+ ** time around. -+ ** -+ ** There are other error log that would pop up accordingly if someone -+ ** subsequently tries to use something invalid after being told not to -+ ** use it... -+ */ -+ if (handle == 0) { -+ pr_debug("[%s]: not a valid map (pid %u, addr %x)\n", -+ __func__, pid, addr); -+ } -+ -+ return handle; -+} -+ -+/* Fetch a user handle corresponding to a mapping of the pid+address -+** returns 0 (ie NULL) if no such handle exists in the global map. -+*/ -+static unsigned int vmcs_sm_usr_handle_from_pid_and_address(unsigned int pid, -+ unsigned int addr) -+{ -+ struct sm_mmap *map = NULL; -+ unsigned int handle = 0; -+ -+ if (!sm_state || addr == 0) -+ goto out; -+ -+ mutex_lock(&(sm_state->map_lock)); -+ -+ /* Lookup the resource. -+ */ -+ if (!list_empty(&sm_state->map_list)) { -+ list_for_each_entry(map, &sm_state->map_list, map_list) { -+ if (map->res_pid != pid || map->res_addr != addr) -+ continue; -+ -+ pr_debug("[%s]: global map %p (pid %u, addr %lx) -> usr-hdl %x (vc-hdl %x)\n", -+ __func__, map, map->res_pid, map->res_addr, -+ map->res_usr_hdl, map->res_vc_hdl); -+ -+ handle = map->res_usr_hdl; -+ break; -+ } -+ } -+ -+ mutex_unlock(&(sm_state->map_lock)); -+ -+out: -+ /* Use a debug log here as it may be a valid situation that we query -+ * for something that is not mapped yet. -+ * -+ * There are other error log that would pop up accordingly if someone -+ * subsequently tries to use something invalid after being told not to -+ * use it... -+ */ -+ if (handle == 0) -+ pr_debug("[%s]: not a valid map (pid %u, addr %x)\n", -+ __func__, pid, addr); -+ -+ return handle; -+} -+ -+#if defined(DO_NOT_USE) -+/* Fetch an address corresponding to a mapping of the pid+handle -+** returns 0 (ie NULL) if no such address exists in the global map. -+*/ -+static unsigned int vmcs_sm_usr_address_from_pid_and_vc_handle(unsigned int pid, -+ unsigned int hdl) -+{ -+ struct sm_mmap *map = NULL; -+ unsigned int addr = 0; -+ -+ if (sm_state == NULL || hdl == 0) -+ goto out; -+ -+ mutex_lock(&(sm_state->map_lock)); -+ -+ /* Lookup the resource. -+ */ -+ if (!list_empty(&sm_state->map_list)) { -+ list_for_each_entry(map, &sm_state->map_list, map_list) { -+ if (map->res_pid != pid || map->res_vc_hdl != hdl) -+ continue; -+ -+ pr_debug("[%s]: global map %p (pid %u, vc-hdl %x, usr-hdl %x) -> addr %lx\n", -+ __func__, map, map->res_pid, map->res_vc_hdl, -+ map->res_usr_hdl, map->res_addr); -+ -+ addr = map->res_addr; -+ break; -+ } -+ } -+ -+ mutex_unlock(&(sm_state->map_lock)); -+ -+out: -+ /* Use a debug log here as it may be a valid situation that we query -+ ** for something that is not mapped, we do not want a kernel log each -+ ** time around. -+ ** -+ ** There are other error log that would pop up accordingly if someone -+ ** subsequently tries to use something invalid after being told not to -+ ** use it... -+ */ -+ if (addr == 0) -+ pr_debug("[%s]: not a valid map (pid %u, hdl %x)\n", -+ __func__, pid, hdl); -+ -+ return addr; -+} -+#endif -+ -+/* Fetch an address corresponding to a mapping of the pid+handle -+** returns 0 (ie NULL) if no such address exists in the global map. -+*/ -+static unsigned int vmcs_sm_usr_address_from_pid_and_usr_handle(unsigned int -+ pid, -+ unsigned int -+ hdl) -+{ -+ struct sm_mmap *map = NULL; -+ unsigned int addr = 0; -+ -+ if (sm_state == NULL || hdl == 0) -+ goto out; -+ -+ mutex_lock(&(sm_state->map_lock)); -+ -+ /* Lookup the resource. -+ */ -+ if (!list_empty(&sm_state->map_list)) { -+ list_for_each_entry(map, &sm_state->map_list, map_list) { -+ if (map->res_pid != pid || map->res_usr_hdl != hdl) -+ continue; -+ -+ pr_debug("[%s]: global map %p (pid %u, vc-hdl %x, usr-hdl %x) -> addr %lx\n", -+ __func__, map, map->res_pid, map->res_vc_hdl, -+ map->res_usr_hdl, map->res_addr); -+ -+ addr = map->res_addr; -+ break; -+ } -+ } -+ -+ mutex_unlock(&(sm_state->map_lock)); -+ -+out: -+ /* Use a debug log here as it may be a valid situation that we query -+ * for something that is not mapped, we do not want a kernel log each -+ * time around. -+ * -+ * There are other error log that would pop up accordingly if someone -+ * subsequently tries to use something invalid after being told not to -+ * use it... -+ */ -+ if (addr == 0) -+ pr_debug("[%s]: not a valid map (pid %u, hdl %x)\n", __func__, -+ pid, hdl); -+ -+ return addr; -+} -+ -+/* Adds a resource mapping to the global data list. -+*/ -+static void vmcs_sm_add_map(struct SM_STATE_T *state, -+ struct SM_RESOURCE_T *resource, struct sm_mmap *map) -+{ -+ mutex_lock(&(state->map_lock)); -+ -+ /* Add to the global list of mappings -+ */ -+ list_add(&map->map_list, &state->map_list); -+ -+ /* Add to the list of mappings for this resource -+ */ -+ list_add(&map->resource_map_list, &resource->map_list); -+ resource->map_count++; -+ -+ mutex_unlock(&(state->map_lock)); -+ -+ pr_debug("[%s]: added map %p (pid %u, vc-hdl %x, usr-hdl %x, addr %lx)\n", -+ __func__, map, map->res_pid, map->res_vc_hdl, -+ map->res_usr_hdl, map->res_addr); -+} -+ -+/* Removes a resource mapping from the global data list. -+*/ -+static void vmcs_sm_remove_map(struct SM_STATE_T *state, -+ struct SM_RESOURCE_T *resource, -+ struct sm_mmap *map) -+{ -+ mutex_lock(&(state->map_lock)); -+ -+ /* Remove from the global list of mappings -+ */ -+ list_del(&map->map_list); -+ -+ /* Remove from the list of mapping for this resource -+ */ -+ list_del(&map->resource_map_list); -+ if (resource->map_count > 0) -+ resource->map_count--; -+ -+ mutex_unlock(&(state->map_lock)); -+ -+ pr_debug("[%s]: removed map %p (pid %d, vc-hdl %x, usr-hdl %x, addr %lx)\n", -+ __func__, map, map->res_pid, map->res_vc_hdl, map->res_usr_hdl, -+ map->res_addr); -+ -+ kfree(map); -+} -+ -+/* Read callback for the global state proc entry. -+*/ -+static int vc_sm_global_state_show(struct seq_file *s, void *v) -+{ -+ struct sm_mmap *map = NULL; -+ int map_count = 0; -+ -+ if (sm_state == NULL) -+ return 0; -+ -+ seq_printf(s, "\nVC-ServiceHandle 0x%x\n", -+ (unsigned int)sm_state->sm_handle); -+ -+ /* Log all applicable mapping(s). -+ */ -+ -+ mutex_lock(&(sm_state->map_lock)); -+ -+ if (!list_empty(&sm_state->map_list)) { -+ list_for_each_entry(map, &sm_state->map_list, map_list) { -+ map_count++; -+ -+ seq_printf(s, "\nMapping 0x%x\n", -+ (unsigned int)map); -+ seq_printf(s, " TGID %u\n", -+ map->res_pid); -+ seq_printf(s, " VC-HDL 0x%x\n", -+ map->res_vc_hdl); -+ seq_printf(s, " USR-HDL 0x%x\n", -+ map->res_usr_hdl); -+ seq_printf(s, " USR-ADDR 0x%lx\n", -+ map->res_addr); -+ } -+ } -+ -+ mutex_unlock(&(sm_state->map_lock)); -+ seq_printf(s, "\n\nTotal map count: %d\n\n", map_count); -+ -+ return 0; -+} -+ -+static int vc_sm_global_statistics_show(struct seq_file *s, void *v) -+{ -+ int ix; -+ -+ /* Global state tracked statistics. -+ */ -+ if (sm_state != NULL) { -+ seq_puts(s, "\nDeceased Resources Statistics\n"); -+ -+ seq_printf(s, "\nNatural Cause (%u occurences)\n", -+ sm_state->res_deceased_cnt); -+ for (ix = 0; ix < END_ATTEMPT; ix++) { -+ if (sm_state->deceased[ix] > 0) { -+ seq_printf(s, " %u\t%s\n", -+ sm_state->deceased[ix], -+ sm_stats_human_read[ix]); -+ } -+ } -+ seq_puts(s, "\n"); -+ for (ix = 0; ix < END_ATTEMPT; ix++) { -+ if (sm_state->deceased[ix + END_ATTEMPT] > 0) { -+ seq_printf(s, " %u\tFAILED %s\n", -+ sm_state->deceased[ix + END_ATTEMPT], -+ sm_stats_human_read[ix]); -+ } -+ } -+ -+ seq_printf(s, "\nForcefull (%u occurences)\n", -+ sm_state->res_terminated_cnt); -+ for (ix = 0; ix < END_ATTEMPT; ix++) { -+ if (sm_state->terminated[ix] > 0) { -+ seq_printf(s, " %u\t%s\n", -+ sm_state->terminated[ix], -+ sm_stats_human_read[ix]); -+ } -+ } -+ seq_puts(s, "\n"); -+ for (ix = 0; ix < END_ATTEMPT; ix++) { -+ if (sm_state->terminated[ix + END_ATTEMPT] > 0) { -+ seq_printf(s, " %u\tFAILED %s\n", -+ sm_state->terminated[ix + -+ END_ATTEMPT], -+ sm_stats_human_read[ix]); -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+#if 0 -+/* Read callback for the statistics proc entry. -+*/ -+static int vc_sm_statistics_show(struct seq_file *s, void *v) -+{ -+ int ix; -+ struct SM_PRIV_DATA_T *file_data; -+ struct SM_RESOURCE_T *resource; -+ int res_count = 0; -+ struct SM_PDE_T *p_pde; -+ -+ p_pde = (struct SM_PDE_T *)(s->private); -+ file_data = (struct SM_PRIV_DATA_T *)(p_pde->priv_data); -+ -+ if (file_data == NULL) -+ return 0; -+ -+ /* Per process statistics. -+ */ -+ -+ seq_printf(s, "\nStatistics for TGID %d\n", file_data->pid); -+ -+ mutex_lock(&(sm_state->map_lock)); -+ -+ if (!list_empty(&file_data->resource_list)) { -+ list_for_each_entry(resource, &file_data->resource_list, -+ resource_list) { -+ res_count++; -+ -+ seq_printf(s, "\nGUID: 0x%x\n\n", -+ resource->res_guid); -+ for (ix = 0; ix < END_ATTEMPT; ix++) { -+ if (resource->res_stats[ix] > 0) { -+ seq_printf(s, -+ " %u\t%s\n", -+ resource->res_stats[ix], -+ sm_stats_human_read[ix]); -+ } -+ } -+ seq_puts(s, "\n"); -+ for (ix = 0; ix < END_ATTEMPT; ix++) { -+ if (resource->res_stats[ix + END_ATTEMPT] > 0) { -+ seq_printf(s, -+ " %u\tFAILED %s\n", -+ resource->res_stats[ -+ ix + END_ATTEMPT], -+ sm_stats_human_read[ix]); -+ } -+ } -+ } -+ } -+ -+ mutex_unlock(&(sm_state->map_lock)); -+ -+ seq_printf(s, "\nResources Count %d\n", res_count); -+ -+ return 0; -+} -+#endif -+ -+#if 0 -+/* Read callback for the allocation proc entry. */ -+static int vc_sm_alloc_show(struct seq_file *s, void *v) -+{ -+ struct SM_PRIV_DATA_T *file_data; -+ struct SM_RESOURCE_T *resource; -+ int alloc_count = 0; -+ struct SM_PDE_T *p_pde; -+ -+ p_pde = (struct SM_PDE_T *)(s->private); -+ file_data = (struct SM_PRIV_DATA_T *)(p_pde->priv_data); -+ -+ if (!file_data) -+ return 0; -+ -+ /* Per process statistics. */ -+ seq_printf(s, "\nAllocation for TGID %d\n", file_data->pid); -+ -+ mutex_lock(&(sm_state->map_lock)); -+ -+ if (!list_empty(&file_data->resource_list)) { -+ list_for_each_entry(resource, &file_data->resource_list, -+ resource_list) { -+ alloc_count++; -+ -+ seq_printf(s, "\nGUID: 0x%x\n", -+ resource->res_guid); -+ seq_printf(s, "Lock Count: %u\n", -+ resource->lock_count); -+ seq_printf(s, "Mapped: %s\n", -+ (resource->map_count ? "yes" : "no")); -+ seq_printf(s, "VC-handle: 0x%x\n", -+ resource->res_handle); -+ seq_printf(s, "VC-address: 0x%p\n", -+ resource->res_base_mem); -+ seq_printf(s, "VC-size (bytes): %u\n", -+ resource->res_size); -+ seq_printf(s, "Cache: %s\n", -+ sm_cache_map_vector[resource->res_cached]); -+ } -+ } -+ -+ mutex_unlock(&(sm_state->map_lock)); -+ -+ seq_printf(s, "\n\nTotal allocation count: %d\n\n", alloc_count); -+ -+ return 0; -+} -+#endif -+ -+static int vc_sm_seq_file_show(struct seq_file *s, void *v) -+{ -+ struct SM_PDE_T *sm_pde; -+ -+ sm_pde = (struct SM_PDE_T *)(s->private); -+ -+ if (sm_pde && sm_pde->show) -+ sm_pde->show(s, v); -+ -+ return 0; -+} -+ -+static int vc_sm_single_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, vc_sm_seq_file_show, inode->i_private); -+} -+ -+static const struct file_operations vc_sm_debug_fs_fops = { -+ .open = vc_sm_single_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; -+ -+/* Adds a resource to the private data list which tracks all the allocated -+** data. -+*/ -+static void vmcs_sm_add_resource(struct SM_PRIV_DATA_T *privdata, -+ struct SM_RESOURCE_T *resource) -+{ -+ mutex_lock(&(sm_state->map_lock)); -+ list_add(&resource->resource_list, &privdata->resource_list); -+ list_add(&resource->global_resource_list, &sm_state->resource_list); -+ mutex_unlock(&(sm_state->map_lock)); -+ -+ pr_debug("[%s]: added resource %p (base addr %p, hdl %x, size %u, cache %u)\n", -+ __func__, resource, resource->res_base_mem, -+ resource->res_handle, resource->res_size, resource->res_cached); -+} -+ -+/* Locates a resource and acquire a reference on it. -+** The resource won't be deleted while there is a reference on it. -+*/ -+static struct SM_RESOURCE_T *vmcs_sm_acquire_resource(struct SM_PRIV_DATA_T -+ *private, -+ unsigned int res_guid) -+{ -+ struct SM_RESOURCE_T *resource, *ret = NULL; -+ -+ mutex_lock(&(sm_state->map_lock)); -+ -+ list_for_each_entry(resource, &private->resource_list, resource_list) { -+ if (resource->res_guid != res_guid) -+ continue; -+ -+ pr_debug("[%s]: located resource %p (guid: %x, base addr %p, hdl %x, size %u, cache %u)\n", -+ __func__, resource, resource->res_guid, -+ resource->res_base_mem, resource->res_handle, -+ resource->res_size, resource->res_cached); -+ resource->ref_count++; -+ ret = resource; -+ break; -+ } -+ -+ mutex_unlock(&(sm_state->map_lock)); -+ -+ return ret; -+} -+ -+/* Locates a resource and acquire a reference on it. -+** The resource won't be deleted while there is a reference on it. -+*/ -+static struct SM_RESOURCE_T *vmcs_sm_acquire_first_resource( -+ struct SM_PRIV_DATA_T *private) -+{ -+ struct SM_RESOURCE_T *resource, *ret = NULL; -+ -+ mutex_lock(&(sm_state->map_lock)); -+ -+ list_for_each_entry(resource, &private->resource_list, resource_list) { -+ pr_debug("[%s]: located resource %p (guid: %x, base addr %p, hdl %x, size %u, cache %u)\n", -+ __func__, resource, resource->res_guid, -+ resource->res_base_mem, resource->res_handle, -+ resource->res_size, resource->res_cached); -+ resource->ref_count++; -+ ret = resource; -+ break; -+ } -+ -+ mutex_unlock(&(sm_state->map_lock)); -+ -+ return ret; -+} -+ -+/* Locates a resource and acquire a reference on it. -+** The resource won't be deleted while there is a reference on it. -+*/ -+static struct SM_RESOURCE_T *vmcs_sm_acquire_global_resource(unsigned int -+ res_guid) -+{ -+ struct SM_RESOURCE_T *resource, *ret = NULL; -+ -+ mutex_lock(&(sm_state->map_lock)); -+ -+ list_for_each_entry(resource, &sm_state->resource_list, -+ global_resource_list) { -+ if (resource->res_guid != res_guid) -+ continue; -+ -+ pr_debug("[%s]: located resource %p (guid: %x, base addr %p, hdl %x, size %u, cache %u)\n", -+ __func__, resource, resource->res_guid, -+ resource->res_base_mem, resource->res_handle, -+ resource->res_size, resource->res_cached); -+ resource->ref_count++; -+ ret = resource; -+ break; -+ } -+ -+ mutex_unlock(&(sm_state->map_lock)); -+ -+ return ret; -+} -+ -+/* Release a previously acquired resource. -+** The resource will be deleted when its refcount reaches 0. -+*/ -+static void vmcs_sm_release_resource(struct SM_RESOURCE_T *resource, int force) -+{ -+ struct SM_PRIV_DATA_T *private = resource->private; -+ struct sm_mmap *map, *map_tmp; -+ struct SM_RESOURCE_T *res_tmp; -+ int ret; -+ -+ mutex_lock(&(sm_state->map_lock)); -+ -+ if (--resource->ref_count) { -+ if (force) -+ pr_err("[%s]: resource %p in use\n", __func__, resource); -+ -+ mutex_unlock(&(sm_state->map_lock)); -+ return; -+ } -+ -+ /* Time to free the resource. Start by removing it from the list */ -+ list_del(&resource->resource_list); -+ list_del(&resource->global_resource_list); -+ -+ /* Walk the global resource list, find out if the resource is used -+ * somewhere else. In which case we don't want to delete it. -+ */ -+ list_for_each_entry(res_tmp, &sm_state->resource_list, -+ global_resource_list) { -+ if (res_tmp->res_handle == resource->res_handle) { -+ resource->res_handle = 0; -+ break; -+ } -+ } -+ -+ mutex_unlock(&(sm_state->map_lock)); -+ -+ pr_debug("[%s]: freeing data - guid %x, hdl %x, base address %p\n", -+ __func__, resource->res_guid, resource->res_handle, -+ resource->res_base_mem); -+ resource->res_stats[FREE]++; -+ -+ /* Make sure the resource we're removing is unmapped first */ -+ if (resource->map_count && !list_empty(&resource->map_list)) { -+ down_write(¤t->mm->mmap_sem); -+ list_for_each_entry_safe(map, map_tmp, &resource->map_list, -+ resource_map_list) { -+ ret = -+ do_munmap(current->mm, map->res_addr, -+ resource->res_size); -+ if (ret) { -+ pr_err("[%s]: could not unmap resource %p\n", -+ __func__, resource); -+ } -+ } -+ up_write(¤t->mm->mmap_sem); -+ } -+ -+ /* Free up the videocore allocated resource. -+ */ -+ if (resource->res_handle) { -+ VC_SM_FREE_T free = { -+ resource->res_handle, resource->res_base_mem -+ }; -+ int status = vc_vchi_sm_free(sm_state->sm_handle, &free, -+ &private->int_trans_id); -+ if (status != 0 && status != -EINTR) { -+ pr_err("[%s]: failed to free memory on videocore (status: %u, trans_id: %u)\n", -+ __func__, status, private->int_trans_id); -+ resource->res_stats[FREE_FAIL]++; -+ ret = -EPERM; -+ } -+ } -+ -+ /* Free up the shared resource. -+ */ -+ if (resource->res_shared) -+ vmcs_sm_release_resource(resource->res_shared, 0); -+ -+ /* Free up the local resource tracking this allocation. -+ */ -+ vc_sm_resource_deceased(resource, force); -+ kfree(resource); -+} -+ -+/* Dump the map table for the driver. If process is -1, dumps the whole table, -+** if process is a valid pid (non -1) dump only the entries associated with the -+** pid of interest. -+*/ -+static void vmcs_sm_host_walk_map_per_pid(int pid) -+{ -+ struct sm_mmap *map = NULL; -+ -+ /* Make sure the device was started properly. -+ */ -+ if (sm_state == NULL) { -+ pr_err("[%s]: invalid device\n", __func__); -+ return; -+ } -+ -+ mutex_lock(&(sm_state->map_lock)); -+ -+ /* Log all applicable mapping(s). -+ */ -+ if (!list_empty(&sm_state->map_list)) { -+ list_for_each_entry(map, &sm_state->map_list, map_list) { -+ if (pid == -1 || map->res_pid == pid) { -+ pr_info("[%s]: tgid: %u - vc-hdl: %x, usr-hdl: %x, usr-addr: %lx\n", -+ __func__, map->res_pid, map->res_vc_hdl, -+ map->res_usr_hdl, map->res_addr); -+ } -+ } -+ } -+ -+ mutex_unlock(&(sm_state->map_lock)); -+ -+ return; -+} -+ -+/* Dump the allocation table from host side point of view. This only dumps the -+** data allocated for this process/device referenced by the file_data. -+*/ -+static void vmcs_sm_host_walk_alloc(struct SM_PRIV_DATA_T *file_data) -+{ -+ struct SM_RESOURCE_T *resource = NULL; -+ -+ /* Make sure the device was started properly. -+ */ -+ if ((sm_state == NULL) || (file_data == NULL)) { -+ pr_err("[%s]: invalid device\n", __func__); -+ return; -+ } -+ -+ mutex_lock(&(sm_state->map_lock)); -+ -+ if (!list_empty(&file_data->resource_list)) { -+ list_for_each_entry(resource, &file_data->resource_list, -+ resource_list) { -+ pr_info("[%s]: guid: %x - hdl: %x, vc-mem: %p, size: %u, cache: %u\n", -+ __func__, resource->res_guid, resource->res_handle, -+ resource->res_base_mem, resource->res_size, -+ resource->res_cached); -+ } -+ } -+ -+ mutex_unlock(&(sm_state->map_lock)); -+ -+ return; -+} -+ -+/* Create support for private data tracking. -+*/ -+static struct SM_PRIV_DATA_T *vc_sm_create_priv_data(pid_t id) -+{ -+ char alloc_name[32]; -+ struct SM_PRIV_DATA_T *file_data = NULL; -+ -+ /* Allocate private structure. */ -+ file_data = kzalloc(sizeof(*file_data), GFP_KERNEL); -+ -+ if (!file_data) { -+ pr_err("[%s]: cannot allocate file data\n", __func__); -+ goto out; -+ } -+ -+ snprintf(alloc_name, sizeof(alloc_name), "%d", id); -+ -+ INIT_LIST_HEAD(&file_data->resource_list); -+ file_data->pid = id; -+ file_data->dir_pid = debugfs_create_dir(alloc_name, -+ sm_state->dir_alloc); -+#if 0 -+ /* TODO: fix this to support querying statistics per pid */ -+ -+ if (IS_ERR_OR_NULL(file_data->dir_pid)) { -+ file_data->dir_pid = NULL; -+ } else { -+ struct dentry *dir_entry; -+ -+ dir_entry = debugfs_create_file(VC_SM_RESOURCES, S_IRUGO, -+ file_data->dir_pid, file_data, -+ vc_sm_debug_fs_fops); -+ -+ file_data->dir_res.dir_entry = dir_entry; -+ file_data->dir_res.priv_data = file_data; -+ file_data->dir_res.show = &vc_sm_alloc_show; -+ -+ dir_entry = debugfs_create_file(VC_SM_STATS, S_IRUGO, -+ file_data->dir_pid, file_data, -+ vc_sm_debug_fs_fops); -+ -+ file_data->dir_res.dir_entry = dir_entry; -+ file_data->dir_res.priv_data = file_data; -+ file_data->dir_res.show = &vc_sm_statistics_show; -+ } -+ pr_debug("[%s]: private data allocated %p\n", __func__, file_data); -+ -+#endif -+out: -+ return file_data; -+} -+ -+/* Open the device. Creates a private state to help track all allocation -+** associated with this device. -+*/ -+static int vc_sm_open(struct inode *inode, struct file *file) -+{ -+ int ret = 0; -+ -+ /* Make sure the device was started properly. -+ */ -+ if (!sm_state) { -+ pr_err("[%s]: invalid device\n", __func__); -+ ret = -EPERM; -+ goto out; -+ } -+ -+ file->private_data = vc_sm_create_priv_data(current->tgid); -+ if (file->private_data == NULL) { -+ pr_err("[%s]: failed to create data tracker\n", __func__); -+ -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+out: -+ return ret; -+} -+ -+/* Close the device. Free up all resources still associated with this device -+** at the time. -+*/ -+static int vc_sm_release(struct inode *inode, struct file *file) -+{ -+ struct SM_PRIV_DATA_T *file_data = -+ (struct SM_PRIV_DATA_T *)file->private_data; -+ struct SM_RESOURCE_T *resource; -+ int ret = 0; -+ -+ /* Make sure the device was started properly. -+ */ -+ if (sm_state == NULL || file_data == NULL) { -+ pr_err("[%s]: invalid device\n", __func__); -+ ret = -EPERM; -+ goto out; -+ } -+ -+ pr_debug("[%s]: using private data %p\n", __func__, file_data); -+ -+ if (file_data->restart_sys == -EINTR) { -+ VC_SM_ACTION_CLEAN_T action_clean; -+ -+ pr_debug("[%s]: releasing following EINTR on %u (trans_id: %u) (likely due to signal)...\n", -+ __func__, file_data->int_action, -+ file_data->int_trans_id); -+ -+ action_clean.res_action = file_data->int_action; -+ action_clean.action_trans_id = file_data->int_trans_id; -+ -+ vc_vchi_sm_clean_up(sm_state->sm_handle, &action_clean); -+ } -+ -+ while ((resource = vmcs_sm_acquire_first_resource(file_data)) != NULL) { -+ vmcs_sm_release_resource(resource, 0); -+ vmcs_sm_release_resource(resource, 1); -+ } -+ -+ /* Remove the corresponding proc entry. */ -+ debugfs_remove_recursive(file_data->dir_pid); -+ -+ /* Terminate the private data. -+ */ -+ kfree(file_data); -+ -+out: -+ return ret; -+} -+ -+static void vcsm_vma_open(struct vm_area_struct *vma) -+{ -+ struct sm_mmap *map = (struct sm_mmap *)vma->vm_private_data; -+ -+ pr_debug("[%s]: virt %lx-%lx, pid %i, pfn %i\n", -+ __func__, vma->vm_start, vma->vm_end, (int)current->tgid, -+ (int)vma->vm_pgoff); -+ -+ map->ref_count++; -+} -+ -+static void vcsm_vma_close(struct vm_area_struct *vma) -+{ -+ struct sm_mmap *map = (struct sm_mmap *)vma->vm_private_data; -+ -+ pr_debug("[%s]: virt %lx-%lx, pid %i, pfn %i\n", -+ __func__, vma->vm_start, vma->vm_end, (int)current->tgid, -+ (int)vma->vm_pgoff); -+ -+ map->ref_count--; -+ -+ /* Remove from the map table. -+ */ -+ if (map->ref_count == 0) -+ vmcs_sm_remove_map(sm_state, map->resource, map); -+} -+ -+static int vcsm_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -+{ -+ struct sm_mmap *map = (struct sm_mmap *)vma->vm_private_data; -+ struct SM_RESOURCE_T *resource = map->resource; -+ pgoff_t page_offset; -+ unsigned long pfn; -+ int ret = 0; -+ -+ /* Lock the resource if necessary. -+ */ -+ if (!resource->lock_count) { -+ VC_SM_LOCK_UNLOCK_T lock_unlock; -+ VC_SM_LOCK_RESULT_T lock_result; -+ int status; -+ -+ lock_unlock.res_handle = resource->res_handle; -+ lock_unlock.res_mem = resource->res_base_mem; -+ -+ pr_debug("[%s]: attempt to lock data - hdl %x, base address %p\n", -+ __func__, lock_unlock.res_handle, lock_unlock.res_mem); -+ -+ /* Lock the videocore allocated resource. -+ */ -+ status = vc_vchi_sm_lock(sm_state->sm_handle, -+ &lock_unlock, &lock_result, 0); -+ if ((status != 0) || -+ ((status == 0) && (lock_result.res_mem == NULL))) { -+ pr_err("[%s]: failed to lock memory on videocore (status: %u)\n", -+ __func__, status); -+ resource->res_stats[LOCK_FAIL]++; -+ return VM_FAULT_SIGBUS; -+ } -+ -+ pfn = vcaddr_to_pfn((unsigned long)resource->res_base_mem); -+ outer_inv_range(__pfn_to_phys(pfn), -+ __pfn_to_phys(pfn) + resource->res_size); -+ -+ resource->res_stats[LOCK]++; -+ resource->lock_count++; -+ -+ /* Keep track of the new base memory. -+ */ -+ if ((lock_result.res_mem != NULL) && -+ (lock_result.res_old_mem != NULL) && -+ (lock_result.res_mem != lock_result.res_old_mem)) { -+ resource->res_base_mem = lock_result.res_mem; -+ } -+ } -+ -+ /* We don't use vmf->pgoff since that has the fake offset */ -+ page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start); -+ pfn = (uint32_t)resource->res_base_mem & 0x3FFFFFFF; -+ pfn += mm_vc_mem_phys_addr; -+ pfn += page_offset; -+ pfn >>= PAGE_SHIFT; -+ -+ /* Finally, remap it */ -+ ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn); -+ -+ switch (ret) { -+ case 0: -+ case -ERESTARTSYS: -+ return VM_FAULT_NOPAGE; -+ case -ENOMEM: -+ case -EAGAIN: -+ return VM_FAULT_OOM; -+ default: -+ return VM_FAULT_SIGBUS; -+ } -+} -+ -+static struct vm_operations_struct vcsm_vm_ops = { -+ .open = vcsm_vma_open, -+ .close = vcsm_vma_close, -+ .fault = vcsm_vma_fault, -+}; -+ -+/* Walks a VMA and clean each valid page from the cache */ -+static void vcsm_vma_cache_clean_page_range(unsigned long addr, -+ unsigned long end) -+{ -+ pgd_t *pgd; -+ pud_t *pud; -+ pmd_t *pmd; -+ pte_t *pte; -+ unsigned long pgd_next, pud_next, pmd_next; -+ -+ if (addr >= end) -+ return; -+ -+ /* Walk PGD */ -+ pgd = pgd_offset(current->mm, addr); -+ do { -+ pgd_next = pgd_addr_end(addr, end); -+ -+ if (pgd_none(*pgd) || pgd_bad(*pgd)) -+ continue; -+ -+ /* Walk PUD */ -+ pud = pud_offset(pgd, addr); -+ do { -+ pud_next = pud_addr_end(addr, pgd_next); -+ if (pud_none(*pud) || pud_bad(*pud)) -+ continue; -+ -+ /* Walk PMD */ -+ pmd = pmd_offset(pud, addr); -+ do { -+ pmd_next = pmd_addr_end(addr, pud_next); -+ if (pmd_none(*pmd) || pmd_bad(*pmd)) -+ continue; -+ -+ /* Walk PTE */ -+ pte = pte_offset_map(pmd, addr); -+ do { -+ if (pte_none(*pte) -+ || !pte_present(*pte)) -+ continue; -+ -+ /* Clean + invalidate */ -+ dmac_flush_range((const void *) addr, -+ (const void *) -+ (addr + PAGE_SIZE)); -+ -+ } while (pte++, addr += -+ PAGE_SIZE, addr != pmd_next); -+ pte_unmap(pte); -+ -+ } while (pmd++, addr = pmd_next, addr != pud_next); -+ -+ } while (pud++, addr = pud_next, addr != pgd_next); -+ } while (pgd++, addr = pgd_next, addr != end); -+} -+ -+/* Map an allocated data into something that the user space. -+*/ -+static int vc_sm_mmap(struct file *file, struct vm_area_struct *vma) -+{ -+ int ret = 0; -+ struct SM_PRIV_DATA_T *file_data = -+ (struct SM_PRIV_DATA_T *)file->private_data; -+ struct SM_RESOURCE_T *resource = NULL; -+ struct sm_mmap *map = NULL; -+ -+ /* Make sure the device was started properly. -+ */ -+ if ((sm_state == NULL) || (file_data == NULL)) { -+ pr_err("[%s]: invalid device\n", __func__); -+ return -EPERM; -+ } -+ -+ pr_debug("[%s]: private data %p, guid %x\n", __func__, file_data, -+ ((unsigned int)vma->vm_pgoff << PAGE_SHIFT)); -+ -+ /* We lookup to make sure that the data we are being asked to mmap is -+ ** something that we allocated. -+ ** -+ ** We use the offset information as the key to tell us which resource -+ ** we are mapping. -+ */ -+ resource = vmcs_sm_acquire_resource(file_data, -+ ((unsigned int)vma->vm_pgoff << -+ PAGE_SHIFT)); -+ if (resource == NULL) { -+ pr_err("[%s]: failed to locate resource for guid %x\n", __func__, -+ ((unsigned int)vma->vm_pgoff << PAGE_SHIFT)); -+ return -ENOMEM; -+ } -+ -+ pr_debug("[%s]: guid %x, tgid %u, %u, %u\n", -+ __func__, resource->res_guid, current->tgid, resource->pid, -+ file_data->pid); -+ -+ /* Check permissions. -+ */ -+ if (resource->pid && (resource->pid != current->tgid)) { -+ pr_err("[%s]: current tgid %u != %u owner\n", -+ __func__, current->tgid, resource->pid); -+ ret = -EPERM; -+ goto error; -+ } -+ -+ /* Verify that what we are asked to mmap is proper. -+ */ -+ if (resource->res_size != (unsigned int)(vma->vm_end - vma->vm_start)) { -+ pr_err("[%s]: size inconsistency (resource: %u - mmap: %u)\n", -+ __func__, -+ resource->res_size, -+ (unsigned int)(vma->vm_end - vma->vm_start)); -+ -+ ret = -EINVAL; -+ goto error; -+ } -+ -+ /* Keep track of the tuple in the global resource list such that one -+ * can do a mapping lookup for address/memory handle. -+ */ -+ map = kzalloc(sizeof(*map), GFP_KERNEL); -+ if (map == NULL) { -+ pr_err("[%s]: failed to allocate global tracking resource\n", -+ __func__); -+ ret = -ENOMEM; -+ goto error; -+ } -+ -+ map->res_pid = current->tgid; -+ map->res_vc_hdl = resource->res_handle; -+ map->res_usr_hdl = resource->res_guid; -+ map->res_addr = (long unsigned int)vma->vm_start; -+ map->resource = resource; -+ map->vma = vma; -+ vmcs_sm_add_map(sm_state, resource, map); -+ -+ /* We are not actually mapping the pages, we just provide a fault -+ ** handler to allow pages to be mapped when accessed -+ */ -+ vma->vm_flags |= -+ VM_IO | VM_PFNMAP | VM_DONTCOPY | VM_DONTEXPAND; -+ vma->vm_ops = &vcsm_vm_ops; -+ vma->vm_private_data = map; -+ -+ /* vm_pgoff is the first PFN of the mapped memory */ -+ vma->vm_pgoff = (unsigned long)resource->res_base_mem & 0x3FFFFFFF; -+ vma->vm_pgoff += mm_vc_mem_phys_addr; -+ vma->vm_pgoff >>= PAGE_SHIFT; -+ -+ if ((resource->res_cached == VMCS_SM_CACHE_NONE) || -+ (resource->res_cached == VMCS_SM_CACHE_VC)) { -+ /* Allocated non host cached memory, honour it. -+ */ -+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); -+ } -+ -+ pr_debug("[%s]: resource %p (guid %x) - cnt %u, base address %p, handle %x, size %u (%u), cache %u\n", -+ __func__, -+ resource, resource->res_guid, resource->lock_count, -+ resource->res_base_mem, resource->res_handle, -+ resource->res_size, (unsigned int)(vma->vm_end - vma->vm_start), -+ resource->res_cached); -+ -+ pr_debug("[%s]: resource %p (base address %p, handle %x) - map-count %d, usr-addr %x\n", -+ __func__, resource, resource->res_base_mem, -+ resource->res_handle, resource->map_count, -+ (unsigned int)vma->vm_start); -+ -+ vcsm_vma_open(vma); -+ resource->res_stats[MAP]++; -+ vmcs_sm_release_resource(resource, 0); -+ return 0; -+ -+error: -+ resource->res_stats[MAP_FAIL]++; -+ vmcs_sm_release_resource(resource, 0); -+ return ret; -+} -+ -+/* Allocate a shared memory handle and block. -+*/ -+int vc_sm_ioctl_alloc(struct SM_PRIV_DATA_T *private, -+ struct vmcs_sm_ioctl_alloc *ioparam) -+{ -+ int ret = 0; -+ int status; -+ struct SM_RESOURCE_T *resource; -+ VC_SM_ALLOC_T alloc = { 0 }; -+ VC_SM_ALLOC_RESULT_T result = { 0 }; -+ -+ /* Setup our allocation parameters */ -+ alloc.type = ((ioparam->cached == VMCS_SM_CACHE_VC) -+ || (ioparam->cached == -+ VMCS_SM_CACHE_BOTH)) ? VC_SM_ALLOC_CACHED : -+ VC_SM_ALLOC_NON_CACHED; -+ alloc.base_unit = ioparam->size; -+ alloc.num_unit = ioparam->num; -+ alloc.allocator = current->tgid; -+ /* Align to kernel page size */ -+ alloc.alignement = 4096; -+ /* Align the size to the kernel page size */ -+ alloc.base_unit = -+ (alloc.base_unit + alloc.alignement - 1) & ~(alloc.alignement - 1); -+ if (*ioparam->name) { -+ memcpy(alloc.name, ioparam->name, sizeof(alloc.name) - 1); -+ } else { -+ memcpy(alloc.name, VMCS_SM_RESOURCE_NAME_DEFAULT, -+ sizeof(VMCS_SM_RESOURCE_NAME_DEFAULT)); -+ } -+ -+ pr_debug("[%s]: attempt to allocate \"%s\" data - type %u, base %u (%u), num %u, alignement %u\n", -+ __func__, alloc.name, alloc.type, ioparam->size, -+ alloc.base_unit, alloc.num_unit, alloc.alignement); -+ -+ /* Allocate local resource to track this allocation. -+ */ -+ resource = kzalloc(sizeof(*resource), GFP_KERNEL); -+ if (!resource) { -+ ret = -ENOMEM; -+ goto error; -+ } -+ INIT_LIST_HEAD(&resource->map_list); -+ resource->ref_count++; -+ resource->pid = current->tgid; -+ -+ /* Allocate the videocore resource. -+ */ -+ status = vc_vchi_sm_alloc(sm_state->sm_handle, &alloc, &result, -+ &private->int_trans_id); -+ if (status == -EINTR) { -+ pr_debug("[%s]: requesting allocate memory action restart (trans_id: %u)\n", -+ __func__, private->int_trans_id); -+ ret = -ERESTARTSYS; -+ private->restart_sys = -EINTR; -+ private->int_action = VC_SM_MSG_TYPE_ALLOC; -+ goto error; -+ } else if (status != 0 || (status == 0 && result.res_mem == NULL)) { -+ pr_err("[%s]: failed to allocate memory on videocore (status: %u, trans_id: %u)\n", -+ __func__, status, private->int_trans_id); -+ ret = -ENOMEM; -+ resource->res_stats[ALLOC_FAIL]++; -+ goto error; -+ } -+ -+ /* Keep track of the resource we created. -+ */ -+ resource->private = private; -+ resource->res_handle = result.res_handle; -+ resource->res_base_mem = result.res_mem; -+ resource->res_size = alloc.base_unit * alloc.num_unit; -+ resource->res_cached = ioparam->cached; -+ -+ /* Kernel/user GUID. This global identifier is used for mmap'ing the -+ * allocated region from user space, it is passed as the mmap'ing -+ * offset, we use it to 'hide' the videocore handle/address. -+ */ -+ mutex_lock(&sm_state->lock); -+ resource->res_guid = ++sm_state->guid; -+ mutex_unlock(&sm_state->lock); -+ resource->res_guid <<= PAGE_SHIFT; -+ -+ vmcs_sm_add_resource(private, resource); -+ -+ pr_debug("[%s]: allocated data - guid %x, hdl %x, base address %p, size %d, cache %d\n", -+ __func__, resource->res_guid, resource->res_handle, -+ resource->res_base_mem, resource->res_size, -+ resource->res_cached); -+ -+ /* We're done */ -+ resource->res_stats[ALLOC]++; -+ ioparam->handle = resource->res_guid; -+ return 0; -+ -+error: -+ pr_err("[%s]: failed to allocate \"%s\" data (%i) - type %u, base %u (%u), num %u, alignment %u\n", -+ __func__, alloc.name, ret, alloc.type, ioparam->size, -+ alloc.base_unit, alloc.num_unit, alloc.alignement); -+ if (resource != NULL) { -+ vc_sm_resource_deceased(resource, 1); -+ kfree(resource); -+ } -+ return ret; -+} -+ -+/* Share an allocate memory handle and block. -+*/ -+int vc_sm_ioctl_alloc_share(struct SM_PRIV_DATA_T *private, -+ struct vmcs_sm_ioctl_alloc_share *ioparam) -+{ -+ struct SM_RESOURCE_T *resource, *shared_resource; -+ int ret = 0; -+ -+ pr_debug("[%s]: attempt to share resource %u\n", __func__, -+ ioparam->handle); -+ -+ shared_resource = vmcs_sm_acquire_global_resource(ioparam->handle); -+ if (shared_resource == NULL) { -+ ret = -ENOMEM; -+ goto error; -+ } -+ -+ /* Allocate local resource to track this allocation. -+ */ -+ resource = kzalloc(sizeof(*resource), GFP_KERNEL); -+ if (resource == NULL) { -+ pr_err("[%s]: failed to allocate local tracking resource\n", -+ __func__); -+ ret = -ENOMEM; -+ goto error; -+ } -+ INIT_LIST_HEAD(&resource->map_list); -+ resource->ref_count++; -+ resource->pid = current->tgid; -+ -+ /* Keep track of the resource we created. -+ */ -+ resource->private = private; -+ resource->res_handle = shared_resource->res_handle; -+ resource->res_base_mem = shared_resource->res_base_mem; -+ resource->res_size = shared_resource->res_size; -+ resource->res_cached = shared_resource->res_cached; -+ resource->res_shared = shared_resource; -+ -+ mutex_lock(&sm_state->lock); -+ resource->res_guid = ++sm_state->guid; -+ mutex_unlock(&sm_state->lock); -+ resource->res_guid <<= PAGE_SHIFT; -+ -+ vmcs_sm_add_resource(private, resource); -+ -+ pr_debug("[%s]: allocated data - guid %x, hdl %x, base address %p, size %d, cache %d\n", -+ __func__, resource->res_guid, resource->res_handle, -+ resource->res_base_mem, resource->res_size, -+ resource->res_cached); -+ -+ /* We're done */ -+ resource->res_stats[ALLOC]++; -+ ioparam->handle = resource->res_guid; -+ ioparam->size = resource->res_size; -+ return 0; -+ -+error: -+ pr_err("[%s]: failed to share %u\n", __func__, ioparam->handle); -+ if (shared_resource != NULL) -+ vmcs_sm_release_resource(shared_resource, 0); -+ -+ return ret; -+} -+ -+/* Free a previously allocated shared memory handle and block. -+*/ -+static int vc_sm_ioctl_free(struct SM_PRIV_DATA_T *private, -+ struct vmcs_sm_ioctl_free *ioparam) -+{ -+ struct SM_RESOURCE_T *resource = -+ vmcs_sm_acquire_resource(private, ioparam->handle); -+ -+ if (resource == NULL) { -+ pr_err("[%s]: resource for guid %u does not exist\n", __func__, -+ ioparam->handle); -+ return -EINVAL; -+ } -+ -+ /* Check permissions. -+ */ -+ if (resource->pid && (resource->pid != current->tgid)) { -+ pr_err("[%s]: current tgid %u != %u owner\n", -+ __func__, current->tgid, resource->pid); -+ vmcs_sm_release_resource(resource, 0); -+ return -EPERM; -+ } -+ -+ vmcs_sm_release_resource(resource, 0); -+ vmcs_sm_release_resource(resource, 0); -+ return 0; -+} -+ -+/* Resize a previously allocated shared memory handle and block. -+*/ -+static int vc_sm_ioctl_resize(struct SM_PRIV_DATA_T *private, -+ struct vmcs_sm_ioctl_resize *ioparam) -+{ -+ int ret = 0; -+ int status; -+ VC_SM_RESIZE_T resize; -+ struct SM_RESOURCE_T *resource; -+ -+ /* Locate resource from GUID. -+ */ -+ resource = vmcs_sm_acquire_resource(private, ioparam->handle); -+ if (!resource) { -+ pr_err("[%s]: failed resource - guid %x\n", -+ __func__, ioparam->handle); -+ ret = -EFAULT; -+ goto error; -+ } -+ -+ /* If the resource is locked, its reference count will be not NULL, -+ ** in which case we will not be allowed to resize it anyways, so -+ ** reject the attempt here. -+ */ -+ if (resource->lock_count != 0) { -+ pr_err("[%s]: cannot resize - guid %x, ref-cnt %d\n", -+ __func__, ioparam->handle, resource->lock_count); -+ ret = -EFAULT; -+ goto error; -+ } -+ -+ /* Check permissions. -+ */ -+ if (resource->pid && (resource->pid != current->tgid)) { -+ pr_err("[%s]: current tgid %u != %u owner\n", __func__, -+ current->tgid, resource->pid); -+ ret = -EPERM; -+ goto error; -+ } -+ -+ if (resource->map_count != 0) { -+ pr_err("[%s]: cannot resize - guid %x, ref-cnt %d\n", -+ __func__, ioparam->handle, resource->map_count); -+ ret = -EFAULT; -+ goto error; -+ } -+ -+ resize.res_handle = resource->res_handle; -+ resize.res_mem = resource->res_base_mem; -+ resize.res_new_size = ioparam->new_size; -+ -+ pr_debug("[%s]: attempt to resize data - guid %x, hdl %x, base address %p\n", -+ __func__, ioparam->handle, resize.res_handle, resize.res_mem); -+ -+ /* Resize the videocore allocated resource. -+ */ -+ status = vc_vchi_sm_resize(sm_state->sm_handle, &resize, -+ &private->int_trans_id); -+ if (status == -EINTR) { -+ pr_debug("[%s]: requesting resize memory action restart (trans_id: %u)\n", -+ __func__, private->int_trans_id); -+ ret = -ERESTARTSYS; -+ private->restart_sys = -EINTR; -+ private->int_action = VC_SM_MSG_TYPE_RESIZE; -+ goto error; -+ } else if (status != 0) { -+ pr_err("[%s]: failed to resize memory on videocore (status: %u, trans_id: %u)\n", -+ __func__, status, private->int_trans_id); -+ ret = -EPERM; -+ goto error; -+ } -+ -+ pr_debug("[%s]: success to resize data - hdl %x, size %d -> %d\n", -+ __func__, resize.res_handle, resource->res_size, -+ resize.res_new_size); -+ -+ /* Successfully resized, save the information and inform the user. -+ */ -+ ioparam->old_size = resource->res_size; -+ resource->res_size = resize.res_new_size; -+ -+error: -+ if (resource) -+ vmcs_sm_release_resource(resource, 0); -+ -+ return ret; -+} -+ -+/* Lock a previously allocated shared memory handle and block. -+*/ -+static int vc_sm_ioctl_lock(struct SM_PRIV_DATA_T *private, -+ struct vmcs_sm_ioctl_lock_unlock *ioparam, -+ int change_cache, enum vmcs_sm_cache_e cache_type, -+ unsigned int vc_addr) -+{ -+ int status; -+ VC_SM_LOCK_UNLOCK_T lock; -+ VC_SM_LOCK_RESULT_T result; -+ struct SM_RESOURCE_T *resource; -+ int ret = 0; -+ struct sm_mmap *map, *map_tmp; -+ long unsigned int phys_addr; -+ -+ map = NULL; -+ -+ /* Locate resource from GUID. -+ */ -+ resource = vmcs_sm_acquire_resource(private, ioparam->handle); -+ if (resource == NULL) { -+ ret = -EINVAL; -+ goto error; -+ } -+ -+ /* Check permissions. -+ */ -+ if (resource->pid && (resource->pid != current->tgid)) { -+ pr_err("[%s]: current tgid %u != %u owner\n", __func__, -+ current->tgid, resource->pid); -+ ret = -EPERM; -+ goto error; -+ } -+ -+ lock.res_handle = resource->res_handle; -+ lock.res_mem = resource->res_base_mem; -+ -+ /* Take the lock and get the address to be mapped. -+ */ -+ if (vc_addr == 0) { -+ pr_debug("[%s]: attempt to lock data - guid %x, hdl %x, base address %p\n", -+ __func__, ioparam->handle, lock.res_handle, -+ lock.res_mem); -+ -+ /* Lock the videocore allocated resource. -+ */ -+ status = vc_vchi_sm_lock(sm_state->sm_handle, &lock, &result, -+ &private->int_trans_id); -+ if (status == -EINTR) { -+ pr_debug("[%s]: requesting lock memory action restart (trans_id: %u)\n", -+ __func__, private->int_trans_id); -+ ret = -ERESTARTSYS; -+ private->restart_sys = -EINTR; -+ private->int_action = VC_SM_MSG_TYPE_LOCK; -+ goto error; -+ } else if (status != 0 || -+ (status == 0 && result.res_mem == NULL)) { -+ pr_err("[%s]: failed to lock memory on videocore (status: %u, trans_id: %u)\n", -+ __func__, status, private->int_trans_id); -+ ret = -EPERM; -+ resource->res_stats[LOCK_FAIL]++; -+ goto error; -+ } -+ -+ pr_debug("[%s]: succeed to lock data - hdl %x, base address %p (%p), ref-cnt %d\n", -+ __func__, lock.res_handle, result.res_mem, -+ lock.res_mem, resource->lock_count); -+ } -+ /* Lock assumed taken already, address to be mapped is known. -+ */ -+ else -+ resource->res_base_mem = (void *)vc_addr; -+ -+ resource->res_stats[LOCK]++; -+ resource->lock_count++; -+ -+ /* Keep track of the new base memory allocation if it has changed. -+ */ -+ if ((vc_addr == 0) && -+ (result.res_mem != NULL) && -+ (result.res_old_mem != NULL) && -+ (result.res_mem != result.res_old_mem)) { -+ resource->res_base_mem = result.res_mem; -+ -+ /* Kernel allocated resources. -+ */ -+ if (resource->pid == 0) { -+ if (!list_empty(&resource->map_list)) { -+ list_for_each_entry_safe(map, map_tmp, -+ &resource->map_list, -+ resource_map_list) { -+ if (map->res_addr) { -+ iounmap((void *)map->res_addr); -+ map->res_addr = 0; -+ -+ vmcs_sm_remove_map(sm_state, -+ map->resource, -+ map); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (change_cache) -+ resource->res_cached = cache_type; -+ -+ if (resource->map_count) { -+ ioparam->addr = -+ vmcs_sm_usr_address_from_pid_and_usr_handle( -+ current->tgid, ioparam->handle); -+ -+ pr_debug("[%s] map_count %d private->pid %d current->tgid %d hnd %x addr %u\n", -+ __func__, resource->map_count, private->pid, -+ current->tgid, ioparam->handle, ioparam->addr); -+ } else { -+ /* Kernel allocated resources. -+ */ -+ if (resource->pid == 0) { -+ pr_debug("[%s]: attempt mapping kernel resource - guid %x, hdl %x\n", -+ __func__, ioparam->handle, lock.res_handle); -+ -+ ioparam->addr = 0; -+ -+ map = kzalloc(sizeof(*map), GFP_KERNEL); -+ if (map == NULL) { -+ pr_err("[%s]: failed allocating tracker\n", -+ __func__); -+ ret = -ENOMEM; -+ goto error; -+ } else { -+ phys_addr = (uint32_t)resource->res_base_mem & -+ 0x3FFFFFFF; -+ phys_addr += mm_vc_mem_phys_addr; -+ if (resource->res_cached -+ == VMCS_SM_CACHE_HOST) { -+ ioparam->addr = (long unsigned int) -+ /* TODO - make cached work */ -+ ioremap_nocache(phys_addr, -+ resource->res_size); -+ -+ pr_debug("[%s]: mapping kernel - guid %x, hdl %x - cached mapping %u\n", -+ __func__, ioparam->handle, -+ lock.res_handle, ioparam->addr); -+ } else { -+ ioparam->addr = (long unsigned int) -+ ioremap_nocache(phys_addr, -+ resource->res_size); -+ -+ pr_debug("[%s]: mapping kernel- guid %x, hdl %x - non cached mapping %u\n", -+ __func__, ioparam->handle, -+ lock.res_handle, ioparam->addr); -+ } -+ -+ map->res_pid = 0; -+ map->res_vc_hdl = resource->res_handle; -+ map->res_usr_hdl = resource->res_guid; -+ map->res_addr = ioparam->addr; -+ map->resource = resource; -+ map->vma = NULL; -+ -+ vmcs_sm_add_map(sm_state, resource, map); -+ } -+ } else -+ ioparam->addr = 0; -+ } -+ -+error: -+ if (resource) -+ vmcs_sm_release_resource(resource, 0); -+ -+ return ret; -+} -+ -+/* Unlock a previously allocated shared memory handle and block. -+*/ -+static int vc_sm_ioctl_unlock(struct SM_PRIV_DATA_T *private, -+ struct vmcs_sm_ioctl_lock_unlock *ioparam, -+ int flush, int wait_reply, int no_vc_unlock) -+{ -+ int status; -+ VC_SM_LOCK_UNLOCK_T unlock; -+ struct sm_mmap *map, *map_tmp; -+ struct SM_RESOURCE_T *resource; -+ int ret = 0; -+ -+ map = NULL; -+ -+ /* Locate resource from GUID. -+ */ -+ resource = vmcs_sm_acquire_resource(private, ioparam->handle); -+ if (resource == NULL) { -+ ret = -EINVAL; -+ goto error; -+ } -+ -+ /* Check permissions. -+ */ -+ if (resource->pid && (resource->pid != current->tgid)) { -+ pr_err("[%s]: current tgid %u != %u owner\n", -+ __func__, current->tgid, resource->pid); -+ ret = -EPERM; -+ goto error; -+ } -+ -+ unlock.res_handle = resource->res_handle; -+ unlock.res_mem = resource->res_base_mem; -+ -+ pr_debug("[%s]: attempt to unlock data - guid %x, hdl %x, base address %p\n", -+ __func__, ioparam->handle, unlock.res_handle, unlock.res_mem); -+ -+ /* User space allocated resources. -+ */ -+ if (resource->pid) { -+ /* Flush if requested */ -+ if (resource->res_cached && flush) { -+ dma_addr_t phys_addr = 0; -+ resource->res_stats[FLUSH]++; -+ -+ phys_addr = -+ (dma_addr_t)((uint32_t)resource->res_base_mem & -+ 0x3FFFFFFF); -+ phys_addr += (dma_addr_t)mm_vc_mem_phys_addr; -+ -+ /* L1 cache flush */ -+ down_read(¤t->mm->mmap_sem); -+ list_for_each_entry(map, &resource->map_list, -+ resource_map_list) { -+ if (map->vma) { -+ unsigned long start; -+ unsigned long end; -+ start = map->vma->vm_start; -+ end = map->vma->vm_end; -+ -+ vcsm_vma_cache_clean_page_range( -+ start, end); -+ } -+ } -+ up_read(¤t->mm->mmap_sem); -+ -+ /* L2 cache flush */ -+ outer_clean_range(phys_addr, -+ phys_addr + -+ (size_t) resource->res_size); -+ } -+ -+ /* We need to zap all the vmas associated with this resource */ -+ if (resource->lock_count == 1) { -+ down_read(¤t->mm->mmap_sem); -+ list_for_each_entry(map, &resource->map_list, -+ resource_map_list) { -+ if (map->vma) { -+ zap_vma_ptes(map->vma, -+ map->vma->vm_start, -+ map->vma->vm_end - -+ map->vma->vm_start); -+ } -+ } -+ up_read(¤t->mm->mmap_sem); -+ } -+ } -+ /* Kernel allocated resources. */ -+ else { -+ /* Global + Taken in this context */ -+ if (resource->ref_count == 2) { -+ if (!list_empty(&resource->map_list)) { -+ list_for_each_entry_safe(map, map_tmp, -+ &resource->map_list, -+ resource_map_list) { -+ if (map->res_addr) { -+ if (flush && -+ (resource->res_cached == -+ VMCS_SM_CACHE_HOST)) { -+ long unsigned int -+ phys_addr; -+ phys_addr = (uint32_t) -+ resource->res_base_mem & 0x3FFFFFFF; -+ phys_addr += -+ mm_vc_mem_phys_addr; -+ -+ /* L1 cache flush */ -+ dmac_flush_range((const -+ void -+ *) -+ map->res_addr, (const void *) -+ (map->res_addr + resource->res_size)); -+ -+ /* L2 cache flush */ -+ outer_clean_range -+ (phys_addr, -+ phys_addr + -+ (size_t) -+ resource->res_size); -+ } -+ -+ iounmap((void *)map->res_addr); -+ map->res_addr = 0; -+ -+ vmcs_sm_remove_map(sm_state, -+ map->resource, -+ map); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (resource->lock_count) { -+ /* Bypass the videocore unlock. -+ */ -+ if (no_vc_unlock) -+ status = 0; -+ /* Unlock the videocore allocated resource. -+ */ -+ else { -+ status = -+ vc_vchi_sm_unlock(sm_state->sm_handle, &unlock, -+ &private->int_trans_id, -+ wait_reply); -+ if (status == -EINTR) { -+ pr_debug("[%s]: requesting unlock memory action restart (trans_id: %u)\n", -+ __func__, private->int_trans_id); -+ -+ ret = -ERESTARTSYS; -+ resource->res_stats[UNLOCK]--; -+ private->restart_sys = -EINTR; -+ private->int_action = VC_SM_MSG_TYPE_UNLOCK; -+ goto error; -+ } else if (status != 0) { -+ pr_err("[%s]: failed to unlock vc mem (status: %u, trans_id: %u)\n", -+ __func__, status, private->int_trans_id); -+ -+ ret = -EPERM; -+ resource->res_stats[UNLOCK_FAIL]++; -+ goto error; -+ } -+ } -+ -+ resource->res_stats[UNLOCK]++; -+ resource->lock_count--; -+ } -+ -+ pr_debug("[%s]: success to unlock data - hdl %x, base address %p, ref-cnt %d\n", -+ __func__, unlock.res_handle, unlock.res_mem, -+ resource->lock_count); -+ -+error: -+ if (resource) -+ vmcs_sm_release_resource(resource, 0); -+ -+ return ret; -+} -+ -+/* Handle control from host. */ -+static long vc_sm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ int ret = 0; -+ unsigned int cmdnr = _IOC_NR(cmd); -+ struct SM_PRIV_DATA_T *file_data = -+ (struct SM_PRIV_DATA_T *)file->private_data; -+ struct SM_RESOURCE_T *resource = NULL; -+ -+ /* Validate we can work with this device. */ -+ if ((sm_state == NULL) || (file_data == NULL)) { -+ pr_err("[%s]: invalid device\n", __func__); -+ ret = -EPERM; -+ goto out; -+ } -+ -+ pr_debug("[%s]: cmd %x tgid %u, owner %u\n", __func__, cmdnr, -+ current->tgid, file_data->pid); -+ -+ /* Action is a re-post of a previously interrupted action? */ -+ if (file_data->restart_sys == -EINTR) { -+ VC_SM_ACTION_CLEAN_T action_clean; -+ -+ pr_debug("[%s]: clean up of action %u (trans_id: %u) following EINTR\n", -+ __func__, file_data->int_action, -+ file_data->int_trans_id); -+ -+ action_clean.res_action = file_data->int_action; -+ action_clean.action_trans_id = file_data->int_trans_id; -+ -+ vc_vchi_sm_clean_up(sm_state->sm_handle, &action_clean); -+ -+ file_data->restart_sys = 0; -+ } -+ -+ /* Now process the command. -+ */ -+ switch (cmdnr) { -+ /* New memory allocation. -+ */ -+ case VMCS_SM_CMD_ALLOC: -+ { -+ struct vmcs_sm_ioctl_alloc ioparam; -+ -+ /* Get the parameter data. -+ */ -+ if (copy_from_user -+ (&ioparam, (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ goto out; -+ } -+ -+ ret = vc_sm_ioctl_alloc(file_data, &ioparam); -+ if (!ret && -+ (copy_to_user((void *)arg, -+ &ioparam, sizeof(ioparam)) != 0)) { -+ struct vmcs_sm_ioctl_free freeparam = { -+ ioparam.handle -+ }; -+ pr_err("[%s]: failed to copy-to-user for cmd %x\n", -+ __func__, cmdnr); -+ vc_sm_ioctl_free(file_data, &freeparam); -+ ret = -EFAULT; -+ } -+ -+ /* Done. -+ */ -+ goto out; -+ } -+ break; -+ -+ /* Share existing memory allocation. -+ */ -+ case VMCS_SM_CMD_ALLOC_SHARE: -+ { -+ struct vmcs_sm_ioctl_alloc_share ioparam; -+ -+ /* Get the parameter data. -+ */ -+ if (copy_from_user -+ (&ioparam, (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ goto out; -+ } -+ -+ ret = vc_sm_ioctl_alloc_share(file_data, &ioparam); -+ -+ /* Copy result back to user. -+ */ -+ if (!ret -+ && copy_to_user((void *)arg, &ioparam, -+ sizeof(ioparam)) != 0) { -+ struct vmcs_sm_ioctl_free freeparam = { -+ ioparam.handle -+ }; -+ pr_err("[%s]: failed to copy-to-user for cmd %x\n", -+ __func__, cmdnr); -+ vc_sm_ioctl_free(file_data, &freeparam); -+ ret = -EFAULT; -+ } -+ -+ /* Done. -+ */ -+ goto out; -+ } -+ break; -+ -+ /* Lock (attempt to) *and* register a cache behavior change. -+ */ -+ case VMCS_SM_CMD_LOCK_CACHE: -+ { -+ struct vmcs_sm_ioctl_lock_cache ioparam; -+ struct vmcs_sm_ioctl_lock_unlock lock; -+ -+ /* Get parameter data. -+ */ -+ if (copy_from_user -+ (&ioparam, (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ goto out; -+ } -+ -+ lock.handle = ioparam.handle; -+ ret = -+ vc_sm_ioctl_lock(file_data, &lock, 1, -+ ioparam.cached, 0); -+ -+ /* Done. -+ */ -+ goto out; -+ } -+ break; -+ -+ /* Lock (attempt to) existing memory allocation. -+ */ -+ case VMCS_SM_CMD_LOCK: -+ { -+ struct vmcs_sm_ioctl_lock_unlock ioparam; -+ -+ /* Get parameter data. -+ */ -+ if (copy_from_user -+ (&ioparam, (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ goto out; -+ } -+ -+ ret = vc_sm_ioctl_lock(file_data, &ioparam, 0, 0, 0); -+ -+ /* Copy result back to user. -+ */ -+ if (copy_to_user((void *)arg, &ioparam, sizeof(ioparam)) -+ != 0) { -+ pr_err("[%s]: failed to copy-to-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ } -+ -+ /* Done. -+ */ -+ goto out; -+ } -+ break; -+ -+ /* Unlock (attempt to) existing memory allocation. -+ */ -+ case VMCS_SM_CMD_UNLOCK: -+ { -+ struct vmcs_sm_ioctl_lock_unlock ioparam; -+ -+ /* Get parameter data. -+ */ -+ if (copy_from_user -+ (&ioparam, (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ goto out; -+ } -+ -+ ret = vc_sm_ioctl_unlock(file_data, &ioparam, 0, 1, 0); -+ -+ /* Done. -+ */ -+ goto out; -+ } -+ break; -+ -+ /* Resize (attempt to) existing memory allocation. -+ */ -+ case VMCS_SM_CMD_RESIZE: -+ { -+ struct vmcs_sm_ioctl_resize ioparam; -+ -+ /* Get parameter data. -+ */ -+ if (copy_from_user -+ (&ioparam, (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ goto out; -+ } -+ -+ ret = vc_sm_ioctl_resize(file_data, &ioparam); -+ -+ /* Copy result back to user. -+ */ -+ if (copy_to_user((void *)arg, &ioparam, sizeof(ioparam)) -+ != 0) { -+ pr_err("[%s]: failed to copy-to-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ } -+ -+ /* Done. -+ */ -+ goto out; -+ } -+ break; -+ -+ /* Terminate existing memory allocation. -+ */ -+ case VMCS_SM_CMD_FREE: -+ { -+ struct vmcs_sm_ioctl_free ioparam; -+ -+ /* Get parameter data. -+ */ -+ if (copy_from_user -+ (&ioparam, (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ goto out; -+ } -+ -+ ret = vc_sm_ioctl_free(file_data, &ioparam); -+ -+ /* Done. -+ */ -+ goto out; -+ } -+ break; -+ -+ /* Walk allocation on videocore, information shows up in the -+ ** videocore log. -+ */ -+ case VMCS_SM_CMD_VC_WALK_ALLOC: -+ { -+ pr_debug("[%s]: invoking walk alloc\n", __func__); -+ -+ if (vc_vchi_sm_walk_alloc(sm_state->sm_handle) != 0) -+ pr_err("[%s]: failed to walk-alloc on videocore\n", -+ __func__); -+ -+ /* Done. -+ */ -+ goto out; -+ } -+ break; -+/* Walk mapping table on host, information shows up in the -+ ** kernel log. -+ */ -+ case VMCS_SM_CMD_HOST_WALK_MAP: -+ { -+ /* Use pid of -1 to tell to walk the whole map. */ -+ vmcs_sm_host_walk_map_per_pid(-1); -+ -+ /* Done. */ -+ goto out; -+ } -+ break; -+ -+ /* Walk mapping table per process on host. */ -+ case VMCS_SM_CMD_HOST_WALK_PID_ALLOC: -+ { -+ struct vmcs_sm_ioctl_walk ioparam; -+ -+ /* Get parameter data. */ -+ if (copy_from_user(&ioparam, -+ (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ goto out; -+ } -+ -+ vmcs_sm_host_walk_alloc(file_data); -+ -+ /* Done. */ -+ goto out; -+ } -+ break; -+ -+ /* Walk allocation per process on host. */ -+ case VMCS_SM_CMD_HOST_WALK_PID_MAP: -+ { -+ struct vmcs_sm_ioctl_walk ioparam; -+ -+ /* Get parameter data. */ -+ if (copy_from_user(&ioparam, -+ (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ goto out; -+ } -+ -+ vmcs_sm_host_walk_map_per_pid(ioparam.pid); -+ -+ /* Done. */ -+ goto out; -+ } -+ break; -+ -+ /* Gets the size of the memory associated with a user handle. */ -+ case VMCS_SM_CMD_SIZE_USR_HANDLE: -+ { -+ struct vmcs_sm_ioctl_size ioparam; -+ -+ /* Get parameter data. */ -+ if (copy_from_user(&ioparam, -+ (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ goto out; -+ } -+ -+ /* Locate resource from GUID. */ -+ resource = -+ vmcs_sm_acquire_resource(file_data, ioparam.handle); -+ if (resource != NULL) { -+ ioparam.size = resource->res_size; -+ vmcs_sm_release_resource(resource, 0); -+ } else { -+ ioparam.size = 0; -+ } -+ -+ if (copy_to_user((void *)arg, -+ &ioparam, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-to-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ } -+ -+ /* Done. */ -+ goto out; -+ } -+ break; -+ -+ /* Verify we are dealing with a valid resource. */ -+ case VMCS_SM_CMD_CHK_USR_HANDLE: -+ { -+ struct vmcs_sm_ioctl_chk ioparam; -+ -+ /* Get parameter data. -+ */ -+ if (copy_from_user(&ioparam, -+ (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ -+ ret = -EFAULT; -+ goto out; -+ } -+ -+ /* Locate resource from GUID. */ -+ resource = -+ vmcs_sm_acquire_resource(file_data, ioparam.handle); -+ if (resource == NULL) -+ ret = -EINVAL; -+ /* If the resource is cacheable, return additional -+ * information that may be needed to flush the cache. -+ */ -+ else if ((resource->res_cached == VMCS_SM_CACHE_HOST) || -+ (resource->res_cached == VMCS_SM_CACHE_BOTH)) { -+ ioparam.addr = -+ vmcs_sm_usr_address_from_pid_and_usr_handle -+ (current->tgid, ioparam.handle); -+ ioparam.size = resource->res_size; -+ ioparam.cache = resource->res_cached; -+ } else { -+ ioparam.addr = 0; -+ ioparam.size = 0; -+ ioparam.cache = resource->res_cached; -+ } -+ -+ if (resource) -+ vmcs_sm_release_resource(resource, 0); -+ -+ if (copy_to_user((void *)arg, -+ &ioparam, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-to-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ } -+ -+ /* Done. -+ */ -+ goto out; -+ } -+ break; -+ -+ /* -+ * Maps a user handle given the process and the virtual address. -+ */ -+ case VMCS_SM_CMD_MAPPED_USR_HANDLE: -+ { -+ struct vmcs_sm_ioctl_map ioparam; -+ -+ /* Get parameter data. */ -+ if (copy_from_user(&ioparam, -+ (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ -+ ret = -EFAULT; -+ goto out; -+ } -+ -+ ioparam.handle = -+ vmcs_sm_usr_handle_from_pid_and_address( -+ ioparam.pid, ioparam.addr); -+ -+ resource = -+ vmcs_sm_acquire_resource(file_data, ioparam.handle); -+ if ((resource != NULL) -+ && ((resource->res_cached == VMCS_SM_CACHE_HOST) -+ || (resource->res_cached == -+ VMCS_SM_CACHE_BOTH))) { -+ ioparam.size = resource->res_size; -+ } else { -+ ioparam.size = 0; -+ } -+ -+ if (resource) -+ vmcs_sm_release_resource(resource, 0); -+ -+ if (copy_to_user((void *)arg, -+ &ioparam, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-to-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ } -+ -+ /* Done. */ -+ goto out; -+ } -+ break; -+ -+ /* -+ * Maps a videocore handle given process and virtual address. -+ */ -+ case VMCS_SM_CMD_MAPPED_VC_HDL_FROM_ADDR: -+ { -+ struct vmcs_sm_ioctl_map ioparam; -+ -+ /* Get parameter data. */ -+ if (copy_from_user(&ioparam, -+ (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ goto out; -+ } -+ -+ ioparam.handle = vmcs_sm_vc_handle_from_pid_and_address( -+ ioparam.pid, ioparam.addr); -+ -+ if (copy_to_user((void *)arg, -+ &ioparam, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-to-user for cmd %x\n", -+ __func__, cmdnr); -+ -+ ret = -EFAULT; -+ } -+ -+ /* Done. -+ */ -+ goto out; -+ } -+ break; -+ -+ /* Maps a videocore handle given process and user handle. */ -+ case VMCS_SM_CMD_MAPPED_VC_HDL_FROM_HDL: -+ { -+ struct vmcs_sm_ioctl_map ioparam; -+ -+ /* Get parameter data. */ -+ if (copy_from_user(&ioparam, -+ (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ goto out; -+ } -+ -+ /* Locate resource from GUID. */ -+ resource = -+ vmcs_sm_acquire_resource(file_data, ioparam.handle); -+ if (resource != NULL) { -+ ioparam.handle = resource->res_handle; -+ vmcs_sm_release_resource(resource, 0); -+ } else { -+ ioparam.handle = 0; -+ } -+ -+ if (copy_to_user((void *)arg, -+ &ioparam, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-to-user for cmd %x\n", -+ __func__, cmdnr); -+ -+ ret = -EFAULT; -+ } -+ -+ /* Done. */ -+ goto out; -+ } -+ break; -+ -+ /* -+ * Maps a videocore address given process and videocore handle. -+ */ -+ case VMCS_SM_CMD_MAPPED_VC_ADDR_FROM_HDL: -+ { -+ struct vmcs_sm_ioctl_map ioparam; -+ -+ /* Get parameter data. */ -+ if (copy_from_user(&ioparam, -+ (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ -+ ret = -EFAULT; -+ goto out; -+ } -+ -+ /* Locate resource from GUID. */ -+ resource = -+ vmcs_sm_acquire_resource(file_data, ioparam.handle); -+ if (resource != NULL) { -+ ioparam.addr = -+ (unsigned int)resource->res_base_mem; -+ vmcs_sm_release_resource(resource, 0); -+ } else { -+ ioparam.addr = 0; -+ } -+ -+ if (copy_to_user((void *)arg, -+ &ioparam, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-to-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ } -+ -+ /* Done. */ -+ goto out; -+ } -+ break; -+ -+ /* Maps a user address given process and vc handle. -+ */ -+ case VMCS_SM_CMD_MAPPED_USR_ADDRESS: -+ { -+ struct vmcs_sm_ioctl_map ioparam; -+ -+ /* Get parameter data. */ -+ if (copy_from_user(&ioparam, -+ (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ goto out; -+ } -+ -+ /* -+ * Return the address information from the mapping, -+ * 0 (ie NULL) if it cannot locate the actual mapping. -+ */ -+ ioparam.addr = -+ vmcs_sm_usr_address_from_pid_and_usr_handle -+ (ioparam.pid, ioparam.handle); -+ -+ if (copy_to_user((void *)arg, -+ &ioparam, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-to-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ } -+ -+ /* Done. */ -+ goto out; -+ } -+ break; -+ -+ /* Flush the cache for a given mapping. */ -+ case VMCS_SM_CMD_FLUSH: -+ { -+ struct vmcs_sm_ioctl_cache ioparam; -+ -+ /* Get parameter data. */ -+ if (copy_from_user(&ioparam, -+ (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ goto out; -+ } -+ -+ /* Locate resource from GUID. */ -+ resource = -+ vmcs_sm_acquire_resource(file_data, ioparam.handle); -+ -+ if ((resource != NULL) && resource->res_cached) { -+ dma_addr_t phys_addr = 0; -+ -+ resource->res_stats[FLUSH]++; -+ -+ phys_addr = -+ (dma_addr_t)((uint32_t) -+ resource->res_base_mem & -+ 0x3FFFFFFF); -+ phys_addr += (dma_addr_t)mm_vc_mem_phys_addr; -+ -+ /* L1 cache flush */ -+ down_read(¤t->mm->mmap_sem); -+ vcsm_vma_cache_clean_page_range((unsigned long) -+ ioparam.addr, -+ (unsigned long) -+ ioparam.addr + -+ ioparam.size); -+ up_read(¤t->mm->mmap_sem); -+ -+ /* L2 cache flush */ -+ outer_clean_range(phys_addr, -+ phys_addr + -+ (size_t) ioparam.size); -+ } else if (resource == NULL) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ if (resource) -+ vmcs_sm_release_resource(resource, 0); -+ -+ /* Done. */ -+ goto out; -+ } -+ break; -+ -+ /* Invalidate the cache for a given mapping. */ -+ case VMCS_SM_CMD_INVALID: -+ { -+ struct vmcs_sm_ioctl_cache ioparam; -+ -+ /* Get parameter data. */ -+ if (copy_from_user(&ioparam, -+ (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ goto out; -+ } -+ -+ /* Locate resource from GUID. -+ */ -+ resource = -+ vmcs_sm_acquire_resource(file_data, ioparam.handle); -+ -+ if ((resource != NULL) && resource->res_cached) { -+ dma_addr_t phys_addr = 0; -+ -+ resource->res_stats[INVALID]++; -+ -+ phys_addr = -+ (dma_addr_t)((uint32_t) -+ resource->res_base_mem & -+ 0x3FFFFFFF); -+ phys_addr += (dma_addr_t)mm_vc_mem_phys_addr; -+ -+ /* L2 cache invalidate */ -+ outer_inv_range(phys_addr, -+ phys_addr + -+ (size_t) ioparam.size); -+ -+ /* L1 cache invalidate */ -+ down_read(¤t->mm->mmap_sem); -+ vcsm_vma_cache_clean_page_range((unsigned long) -+ ioparam.addr, -+ (unsigned long) -+ ioparam.addr + -+ ioparam.size); -+ up_read(¤t->mm->mmap_sem); -+ } else if (resource == NULL) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ if (resource) -+ vmcs_sm_release_resource(resource, 0); -+ -+ /* Done. -+ */ -+ goto out; -+ } -+ break; -+ -+ /* Flush/Invalidate the cache for a given mapping. */ -+ case VMCS_SM_CMD_CLEAN_INVALID: -+ { -+ int i; -+ struct vmcs_sm_ioctl_clean_invalid ioparam; -+ -+ /* Get parameter data. */ -+ if (copy_from_user(&ioparam, -+ (void *)arg, sizeof(ioparam)) != 0) { -+ pr_err("[%s]: failed to copy-from-user for cmd %x\n", -+ __func__, cmdnr); -+ ret = -EFAULT; -+ goto out; -+ } -+ for (i=0; ires_cached) { -+ unsigned long base = ioparam.s[i].addr & ~(PAGE_SIZE-1); -+ unsigned long end = (ioparam.s[i].addr + ioparam.s[i].size + PAGE_SIZE-1) & ~(PAGE_SIZE-1); -+ resource->res_stats[ioparam.s[i].cmd == 1 ? INVALID:FLUSH]++; -+ -+ /* L1/L2 cache flush */ -+ down_read(¤t->mm->mmap_sem); -+ vcsm_vma_cache_clean_page_range(base, end); -+ up_read(¤t->mm->mmap_sem); -+ } else if (resource == NULL) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ if (resource) -+ vmcs_sm_release_resource(resource, 0); -+ } -+ break; -+ } -+ } -+ } -+ break; -+ -+ default: -+ { -+ ret = -EINVAL; -+ goto out; -+ } -+ break; -+ } -+ -+out: -+ return ret; -+} -+ -+/* Device operations that we managed in this driver. -+*/ -+static const struct file_operations vmcs_sm_ops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = vc_sm_ioctl, -+ .open = vc_sm_open, -+ .release = vc_sm_release, -+ .mmap = vc_sm_mmap, -+}; -+ -+/* Creation of device. -+*/ -+static int vc_sm_create_sharedmemory(void) -+{ -+ int ret; -+ -+ if (sm_state == NULL) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ /* Create a device class for creating dev nodes. -+ */ -+ sm_state->sm_class = class_create(THIS_MODULE, "vc-sm"); -+ if (IS_ERR(sm_state->sm_class)) { -+ pr_err("[%s]: unable to create device class\n", __func__); -+ ret = PTR_ERR(sm_state->sm_class); -+ goto out; -+ } -+ -+ /* Create a character driver. -+ */ -+ ret = alloc_chrdev_region(&sm_state->sm_devid, -+ DEVICE_MINOR, 1, DEVICE_NAME); -+ if (ret != 0) { -+ pr_err("[%s]: unable to allocate device number\n", __func__); -+ goto out_dev_class_destroy; -+ } -+ -+ cdev_init(&sm_state->sm_cdev, &vmcs_sm_ops); -+ ret = cdev_add(&sm_state->sm_cdev, sm_state->sm_devid, 1); -+ if (ret != 0) { -+ pr_err("[%s]: unable to register device\n", __func__); -+ goto out_chrdev_unreg; -+ } -+ -+ /* Create a device node. -+ */ -+ sm_state->sm_dev = device_create(sm_state->sm_class, -+ NULL, -+ MKDEV(MAJOR(sm_state->sm_devid), -+ DEVICE_MINOR), NULL, -+ DEVICE_NAME); -+ if (IS_ERR(sm_state->sm_dev)) { -+ pr_err("[%s]: unable to create device node\n", __func__); -+ ret = PTR_ERR(sm_state->sm_dev); -+ goto out_chrdev_del; -+ } -+ -+ goto out; -+ -+out_chrdev_del: -+ cdev_del(&sm_state->sm_cdev); -+out_chrdev_unreg: -+ unregister_chrdev_region(sm_state->sm_devid, 1); -+out_dev_class_destroy: -+ class_destroy(sm_state->sm_class); -+ sm_state->sm_class = NULL; -+out: -+ return ret; -+} -+ -+/* Termination of the device. -+*/ -+static int vc_sm_remove_sharedmemory(void) -+{ -+ int ret; -+ -+ if (sm_state == NULL) { -+ /* Nothing to do. -+ */ -+ ret = 0; -+ goto out; -+ } -+ -+ /* Remove the sharedmemory character driver. -+ */ -+ cdev_del(&sm_state->sm_cdev); -+ -+ /* Unregister region. -+ */ -+ unregister_chrdev_region(sm_state->sm_devid, 1); -+ -+ ret = 0; -+ goto out; -+ -+out: -+ return ret; -+} -+ -+/* Videocore connected. */ -+static void vc_sm_connected_init(void) -+{ -+ int ret; -+ VCHI_INSTANCE_T vchi_instance; -+ VCHI_CONNECTION_T *vchi_connection = NULL; -+ -+ pr_info("[%s]: start\n", __func__); -+ -+ /* Allocate memory for the state structure. -+ */ -+ sm_state = kzalloc(sizeof(struct SM_STATE_T), GFP_KERNEL); -+ if (sm_state == NULL) { -+ pr_err("[%s]: failed to allocate memory\n", __func__); -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ mutex_init(&sm_state->lock); -+ mutex_init(&sm_state->map_lock); -+ -+ /* Initialize and create a VCHI connection for the shared memory service -+ ** running on videocore. -+ */ -+ ret = vchi_initialise(&vchi_instance); -+ if (ret != 0) { -+ pr_err("[%s]: failed to initialise VCHI instance (ret=%d)\n", -+ __func__, ret); -+ -+ ret = -EIO; -+ goto err_free_mem; -+ } -+ -+ ret = vchi_connect(NULL, 0, vchi_instance); -+ if (ret != 0) { -+ pr_err("[%s]: failed to connect VCHI instance (ret=%d)\n", -+ __func__, ret); -+ -+ ret = -EIO; -+ goto err_free_mem; -+ } -+ -+ /* Initialize an instance of the shared memory service. */ -+ sm_state->sm_handle = -+ vc_vchi_sm_init(vchi_instance, &vchi_connection, 1); -+ if (sm_state->sm_handle == NULL) { -+ pr_err("[%s]: failed to initialize shared memory service\n", -+ __func__); -+ -+ ret = -EPERM; -+ goto err_free_mem; -+ } -+ -+ /* Create a debug fs directory entry (root). */ -+ sm_state->dir_root = debugfs_create_dir(VC_SM_DIR_ROOT_NAME, NULL); -+ if (!sm_state->dir_root) { -+ pr_err("[%s]: failed to create \'%s\' directory entry\n", -+ __func__, VC_SM_DIR_ROOT_NAME); -+ -+ ret = -EPERM; -+ goto err_stop_sm_service; -+ } -+ -+ sm_state->dir_state.show = &vc_sm_global_state_show; -+ sm_state->dir_state.dir_entry = debugfs_create_file(VC_SM_STATE, -+ S_IRUGO, sm_state->dir_root, &sm_state->dir_state, -+ &vc_sm_debug_fs_fops); -+ -+ sm_state->dir_stats.show = &vc_sm_global_statistics_show; -+ sm_state->dir_stats.dir_entry = debugfs_create_file(VC_SM_STATS, -+ S_IRUGO, sm_state->dir_root, &sm_state->dir_stats, -+ &vc_sm_debug_fs_fops); -+ -+ /* Create the proc entry children. */ -+ sm_state->dir_alloc = debugfs_create_dir(VC_SM_DIR_ALLOC_NAME, -+ sm_state->dir_root); -+ -+ /* Create a shared memory device. */ -+ ret = vc_sm_create_sharedmemory(); -+ if (ret != 0) { -+ pr_err("[%s]: failed to create shared memory device\n", -+ __func__); -+ goto err_remove_debugfs; -+ } -+ -+ INIT_LIST_HEAD(&sm_state->map_list); -+ INIT_LIST_HEAD(&sm_state->resource_list); -+ -+ sm_state->data_knl = vc_sm_create_priv_data(0); -+ if (sm_state->data_knl == NULL) { -+ pr_err("[%s]: failed to create kernel private data tracker\n", -+ __func__); -+ goto err_remove_shared_memory; -+ } -+ -+ /* Done! -+ */ -+ sm_inited = 1; -+ goto out; -+ -+err_remove_shared_memory: -+ vc_sm_remove_sharedmemory(); -+err_remove_debugfs: -+ debugfs_remove_recursive(sm_state->dir_root); -+err_stop_sm_service: -+ vc_vchi_sm_stop(&sm_state->sm_handle); -+err_free_mem: -+ kfree(sm_state); -+out: -+ pr_info("[%s]: end - returning %d\n", __func__, ret); -+} -+ -+/* Driver loading. */ -+static int __init vc_sm_init(void) -+{ -+ pr_info("vc-sm: Videocore shared memory driver\n"); -+ vchiq_add_connected_callback(vc_sm_connected_init); -+ return 0; -+} -+ -+/* Driver unloading. */ -+static void __exit vc_sm_exit(void) -+{ -+ pr_debug("[%s]: start\n", __func__); -+ if (sm_inited) { -+ /* Remove shared memory device. -+ */ -+ vc_sm_remove_sharedmemory(); -+ -+ /* Remove all proc entries. -+ */ -+ debugfs_remove_recursive(sm_state->dir_root); -+ -+ /* Stop the videocore shared memory service. -+ */ -+ vc_vchi_sm_stop(&sm_state->sm_handle); -+ -+ /* Free the memory for the state structure. -+ */ -+ mutex_destroy(&(sm_state->map_lock)); -+ kfree(sm_state); -+ } -+ -+ pr_debug("[%s]: end\n", __func__); -+} -+ -+#if defined(__KERNEL__) -+/* Allocate a shared memory handle and block. */ -+int vc_sm_alloc(VC_SM_ALLOC_T *alloc, int *handle) -+{ -+ struct vmcs_sm_ioctl_alloc ioparam = { 0 }; -+ int ret; -+ struct SM_RESOURCE_T *resource; -+ -+ /* Validate we can work with this device. -+ */ -+ if (sm_state == NULL || alloc == NULL || handle == NULL) { -+ pr_err("[%s]: invalid input\n", __func__); -+ return -EPERM; -+ } -+ -+ ioparam.size = alloc->base_unit; -+ ioparam.num = alloc->num_unit; -+ ioparam.cached = -+ alloc->type == VC_SM_ALLOC_CACHED ? VMCS_SM_CACHE_VC : 0; -+ -+ ret = vc_sm_ioctl_alloc(sm_state->data_knl, &ioparam); -+ -+ if (ret == 0) { -+ resource = -+ vmcs_sm_acquire_resource(sm_state->data_knl, -+ ioparam.handle); -+ if (resource) { -+ resource->pid = 0; -+ vmcs_sm_release_resource(resource, 0); -+ -+ /* Assign valid handle at this time. -+ */ -+ *handle = ioparam.handle; -+ } else { -+ ret = -ENOMEM; -+ } -+ } -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(vc_sm_alloc); -+ -+/* Get an internal resource handle mapped from the external one. -+*/ -+int vc_sm_int_handle(int handle) -+{ -+ struct SM_RESOURCE_T *resource; -+ int ret = 0; -+ -+ /* Validate we can work with this device. -+ */ -+ if (sm_state == NULL || handle == 0) { -+ pr_err("[%s]: invalid input\n", __func__); -+ return 0; -+ } -+ -+ /* Locate resource from GUID. -+ */ -+ resource = vmcs_sm_acquire_resource(sm_state->data_knl, handle); -+ if (resource) { -+ ret = resource->res_handle; -+ vmcs_sm_release_resource(resource, 0); -+ } -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(vc_sm_int_handle); -+ -+/* Free a previously allocated shared memory handle and block. -+*/ -+int vc_sm_free(int handle) -+{ -+ struct vmcs_sm_ioctl_free ioparam = { handle }; -+ -+ /* Validate we can work with this device. -+ */ -+ if (sm_state == NULL || handle == 0) { -+ pr_err("[%s]: invalid input\n", __func__); -+ return -EPERM; -+ } -+ -+ return vc_sm_ioctl_free(sm_state->data_knl, &ioparam); -+} -+EXPORT_SYMBOL_GPL(vc_sm_free); -+ -+/* Lock a memory handle for use by kernel. -+*/ -+int vc_sm_lock(int handle, VC_SM_LOCK_CACHE_MODE_T mode, -+ long unsigned int *data) -+{ -+ struct vmcs_sm_ioctl_lock_unlock ioparam; -+ int ret; -+ -+ /* Validate we can work with this device. -+ */ -+ if (sm_state == NULL || handle == 0 || data == NULL) { -+ pr_err("[%s]: invalid input\n", __func__); -+ return -EPERM; -+ } -+ -+ *data = 0; -+ -+ ioparam.handle = handle; -+ ret = vc_sm_ioctl_lock(sm_state->data_knl, -+ &ioparam, -+ 1, -+ ((mode == -+ VC_SM_LOCK_CACHED) ? VMCS_SM_CACHE_HOST : -+ VMCS_SM_CACHE_NONE), 0); -+ -+ *data = ioparam.addr; -+ return ret; -+} -+EXPORT_SYMBOL_GPL(vc_sm_lock); -+ -+/* Unlock a memory handle in use by kernel. -+*/ -+int vc_sm_unlock(int handle, int flush, int no_vc_unlock) -+{ -+ struct vmcs_sm_ioctl_lock_unlock ioparam; -+ -+ /* Validate we can work with this device. -+ */ -+ if (sm_state == NULL || handle == 0) { -+ pr_err("[%s]: invalid input\n", __func__); -+ return -EPERM; -+ } -+ -+ ioparam.handle = handle; -+ return vc_sm_ioctl_unlock(sm_state->data_knl, -+ &ioparam, flush, 0, no_vc_unlock); -+} -+EXPORT_SYMBOL_GPL(vc_sm_unlock); -+ -+/* Map a shared memory region for use by kernel. -+*/ -+int vc_sm_map(int handle, unsigned int sm_addr, VC_SM_LOCK_CACHE_MODE_T mode, -+ long unsigned int *data) -+{ -+ struct vmcs_sm_ioctl_lock_unlock ioparam; -+ int ret; -+ -+ /* Validate we can work with this device. -+ */ -+ if (sm_state == NULL || handle == 0 || data == NULL || sm_addr == 0) { -+ pr_err("[%s]: invalid input\n", __func__); -+ return -EPERM; -+ } -+ -+ *data = 0; -+ -+ ioparam.handle = handle; -+ ret = vc_sm_ioctl_lock(sm_state->data_knl, -+ &ioparam, -+ 1, -+ ((mode == -+ VC_SM_LOCK_CACHED) ? VMCS_SM_CACHE_HOST : -+ VMCS_SM_CACHE_NONE), sm_addr); -+ -+ *data = ioparam.addr; -+ return ret; -+} -+EXPORT_SYMBOL_GPL(vc_sm_map); -+#endif -+ -+late_initcall(vc_sm_init); -+module_exit(vc_sm_exit); -+ -+MODULE_AUTHOR("Broadcom"); -+MODULE_DESCRIPTION("VideoCore SharedMemory Driver"); -+MODULE_LICENSE("GPL v2"); ---- /dev/null -+++ b/include/linux/broadcom/vmcs_sm_ioctl.h -@@ -0,0 +1,248 @@ -+/***************************************************************************** -+* 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. -+* -+*****************************************************************************/ -+ -+#if !defined(__VMCS_SM_IOCTL_H__INCLUDED__) -+#define __VMCS_SM_IOCTL_H__INCLUDED__ -+ -+/* ---- Include Files ---------------------------------------------------- */ -+ -+#if defined(__KERNEL__) -+#include /* Needed for standard types */ -+#else -+#include -+#endif -+ -+#include -+ -+/* ---- Constants and Types ---------------------------------------------- */ -+ -+#define VMCS_SM_RESOURCE_NAME 32 -+#define VMCS_SM_RESOURCE_NAME_DEFAULT "sm-host-resource" -+ -+/* Type define used to create unique IOCTL number */ -+#define VMCS_SM_MAGIC_TYPE 'I' -+ -+/* IOCTL commands */ -+enum vmcs_sm_cmd_e { -+ VMCS_SM_CMD_ALLOC = 0x5A, /* Start at 0x5A arbitrarily */ -+ VMCS_SM_CMD_ALLOC_SHARE, -+ VMCS_SM_CMD_LOCK, -+ VMCS_SM_CMD_LOCK_CACHE, -+ VMCS_SM_CMD_UNLOCK, -+ VMCS_SM_CMD_RESIZE, -+ VMCS_SM_CMD_UNMAP, -+ VMCS_SM_CMD_FREE, -+ VMCS_SM_CMD_FLUSH, -+ VMCS_SM_CMD_INVALID, -+ -+ VMCS_SM_CMD_SIZE_USR_HANDLE, -+ VMCS_SM_CMD_CHK_USR_HANDLE, -+ -+ VMCS_SM_CMD_MAPPED_USR_HANDLE, -+ VMCS_SM_CMD_MAPPED_USR_ADDRESS, -+ VMCS_SM_CMD_MAPPED_VC_HDL_FROM_ADDR, -+ VMCS_SM_CMD_MAPPED_VC_HDL_FROM_HDL, -+ VMCS_SM_CMD_MAPPED_VC_ADDR_FROM_HDL, -+ -+ VMCS_SM_CMD_VC_WALK_ALLOC, -+ VMCS_SM_CMD_HOST_WALK_MAP, -+ VMCS_SM_CMD_HOST_WALK_PID_ALLOC, -+ VMCS_SM_CMD_HOST_WALK_PID_MAP, -+ -+ VMCS_SM_CMD_CLEAN_INVALID, -+ -+ VMCS_SM_CMD_LAST /* Do no delete */ -+}; -+ -+/* Cache type supported, conveniently matches the user space definition in -+** user-vcsm.h. -+*/ -+enum vmcs_sm_cache_e { -+ VMCS_SM_CACHE_NONE, -+ VMCS_SM_CACHE_HOST, -+ VMCS_SM_CACHE_VC, -+ VMCS_SM_CACHE_BOTH, -+}; -+ -+/* IOCTL Data structures */ -+struct vmcs_sm_ioctl_alloc { -+ /* user -> kernel */ -+ unsigned int size; -+ unsigned int num; -+ enum vmcs_sm_cache_e cached; -+ char name[VMCS_SM_RESOURCE_NAME]; -+ -+ /* kernel -> user */ -+ unsigned int handle; -+ /* unsigned int base_addr; */ -+}; -+ -+struct vmcs_sm_ioctl_alloc_share { -+ /* user -> kernel */ -+ unsigned int handle; -+ unsigned int size; -+}; -+ -+struct vmcs_sm_ioctl_free { -+ /* user -> kernel */ -+ unsigned int handle; -+ /* unsigned int base_addr; */ -+}; -+ -+struct vmcs_sm_ioctl_lock_unlock { -+ /* user -> kernel */ -+ unsigned int handle; -+ -+ /* kernel -> user */ -+ unsigned int addr; -+}; -+ -+struct vmcs_sm_ioctl_lock_cache { -+ /* user -> kernel */ -+ unsigned int handle; -+ enum vmcs_sm_cache_e cached; -+}; -+ -+struct vmcs_sm_ioctl_resize { -+ /* user -> kernel */ -+ unsigned int handle; -+ unsigned int new_size; -+ -+ /* kernel -> user */ -+ unsigned int old_size; -+}; -+ -+struct vmcs_sm_ioctl_map { -+ /* user -> kernel */ -+ /* and kernel -> user */ -+ unsigned int pid; -+ unsigned int handle; -+ unsigned int addr; -+ -+ /* kernel -> user */ -+ unsigned int size; -+}; -+ -+struct vmcs_sm_ioctl_walk { -+ /* user -> kernel */ -+ unsigned int pid; -+}; -+ -+struct vmcs_sm_ioctl_chk { -+ /* user -> kernel */ -+ unsigned int handle; -+ -+ /* kernel -> user */ -+ unsigned int addr; -+ unsigned int size; -+ enum vmcs_sm_cache_e cache; -+}; -+ -+struct vmcs_sm_ioctl_size { -+ /* user -> kernel */ -+ unsigned int handle; -+ -+ /* kernel -> user */ -+ unsigned int size; -+}; -+ -+struct vmcs_sm_ioctl_cache { -+ /* user -> kernel */ -+ unsigned int handle; -+ unsigned int addr; -+ unsigned int size; -+}; -+ -+struct vmcs_sm_ioctl_clean_invalid { -+ /* user -> kernel */ -+ struct { -+ unsigned int cmd; -+ unsigned int handle; -+ unsigned int addr; -+ unsigned int size; -+ } s[8]; -+}; -+ -+/* IOCTL numbers */ -+#define VMCS_SM_IOCTL_MEM_ALLOC\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_ALLOC,\ -+ struct vmcs_sm_ioctl_alloc) -+#define VMCS_SM_IOCTL_MEM_ALLOC_SHARE\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_ALLOC_SHARE,\ -+ struct vmcs_sm_ioctl_alloc_share) -+#define VMCS_SM_IOCTL_MEM_LOCK\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_LOCK,\ -+ struct vmcs_sm_ioctl_lock_unlock) -+#define VMCS_SM_IOCTL_MEM_LOCK_CACHE\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_LOCK_CACHE,\ -+ struct vmcs_sm_ioctl_lock_cache) -+#define VMCS_SM_IOCTL_MEM_UNLOCK\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_UNLOCK,\ -+ struct vmcs_sm_ioctl_lock_unlock) -+#define VMCS_SM_IOCTL_MEM_RESIZE\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_RESIZE,\ -+ struct vmcs_sm_ioctl_resize) -+#define VMCS_SM_IOCTL_MEM_FREE\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_FREE,\ -+ struct vmcs_sm_ioctl_free) -+#define VMCS_SM_IOCTL_MEM_FLUSH\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_FLUSH,\ -+ struct vmcs_sm_ioctl_cache) -+#define VMCS_SM_IOCTL_MEM_INVALID\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_INVALID,\ -+ struct vmcs_sm_ioctl_cache) -+#define VMCS_SM_IOCTL_MEM_CLEAN_INVALID\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_CLEAN_INVALID,\ -+ struct vmcs_sm_ioctl_clean_invalid) -+ -+#define VMCS_SM_IOCTL_SIZE_USR_HDL\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_SIZE_USR_HANDLE,\ -+ struct vmcs_sm_ioctl_size) -+#define VMCS_SM_IOCTL_CHK_USR_HDL\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_CHK_USR_HANDLE,\ -+ struct vmcs_sm_ioctl_chk) -+ -+#define VMCS_SM_IOCTL_MAP_USR_HDL\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_MAPPED_USR_HANDLE,\ -+ struct vmcs_sm_ioctl_map) -+#define VMCS_SM_IOCTL_MAP_USR_ADDRESS\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_MAPPED_USR_ADDRESS,\ -+ struct vmcs_sm_ioctl_map) -+#define VMCS_SM_IOCTL_MAP_VC_HDL_FR_ADDR\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_MAPPED_VC_HDL_FROM_ADDR,\ -+ struct vmcs_sm_ioctl_map) -+#define VMCS_SM_IOCTL_MAP_VC_HDL_FR_HDL\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_MAPPED_VC_HDL_FROM_HDL,\ -+ struct vmcs_sm_ioctl_map) -+#define VMCS_SM_IOCTL_MAP_VC_ADDR_FR_HDL\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_MAPPED_VC_ADDR_FROM_HDL,\ -+ struct vmcs_sm_ioctl_map) -+ -+#define VMCS_SM_IOCTL_VC_WALK_ALLOC\ -+ _IO(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_VC_WALK_ALLOC) -+#define VMCS_SM_IOCTL_HOST_WALK_MAP\ -+ _IO(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_HOST_WALK_MAP) -+#define VMCS_SM_IOCTL_HOST_WALK_PID_ALLOC\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_HOST_WALK_PID_ALLOC,\ -+ struct vmcs_sm_ioctl_walk) -+#define VMCS_SM_IOCTL_HOST_WALK_PID_MAP\ -+ _IOR(VMCS_SM_MAGIC_TYPE, VMCS_SM_CMD_HOST_WALK_PID_MAP,\ -+ struct vmcs_sm_ioctl_walk) -+ -+/* ---- Variable Externs ------------------------------------------------- */ -+ -+/* ---- Function Prototypes ---------------------------------------------- */ -+ -+#endif /* __VMCS_SM_IOCTL_H__INCLUDED__ */ diff --git a/target/linux/brcm2708/patches-4.4/0040-Add-SMI-driver.patch b/target/linux/brcm2708/patches-4.4/0040-Add-SMI-driver.patch new file mode 100644 index 0000000000..cc431955e8 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0040-Add-SMI-driver.patch @@ -0,0 +1,1930 @@ +From e19c303e7d54d986e0bd3e47107a83e30329c1d0 Mon Sep 17 00:00:00 2001 +From: Luke Wren +Date: Sat, 5 Sep 2015 01:14:45 +0100 +Subject: [PATCH 040/232] Add SMI driver + +Signed-off-by: Luke Wren +--- + .../bindings/misc/brcm,bcm2835-smi-dev.txt | 17 + + .../devicetree/bindings/misc/brcm,bcm2835-smi.txt | 48 + + drivers/char/broadcom/Kconfig | 8 + + drivers/char/broadcom/Makefile | 2 +- + drivers/char/broadcom/bcm2835_smi_dev.c | 402 +++++++++ + drivers/misc/Kconfig | 8 + + drivers/misc/Makefile | 1 + + drivers/misc/bcm2835_smi.c | 985 +++++++++++++++++++++ + include/linux/broadcom/bcm2835_smi.h | 391 ++++++++ + 9 files changed, 1861 insertions(+), 1 deletion(-) + create mode 100644 Documentation/devicetree/bindings/misc/brcm,bcm2835-smi-dev.txt + create mode 100644 Documentation/devicetree/bindings/misc/brcm,bcm2835-smi.txt + create mode 100644 drivers/char/broadcom/bcm2835_smi_dev.c + create mode 100644 drivers/misc/bcm2835_smi.c + create mode 100644 include/linux/broadcom/bcm2835_smi.h + +--- /dev/null ++++ b/Documentation/devicetree/bindings/misc/brcm,bcm2835-smi-dev.txt +@@ -0,0 +1,17 @@ ++* Broadcom BCM2835 SMI character device driver. ++ ++SMI or secondary memory interface is a peripheral specific to certain Broadcom ++SOCs, and is helpful for talking to things like parallel-interface displays ++and NAND flashes (in fact, most things with a parallel register interface). ++ ++This driver adds a character device which provides a user-space interface to ++an instance of the SMI driver. ++ ++Required properties: ++- compatible: "brcm,bcm2835-smi-dev" ++- smi_handle: a phandle to the smi node. ++ ++Optional properties: ++- None. ++ ++ +--- /dev/null ++++ b/Documentation/devicetree/bindings/misc/brcm,bcm2835-smi.txt +@@ -0,0 +1,48 @@ ++* Broadcom BCM2835 SMI driver. ++ ++SMI or secondary memory interface is a peripheral specific to certain Broadcom ++SOCs, and is helpful for talking to things like parallel-interface displays ++and NAND flashes (in fact, most things with a parallel register interface). ++ ++Required properties: ++- compatible: "brcm,bcm2835-smi" ++- reg: Should contain location and length of SMI registers and SMI clkman regs ++- interrupts: *the* SMI interrupt. ++- pinctrl-names: should be "default". ++- pinctrl-0: the phandle of the gpio pin node. ++- brcm,smi-clock-source: the clock source for clkman ++- brcm,smi-clock-divisor: the integer clock divisor for clkman ++- dmas: the dma controller phandle and the DREQ number (4 on a 2835) ++- dma-names: the name used by the driver to request its channel. ++ Should be "rx-tx". ++ ++Optional properties: ++- None. ++ ++Examples: ++ ++8 data pin configuration: ++ ++smi: smi@7e600000 { ++ compatible = "brcm,bcm2835-smi"; ++ reg = <0x7e600000 0x44>, <0x7e1010b0 0x8>; ++ interrupts = <2 16>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&smi_pins>; ++ brcm,smi-clock-source = <6>; ++ brcm,smi-clock-divisor = <4>; ++ dmas = <&dma 4>; ++ dma-names = "rx-tx"; ++ ++ status = "okay"; ++}; ++ ++smi_pins: smi_pins { ++ brcm,pins = <2 3 4 5 6 7 8 9 10 11 12 13 14 15>; ++ /* Alt 1: SMI */ ++ brcm,function = <5 5 5 5 5 5 5 5 5 5 5 5 5 5>; ++ /* /CS, /WE and /OE are pulled high, as they are ++ generally active low signals */ ++ brcm,pull = <2 2 2 2 2 2 0 0 0 0 0 0 0 0>; ++}; ++ +--- a/drivers/char/broadcom/Kconfig ++++ b/drivers/char/broadcom/Kconfig +@@ -41,3 +41,11 @@ config BCM2835_DEVGPIOMEM + on the 2835. Calling mmap(/dev/gpiomem) will map the GPIO + register page to the user's pointer. + ++config BCM2835_SMI_DEV ++ tristate "Character device driver for BCM2835 Secondary Memory Interface" ++ depends on (MACH_BCM2708 || MACH_BCM2709 || ARCH_BCM2835) && BCM2835_SMI ++ default m ++ help ++ This driver provides a character device interface (ioctl + read/write) to ++ Broadcom's Secondary Memory interface. The low-level functionality is provided ++ by the SMI driver itself. +--- a/drivers/char/broadcom/Makefile ++++ b/drivers/char/broadcom/Makefile +@@ -3,4 +3,4 @@ obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o + obj-$(CONFIG_BCM_VC_SM) += vc_sm/ + + obj-$(CONFIG_BCM2835_DEVGPIOMEM)+= bcm2835-gpiomem.o +- ++obj-$(CONFIG_BCM2835_SMI_DEV) += bcm2835_smi_dev.o +--- /dev/null ++++ b/drivers/char/broadcom/bcm2835_smi_dev.c +@@ -0,0 +1,402 @@ ++/** ++ * Character device driver for Broadcom Secondary Memory Interface ++ * ++ * Written by Luke Wren ++ * Copyright (c) 2015, 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define DEVICE_NAME "bcm2835-smi-dev" ++#define DRIVER_NAME "smi-dev-bcm2835" ++#define DEVICE_MINOR 0 ++ ++static struct cdev bcm2835_smi_cdev; ++static dev_t bcm2835_smi_devid; ++static struct class *bcm2835_smi_class; ++static struct device *bcm2835_smi_dev; ++ ++struct bcm2835_smi_dev_instance { ++ struct device *dev; ++}; ++ ++static struct bcm2835_smi_instance *smi_inst; ++static struct bcm2835_smi_dev_instance *inst; ++ ++static const char *const ioctl_names[] = { ++ "READ_SETTINGS", ++ "WRITE_SETTINGS", ++ "ADDRESS" ++}; ++ ++/**************************************************************************** ++* ++* SMI chardev file ops ++* ++***************************************************************************/ ++static long ++bcm2835_smi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ long ret = 0; ++ ++ dev_info(inst->dev, "serving ioctl..."); ++ ++ switch (cmd) { ++ case BCM2835_SMI_IOC_GET_SETTINGS:{ ++ struct smi_settings *settings; ++ ++ dev_info(inst->dev, "Reading SMI settings to user."); ++ settings = bcm2835_smi_get_settings_from_regs(smi_inst); ++ if (copy_to_user((void *)arg, settings, ++ sizeof(struct smi_settings))) ++ dev_err(inst->dev, "settings copy failed."); ++ break; ++ } ++ case BCM2835_SMI_IOC_WRITE_SETTINGS:{ ++ struct smi_settings *settings; ++ ++ dev_info(inst->dev, "Setting user's SMI settings."); ++ settings = bcm2835_smi_get_settings_from_regs(smi_inst); ++ if (copy_from_user(settings, (void *)arg, ++ sizeof(struct smi_settings))) ++ dev_err(inst->dev, "settings copy failed."); ++ else ++ bcm2835_smi_set_regs_from_settings(smi_inst); ++ break; ++ } ++ case BCM2835_SMI_IOC_ADDRESS: ++ dev_info(inst->dev, "SMI address set: 0x%02x", (int)arg); ++ bcm2835_smi_set_address(smi_inst, arg); ++ break; ++ default: ++ dev_err(inst->dev, "invalid ioctl cmd: %d", cmd); ++ ret = -ENOTTY; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int bcm2835_smi_open(struct inode *inode, struct file *file) ++{ ++ int dev = iminor(inode); ++ ++ dev_dbg(inst->dev, "SMI device opened."); ++ ++ if (dev != DEVICE_MINOR) { ++ dev_err(inst->dev, ++ "bcm2835_smi_release: Unknown minor device: %d", ++ dev); ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++static int bcm2835_smi_release(struct inode *inode, struct file *file) ++{ ++ int dev = iminor(inode); ++ ++ if (dev != DEVICE_MINOR) { ++ dev_err(inst->dev, ++ "bcm2835_smi_release: Unknown minor device %d", dev); ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++static ssize_t dma_bounce_user( ++ enum dma_transfer_direction dma_dir, ++ char __user *user_ptr, ++ size_t count, ++ struct bcm2835_smi_bounce_info *bounce) ++{ ++ int chunk_size; ++ int chunk_no = 0; ++ int count_left = count; ++ ++ while (count_left) { ++ int rv; ++ void *buf; ++ ++ /* Wait for current chunk to complete: */ ++ if (down_timeout(&bounce->callback_sem, ++ msecs_to_jiffies(1000))) { ++ dev_err(inst->dev, "DMA bounce timed out"); ++ count -= (count_left); ++ break; ++ } ++ ++ if (bounce->callback_sem.count >= DMA_BOUNCE_BUFFER_COUNT - 1) ++ dev_err(inst->dev, "WARNING: Ring buffer overflow"); ++ chunk_size = count_left > DMA_BOUNCE_BUFFER_SIZE ? ++ DMA_BOUNCE_BUFFER_SIZE : count_left; ++ buf = bounce->buffer[chunk_no % DMA_BOUNCE_BUFFER_COUNT]; ++ if (dma_dir == DMA_DEV_TO_MEM) ++ rv = copy_to_user(user_ptr, buf, chunk_size); ++ else ++ rv = copy_from_user(buf, user_ptr, chunk_size); ++ if (rv) ++ dev_err(inst->dev, "copy_*_user() failed!: %d", rv); ++ user_ptr += chunk_size; ++ count_left -= chunk_size; ++ chunk_no++; ++ } ++ return count; ++} ++ ++static ssize_t ++bcm2835_read_file(struct file *f, char __user *user_ptr, ++ size_t count, loff_t *offs) ++{ ++ int odd_bytes; ++ ++ dev_dbg(inst->dev, "User reading %d bytes from SMI.", count); ++ /* We don't want to DMA a number of bytes % 4 != 0 (32 bit FIFO) */ ++ if (count > DMA_THRESHOLD_BYTES) ++ odd_bytes = count & 0x3; ++ else ++ odd_bytes = count; ++ count -= odd_bytes; ++ if (count) { ++ struct bcm2835_smi_bounce_info *bounce; ++ ++ count = bcm2835_smi_user_dma(smi_inst, ++ DMA_DEV_TO_MEM, user_ptr, count, ++ &bounce); ++ if (count) ++ count = dma_bounce_user(DMA_DEV_TO_MEM, user_ptr, ++ count, bounce); ++ } ++ if (odd_bytes) { ++ /* Read from FIFO directly if not using DMA */ ++ uint8_t buf[DMA_THRESHOLD_BYTES]; ++ ++ bcm2835_smi_read_buf(smi_inst, buf, odd_bytes); ++ if (copy_to_user(user_ptr, buf, odd_bytes)) ++ dev_err(inst->dev, "copy_to_user() failed."); ++ count += odd_bytes; ++ ++ } ++ return count; ++} ++ ++static ssize_t ++bcm2835_write_file(struct file *f, const char __user *user_ptr, ++ size_t count, loff_t *offs) ++{ ++ int odd_bytes; ++ ++ dev_dbg(inst->dev, "User writing %d bytes to SMI.", count); ++ if (count > DMA_THRESHOLD_BYTES) ++ odd_bytes = count & 0x3; ++ else ++ odd_bytes = count; ++ count -= odd_bytes; ++ if (count) { ++ struct bcm2835_smi_bounce_info *bounce; ++ ++ count = bcm2835_smi_user_dma(smi_inst, ++ DMA_MEM_TO_DEV, (char __user *)user_ptr, count, ++ &bounce); ++ if (count) ++ count = dma_bounce_user(DMA_MEM_TO_DEV, ++ (char __user *)user_ptr, ++ count, bounce); ++ } ++ if (odd_bytes) { ++ uint8_t buf[DMA_THRESHOLD_BYTES]; ++ ++ if (copy_from_user(buf, user_ptr, odd_bytes)) ++ dev_err(inst->dev, "copy_from_user() failed."); ++ else ++ bcm2835_smi_write_buf(smi_inst, buf, odd_bytes); ++ count += odd_bytes; ++ } ++ return count; ++} ++ ++static const struct file_operations ++bcm2835_smi_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = bcm2835_smi_ioctl, ++ .open = bcm2835_smi_open, ++ .release = bcm2835_smi_release, ++ .read = bcm2835_read_file, ++ .write = bcm2835_write_file, ++}; ++ ++ ++/**************************************************************************** ++* ++* bcm2835_smi_probe - called when the driver is loaded. ++* ++***************************************************************************/ ++ ++static int bcm2835_smi_dev_probe(struct platform_device *pdev) ++{ ++ int err; ++ void *ptr_err; ++ struct device *dev = &pdev->dev; ++ struct device_node *node = dev->of_node, *smi_node; ++ ++ if (!node) { ++ dev_err(dev, "No device tree node supplied!"); ++ return -EINVAL; ++ } ++ ++ smi_node = of_parse_phandle(node, "smi_handle", 0); ++ ++ if (!smi_node) { ++ dev_err(dev, "No such property: smi_handle"); ++ return -ENXIO; ++ } ++ ++ smi_inst = bcm2835_smi_get(smi_node); ++ ++ if (!smi_inst) ++ return -EPROBE_DEFER; ++ ++ /* Allocate buffers and instance data */ ++ ++ inst = devm_kzalloc(dev, sizeof(*inst), GFP_KERNEL); ++ ++ if (!inst) ++ return -ENOMEM; ++ ++ inst->dev = dev; ++ ++ /* Create character device entries */ ++ ++ err = alloc_chrdev_region(&bcm2835_smi_devid, ++ DEVICE_MINOR, 1, DEVICE_NAME); ++ if (err != 0) { ++ dev_err(inst->dev, "unable to allocate device number"); ++ return -ENOMEM; ++ } ++ cdev_init(&bcm2835_smi_cdev, &bcm2835_smi_fops); ++ bcm2835_smi_cdev.owner = THIS_MODULE; ++ err = cdev_add(&bcm2835_smi_cdev, bcm2835_smi_devid, 1); ++ if (err != 0) { ++ dev_err(inst->dev, "unable to register device"); ++ err = -ENOMEM; ++ goto failed_cdev_add; ++ } ++ ++ /* Create sysfs entries */ ++ ++ bcm2835_smi_class = class_create(THIS_MODULE, DEVICE_NAME); ++ ptr_err = bcm2835_smi_class; ++ if (IS_ERR(ptr_err)) ++ goto failed_class_create; ++ ++ bcm2835_smi_dev = device_create(bcm2835_smi_class, NULL, ++ bcm2835_smi_devid, NULL, ++ "smi"); ++ ptr_err = bcm2835_smi_dev; ++ if (IS_ERR(ptr_err)) ++ goto failed_device_create; ++ ++ dev_info(inst->dev, "initialised"); ++ ++ return 0; ++ ++failed_device_create: ++ class_destroy(bcm2835_smi_class); ++failed_class_create: ++ cdev_del(&bcm2835_smi_cdev); ++ err = PTR_ERR(ptr_err); ++failed_cdev_add: ++ unregister_chrdev_region(bcm2835_smi_devid, 1); ++ dev_err(dev, "could not load bcm2835_smi_dev"); ++ return err; ++} ++ ++/**************************************************************************** ++* ++* bcm2835_smi_remove - called when the driver is unloaded. ++* ++***************************************************************************/ ++ ++static int bcm2835_smi_dev_remove(struct platform_device *pdev) ++{ ++ device_destroy(bcm2835_smi_class, bcm2835_smi_devid); ++ class_destroy(bcm2835_smi_class); ++ cdev_del(&bcm2835_smi_cdev); ++ unregister_chrdev_region(bcm2835_smi_devid, 1); ++ ++ dev_info(inst->dev, "SMI character dev removed - OK"); ++ return 0; ++} ++ ++/**************************************************************************** ++* ++* Register the driver with device tree ++* ++***************************************************************************/ ++ ++static const struct of_device_id bcm2835_smi_dev_of_match[] = { ++ {.compatible = "brcm,bcm2835-smi-dev",}, ++ { /* sentinel */ }, ++}; ++ ++MODULE_DEVICE_TABLE(of, bcm2835_smi_dev_of_match); ++ ++static struct platform_driver bcm2835_smi_dev_driver = { ++ .probe = bcm2835_smi_dev_probe, ++ .remove = bcm2835_smi_dev_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = bcm2835_smi_dev_of_match, ++ }, ++}; ++ ++module_platform_driver(bcm2835_smi_dev_driver); ++ ++MODULE_ALIAS("platform:smi-dev-bcm2835"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION( ++ "Character device driver for BCM2835's secondary memory interface"); ++MODULE_AUTHOR("Luke Wren "); +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -10,6 +10,14 @@ config SENSORS_LIS3LV02D + select INPUT_POLLDEV + default n + ++config BCM2835_SMI ++ tristate "Broadcom 283x Secondary Memory Interface driver" ++ depends on MACH_BCM2708 || MACH_BCM2709 || ARCH_BCM2835 ++ default m ++ help ++ Driver for enabling and using Broadcom's Secondary/Slow Memory Interface. ++ Appears as /dev/bcm2835_smi. For ioctl interface see drivers/misc/bcm2835_smi.h ++ + config AD525X_DPOT + tristate "Analog Devices Digital Potentiometers" + depends on (I2C || SPI) && SYSFS +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -9,6 +9,7 @@ obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_ + obj-$(CONFIG_INTEL_MID_PTI) += pti.o + obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o + obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o ++obj-$(CONFIG_BCM2835_SMI) += bcm2835_smi.o + obj-$(CONFIG_BMP085) += bmp085.o + obj-$(CONFIG_BMP085_I2C) += bmp085-i2c.o + obj-$(CONFIG_BMP085_SPI) += bmp085-spi.o +--- /dev/null ++++ b/drivers/misc/bcm2835_smi.c +@@ -0,0 +1,985 @@ ++/** ++ * Broadcom Secondary Memory Interface driver ++ * ++ * Written by Luke Wren ++ * Copyright (c) 2015, 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define BCM2835_SMI_IMPLEMENTATION ++#include ++ ++#define DRIVER_NAME "smi-bcm2835" ++ ++#define N_PAGES_FROM_BYTES(n) ((n + PAGE_SIZE-1) / PAGE_SIZE) ++ ++#define DMA_WRITE_TO_MEM true ++#define DMA_READ_FROM_MEM false ++ ++struct bcm2835_smi_instance { ++ struct device *dev; ++ struct smi_settings settings; ++ __iomem void *smi_regs_ptr, *cm_smi_regs_ptr; ++ dma_addr_t smi_regs_busaddr; ++ ++ struct dma_chan *dma_chan; ++ struct dma_slave_config dma_config; ++ ++ struct bcm2835_smi_bounce_info bounce; ++ ++ struct scatterlist buffer_sgl; ++ ++ int clock_source; ++ int clock_divisor; ++ ++ /* Sometimes we are called into in an atomic context (e.g. by ++ JFFS2 + MTD) so we can't use a mutex */ ++ spinlock_t transaction_lock; ++}; ++ ++/**************************************************************************** ++* ++* SMI clock manager setup ++* ++***************************************************************************/ ++ ++static inline void write_smi_cm_reg(struct bcm2835_smi_instance *inst, ++ u32 val, unsigned reg) ++{ ++ writel(CM_PWD | val, inst->cm_smi_regs_ptr + reg); ++} ++ ++static inline u32 read_smi_cm_reg(struct bcm2835_smi_instance *inst, ++ unsigned reg) ++{ ++ return readl(inst->cm_smi_regs_ptr + reg); ++} ++ ++static void smi_setup_clock(struct bcm2835_smi_instance *inst) ++{ ++ dev_dbg(inst->dev, "Setting up clock..."); ++ /* Disable SMI clock and wait for it to stop. */ ++ write_smi_cm_reg(inst, 0, CM_SMI_CTL); ++ while (read_smi_cm_reg(inst, CM_SMI_CTL) & CM_SMI_CTL_BUSY) ++ ; ++ ++ write_smi_cm_reg(inst, (inst->clock_divisor << CM_SMI_DIV_DIVI_OFFS), ++ CM_SMI_DIV); ++ write_smi_cm_reg(inst, (inst->clock_source << CM_SMI_CTL_SRC_OFFS), ++ CM_SMI_CTL); ++ ++ /* Enable the clock */ ++ write_smi_cm_reg(inst, (inst->clock_source << CM_SMI_CTL_SRC_OFFS) | ++ CM_SMI_CTL_ENAB, CM_SMI_CTL); ++} ++ ++/**************************************************************************** ++* ++* SMI peripheral setup ++* ++***************************************************************************/ ++ ++static inline void write_smi_reg(struct bcm2835_smi_instance *inst, ++ u32 val, unsigned reg) ++{ ++ writel(val, inst->smi_regs_ptr + reg); ++} ++ ++static inline u32 read_smi_reg(struct bcm2835_smi_instance *inst, unsigned reg) ++{ ++ return readl(inst->smi_regs_ptr + reg); ++} ++ ++/* Token-paste macro for e.g SMIDSR_RSTROBE -> value of SMIDSR_RSTROBE_MASK */ ++#define _CONCAT(x, y) x##y ++#define CONCAT(x, y) _CONCAT(x, y) ++ ++#define SET_BIT_FIELD(dest, field, bits) ((dest) = \ ++ ((dest) & ~CONCAT(field, _MASK)) | (((bits) << CONCAT(field, _OFFS))& \ ++ CONCAT(field, _MASK))) ++#define GET_BIT_FIELD(src, field) (((src) & \ ++ CONCAT(field, _MASK)) >> CONCAT(field, _OFFS)) ++ ++static void smi_dump_context_labelled(struct bcm2835_smi_instance *inst, ++ const char *label) ++{ ++ dev_err(inst->dev, "SMI context dump: %s", label); ++ dev_err(inst->dev, "SMICS: 0x%08x", read_smi_reg(inst, SMICS)); ++ dev_err(inst->dev, "SMIL: 0x%08x", read_smi_reg(inst, SMIL)); ++ dev_err(inst->dev, "SMIDSR: 0x%08x", read_smi_reg(inst, SMIDSR0)); ++ dev_err(inst->dev, "SMIDSW: 0x%08x", read_smi_reg(inst, SMIDSW0)); ++ dev_err(inst->dev, "SMIDC: 0x%08x", read_smi_reg(inst, SMIDC)); ++ dev_err(inst->dev, "SMIFD: 0x%08x", read_smi_reg(inst, SMIFD)); ++ dev_err(inst->dev, " "); ++} ++ ++static inline void smi_dump_context(struct bcm2835_smi_instance *inst) ++{ ++ smi_dump_context_labelled(inst, ""); ++} ++ ++static void smi_get_default_settings(struct bcm2835_smi_instance *inst) ++{ ++ struct smi_settings *settings = &inst->settings; ++ ++ settings->data_width = SMI_WIDTH_16BIT; ++ settings->pack_data = true; ++ ++ settings->read_setup_time = 1; ++ settings->read_hold_time = 1; ++ settings->read_pace_time = 1; ++ settings->read_strobe_time = 3; ++ ++ settings->write_setup_time = settings->read_setup_time; ++ settings->write_hold_time = settings->read_hold_time; ++ settings->write_pace_time = settings->read_pace_time; ++ settings->write_strobe_time = settings->read_strobe_time; ++ ++ settings->dma_enable = true; ++ settings->dma_passthrough_enable = false; ++ settings->dma_read_thresh = 0x01; ++ settings->dma_write_thresh = 0x3f; ++ settings->dma_panic_read_thresh = 0x20; ++ settings->dma_panic_write_thresh = 0x20; ++} ++ ++void bcm2835_smi_set_regs_from_settings(struct bcm2835_smi_instance *inst) ++{ ++ struct smi_settings *settings = &inst->settings; ++ int smidsr_temp = 0, smidsw_temp = 0, smics_temp, ++ smidcs_temp, smidc_temp = 0; ++ ++ spin_lock(&inst->transaction_lock); ++ ++ /* temporarily disable the peripheral: */ ++ smics_temp = read_smi_reg(inst, SMICS); ++ write_smi_reg(inst, 0, SMICS); ++ smidcs_temp = read_smi_reg(inst, SMIDCS); ++ write_smi_reg(inst, 0, SMIDCS); ++ ++ if (settings->pack_data) ++ smics_temp |= SMICS_PXLDAT; ++ else ++ smics_temp &= ~SMICS_PXLDAT; ++ ++ SET_BIT_FIELD(smidsr_temp, SMIDSR_RWIDTH, settings->data_width); ++ SET_BIT_FIELD(smidsr_temp, SMIDSR_RSETUP, settings->read_setup_time); ++ SET_BIT_FIELD(smidsr_temp, SMIDSR_RHOLD, settings->read_hold_time); ++ SET_BIT_FIELD(smidsr_temp, SMIDSR_RPACE, settings->read_pace_time); ++ SET_BIT_FIELD(smidsr_temp, SMIDSR_RSTROBE, settings->read_strobe_time); ++ write_smi_reg(inst, smidsr_temp, SMIDSR0); ++ ++ SET_BIT_FIELD(smidsw_temp, SMIDSW_WWIDTH, settings->data_width); ++ if (settings->data_width == SMI_WIDTH_8BIT) ++ smidsw_temp |= SMIDSW_WSWAP; ++ else ++ smidsw_temp &= ~SMIDSW_WSWAP; ++ SET_BIT_FIELD(smidsw_temp, SMIDSW_WSETUP, settings->write_setup_time); ++ SET_BIT_FIELD(smidsw_temp, SMIDSW_WHOLD, settings->write_hold_time); ++ SET_BIT_FIELD(smidsw_temp, SMIDSW_WPACE, settings->write_pace_time); ++ SET_BIT_FIELD(smidsw_temp, SMIDSW_WSTROBE, ++ settings->write_strobe_time); ++ write_smi_reg(inst, smidsw_temp, SMIDSW0); ++ ++ SET_BIT_FIELD(smidc_temp, SMIDC_REQR, settings->dma_read_thresh); ++ SET_BIT_FIELD(smidc_temp, SMIDC_REQW, settings->dma_write_thresh); ++ SET_BIT_FIELD(smidc_temp, SMIDC_PANICR, ++ settings->dma_panic_read_thresh); ++ SET_BIT_FIELD(smidc_temp, SMIDC_PANICW, ++ settings->dma_panic_write_thresh); ++ if (settings->dma_passthrough_enable) { ++ smidc_temp |= SMIDC_DMAP; ++ smidsr_temp |= SMIDSR_RDREQ; ++ write_smi_reg(inst, smidsr_temp, SMIDSR0); ++ smidsw_temp |= SMIDSW_WDREQ; ++ write_smi_reg(inst, smidsw_temp, SMIDSW0); ++ } else ++ smidc_temp &= ~SMIDC_DMAP; ++ if (settings->dma_enable) ++ smidc_temp |= SMIDC_DMAEN; ++ else ++ smidc_temp &= ~SMIDC_DMAEN; ++ ++ write_smi_reg(inst, smidc_temp, SMIDC); ++ ++ /* re-enable (if was previously enabled) */ ++ write_smi_reg(inst, smics_temp, SMICS); ++ write_smi_reg(inst, smidcs_temp, SMIDCS); ++ ++ spin_unlock(&inst->transaction_lock); ++} ++EXPORT_SYMBOL(bcm2835_smi_set_regs_from_settings); ++ ++struct smi_settings *bcm2835_smi_get_settings_from_regs ++ (struct bcm2835_smi_instance *inst) ++{ ++ struct smi_settings *settings = &inst->settings; ++ int smidsr, smidsw, smidc; ++ ++ spin_lock(&inst->transaction_lock); ++ ++ smidsr = read_smi_reg(inst, SMIDSR0); ++ smidsw = read_smi_reg(inst, SMIDSW0); ++ smidc = read_smi_reg(inst, SMIDC); ++ ++ settings->pack_data = (read_smi_reg(inst, SMICS) & SMICS_PXLDAT) ? ++ true : false; ++ ++ settings->data_width = GET_BIT_FIELD(smidsr, SMIDSR_RWIDTH); ++ settings->read_setup_time = GET_BIT_FIELD(smidsr, SMIDSR_RSETUP); ++ settings->read_hold_time = GET_BIT_FIELD(smidsr, SMIDSR_RHOLD); ++ settings->read_pace_time = GET_BIT_FIELD(smidsr, SMIDSR_RPACE); ++ settings->read_strobe_time = GET_BIT_FIELD(smidsr, SMIDSR_RSTROBE); ++ ++ settings->write_setup_time = GET_BIT_FIELD(smidsw, SMIDSW_WSETUP); ++ settings->write_hold_time = GET_BIT_FIELD(smidsw, SMIDSW_WHOLD); ++ settings->write_pace_time = GET_BIT_FIELD(smidsw, SMIDSW_WPACE); ++ settings->write_strobe_time = GET_BIT_FIELD(smidsw, SMIDSW_WSTROBE); ++ ++ settings->dma_read_thresh = GET_BIT_FIELD(smidc, SMIDC_REQR); ++ settings->dma_write_thresh = GET_BIT_FIELD(smidc, SMIDC_REQW); ++ settings->dma_panic_read_thresh = GET_BIT_FIELD(smidc, SMIDC_PANICR); ++ settings->dma_panic_write_thresh = GET_BIT_FIELD(smidc, SMIDC_PANICW); ++ settings->dma_passthrough_enable = (smidc & SMIDC_DMAP) ? true : false; ++ settings->dma_enable = (smidc & SMIDC_DMAEN) ? true : false; ++ ++ spin_unlock(&inst->transaction_lock); ++ ++ return settings; ++} ++EXPORT_SYMBOL(bcm2835_smi_get_settings_from_regs); ++ ++static inline void smi_set_address(struct bcm2835_smi_instance *inst, ++ unsigned int address) ++{ ++ int smia_temp = 0, smida_temp = 0; ++ ++ SET_BIT_FIELD(smia_temp, SMIA_ADDR, address); ++ SET_BIT_FIELD(smida_temp, SMIDA_ADDR, address); ++ ++ /* Write to both address registers - user doesn't care whether we're ++ doing programmed or direct transfers. */ ++ write_smi_reg(inst, smia_temp, SMIA); ++ write_smi_reg(inst, smida_temp, SMIDA); ++} ++ ++static void smi_setup_regs(struct bcm2835_smi_instance *inst) ++{ ++ ++ dev_dbg(inst->dev, "Initialising SMI registers..."); ++ /* Disable the peripheral if already enabled */ ++ write_smi_reg(inst, 0, SMICS); ++ write_smi_reg(inst, 0, SMIDCS); ++ ++ smi_get_default_settings(inst); ++ bcm2835_smi_set_regs_from_settings(inst); ++ smi_set_address(inst, 0); ++ ++ write_smi_reg(inst, read_smi_reg(inst, SMICS) | SMICS_ENABLE, SMICS); ++ write_smi_reg(inst, read_smi_reg(inst, SMIDCS) | SMIDCS_ENABLE, ++ SMIDCS); ++} ++ ++/**************************************************************************** ++* ++* Low-level SMI access functions ++* Other modules should use the exported higher-level functions e.g. ++* bcm2835_smi_write_buf() unless they have a good reason to use these ++* ++***************************************************************************/ ++ ++static inline uint32_t smi_read_single_word(struct bcm2835_smi_instance *inst) ++{ ++ int timeout = 0; ++ ++ write_smi_reg(inst, SMIDCS_ENABLE, SMIDCS); ++ write_smi_reg(inst, SMIDCS_ENABLE | SMIDCS_START, SMIDCS); ++ /* Make sure things happen in the right order...*/ ++ mb(); ++ while (!(read_smi_reg(inst, SMIDCS) & SMIDCS_DONE) && ++ ++timeout < 10000) ++ ; ++ if (timeout < 10000) ++ return read_smi_reg(inst, SMIDD); ++ ++ dev_err(inst->dev, ++ "SMI direct read timed out (is the clock set up correctly?)"); ++ return 0; ++} ++ ++static inline void smi_write_single_word(struct bcm2835_smi_instance *inst, ++ uint32_t data) ++{ ++ int timeout = 0; ++ ++ write_smi_reg(inst, SMIDCS_ENABLE | SMIDCS_WRITE, SMIDCS); ++ write_smi_reg(inst, data, SMIDD); ++ write_smi_reg(inst, SMIDCS_ENABLE | SMIDCS_WRITE | SMIDCS_START, ++ SMIDCS); ++ ++ while (!(read_smi_reg(inst, SMIDCS) & SMIDCS_DONE) && ++ ++timeout < 10000) ++ ; ++ if (timeout >= 10000) ++ dev_err(inst->dev, ++ "SMI direct write timed out (is the clock set up correctly?)"); ++} ++ ++/* Initiates a programmed read into the read FIFO. It is up to the caller to ++ * read data from the FIFO - either via paced DMA transfer, ++ * or polling SMICS_RXD to check whether data is available. ++ * SMICS_ACTIVE will go low upon completion. */ ++static void smi_init_programmed_read(struct bcm2835_smi_instance *inst, ++ int num_transfers) ++{ ++ int smics_temp; ++ ++ /* Disable the peripheral: */ ++ smics_temp = read_smi_reg(inst, SMICS) & ~(SMICS_ENABLE | SMICS_WRITE); ++ write_smi_reg(inst, smics_temp, SMICS); ++ while (read_smi_reg(inst, SMICS) & SMICS_ENABLE) ++ ; ++ ++ /* Program the transfer count: */ ++ write_smi_reg(inst, num_transfers, SMIL); ++ ++ /* re-enable and start: */ ++ smics_temp |= SMICS_ENABLE; ++ write_smi_reg(inst, smics_temp, SMICS); ++ smics_temp |= SMICS_CLEAR; ++ /* Just to be certain: */ ++ mb(); ++ while (read_smi_reg(inst, SMICS) & SMICS_ACTIVE) ++ ; ++ write_smi_reg(inst, smics_temp, SMICS); ++ smics_temp |= SMICS_START; ++ write_smi_reg(inst, smics_temp, SMICS); ++} ++ ++/* Initiates a programmed write sequence, using data from the write FIFO. ++ * It is up to the caller to initiate a DMA transfer before calling, ++ * or use another method to keep the write FIFO topped up. ++ * SMICS_ACTIVE will go low upon completion. ++ */ ++static void smi_init_programmed_write(struct bcm2835_smi_instance *inst, ++ int num_transfers) ++{ ++ int smics_temp; ++ ++ /* Disable the peripheral: */ ++ smics_temp = read_smi_reg(inst, SMICS) & ~SMICS_ENABLE; ++ write_smi_reg(inst, smics_temp, SMICS); ++ while (read_smi_reg(inst, SMICS) & SMICS_ENABLE) ++ ; ++ ++ /* Program the transfer count: */ ++ write_smi_reg(inst, num_transfers, SMIL); ++ ++ /* setup, re-enable and start: */ ++ smics_temp |= SMICS_WRITE | SMICS_ENABLE; ++ write_smi_reg(inst, smics_temp, SMICS); ++ smics_temp |= SMICS_START; ++ write_smi_reg(inst, smics_temp, SMICS); ++} ++ ++/* Initiate a read and then poll FIFO for data, reading out as it appears. */ ++static void smi_read_fifo(struct bcm2835_smi_instance *inst, ++ uint32_t *dest, int n_bytes) ++{ ++ if (read_smi_reg(inst, SMICS) & SMICS_RXD) { ++ smi_dump_context_labelled(inst, ++ "WARNING: read FIFO not empty at start of read call."); ++ while (read_smi_reg(inst, SMICS)) ++ ; ++ } ++ ++ /* Dispatch the read: */ ++ if (inst->settings.data_width == SMI_WIDTH_8BIT) ++ smi_init_programmed_read(inst, n_bytes); ++ else if (inst->settings.data_width == SMI_WIDTH_16BIT) ++ smi_init_programmed_read(inst, n_bytes / 2); ++ else { ++ dev_err(inst->dev, "Unsupported data width for read."); ++ return; ++ } ++ ++ /* Poll FIFO to keep it empty */ ++ while (!(read_smi_reg(inst, SMICS) & SMICS_DONE)) ++ if (read_smi_reg(inst, SMICS) & SMICS_RXD) ++ *dest++ = read_smi_reg(inst, SMID); ++ ++ /* Ensure that the FIFO is emptied */ ++ if (read_smi_reg(inst, SMICS) & SMICS_RXD) { ++ int fifo_count; ++ ++ fifo_count = GET_BIT_FIELD(read_smi_reg(inst, SMIFD), ++ SMIFD_FCNT); ++ while (fifo_count--) ++ *dest++ = read_smi_reg(inst, SMID); ++ } ++ ++ if (!(read_smi_reg(inst, SMICS) & SMICS_DONE)) ++ smi_dump_context_labelled(inst, ++ "WARNING: transaction finished but done bit not set."); ++ ++ if (read_smi_reg(inst, SMICS) & SMICS_RXD) ++ smi_dump_context_labelled(inst, ++ "WARNING: read FIFO not empty at end of read call."); ++ ++} ++ ++/* Initiate a write, and then keep the FIFO topped up. */ ++static void smi_write_fifo(struct bcm2835_smi_instance *inst, ++ uint32_t *src, int n_bytes) ++{ ++ int i, timeout = 0; ++ ++ /* Empty FIFOs if not already so */ ++ if (!(read_smi_reg(inst, SMICS) & SMICS_TXE)) { ++ smi_dump_context_labelled(inst, ++ "WARNING: write fifo not empty at start of write call."); ++ write_smi_reg(inst, read_smi_reg(inst, SMICS) | SMICS_CLEAR, ++ SMICS); ++ } ++ ++ /* Initiate the transfer */ ++ if (inst->settings.data_width == SMI_WIDTH_8BIT) ++ smi_init_programmed_write(inst, n_bytes); ++ else if (inst->settings.data_width == SMI_WIDTH_16BIT) ++ smi_init_programmed_write(inst, n_bytes / 2); ++ else { ++ dev_err(inst->dev, "Unsupported data width for write."); ++ return; ++ } ++ /* Fill the FIFO: */ ++ for (i = 0; i < (n_bytes - 1) / 4 + 1; ++i) { ++ while (!(read_smi_reg(inst, SMICS) & SMICS_TXD)) ++ ; ++ write_smi_reg(inst, *src++, SMID); ++ } ++ /* Busy wait... */ ++ while (!(read_smi_reg(inst, SMICS) & SMICS_DONE) && ++timeout < ++ 1000000) ++ ; ++ if (timeout >= 1000000) ++ smi_dump_context_labelled(inst, ++ "Timed out on write operation!"); ++ if (!(read_smi_reg(inst, SMICS) & SMICS_TXE)) ++ smi_dump_context_labelled(inst, ++ "WARNING: FIFO not empty at end of write operation."); ++} ++ ++/**************************************************************************** ++* ++* SMI DMA operations ++* ++***************************************************************************/ ++ ++/* Disable SMI and put it into the correct direction before doing DMA setup. ++ Stops spurious DREQs during setup. Peripheral is re-enabled by init_*() */ ++static void smi_disable(struct bcm2835_smi_instance *inst, ++ enum dma_transfer_direction direction) ++{ ++ int smics_temp = read_smi_reg(inst, SMICS) & ~SMICS_ENABLE; ++ ++ if (direction == DMA_DEV_TO_MEM) ++ smics_temp &= ~SMICS_WRITE; ++ else ++ smics_temp |= SMICS_WRITE; ++ write_smi_reg(inst, smics_temp, SMICS); ++ while (read_smi_reg(inst, SMICS) & SMICS_ACTIVE) ++ ; ++} ++ ++static struct scatterlist *smi_scatterlist_from_buffer( ++ struct bcm2835_smi_instance *inst, ++ dma_addr_t buf, ++ size_t len, ++ struct scatterlist *sg) ++{ ++ sg_init_table(sg, 1); ++ sg_dma_address(sg) = buf; ++ sg_dma_len(sg) = len; ++ return sg; ++} ++ ++static void smi_dma_callback_user_copy(void *param) ++{ ++ /* Notify the bottom half that a chunk is ready for user copy */ ++ struct bcm2835_smi_instance *inst = ++ (struct bcm2835_smi_instance *)param; ++ ++ up(&inst->bounce.callback_sem); ++} ++ ++/* Creates a descriptor, assigns the given callback, and submits the ++ descriptor to dmaengine. Does not block - can queue up multiple ++ descriptors and then wait for them all to complete. ++ sg_len is the number of control blocks, NOT the number of bytes. ++ dir can be DMA_MEM_TO_DEV or DMA_DEV_TO_MEM. ++ callback can be NULL - in this case it is not called. */ ++static inline struct dma_async_tx_descriptor *smi_dma_submit_sgl( ++ struct bcm2835_smi_instance *inst, ++ struct scatterlist *sgl, ++ size_t sg_len, ++ enum dma_transfer_direction dir, ++ dma_async_tx_callback callback) ++{ ++ struct dma_async_tx_descriptor *desc; ++ ++ desc = dmaengine_prep_slave_sg(inst->dma_chan, ++ sgl, ++ sg_len, ++ dir, ++ DMA_PREP_INTERRUPT | DMA_CTRL_ACK | ++ DMA_PREP_FENCE); ++ if (!desc) { ++ dev_err(inst->dev, "read_sgl: dma slave preparation failed!"); ++ write_smi_reg(inst, read_smi_reg(inst, SMICS) & ~SMICS_ACTIVE, ++ SMICS); ++ while (read_smi_reg(inst, SMICS) & SMICS_ACTIVE) ++ cpu_relax(); ++ write_smi_reg(inst, read_smi_reg(inst, SMICS) | SMICS_ACTIVE, ++ SMICS); ++ return NULL; ++ } ++ desc->callback = callback; ++ desc->callback_param = inst; ++ if (dmaengine_submit(desc) < 0) ++ return NULL; ++ return desc; ++} ++ ++/* NB this function blocks until the transfer is complete */ ++static void ++smi_dma_read_sgl(struct bcm2835_smi_instance *inst, ++ struct scatterlist *sgl, size_t sg_len, size_t n_bytes) ++{ ++ struct dma_async_tx_descriptor *desc; ++ ++ /* Disable SMI and set to read before dispatching DMA - if SMI is in ++ * write mode and TX fifo is empty, it will generate a DREQ which may ++ * cause the read DMA to complete before the SMI read command is even ++ * dispatched! We want to dispatch DMA before SMI read so that reading ++ * is gapless, for logic analyser. ++ */ ++ ++ smi_disable(inst, DMA_DEV_TO_MEM); ++ ++ desc = smi_dma_submit_sgl(inst, sgl, sg_len, DMA_DEV_TO_MEM, NULL); ++ dma_async_issue_pending(inst->dma_chan); ++ ++ if (inst->settings.data_width == SMI_WIDTH_8BIT) ++ smi_init_programmed_read(inst, n_bytes); ++ else ++ smi_init_programmed_read(inst, n_bytes / 2); ++ ++ if (dma_wait_for_async_tx(desc) == DMA_ERROR) ++ smi_dump_context_labelled(inst, "DMA timeout!"); ++} ++ ++static void ++smi_dma_write_sgl(struct bcm2835_smi_instance *inst, ++ struct scatterlist *sgl, size_t sg_len, size_t n_bytes) ++{ ++ struct dma_async_tx_descriptor *desc; ++ ++ if (inst->settings.data_width == SMI_WIDTH_8BIT) ++ smi_init_programmed_write(inst, n_bytes); ++ else ++ smi_init_programmed_write(inst, n_bytes / 2); ++ ++ desc = smi_dma_submit_sgl(inst, sgl, sg_len, DMA_MEM_TO_DEV, NULL); ++ dma_async_issue_pending(inst->dma_chan); ++ ++ if (dma_wait_for_async_tx(desc) == DMA_ERROR) ++ smi_dump_context_labelled(inst, "DMA timeout!"); ++ else ++ /* Wait for SMI to finish our writes */ ++ while (!(read_smi_reg(inst, SMICS) & SMICS_DONE)) ++ cpu_relax(); ++} ++ ++ssize_t bcm2835_smi_user_dma( ++ struct bcm2835_smi_instance *inst, ++ enum dma_transfer_direction dma_dir, ++ char __user *user_ptr, size_t count, ++ struct bcm2835_smi_bounce_info **bounce) ++{ ++ int chunk_no = 0, chunk_size, count_left = count; ++ struct scatterlist *sgl; ++ void (*init_trans_func)(struct bcm2835_smi_instance *, int); ++ ++ spin_lock(&inst->transaction_lock); ++ ++ if (dma_dir == DMA_DEV_TO_MEM) ++ init_trans_func = smi_init_programmed_read; ++ else ++ init_trans_func = smi_init_programmed_write; ++ ++ smi_disable(inst, dma_dir); ++ ++ sema_init(&inst->bounce.callback_sem, 0); ++ if (bounce) ++ *bounce = &inst->bounce; ++ while (count_left) { ++ chunk_size = count_left > DMA_BOUNCE_BUFFER_SIZE ? ++ DMA_BOUNCE_BUFFER_SIZE : count_left; ++ if (chunk_size == DMA_BOUNCE_BUFFER_SIZE) { ++ sgl = ++ &inst->bounce.sgl[chunk_no % DMA_BOUNCE_BUFFER_COUNT]; ++ } else { ++ sgl = smi_scatterlist_from_buffer( ++ inst, ++ inst->bounce.phys[ ++ chunk_no % DMA_BOUNCE_BUFFER_COUNT], ++ chunk_size, ++ &inst->buffer_sgl); ++ } ++ ++ if (!smi_dma_submit_sgl(inst, sgl, 1, dma_dir, ++ smi_dma_callback_user_copy ++ )) { ++ dev_err(inst->dev, "sgl submit failed"); ++ count = 0; ++ goto out; ++ } ++ count_left -= chunk_size; ++ chunk_no++; ++ } ++ dma_async_issue_pending(inst->dma_chan); ++ ++ if (inst->settings.data_width == SMI_WIDTH_8BIT) ++ init_trans_func(inst, count); ++ else if (inst->settings.data_width == SMI_WIDTH_16BIT) ++ init_trans_func(inst, count / 2); ++out: ++ spin_unlock(&inst->transaction_lock); ++ return count; ++} ++EXPORT_SYMBOL(bcm2835_smi_user_dma); ++ ++ ++/**************************************************************************** ++* ++* High level buffer transfer functions - for use by other drivers ++* ++***************************************************************************/ ++ ++/* Buffer must be physically contiguous - i.e. kmalloc, not vmalloc! */ ++void bcm2835_smi_write_buf( ++ struct bcm2835_smi_instance *inst, ++ const void *buf, size_t n_bytes) ++{ ++ int odd_bytes = n_bytes & 0x3; ++ ++ n_bytes -= odd_bytes; ++ ++ spin_lock(&inst->transaction_lock); ++ ++ if (n_bytes > DMA_THRESHOLD_BYTES) { ++ dma_addr_t phy_addr = dma_map_single( ++ inst->dev, ++ (void *)buf, ++ n_bytes, ++ DMA_MEM_TO_DEV); ++ struct scatterlist *sgl = ++ smi_scatterlist_from_buffer(inst, phy_addr, n_bytes, ++ &inst->buffer_sgl); ++ ++ if (!sgl) { ++ smi_dump_context_labelled(inst, ++ "Error: could not create scatterlist for write!"); ++ goto out; ++ } ++ smi_dma_write_sgl(inst, sgl, 1, n_bytes); ++ ++ dma_unmap_single ++ (inst->dev, phy_addr, n_bytes, DMA_MEM_TO_DEV); ++ } else if (n_bytes) { ++ smi_write_fifo(inst, (uint32_t *) buf, n_bytes); ++ } ++ buf += n_bytes; ++ ++ if (inst->settings.data_width == SMI_WIDTH_8BIT) { ++ while (odd_bytes--) ++ smi_write_single_word(inst, *(uint8_t *) (buf++)); ++ } else { ++ while (odd_bytes >= 2) { ++ smi_write_single_word(inst, *(uint16_t *)buf); ++ buf += 2; ++ odd_bytes -= 2; ++ } ++ if (odd_bytes) { ++ /* Reading an odd number of bytes on a 16 bit bus is ++ a user bug. It's kinder to fail early and tell them ++ than to e.g. transparently give them the bottom byte ++ of a 16 bit transfer. */ ++ dev_err(inst->dev, ++ "WARNING: odd number of bytes specified for wide transfer."); ++ dev_err(inst->dev, ++ "At least one byte dropped as a result."); ++ dump_stack(); ++ } ++ } ++out: ++ spin_unlock(&inst->transaction_lock); ++} ++EXPORT_SYMBOL(bcm2835_smi_write_buf); ++ ++void bcm2835_smi_read_buf(struct bcm2835_smi_instance *inst, ++ void *buf, size_t n_bytes) ++{ ++ ++ /* SMI is inherently 32-bit, which causes surprising amounts of mess ++ for bytes % 4 != 0. Easiest to avoid this mess altogether ++ by handling remainder separately. */ ++ int odd_bytes = n_bytes & 0x3; ++ ++ spin_lock(&inst->transaction_lock); ++ n_bytes -= odd_bytes; ++ if (n_bytes > DMA_THRESHOLD_BYTES) { ++ dma_addr_t phy_addr = dma_map_single(inst->dev, ++ buf, n_bytes, ++ DMA_DEV_TO_MEM); ++ struct scatterlist *sgl = smi_scatterlist_from_buffer( ++ inst, phy_addr, n_bytes, ++ &inst->buffer_sgl); ++ if (!sgl) { ++ smi_dump_context_labelled(inst, ++ "Error: could not create scatterlist for read!"); ++ goto out; ++ } ++ smi_dma_read_sgl(inst, sgl, 1, n_bytes); ++ dma_unmap_single(inst->dev, phy_addr, n_bytes, DMA_DEV_TO_MEM); ++ } else if (n_bytes) { ++ smi_read_fifo(inst, (uint32_t *)buf, n_bytes); ++ } ++ buf += n_bytes; ++ ++ if (inst->settings.data_width == SMI_WIDTH_8BIT) { ++ while (odd_bytes--) ++ *((uint8_t *) (buf++)) = smi_read_single_word(inst); ++ } else { ++ while (odd_bytes >= 2) { ++ *(uint16_t *) buf = smi_read_single_word(inst); ++ buf += 2; ++ odd_bytes -= 2; ++ } ++ if (odd_bytes) { ++ dev_err(inst->dev, ++ "WARNING: odd number of bytes specified for wide transfer."); ++ dev_err(inst->dev, ++ "At least one byte dropped as a result."); ++ dump_stack(); ++ } ++ } ++out: ++ spin_unlock(&inst->transaction_lock); ++} ++EXPORT_SYMBOL(bcm2835_smi_read_buf); ++ ++void bcm2835_smi_set_address(struct bcm2835_smi_instance *inst, ++ unsigned int address) ++{ ++ spin_lock(&inst->transaction_lock); ++ smi_set_address(inst, address); ++ spin_unlock(&inst->transaction_lock); ++} ++EXPORT_SYMBOL(bcm2835_smi_set_address); ++ ++struct bcm2835_smi_instance *bcm2835_smi_get(struct device_node *node) ++{ ++ struct platform_device *pdev; ++ ++ if (!node) ++ return NULL; ++ ++ pdev = of_find_device_by_node(node); ++ if (!pdev) ++ return NULL; ++ ++ return platform_get_drvdata(pdev); ++} ++EXPORT_SYMBOL(bcm2835_smi_get); ++ ++/**************************************************************************** ++* ++* bcm2835_smi_probe - called when the driver is loaded. ++* ++***************************************************************************/ ++ ++static int bcm2835_smi_dma_setup(struct bcm2835_smi_instance *inst) ++{ ++ int i, rv = 0; ++ ++ inst->dma_chan = dma_request_slave_channel(inst->dev, "rx-tx"); ++ ++ inst->dma_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ inst->dma_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ inst->dma_config.src_addr = inst->smi_regs_busaddr + SMID; ++ inst->dma_config.dst_addr = inst->dma_config.src_addr; ++ /* Direction unimportant - always overridden by prep_slave_sg */ ++ inst->dma_config.direction = DMA_DEV_TO_MEM; ++ dmaengine_slave_config(inst->dma_chan, &inst->dma_config); ++ /* Alloc and map bounce buffers */ ++ for (i = 0; i < DMA_BOUNCE_BUFFER_COUNT; ++i) { ++ inst->bounce.buffer[i] = ++ dmam_alloc_coherent(inst->dev, DMA_BOUNCE_BUFFER_SIZE, ++ &inst->bounce.phys[i], ++ GFP_KERNEL); ++ if (!inst->bounce.buffer[i]) { ++ dev_err(inst->dev, "Could not allocate buffer!"); ++ rv = -ENOMEM; ++ break; ++ } ++ smi_scatterlist_from_buffer( ++ inst, ++ inst->bounce.phys[i], ++ DMA_BOUNCE_BUFFER_SIZE, ++ &inst->bounce.sgl[i] ++ ); ++ } ++ ++ return rv; ++} ++ ++static int bcm2835_smi_probe(struct platform_device *pdev) ++{ ++ int err; ++ struct device *dev = &pdev->dev; ++ struct device_node *node = dev->of_node; ++ struct resource *ioresource; ++ struct bcm2835_smi_instance *inst; ++ ++ /* Allocate buffers and instance data */ ++ ++ inst = devm_kzalloc(dev, sizeof(struct bcm2835_smi_instance), ++ GFP_KERNEL); ++ ++ if (!inst) ++ return -ENOMEM; ++ ++ inst->dev = dev; ++ spin_lock_init(&inst->transaction_lock); ++ ++ /* We require device tree support */ ++ if (!node) ++ return -EINVAL; ++ ++ ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ inst->smi_regs_ptr = devm_ioremap_resource(dev, ioresource); ++ ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ inst->cm_smi_regs_ptr = devm_ioremap_resource(dev, ioresource); ++ inst->smi_regs_busaddr = be32_to_cpu( ++ *of_get_address(node, 0, NULL, NULL)); ++ of_property_read_u32(node, ++ "brcm,smi-clock-source", ++ &inst->clock_source); ++ of_property_read_u32(node, ++ "brcm,smi-clock-divisor", ++ &inst->clock_divisor); ++ ++ err = bcm2835_smi_dma_setup(inst); ++ if (err) ++ return err; ++ ++ /* Finally, do peripheral setup */ ++ ++ smi_setup_clock(inst); ++ smi_setup_regs(inst); ++ ++ platform_set_drvdata(pdev, inst); ++ ++ dev_info(inst->dev, "initialised"); ++ ++ return 0; ++} ++ ++/**************************************************************************** ++* ++* bcm2835_smi_remove - called when the driver is unloaded. ++* ++***************************************************************************/ ++ ++static int bcm2835_smi_remove(struct platform_device *pdev) ++{ ++ struct bcm2835_smi_instance *inst = platform_get_drvdata(pdev); ++ struct device *dev = inst->dev; ++ ++ dev_info(dev, "SMI device removed - OK"); ++ return 0; ++} ++ ++/**************************************************************************** ++* ++* Register the driver with device tree ++* ++***************************************************************************/ ++ ++static const struct of_device_id bcm2835_smi_of_match[] = { ++ {.compatible = "brcm,bcm2835-smi",}, ++ { /* sentinel */ }, ++}; ++ ++MODULE_DEVICE_TABLE(of, bcm2835_smi_of_match); ++ ++static struct platform_driver bcm2835_smi_driver = { ++ .probe = bcm2835_smi_probe, ++ .remove = bcm2835_smi_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = bcm2835_smi_of_match, ++ }, ++}; ++ ++module_platform_driver(bcm2835_smi_driver); ++ ++MODULE_ALIAS("platform:smi-bcm2835"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Device driver for BCM2835's secondary memory interface"); ++MODULE_AUTHOR("Luke Wren "); +--- /dev/null ++++ b/include/linux/broadcom/bcm2835_smi.h +@@ -0,0 +1,391 @@ ++/** ++ * Declarations and definitions for Broadcom's Secondary Memory Interface ++ * ++ * Written by Luke Wren ++ * Copyright (c) 2015, Raspberry Pi (Trading) Ltd. ++ * 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 BCM2835_SMI_H ++#define BCM2835_SMI_H ++ ++#include ++ ++#ifndef __KERNEL__ ++#include ++#include ++#endif ++ ++#define BCM2835_SMI_IOC_MAGIC 0x1 ++#define BCM2835_SMI_INVALID_HANDLE (~0) ++ ++/* IOCTLs 0x100...0x1ff are not device-specific - we can use them */ ++#define BCM2835_SMI_IOC_GET_SETTINGS _IO(BCM2835_SMI_IOC_MAGIC, 0) ++#define BCM2835_SMI_IOC_WRITE_SETTINGS _IO(BCM2835_SMI_IOC_MAGIC, 1) ++#define BCM2835_SMI_IOC_ADDRESS _IO(BCM2835_SMI_IOC_MAGIC, 2) ++#define BCM2835_SMI_IOC_MAX 2 ++ ++#define SMI_WIDTH_8BIT 0 ++#define SMI_WIDTH_16BIT 1 ++#define SMI_WIDTH_9BIT 2 ++#define SMI_WIDTH_18BIT 3 ++ ++/* max number of bytes where DMA will not be used */ ++#define DMA_THRESHOLD_BYTES 128 ++#define DMA_BOUNCE_BUFFER_SIZE (1024 * 1024 / 2) ++#define DMA_BOUNCE_BUFFER_COUNT 3 ++ ++ ++struct smi_settings { ++ int data_width; ++ /* Whether or not to pack multiple SMI transfers into a ++ single 32 bit FIFO word */ ++ bool pack_data; ++ ++ /* Timing for reads (writes the same but for WE) ++ * ++ * OE ----------+ +-------------------- ++ * | | ++ * +----------+ ++ * SD -<==============================>----------- ++ * SA -<=========================================>- ++ * <-setup-> <-strobe -> <-hold -> <- pace -> ++ */ ++ ++ int read_setup_time; ++ int read_hold_time; ++ int read_pace_time; ++ int read_strobe_time; ++ ++ int write_setup_time; ++ int write_hold_time; ++ int write_pace_time; ++ int write_strobe_time; ++ ++ bool dma_enable; /* DREQs */ ++ bool dma_passthrough_enable; /* External DREQs */ ++ int dma_read_thresh; ++ int dma_write_thresh; ++ int dma_panic_read_thresh; ++ int dma_panic_write_thresh; ++}; ++ ++/**************************************************************************** ++* ++* Declare exported SMI functions ++* ++***************************************************************************/ ++ ++#ifdef __KERNEL__ ++ ++#include /* for enum dma_transfer_direction */ ++#include ++#include ++ ++struct bcm2835_smi_instance; ++ ++struct bcm2835_smi_bounce_info { ++ struct semaphore callback_sem; ++ void *buffer[DMA_BOUNCE_BUFFER_COUNT]; ++ dma_addr_t phys[DMA_BOUNCE_BUFFER_COUNT]; ++ struct scatterlist sgl[DMA_BOUNCE_BUFFER_COUNT]; ++}; ++ ++ ++void bcm2835_smi_set_regs_from_settings(struct bcm2835_smi_instance *); ++ ++struct smi_settings *bcm2835_smi_get_settings_from_regs( ++ struct bcm2835_smi_instance *inst); ++ ++void bcm2835_smi_write_buf( ++ struct bcm2835_smi_instance *inst, ++ const void *buf, ++ size_t n_bytes); ++ ++void bcm2835_smi_read_buf( ++ struct bcm2835_smi_instance *inst, ++ void *buf, ++ size_t n_bytes); ++ ++void bcm2835_smi_set_address(struct bcm2835_smi_instance *inst, ++ unsigned int address); ++ ++ssize_t bcm2835_smi_user_dma( ++ struct bcm2835_smi_instance *inst, ++ enum dma_transfer_direction dma_dir, ++ char __user *user_ptr, ++ size_t count, ++ struct bcm2835_smi_bounce_info **bounce); ++ ++struct bcm2835_smi_instance *bcm2835_smi_get(struct device_node *node); ++ ++#endif /* __KERNEL__ */ ++ ++/**************************************************************** ++* ++* Implementation-only declarations ++* ++****************************************************************/ ++ ++#ifdef BCM2835_SMI_IMPLEMENTATION ++ ++/* Clock manager registers for SMI clock: */ ++#define CM_SMI_BASE_ADDRESS ((BCM2708_PERI_BASE) + 0x1010b0) ++/* Clock manager "password" to protect registers from spurious writes */ ++#define CM_PWD (0x5a << 24) ++ ++#define CM_SMI_CTL 0x00 ++#define CM_SMI_DIV 0x04 ++ ++#define CM_SMI_CTL_FLIP (1 << 8) ++#define CM_SMI_CTL_BUSY (1 << 7) ++#define CM_SMI_CTL_KILL (1 << 5) ++#define CM_SMI_CTL_ENAB (1 << 4) ++#define CM_SMI_CTL_SRC_MASK (0xf) ++#define CM_SMI_CTL_SRC_OFFS (0) ++ ++#define CM_SMI_DIV_DIVI_MASK (0xf << 12) ++#define CM_SMI_DIV_DIVI_OFFS (12) ++#define CM_SMI_DIV_DIVF_MASK (0xff << 4) ++#define CM_SMI_DIV_DIVF_OFFS (4) ++ ++/* SMI register mapping:*/ ++#define SMI_BASE_ADDRESS ((BCM2708_PERI_BASE) + 0x600000) ++ ++#define SMICS 0x00 /* control + status register */ ++#define SMIL 0x04 /* length/count (n external txfers) */ ++#define SMIA 0x08 /* address register */ ++#define SMID 0x0c /* data register */ ++#define SMIDSR0 0x10 /* device 0 read settings */ ++#define SMIDSW0 0x14 /* device 0 write settings */ ++#define SMIDSR1 0x18 /* device 1 read settings */ ++#define SMIDSW1 0x1c /* device 1 write settings */ ++#define SMIDSR2 0x20 /* device 2 read settings */ ++#define SMIDSW2 0x24 /* device 2 write settings */ ++#define SMIDSR3 0x28 /* device 3 read settings */ ++#define SMIDSW3 0x2c /* device 3 write settings */ ++#define SMIDC 0x30 /* DMA control registers */ ++#define SMIDCS 0x34 /* direct control/status register */ ++#define SMIDA 0x38 /* direct address register */ ++#define SMIDD 0x3c /* direct data registers */ ++#define SMIFD 0x40 /* FIFO debug register */ ++ ++ ++ ++/* Control and Status register bits: ++ * SMICS_RXF : RX fifo full: 1 when RX fifo is full ++ * SMICS_TXE : TX fifo empty: 1 when empty. ++ * SMICS_RXD : RX fifo contains data: 1 when there is data. ++ * SMICS_TXD : TX fifo can accept data: 1 when true. ++ * SMICS_RXR : RX fifo needs reading: 1 when fifo more than 3/4 full, or ++ * when "DONE" and fifo not emptied. ++ * SMICS_TXW : TX fifo needs writing: 1 when less than 1/4 full. ++ * SMICS_AFERR : AXI FIFO error: 1 when fifo read when empty or written ++ * when full. Write 1 to clear. ++ * SMICS_EDREQ : 1 when external DREQ received. ++ * SMICS_PXLDAT : Pixel data: write 1 to enable pixel transfer modes. ++ * SMICS_SETERR : 1 if there was an error writing to setup regs (e.g. ++ * tx was in progress). Write 1 to clear. ++ * SMICS_PVMODE : Set to 1 to enable pixel valve mode. ++ * SMICS_INTR : Set to 1 to enable interrupt on RX. ++ * SMICS_INTT : Set to 1 to enable interrupt on TX. ++ * SMICS_INTD : Set to 1 to enable interrupt on DONE condition. ++ * SMICS_TEEN : Tear effect mode enabled: Programmed transfers will wait ++ * for a TE trigger before writing. ++ * SMICS_PAD1 : Padding settings for external transfers. For writes: the ++ * number of bytes initially written to the TX fifo that ++ * SMICS_PAD0 : should be ignored. For reads: the number of bytes that will ++ * be read before the data, and should be dropped. ++ * SMICS_WRITE : Transfer direction: 1 = write to external device, 0 = read ++ * SMICS_CLEAR : Write 1 to clear the FIFOs. ++ * SMICS_START : Write 1 to start the programmed transfer. ++ * SMICS_ACTIVE : Reads as 1 when a programmed transfer is underway. ++ * SMICS_DONE : Reads as 1 when transfer finished. For RX, not set until ++ * FIFO emptied. ++ * SMICS_ENABLE : Set to 1 to enable the SMI peripheral, 0 to disable. ++ */ ++ ++#define SMICS_RXF (1 << 31) ++#define SMICS_TXE (1 << 30) ++#define SMICS_RXD (1 << 29) ++#define SMICS_TXD (1 << 28) ++#define SMICS_RXR (1 << 27) ++#define SMICS_TXW (1 << 26) ++#define SMICS_AFERR (1 << 25) ++#define SMICS_EDREQ (1 << 15) ++#define SMICS_PXLDAT (1 << 14) ++#define SMICS_SETERR (1 << 13) ++#define SMICS_PVMODE (1 << 12) ++#define SMICS_INTR (1 << 11) ++#define SMICS_INTT (1 << 10) ++#define SMICS_INTD (1 << 9) ++#define SMICS_TEEN (1 << 8) ++#define SMICS_PAD1 (1 << 7) ++#define SMICS_PAD0 (1 << 6) ++#define SMICS_WRITE (1 << 5) ++#define SMICS_CLEAR (1 << 4) ++#define SMICS_START (1 << 3) ++#define SMICS_ACTIVE (1 << 2) ++#define SMICS_DONE (1 << 1) ++#define SMICS_ENABLE (1 << 0) ++ ++/* Address register bits: */ ++ ++#define SMIA_DEVICE_MASK ((1 << 9) | (1 << 8)) ++#define SMIA_DEVICE_OFFS (8) ++#define SMIA_ADDR_MASK (0x3f) /* bits 5 -> 0 */ ++#define SMIA_ADDR_OFFS (0) ++ ++/* DMA control register bits: ++ * SMIDC_DMAEN : DMA enable: set 1: DMA requests will be issued. ++ * SMIDC_DMAP : DMA passthrough: when set to 0, top two data pins are used by ++ * SMI as usual. When set to 1, the top two pins are used for ++ * external DREQs: pin 16 read request, 17 write. ++ * SMIDC_PANIC* : Threshold at which DMA will panic during read/write. ++ * SMIDC_REQ* : Threshold at which DMA will generate a DREQ. ++ */ ++ ++#define SMIDC_DMAEN (1 << 28) ++#define SMIDC_DMAP (1 << 24) ++#define SMIDC_PANICR_MASK (0x3f << 18) ++#define SMIDC_PANICR_OFFS (18) ++#define SMIDC_PANICW_MASK (0x3f << 12) ++#define SMIDC_PANICW_OFFS (12) ++#define SMIDC_REQR_MASK (0x3f << 6) ++#define SMIDC_REQR_OFFS (6) ++#define SMIDC_REQW_MASK (0x3f) ++#define SMIDC_REQW_OFFS (0) ++ ++/* Device settings register bits: same for all 4 (or 3?) device register sets. ++ * Device read settings: ++ * SMIDSR_RWIDTH : Read transfer width. 00 = 8bit, 01 = 16bit, ++ * 10 = 18bit, 11 = 9bit. ++ * SMIDSR_RSETUP : Read setup time: number of core cycles between chip ++ * select/address and read strobe. Min 1, max 64. ++ * SMIDSR_MODE68 : 1 for System 68 mode (i.e. enable + direction pins, ++ * rather than OE + WE pin) ++ * SMIDSR_FSETUP : If set to 1, setup time only applies to first ++ * transfer after address change. ++ * SMIDSR_RHOLD : Number of core cycles between read strobe going ++ * inactive and CS/address going inactive. Min 1, max 64 ++ * SMIDSR_RPACEALL : When set to 1, this device's RPACE value will always ++ * be used for the next transaction, even if it is not ++ * to this device. ++ * SMIDSR_RPACE : Number of core cycles spent waiting between CS ++ * deassert and start of next transfer. Min 1, max 128 ++ * SMIDSR_RDREQ : 1 = use external DMA request on SD16 to pace reads ++ * from device. Must also set DMAP in SMICS. ++ * SMIDSR_RSTROBE : Number of cycles to assert the read strobe. ++ * min 1, max 128. ++ */ ++#define SMIDSR_RWIDTH_MASK ((1<<31)|(1<<30)) ++#define SMIDSR_RWIDTH_OFFS (30) ++#define SMIDSR_RSETUP_MASK (0x3f << 24) ++#define SMIDSR_RSETUP_OFFS (24) ++#define SMIDSR_MODE68 (1 << 23) ++#define SMIDSR_FSETUP (1 << 22) ++#define SMIDSR_RHOLD_MASK (0x3f << 16) ++#define SMIDSR_RHOLD_OFFS (16) ++#define SMIDSR_RPACEALL (1 << 15) ++#define SMIDSR_RPACE_MASK (0x7f << 8) ++#define SMIDSR_RPACE_OFFS (8) ++#define SMIDSR_RDREQ (1 << 7) ++#define SMIDSR_RSTROBE_MASK (0x7f) ++#define SMIDSR_RSTROBE_OFFS (0) ++ ++/* Device write settings: ++ * SMIDSW_WWIDTH : Write transfer width. 00 = 8bit, 01 = 16bit, ++ * 10= 18bit, 11 = 9bit. ++ * SMIDSW_WSETUP : Number of cycles between CS assert and write strobe. ++ * Min 1, max 64. ++ * SMIDSW_WFORMAT : Pixel format of input. 0 = 16bit RGB 565, ++ * 1 = 32bit RGBA 8888 ++ * SMIDSW_WSWAP : 1 = swap pixel data bits. (Use with SMICS_PXLDAT) ++ * SMIDSW_WHOLD : Time between WE deassert and CS deassert. 1 to 64 ++ * SMIDSW_WPACEALL : 1: this device's WPACE will be used for the next ++ * transfer, regardless of that transfer's device. ++ * SMIDSW_WPACE : Cycles between CS deassert and next CS assert. ++ * Min 1, max 128 ++ * SMIDSW_WDREQ : Use external DREQ on pin 17 to pace writes. DMAP must ++ * be set in SMICS. ++ * SMIDSW_WSTROBE : Number of cycles to assert the write strobe. ++ * Min 1, max 128 ++ */ ++#define SMIDSW_WWIDTH_MASK ((1<<31)|(1<<30)) ++#define SMIDSW_WWIDTH_OFFS (30) ++#define SMIDSW_WSETUP_MASK (0x3f << 24) ++#define SMIDSW_WSETUP_OFFS (24) ++#define SMIDSW_WFORMAT (1 << 23) ++#define SMIDSW_WSWAP (1 << 22) ++#define SMIDSW_WHOLD_MASK (0x3f << 16) ++#define SMIDSW_WHOLD_OFFS (16) ++#define SMIDSW_WPACEALL (1 << 15) ++#define SMIDSW_WPACE_MASK (0x7f << 8) ++#define SMIDSW_WPACE_OFFS (8) ++#define SMIDSW_WDREQ (1 << 7) ++#define SMIDSW_WSTROBE_MASK (0x7f) ++#define SMIDSW_WSTROBE_OFFS (0) ++ ++/* Direct transfer control + status register ++ * SMIDCS_WRITE : Direction of transfer: 1 -> write, 0 -> read ++ * SMIDCS_DONE : 1 when a transfer has finished. Write 1 to clear. ++ * SMIDCS_START : Write 1 to start a transfer, if one is not already underway. ++ * SMIDCE_ENABLE: Write 1 to enable SMI in direct mode. ++ */ ++ ++#define SMIDCS_WRITE (1 << 3) ++#define SMIDCS_DONE (1 << 2) ++#define SMIDCS_START (1 << 1) ++#define SMIDCS_ENABLE (1 << 0) ++ ++/* Direct transfer address register ++ * SMIDA_DEVICE : Indicates which of the device settings banks should be used. ++ * SMIDA_ADDR : The value to be asserted on the address pins. ++ */ ++ ++#define SMIDA_DEVICE_MASK ((1<<9)|(1<<8)) ++#define SMIDA_DEVICE_OFFS (8) ++#define SMIDA_ADDR_MASK (0x3f) ++#define SMIDA_ADDR_OFFS (0) ++ ++/* FIFO debug register ++ * SMIFD_FLVL : The high-tide mark of FIFO count during the most recent txfer ++ * SMIFD_FCNT : The current FIFO count. ++ */ ++#define SMIFD_FLVL_MASK (0x3f << 8) ++#define SMIFD_FLVL_OFFS (8) ++#define SMIFD_FCNT_MASK (0x3f) ++#define SMIFD_FCNT_OFFS (0) ++ ++#endif /* BCM2835_SMI_IMPLEMENTATION */ ++ ++#endif /* BCM2835_SMI_H */ diff --git a/target/linux/brcm2708/patches-4.4/0040-Add-dev-gpiomem-device-for-rootless-user-GPIO-access.patch b/target/linux/brcm2708/patches-4.4/0040-Add-dev-gpiomem-device-for-rootless-user-GPIO-access.patch deleted file mode 100644 index 34806e682b..0000000000 --- a/target/linux/brcm2708/patches-4.4/0040-Add-dev-gpiomem-device-for-rootless-user-GPIO-access.patch +++ /dev/null @@ -1,306 +0,0 @@ -From 832077a0f61e44a9259b153f60eb233fa723c2a9 Mon Sep 17 00:00:00 2001 -From: Luke Wren -Date: Fri, 21 Aug 2015 23:14:48 +0100 -Subject: [PATCH 040/170] Add /dev/gpiomem device for rootless user GPIO access - -Signed-off-by: Luke Wren - -bcm2835-gpiomem: Fix for ARCH_BCM2835 builds - -Build on ARCH_BCM2835, and fail to probe if no IO resource. - -See: https://github.com/raspberrypi/linux/issues/1154 ---- - drivers/char/broadcom/Kconfig | 9 ++ - drivers/char/broadcom/Makefile | 3 + - drivers/char/broadcom/bcm2835-gpiomem.c | 260 ++++++++++++++++++++++++++++++++ - 3 files changed, 272 insertions(+) - create mode 100644 drivers/char/broadcom/bcm2835-gpiomem.c - ---- a/drivers/char/broadcom/Kconfig -+++ b/drivers/char/broadcom/Kconfig -@@ -32,3 +32,12 @@ config BCM_VC_SM - help - Support for the VC shared memory on the Broadcom reference - design. Uses the VCHIQ stack. -+ -+config BCM2835_DEVGPIOMEM -+ tristate "/dev/gpiomem rootless GPIO access via mmap() on the BCM2835" -+ default m -+ help -+ Provides users with root-free access to the GPIO registers -+ on the 2835. Calling mmap(/dev/gpiomem) will map the GPIO -+ register page to the user's pointer. -+ ---- a/drivers/char/broadcom/Makefile -+++ b/drivers/char/broadcom/Makefile -@@ -1,3 +1,6 @@ - obj-$(CONFIG_BCM_VC_CMA) += vc_cma/ - obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o - obj-$(CONFIG_BCM_VC_SM) += vc_sm/ -+ -+obj-$(CONFIG_BCM2835_DEVGPIOMEM)+= bcm2835-gpiomem.o -+ ---- /dev/null -+++ b/drivers/char/broadcom/bcm2835-gpiomem.c -@@ -0,0 +1,260 @@ -+/** -+ * GPIO memory device driver -+ * -+ * Creates a chardev /dev/gpiomem which will provide user access to -+ * the BCM2835's GPIO registers when it is mmap()'d. -+ * No longer need root for user GPIO access, but without relaxing permissions -+ * on /dev/mem. -+ * -+ * Written by Luke Wren -+ * Copyright (c) 2015, 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define DEVICE_NAME "bcm2835-gpiomem" -+#define DRIVER_NAME "gpiomem-bcm2835" -+#define DEVICE_MINOR 0 -+ -+struct bcm2835_gpiomem_instance { -+ unsigned long gpio_regs_phys; -+ struct device *dev; -+}; -+ -+static struct cdev bcm2835_gpiomem_cdev; -+static dev_t bcm2835_gpiomem_devid; -+static struct class *bcm2835_gpiomem_class; -+static struct device *bcm2835_gpiomem_dev; -+static struct bcm2835_gpiomem_instance *inst; -+ -+ -+/**************************************************************************** -+* -+* GPIO mem chardev file ops -+* -+***************************************************************************/ -+ -+static int bcm2835_gpiomem_open(struct inode *inode, struct file *file) -+{ -+ int dev = iminor(inode); -+ int ret = 0; -+ -+ dev_info(inst->dev, "gpiomem device opened."); -+ -+ if (dev != DEVICE_MINOR) { -+ dev_err(inst->dev, "Unknown minor device: %d", dev); -+ ret = -ENXIO; -+ } -+ return ret; -+} -+ -+static int bcm2835_gpiomem_release(struct inode *inode, struct file *file) -+{ -+ int dev = iminor(inode); -+ int ret = 0; -+ -+ if (dev != DEVICE_MINOR) { -+ dev_err(inst->dev, "Unknown minor device %d", dev); -+ ret = -ENXIO; -+ } -+ return ret; -+} -+ -+static const struct vm_operations_struct bcm2835_gpiomem_vm_ops = { -+#ifdef CONFIG_HAVE_IOREMAP_PROT -+ .access = generic_access_phys -+#endif -+}; -+ -+static int bcm2835_gpiomem_mmap(struct file *file, struct vm_area_struct *vma) -+{ -+ /* Ignore what the user says - they're getting the GPIO regs -+ whether they like it or not! */ -+ unsigned long gpio_page = inst->gpio_regs_phys >> PAGE_SHIFT; -+ -+ vma->vm_page_prot = phys_mem_access_prot(file, gpio_page, -+ PAGE_SIZE, -+ vma->vm_page_prot); -+ vma->vm_ops = &bcm2835_gpiomem_vm_ops; -+ if (remap_pfn_range(vma, vma->vm_start, -+ gpio_page, -+ PAGE_SIZE, -+ vma->vm_page_prot)) { -+ return -EAGAIN; -+ } -+ return 0; -+} -+ -+static const struct file_operations -+bcm2835_gpiomem_fops = { -+ .owner = THIS_MODULE, -+ .open = bcm2835_gpiomem_open, -+ .release = bcm2835_gpiomem_release, -+ .mmap = bcm2835_gpiomem_mmap, -+}; -+ -+ -+ /**************************************************************************** -+* -+* Probe and remove functions -+* -+***************************************************************************/ -+ -+ -+static int bcm2835_gpiomem_probe(struct platform_device *pdev) -+{ -+ int err; -+ void *ptr_err; -+ struct device *dev = &pdev->dev; -+ struct resource *ioresource; -+ -+ /* Allocate buffers and instance data */ -+ -+ inst = kzalloc(sizeof(struct bcm2835_gpiomem_instance), GFP_KERNEL); -+ -+ if (!inst) { -+ err = -ENOMEM; -+ goto failed_inst_alloc; -+ } -+ -+ inst->dev = dev; -+ -+ ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (ioresource) { -+ inst->gpio_regs_phys = ioresource->start; -+ } else { -+ dev_err(inst->dev, "failed to get IO resource"); -+ err = -ENOENT; -+ goto failed_get_resource; -+ } -+ -+ /* Create character device entries */ -+ -+ err = alloc_chrdev_region(&bcm2835_gpiomem_devid, -+ DEVICE_MINOR, 1, DEVICE_NAME); -+ if (err != 0) { -+ dev_err(inst->dev, "unable to allocate device number"); -+ goto failed_alloc_chrdev; -+ } -+ cdev_init(&bcm2835_gpiomem_cdev, &bcm2835_gpiomem_fops); -+ bcm2835_gpiomem_cdev.owner = THIS_MODULE; -+ err = cdev_add(&bcm2835_gpiomem_cdev, bcm2835_gpiomem_devid, 1); -+ if (err != 0) { -+ dev_err(inst->dev, "unable to register device"); -+ goto failed_cdev_add; -+ } -+ -+ /* Create sysfs entries */ -+ -+ bcm2835_gpiomem_class = class_create(THIS_MODULE, DEVICE_NAME); -+ ptr_err = bcm2835_gpiomem_class; -+ if (IS_ERR(ptr_err)) -+ goto failed_class_create; -+ -+ bcm2835_gpiomem_dev = device_create(bcm2835_gpiomem_class, NULL, -+ bcm2835_gpiomem_devid, NULL, -+ "gpiomem"); -+ ptr_err = bcm2835_gpiomem_dev; -+ if (IS_ERR(ptr_err)) -+ goto failed_device_create; -+ -+ dev_info(inst->dev, "Initialised: Registers at 0x%08lx", -+ inst->gpio_regs_phys); -+ -+ return 0; -+ -+failed_device_create: -+ class_destroy(bcm2835_gpiomem_class); -+failed_class_create: -+ cdev_del(&bcm2835_gpiomem_cdev); -+ err = PTR_ERR(ptr_err); -+failed_cdev_add: -+ unregister_chrdev_region(bcm2835_gpiomem_devid, 1); -+failed_alloc_chrdev: -+failed_get_resource: -+ kfree(inst); -+failed_inst_alloc: -+ dev_err(inst->dev, "could not load bcm2835_gpiomem"); -+ return err; -+} -+ -+static int bcm2835_gpiomem_remove(struct platform_device *pdev) -+{ -+ struct device *dev = inst->dev; -+ -+ kfree(inst); -+ device_destroy(bcm2835_gpiomem_class, bcm2835_gpiomem_devid); -+ class_destroy(bcm2835_gpiomem_class); -+ cdev_del(&bcm2835_gpiomem_cdev); -+ unregister_chrdev_region(bcm2835_gpiomem_devid, 1); -+ -+ dev_info(dev, "GPIO mem driver removed - OK"); -+ return 0; -+} -+ -+ /**************************************************************************** -+* -+* Register the driver with device tree -+* -+***************************************************************************/ -+ -+static const struct of_device_id bcm2835_gpiomem_of_match[] = { -+ {.compatible = "brcm,bcm2835-gpiomem",}, -+ { /* sentinel */ }, -+}; -+ -+MODULE_DEVICE_TABLE(of, bcm2835_gpiomem_of_match); -+ -+static struct platform_driver bcm2835_gpiomem_driver = { -+ .probe = bcm2835_gpiomem_probe, -+ .remove = bcm2835_gpiomem_remove, -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ .of_match_table = bcm2835_gpiomem_of_match, -+ }, -+}; -+ -+module_platform_driver(bcm2835_gpiomem_driver); -+ -+MODULE_ALIAS("platform:gpiomem-bcm2835"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("gpiomem driver for accessing GPIO from userspace"); -+MODULE_AUTHOR("Luke Wren "); diff --git a/target/linux/brcm2708/patches-4.4/0041-Add-SMI-NAND-driver.patch b/target/linux/brcm2708/patches-4.4/0041-Add-SMI-NAND-driver.patch new file mode 100644 index 0000000000..de0c174382 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0041-Add-SMI-NAND-driver.patch @@ -0,0 +1,358 @@ +From c0fe401b61ae4d002f6fc84438ce69c5917c580a Mon Sep 17 00:00:00 2001 +From: Luke Wren +Date: Sat, 5 Sep 2015 01:16:10 +0100 +Subject: [PATCH 041/232] Add SMI NAND driver + +Signed-off-by: Luke Wren +--- + .../bindings/mtd/brcm,bcm2835-smi-nand.txt | 42 ++++ + drivers/mtd/nand/Kconfig | 7 + + drivers/mtd/nand/Makefile | 1 + + drivers/mtd/nand/bcm2835_smi_nand.c | 268 +++++++++++++++++++++ + 4 files changed, 318 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mtd/brcm,bcm2835-smi-nand.txt + create mode 100644 drivers/mtd/nand/bcm2835_smi_nand.c + +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/brcm,bcm2835-smi-nand.txt +@@ -0,0 +1,42 @@ ++* BCM2835 SMI NAND flash ++ ++This driver is a shim between the BCM2835 SMI driver (SMI is a peripheral for ++talking to parallel register interfaces) and Linux's MTD layer. ++ ++Required properties: ++- compatible: "brcm,bcm2835-smi-nand" ++- status: "okay" ++ ++Optional properties: ++- partition@n, where n is an integer from a consecutive sequence starting at 0 ++ - Difficult to store partition table on NAND device - normally put it ++ in the source code, kernel bootparams, or device tree (the best way!) ++ - Sub-properties: ++ - label: the partition name, as shown by mtdinfo /dev/mtd* ++ - reg: the size and offset of this partition. ++ - (optional) read-only: an empty property flagging as read only ++ ++Example: ++ ++nand: flash@0 { ++ compatible = "brcm,bcm2835-smi-nand"; ++ status = "okay"; ++ ++ partition@0 { ++ label = "stage2"; ++ // 128k ++ reg = <0 0x20000>; ++ read-only; ++ }; ++ partition@1 { ++ label = "firmware"; ++ // 16M ++ reg = <0x20000 0x1000000>; ++ read-only; ++ }; ++ partition@2 { ++ label = "root"; ++ // 2G ++ reg = <0x1020000 0x80000000>; ++ }; ++}; +\ No newline at end of file +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -41,6 +41,13 @@ config MTD_SM_COMMON + tristate + default n + ++config MTD_NAND_BCM2835_SMI ++ tristate "Use Broadcom's Secondary Memory Interface as a NAND controller (BCM283x)" ++ depends on (MACH_BCM2708 || MACH_BCM2709 || ARCH_BCM2835) && BCM2835_SMI && MTD_NAND ++ default m ++ help ++ Uses the BCM2835's SMI peripheral as a NAND controller. ++ + config MTD_NAND_DENALI + tristate + +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -14,6 +14,7 @@ obj-$(CONFIG_MTD_NAND_DENALI) += denali + obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o + obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o + obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o ++obj-$(CONFIG_MTD_NAND_BCM2835_SMI) += bcm2835_smi_nand.o + obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o + obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o + obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o +--- /dev/null ++++ b/drivers/mtd/nand/bcm2835_smi_nand.c +@@ -0,0 +1,268 @@ ++/** ++ * NAND flash driver for Broadcom Secondary Memory Interface ++ * ++ * Written by Luke Wren ++ * Copyright (c) 2015, 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define DEVICE_NAME "bcm2835-smi-nand" ++#define DRIVER_NAME "smi-nand-bcm2835" ++ ++struct bcm2835_smi_nand_host { ++ struct bcm2835_smi_instance *smi_inst; ++ struct nand_chip nand_chip; ++ struct mtd_info mtd; ++ struct device *dev; ++}; ++ ++/**************************************************************************** ++* ++* NAND functionality implementation ++* ++****************************************************************************/ ++ ++#define SMI_NAND_CLE_PIN 0x01 ++#define SMI_NAND_ALE_PIN 0x02 ++ ++static inline void bcm2835_smi_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ uint32_t cmd32 = cmd; ++ uint32_t addr = ~(SMI_NAND_CLE_PIN | SMI_NAND_ALE_PIN); ++ struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent); ++ struct bcm2835_smi_instance *inst = host->smi_inst; ++ ++ if (ctrl & NAND_CLE) ++ addr |= SMI_NAND_CLE_PIN; ++ if (ctrl & NAND_ALE) ++ addr |= SMI_NAND_ALE_PIN; ++ /* Lower ALL the CS pins! */ ++ if (ctrl & NAND_NCE) ++ addr &= (SMI_NAND_CLE_PIN | SMI_NAND_ALE_PIN); ++ ++ bcm2835_smi_set_address(inst, addr); ++ ++ if (cmd != NAND_CMD_NONE) ++ bcm2835_smi_write_buf(inst, &cmd32, 1); ++} ++ ++static inline uint8_t bcm2835_smi_nand_read_byte(struct mtd_info *mtd) ++{ ++ uint8_t byte; ++ struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent); ++ struct bcm2835_smi_instance *inst = host->smi_inst; ++ ++ bcm2835_smi_read_buf(inst, &byte, 1); ++ return byte; ++} ++ ++static inline void bcm2835_smi_nand_write_byte(struct mtd_info *mtd, ++ uint8_t byte) ++{ ++ struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent); ++ struct bcm2835_smi_instance *inst = host->smi_inst; ++ ++ bcm2835_smi_write_buf(inst, &byte, 1); ++} ++ ++static inline void bcm2835_smi_nand_write_buf(struct mtd_info *mtd, ++ const uint8_t *buf, int len) ++{ ++ struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent); ++ struct bcm2835_smi_instance *inst = host->smi_inst; ++ ++ bcm2835_smi_write_buf(inst, buf, len); ++} ++ ++static inline void bcm2835_smi_nand_read_buf(struct mtd_info *mtd, ++ uint8_t *buf, int len) ++{ ++ struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent); ++ struct bcm2835_smi_instance *inst = host->smi_inst; ++ ++ bcm2835_smi_read_buf(inst, buf, len); ++} ++ ++/**************************************************************************** ++* ++* Probe and remove functions ++* ++***************************************************************************/ ++ ++static int bcm2835_smi_nand_probe(struct platform_device *pdev) ++{ ++ struct bcm2835_smi_nand_host *host; ++ struct nand_chip *this; ++ struct mtd_info *mtd; ++ struct device *dev = &pdev->dev; ++ struct device_node *node = dev->of_node, *smi_node; ++ struct mtd_part_parser_data ppdata; ++ struct smi_settings *smi_settings; ++ struct bcm2835_smi_instance *smi_inst; ++ int ret = -ENXIO; ++ ++ if (!node) { ++ dev_err(dev, "No device tree node supplied!"); ++ return -EINVAL; ++ } ++ ++ smi_node = of_parse_phandle(node, "smi_handle", 0); ++ ++ /* Request use of SMI peripheral: */ ++ smi_inst = bcm2835_smi_get(smi_node); ++ ++ if (!smi_inst) { ++ dev_err(dev, "Could not register with SMI."); ++ return -EPROBE_DEFER; ++ } ++ ++ /* Set SMI timing and bus width */ ++ ++ smi_settings = bcm2835_smi_get_settings_from_regs(smi_inst); ++ ++ smi_settings->data_width = SMI_WIDTH_8BIT; ++ smi_settings->read_setup_time = 2; ++ smi_settings->read_hold_time = 1; ++ smi_settings->read_pace_time = 1; ++ smi_settings->read_strobe_time = 3; ++ ++ smi_settings->write_setup_time = 2; ++ smi_settings->write_hold_time = 1; ++ smi_settings->write_pace_time = 1; ++ smi_settings->write_strobe_time = 3; ++ ++ bcm2835_smi_set_regs_from_settings(smi_inst); ++ ++ host = devm_kzalloc(dev, sizeof(struct bcm2835_smi_nand_host), ++ GFP_KERNEL); ++ if (!host) ++ return -ENOMEM; ++ ++ host->dev = dev; ++ host->smi_inst = smi_inst; ++ ++ platform_set_drvdata(pdev, host); ++ ++ /* Link the structures together */ ++ ++ this = &host->nand_chip; ++ mtd = &host->mtd; ++ mtd->priv = this; ++ mtd->owner = THIS_MODULE; ++ mtd->dev.parent = dev; ++ mtd->name = DRIVER_NAME; ++ ppdata.of_node = node; ++ ++ /* 20 us command delay time... */ ++ this->chip_delay = 20; ++ ++ this->priv = host; ++ this->cmd_ctrl = bcm2835_smi_nand_cmd_ctrl; ++ this->read_byte = bcm2835_smi_nand_read_byte; ++ this->write_byte = bcm2835_smi_nand_write_byte; ++ this->write_buf = bcm2835_smi_nand_write_buf; ++ this->read_buf = bcm2835_smi_nand_read_buf; ++ ++ this->ecc.mode = NAND_ECC_SOFT; ++ ++ /* Should never be accessed directly: */ ++ ++ this->IO_ADDR_R = (void *)0xdeadbeef; ++ this->IO_ADDR_W = (void *)0xdeadbeef; ++ ++ /* First scan to find the device and get the page size */ ++ ++ if (nand_scan_ident(mtd, 1, NULL)) ++ return -ENXIO; ++ ++ /* Second phase scan */ ++ ++ if (nand_scan_tail(mtd)) ++ return -ENXIO; ++ ++ ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); ++ if (!ret) ++ return 0; ++ ++ nand_release(mtd); ++ return -EINVAL; ++} ++ ++static int bcm2835_smi_nand_remove(struct platform_device *pdev) ++{ ++ struct bcm2835_smi_nand_host *host = platform_get_drvdata(pdev); ++ ++ nand_release(&host->mtd); ++ ++ return 0; ++} ++ ++/**************************************************************************** ++* ++* Register the driver with device tree ++* ++***************************************************************************/ ++ ++static const struct of_device_id bcm2835_smi_nand_of_match[] = { ++ {.compatible = "brcm,bcm2835-smi-nand",}, ++ { /* sentinel */ } ++}; ++ ++MODULE_DEVICE_TABLE(of, bcm2835_smi_nand_of_match); ++ ++static struct platform_driver bcm2835_smi_nand_driver = { ++ .probe = bcm2835_smi_nand_probe, ++ .remove = bcm2835_smi_nand_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = bcm2835_smi_nand_of_match, ++ }, ++}; ++ ++module_platform_driver(bcm2835_smi_nand_driver); ++ ++MODULE_ALIAS("platform:smi-nand-bcm2835"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION ++ ("Driver for NAND chips using Broadcom Secondary Memory Interface"); ++MODULE_AUTHOR("Luke Wren "); diff --git a/target/linux/brcm2708/patches-4.4/0041-Add-SMI-driver.patch b/target/linux/brcm2708/patches-4.4/0041-Add-SMI-driver.patch deleted file mode 100644 index b2a2b008f0..0000000000 --- a/target/linux/brcm2708/patches-4.4/0041-Add-SMI-driver.patch +++ /dev/null @@ -1,1930 +0,0 @@ -From 6609b15c34f26f34b4b49e7726693a1de321078c Mon Sep 17 00:00:00 2001 -From: Luke Wren -Date: Sat, 5 Sep 2015 01:14:45 +0100 -Subject: [PATCH 041/170] Add SMI driver - -Signed-off-by: Luke Wren ---- - .../bindings/misc/brcm,bcm2835-smi-dev.txt | 17 + - .../devicetree/bindings/misc/brcm,bcm2835-smi.txt | 48 + - drivers/char/broadcom/Kconfig | 8 + - drivers/char/broadcom/Makefile | 2 +- - drivers/char/broadcom/bcm2835_smi_dev.c | 402 +++++++++ - drivers/misc/Kconfig | 8 + - drivers/misc/Makefile | 1 + - drivers/misc/bcm2835_smi.c | 985 +++++++++++++++++++++ - include/linux/broadcom/bcm2835_smi.h | 391 ++++++++ - 9 files changed, 1861 insertions(+), 1 deletion(-) - create mode 100644 Documentation/devicetree/bindings/misc/brcm,bcm2835-smi-dev.txt - create mode 100644 Documentation/devicetree/bindings/misc/brcm,bcm2835-smi.txt - create mode 100644 drivers/char/broadcom/bcm2835_smi_dev.c - create mode 100644 drivers/misc/bcm2835_smi.c - create mode 100644 include/linux/broadcom/bcm2835_smi.h - ---- /dev/null -+++ b/Documentation/devicetree/bindings/misc/brcm,bcm2835-smi-dev.txt -@@ -0,0 +1,17 @@ -+* Broadcom BCM2835 SMI character device driver. -+ -+SMI or secondary memory interface is a peripheral specific to certain Broadcom -+SOCs, and is helpful for talking to things like parallel-interface displays -+and NAND flashes (in fact, most things with a parallel register interface). -+ -+This driver adds a character device which provides a user-space interface to -+an instance of the SMI driver. -+ -+Required properties: -+- compatible: "brcm,bcm2835-smi-dev" -+- smi_handle: a phandle to the smi node. -+ -+Optional properties: -+- None. -+ -+ ---- /dev/null -+++ b/Documentation/devicetree/bindings/misc/brcm,bcm2835-smi.txt -@@ -0,0 +1,48 @@ -+* Broadcom BCM2835 SMI driver. -+ -+SMI or secondary memory interface is a peripheral specific to certain Broadcom -+SOCs, and is helpful for talking to things like parallel-interface displays -+and NAND flashes (in fact, most things with a parallel register interface). -+ -+Required properties: -+- compatible: "brcm,bcm2835-smi" -+- reg: Should contain location and length of SMI registers and SMI clkman regs -+- interrupts: *the* SMI interrupt. -+- pinctrl-names: should be "default". -+- pinctrl-0: the phandle of the gpio pin node. -+- brcm,smi-clock-source: the clock source for clkman -+- brcm,smi-clock-divisor: the integer clock divisor for clkman -+- dmas: the dma controller phandle and the DREQ number (4 on a 2835) -+- dma-names: the name used by the driver to request its channel. -+ Should be "rx-tx". -+ -+Optional properties: -+- None. -+ -+Examples: -+ -+8 data pin configuration: -+ -+smi: smi@7e600000 { -+ compatible = "brcm,bcm2835-smi"; -+ reg = <0x7e600000 0x44>, <0x7e1010b0 0x8>; -+ interrupts = <2 16>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&smi_pins>; -+ brcm,smi-clock-source = <6>; -+ brcm,smi-clock-divisor = <4>; -+ dmas = <&dma 4>; -+ dma-names = "rx-tx"; -+ -+ status = "okay"; -+}; -+ -+smi_pins: smi_pins { -+ brcm,pins = <2 3 4 5 6 7 8 9 10 11 12 13 14 15>; -+ /* Alt 1: SMI */ -+ brcm,function = <5 5 5 5 5 5 5 5 5 5 5 5 5 5>; -+ /* /CS, /WE and /OE are pulled high, as they are -+ generally active low signals */ -+ brcm,pull = <2 2 2 2 2 2 0 0 0 0 0 0 0 0>; -+}; -+ ---- a/drivers/char/broadcom/Kconfig -+++ b/drivers/char/broadcom/Kconfig -@@ -41,3 +41,11 @@ config BCM2835_DEVGPIOMEM - on the 2835. Calling mmap(/dev/gpiomem) will map the GPIO - register page to the user's pointer. - -+config BCM2835_SMI_DEV -+ tristate "Character device driver for BCM2835 Secondary Memory Interface" -+ depends on (MACH_BCM2708 || MACH_BCM2709 || ARCH_BCM2835) && BCM2835_SMI -+ default m -+ help -+ This driver provides a character device interface (ioctl + read/write) to -+ Broadcom's Secondary Memory interface. The low-level functionality is provided -+ by the SMI driver itself. ---- a/drivers/char/broadcom/Makefile -+++ b/drivers/char/broadcom/Makefile -@@ -3,4 +3,4 @@ obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o - obj-$(CONFIG_BCM_VC_SM) += vc_sm/ - - obj-$(CONFIG_BCM2835_DEVGPIOMEM)+= bcm2835-gpiomem.o -- -+obj-$(CONFIG_BCM2835_SMI_DEV) += bcm2835_smi_dev.o ---- /dev/null -+++ b/drivers/char/broadcom/bcm2835_smi_dev.c -@@ -0,0 +1,402 @@ -+/** -+ * Character device driver for Broadcom Secondary Memory Interface -+ * -+ * Written by Luke Wren -+ * Copyright (c) 2015, 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#define DEVICE_NAME "bcm2835-smi-dev" -+#define DRIVER_NAME "smi-dev-bcm2835" -+#define DEVICE_MINOR 0 -+ -+static struct cdev bcm2835_smi_cdev; -+static dev_t bcm2835_smi_devid; -+static struct class *bcm2835_smi_class; -+static struct device *bcm2835_smi_dev; -+ -+struct bcm2835_smi_dev_instance { -+ struct device *dev; -+}; -+ -+static struct bcm2835_smi_instance *smi_inst; -+static struct bcm2835_smi_dev_instance *inst; -+ -+static const char *const ioctl_names[] = { -+ "READ_SETTINGS", -+ "WRITE_SETTINGS", -+ "ADDRESS" -+}; -+ -+/**************************************************************************** -+* -+* SMI chardev file ops -+* -+***************************************************************************/ -+static long -+bcm2835_smi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ long ret = 0; -+ -+ dev_info(inst->dev, "serving ioctl..."); -+ -+ switch (cmd) { -+ case BCM2835_SMI_IOC_GET_SETTINGS:{ -+ struct smi_settings *settings; -+ -+ dev_info(inst->dev, "Reading SMI settings to user."); -+ settings = bcm2835_smi_get_settings_from_regs(smi_inst); -+ if (copy_to_user((void *)arg, settings, -+ sizeof(struct smi_settings))) -+ dev_err(inst->dev, "settings copy failed."); -+ break; -+ } -+ case BCM2835_SMI_IOC_WRITE_SETTINGS:{ -+ struct smi_settings *settings; -+ -+ dev_info(inst->dev, "Setting user's SMI settings."); -+ settings = bcm2835_smi_get_settings_from_regs(smi_inst); -+ if (copy_from_user(settings, (void *)arg, -+ sizeof(struct smi_settings))) -+ dev_err(inst->dev, "settings copy failed."); -+ else -+ bcm2835_smi_set_regs_from_settings(smi_inst); -+ break; -+ } -+ case BCM2835_SMI_IOC_ADDRESS: -+ dev_info(inst->dev, "SMI address set: 0x%02x", (int)arg); -+ bcm2835_smi_set_address(smi_inst, arg); -+ break; -+ default: -+ dev_err(inst->dev, "invalid ioctl cmd: %d", cmd); -+ ret = -ENOTTY; -+ break; -+ } -+ -+ return ret; -+} -+ -+static int bcm2835_smi_open(struct inode *inode, struct file *file) -+{ -+ int dev = iminor(inode); -+ -+ dev_dbg(inst->dev, "SMI device opened."); -+ -+ if (dev != DEVICE_MINOR) { -+ dev_err(inst->dev, -+ "bcm2835_smi_release: Unknown minor device: %d", -+ dev); -+ return -ENXIO; -+ } -+ -+ return 0; -+} -+ -+static int bcm2835_smi_release(struct inode *inode, struct file *file) -+{ -+ int dev = iminor(inode); -+ -+ if (dev != DEVICE_MINOR) { -+ dev_err(inst->dev, -+ "bcm2835_smi_release: Unknown minor device %d", dev); -+ return -ENXIO; -+ } -+ -+ return 0; -+} -+ -+static ssize_t dma_bounce_user( -+ enum dma_transfer_direction dma_dir, -+ char __user *user_ptr, -+ size_t count, -+ struct bcm2835_smi_bounce_info *bounce) -+{ -+ int chunk_size; -+ int chunk_no = 0; -+ int count_left = count; -+ -+ while (count_left) { -+ int rv; -+ void *buf; -+ -+ /* Wait for current chunk to complete: */ -+ if (down_timeout(&bounce->callback_sem, -+ msecs_to_jiffies(1000))) { -+ dev_err(inst->dev, "DMA bounce timed out"); -+ count -= (count_left); -+ break; -+ } -+ -+ if (bounce->callback_sem.count >= DMA_BOUNCE_BUFFER_COUNT - 1) -+ dev_err(inst->dev, "WARNING: Ring buffer overflow"); -+ chunk_size = count_left > DMA_BOUNCE_BUFFER_SIZE ? -+ DMA_BOUNCE_BUFFER_SIZE : count_left; -+ buf = bounce->buffer[chunk_no % DMA_BOUNCE_BUFFER_COUNT]; -+ if (dma_dir == DMA_DEV_TO_MEM) -+ rv = copy_to_user(user_ptr, buf, chunk_size); -+ else -+ rv = copy_from_user(buf, user_ptr, chunk_size); -+ if (rv) -+ dev_err(inst->dev, "copy_*_user() failed!: %d", rv); -+ user_ptr += chunk_size; -+ count_left -= chunk_size; -+ chunk_no++; -+ } -+ return count; -+} -+ -+static ssize_t -+bcm2835_read_file(struct file *f, char __user *user_ptr, -+ size_t count, loff_t *offs) -+{ -+ int odd_bytes; -+ -+ dev_dbg(inst->dev, "User reading %d bytes from SMI.", count); -+ /* We don't want to DMA a number of bytes % 4 != 0 (32 bit FIFO) */ -+ if (count > DMA_THRESHOLD_BYTES) -+ odd_bytes = count & 0x3; -+ else -+ odd_bytes = count; -+ count -= odd_bytes; -+ if (count) { -+ struct bcm2835_smi_bounce_info *bounce; -+ -+ count = bcm2835_smi_user_dma(smi_inst, -+ DMA_DEV_TO_MEM, user_ptr, count, -+ &bounce); -+ if (count) -+ count = dma_bounce_user(DMA_DEV_TO_MEM, user_ptr, -+ count, bounce); -+ } -+ if (odd_bytes) { -+ /* Read from FIFO directly if not using DMA */ -+ uint8_t buf[DMA_THRESHOLD_BYTES]; -+ -+ bcm2835_smi_read_buf(smi_inst, buf, odd_bytes); -+ if (copy_to_user(user_ptr, buf, odd_bytes)) -+ dev_err(inst->dev, "copy_to_user() failed."); -+ count += odd_bytes; -+ -+ } -+ return count; -+} -+ -+static ssize_t -+bcm2835_write_file(struct file *f, const char __user *user_ptr, -+ size_t count, loff_t *offs) -+{ -+ int odd_bytes; -+ -+ dev_dbg(inst->dev, "User writing %d bytes to SMI.", count); -+ if (count > DMA_THRESHOLD_BYTES) -+ odd_bytes = count & 0x3; -+ else -+ odd_bytes = count; -+ count -= odd_bytes; -+ if (count) { -+ struct bcm2835_smi_bounce_info *bounce; -+ -+ count = bcm2835_smi_user_dma(smi_inst, -+ DMA_MEM_TO_DEV, (char __user *)user_ptr, count, -+ &bounce); -+ if (count) -+ count = dma_bounce_user(DMA_MEM_TO_DEV, -+ (char __user *)user_ptr, -+ count, bounce); -+ } -+ if (odd_bytes) { -+ uint8_t buf[DMA_THRESHOLD_BYTES]; -+ -+ if (copy_from_user(buf, user_ptr, odd_bytes)) -+ dev_err(inst->dev, "copy_from_user() failed."); -+ else -+ bcm2835_smi_write_buf(smi_inst, buf, odd_bytes); -+ count += odd_bytes; -+ } -+ return count; -+} -+ -+static const struct file_operations -+bcm2835_smi_fops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = bcm2835_smi_ioctl, -+ .open = bcm2835_smi_open, -+ .release = bcm2835_smi_release, -+ .read = bcm2835_read_file, -+ .write = bcm2835_write_file, -+}; -+ -+ -+/**************************************************************************** -+* -+* bcm2835_smi_probe - called when the driver is loaded. -+* -+***************************************************************************/ -+ -+static int bcm2835_smi_dev_probe(struct platform_device *pdev) -+{ -+ int err; -+ void *ptr_err; -+ struct device *dev = &pdev->dev; -+ struct device_node *node = dev->of_node, *smi_node; -+ -+ if (!node) { -+ dev_err(dev, "No device tree node supplied!"); -+ return -EINVAL; -+ } -+ -+ smi_node = of_parse_phandle(node, "smi_handle", 0); -+ -+ if (!smi_node) { -+ dev_err(dev, "No such property: smi_handle"); -+ return -ENXIO; -+ } -+ -+ smi_inst = bcm2835_smi_get(smi_node); -+ -+ if (!smi_inst) -+ return -EPROBE_DEFER; -+ -+ /* Allocate buffers and instance data */ -+ -+ inst = devm_kzalloc(dev, sizeof(*inst), GFP_KERNEL); -+ -+ if (!inst) -+ return -ENOMEM; -+ -+ inst->dev = dev; -+ -+ /* Create character device entries */ -+ -+ err = alloc_chrdev_region(&bcm2835_smi_devid, -+ DEVICE_MINOR, 1, DEVICE_NAME); -+ if (err != 0) { -+ dev_err(inst->dev, "unable to allocate device number"); -+ return -ENOMEM; -+ } -+ cdev_init(&bcm2835_smi_cdev, &bcm2835_smi_fops); -+ bcm2835_smi_cdev.owner = THIS_MODULE; -+ err = cdev_add(&bcm2835_smi_cdev, bcm2835_smi_devid, 1); -+ if (err != 0) { -+ dev_err(inst->dev, "unable to register device"); -+ err = -ENOMEM; -+ goto failed_cdev_add; -+ } -+ -+ /* Create sysfs entries */ -+ -+ bcm2835_smi_class = class_create(THIS_MODULE, DEVICE_NAME); -+ ptr_err = bcm2835_smi_class; -+ if (IS_ERR(ptr_err)) -+ goto failed_class_create; -+ -+ bcm2835_smi_dev = device_create(bcm2835_smi_class, NULL, -+ bcm2835_smi_devid, NULL, -+ "smi"); -+ ptr_err = bcm2835_smi_dev; -+ if (IS_ERR(ptr_err)) -+ goto failed_device_create; -+ -+ dev_info(inst->dev, "initialised"); -+ -+ return 0; -+ -+failed_device_create: -+ class_destroy(bcm2835_smi_class); -+failed_class_create: -+ cdev_del(&bcm2835_smi_cdev); -+ err = PTR_ERR(ptr_err); -+failed_cdev_add: -+ unregister_chrdev_region(bcm2835_smi_devid, 1); -+ dev_err(dev, "could not load bcm2835_smi_dev"); -+ return err; -+} -+ -+/**************************************************************************** -+* -+* bcm2835_smi_remove - called when the driver is unloaded. -+* -+***************************************************************************/ -+ -+static int bcm2835_smi_dev_remove(struct platform_device *pdev) -+{ -+ device_destroy(bcm2835_smi_class, bcm2835_smi_devid); -+ class_destroy(bcm2835_smi_class); -+ cdev_del(&bcm2835_smi_cdev); -+ unregister_chrdev_region(bcm2835_smi_devid, 1); -+ -+ dev_info(inst->dev, "SMI character dev removed - OK"); -+ return 0; -+} -+ -+/**************************************************************************** -+* -+* Register the driver with device tree -+* -+***************************************************************************/ -+ -+static const struct of_device_id bcm2835_smi_dev_of_match[] = { -+ {.compatible = "brcm,bcm2835-smi-dev",}, -+ { /* sentinel */ }, -+}; -+ -+MODULE_DEVICE_TABLE(of, bcm2835_smi_dev_of_match); -+ -+static struct platform_driver bcm2835_smi_dev_driver = { -+ .probe = bcm2835_smi_dev_probe, -+ .remove = bcm2835_smi_dev_remove, -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ .of_match_table = bcm2835_smi_dev_of_match, -+ }, -+}; -+ -+module_platform_driver(bcm2835_smi_dev_driver); -+ -+MODULE_ALIAS("platform:smi-dev-bcm2835"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION( -+ "Character device driver for BCM2835's secondary memory interface"); -+MODULE_AUTHOR("Luke Wren "); ---- a/drivers/misc/Kconfig -+++ b/drivers/misc/Kconfig -@@ -10,6 +10,14 @@ config SENSORS_LIS3LV02D - select INPUT_POLLDEV - default n - -+config BCM2835_SMI -+ tristate "Broadcom 283x Secondary Memory Interface driver" -+ depends on MACH_BCM2708 || MACH_BCM2709 || ARCH_BCM2835 -+ default m -+ help -+ Driver for enabling and using Broadcom's Secondary/Slow Memory Interface. -+ Appears as /dev/bcm2835_smi. For ioctl interface see drivers/misc/bcm2835_smi.h -+ - config AD525X_DPOT - tristate "Analog Devices Digital Potentiometers" - depends on (I2C || SPI) && SYSFS ---- a/drivers/misc/Makefile -+++ b/drivers/misc/Makefile -@@ -9,6 +9,7 @@ obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_ - obj-$(CONFIG_INTEL_MID_PTI) += pti.o - obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o - obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o -+obj-$(CONFIG_BCM2835_SMI) += bcm2835_smi.o - obj-$(CONFIG_BMP085) += bmp085.o - obj-$(CONFIG_BMP085_I2C) += bmp085-i2c.o - obj-$(CONFIG_BMP085_SPI) += bmp085-spi.o ---- /dev/null -+++ b/drivers/misc/bcm2835_smi.c -@@ -0,0 +1,985 @@ -+/** -+ * Broadcom Secondary Memory Interface driver -+ * -+ * Written by Luke Wren -+ * Copyright (c) 2015, 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define BCM2835_SMI_IMPLEMENTATION -+#include -+ -+#define DRIVER_NAME "smi-bcm2835" -+ -+#define N_PAGES_FROM_BYTES(n) ((n + PAGE_SIZE-1) / PAGE_SIZE) -+ -+#define DMA_WRITE_TO_MEM true -+#define DMA_READ_FROM_MEM false -+ -+struct bcm2835_smi_instance { -+ struct device *dev; -+ struct smi_settings settings; -+ __iomem void *smi_regs_ptr, *cm_smi_regs_ptr; -+ dma_addr_t smi_regs_busaddr; -+ -+ struct dma_chan *dma_chan; -+ struct dma_slave_config dma_config; -+ -+ struct bcm2835_smi_bounce_info bounce; -+ -+ struct scatterlist buffer_sgl; -+ -+ int clock_source; -+ int clock_divisor; -+ -+ /* Sometimes we are called into in an atomic context (e.g. by -+ JFFS2 + MTD) so we can't use a mutex */ -+ spinlock_t transaction_lock; -+}; -+ -+/**************************************************************************** -+* -+* SMI clock manager setup -+* -+***************************************************************************/ -+ -+static inline void write_smi_cm_reg(struct bcm2835_smi_instance *inst, -+ u32 val, unsigned reg) -+{ -+ writel(CM_PWD | val, inst->cm_smi_regs_ptr + reg); -+} -+ -+static inline u32 read_smi_cm_reg(struct bcm2835_smi_instance *inst, -+ unsigned reg) -+{ -+ return readl(inst->cm_smi_regs_ptr + reg); -+} -+ -+static void smi_setup_clock(struct bcm2835_smi_instance *inst) -+{ -+ dev_dbg(inst->dev, "Setting up clock..."); -+ /* Disable SMI clock and wait for it to stop. */ -+ write_smi_cm_reg(inst, 0, CM_SMI_CTL); -+ while (read_smi_cm_reg(inst, CM_SMI_CTL) & CM_SMI_CTL_BUSY) -+ ; -+ -+ write_smi_cm_reg(inst, (inst->clock_divisor << CM_SMI_DIV_DIVI_OFFS), -+ CM_SMI_DIV); -+ write_smi_cm_reg(inst, (inst->clock_source << CM_SMI_CTL_SRC_OFFS), -+ CM_SMI_CTL); -+ -+ /* Enable the clock */ -+ write_smi_cm_reg(inst, (inst->clock_source << CM_SMI_CTL_SRC_OFFS) | -+ CM_SMI_CTL_ENAB, CM_SMI_CTL); -+} -+ -+/**************************************************************************** -+* -+* SMI peripheral setup -+* -+***************************************************************************/ -+ -+static inline void write_smi_reg(struct bcm2835_smi_instance *inst, -+ u32 val, unsigned reg) -+{ -+ writel(val, inst->smi_regs_ptr + reg); -+} -+ -+static inline u32 read_smi_reg(struct bcm2835_smi_instance *inst, unsigned reg) -+{ -+ return readl(inst->smi_regs_ptr + reg); -+} -+ -+/* Token-paste macro for e.g SMIDSR_RSTROBE -> value of SMIDSR_RSTROBE_MASK */ -+#define _CONCAT(x, y) x##y -+#define CONCAT(x, y) _CONCAT(x, y) -+ -+#define SET_BIT_FIELD(dest, field, bits) ((dest) = \ -+ ((dest) & ~CONCAT(field, _MASK)) | (((bits) << CONCAT(field, _OFFS))& \ -+ CONCAT(field, _MASK))) -+#define GET_BIT_FIELD(src, field) (((src) & \ -+ CONCAT(field, _MASK)) >> CONCAT(field, _OFFS)) -+ -+static void smi_dump_context_labelled(struct bcm2835_smi_instance *inst, -+ const char *label) -+{ -+ dev_err(inst->dev, "SMI context dump: %s", label); -+ dev_err(inst->dev, "SMICS: 0x%08x", read_smi_reg(inst, SMICS)); -+ dev_err(inst->dev, "SMIL: 0x%08x", read_smi_reg(inst, SMIL)); -+ dev_err(inst->dev, "SMIDSR: 0x%08x", read_smi_reg(inst, SMIDSR0)); -+ dev_err(inst->dev, "SMIDSW: 0x%08x", read_smi_reg(inst, SMIDSW0)); -+ dev_err(inst->dev, "SMIDC: 0x%08x", read_smi_reg(inst, SMIDC)); -+ dev_err(inst->dev, "SMIFD: 0x%08x", read_smi_reg(inst, SMIFD)); -+ dev_err(inst->dev, " "); -+} -+ -+static inline void smi_dump_context(struct bcm2835_smi_instance *inst) -+{ -+ smi_dump_context_labelled(inst, ""); -+} -+ -+static void smi_get_default_settings(struct bcm2835_smi_instance *inst) -+{ -+ struct smi_settings *settings = &inst->settings; -+ -+ settings->data_width = SMI_WIDTH_16BIT; -+ settings->pack_data = true; -+ -+ settings->read_setup_time = 1; -+ settings->read_hold_time = 1; -+ settings->read_pace_time = 1; -+ settings->read_strobe_time = 3; -+ -+ settings->write_setup_time = settings->read_setup_time; -+ settings->write_hold_time = settings->read_hold_time; -+ settings->write_pace_time = settings->read_pace_time; -+ settings->write_strobe_time = settings->read_strobe_time; -+ -+ settings->dma_enable = true; -+ settings->dma_passthrough_enable = false; -+ settings->dma_read_thresh = 0x01; -+ settings->dma_write_thresh = 0x3f; -+ settings->dma_panic_read_thresh = 0x20; -+ settings->dma_panic_write_thresh = 0x20; -+} -+ -+void bcm2835_smi_set_regs_from_settings(struct bcm2835_smi_instance *inst) -+{ -+ struct smi_settings *settings = &inst->settings; -+ int smidsr_temp = 0, smidsw_temp = 0, smics_temp, -+ smidcs_temp, smidc_temp = 0; -+ -+ spin_lock(&inst->transaction_lock); -+ -+ /* temporarily disable the peripheral: */ -+ smics_temp = read_smi_reg(inst, SMICS); -+ write_smi_reg(inst, 0, SMICS); -+ smidcs_temp = read_smi_reg(inst, SMIDCS); -+ write_smi_reg(inst, 0, SMIDCS); -+ -+ if (settings->pack_data) -+ smics_temp |= SMICS_PXLDAT; -+ else -+ smics_temp &= ~SMICS_PXLDAT; -+ -+ SET_BIT_FIELD(smidsr_temp, SMIDSR_RWIDTH, settings->data_width); -+ SET_BIT_FIELD(smidsr_temp, SMIDSR_RSETUP, settings->read_setup_time); -+ SET_BIT_FIELD(smidsr_temp, SMIDSR_RHOLD, settings->read_hold_time); -+ SET_BIT_FIELD(smidsr_temp, SMIDSR_RPACE, settings->read_pace_time); -+ SET_BIT_FIELD(smidsr_temp, SMIDSR_RSTROBE, settings->read_strobe_time); -+ write_smi_reg(inst, smidsr_temp, SMIDSR0); -+ -+ SET_BIT_FIELD(smidsw_temp, SMIDSW_WWIDTH, settings->data_width); -+ if (settings->data_width == SMI_WIDTH_8BIT) -+ smidsw_temp |= SMIDSW_WSWAP; -+ else -+ smidsw_temp &= ~SMIDSW_WSWAP; -+ SET_BIT_FIELD(smidsw_temp, SMIDSW_WSETUP, settings->write_setup_time); -+ SET_BIT_FIELD(smidsw_temp, SMIDSW_WHOLD, settings->write_hold_time); -+ SET_BIT_FIELD(smidsw_temp, SMIDSW_WPACE, settings->write_pace_time); -+ SET_BIT_FIELD(smidsw_temp, SMIDSW_WSTROBE, -+ settings->write_strobe_time); -+ write_smi_reg(inst, smidsw_temp, SMIDSW0); -+ -+ SET_BIT_FIELD(smidc_temp, SMIDC_REQR, settings->dma_read_thresh); -+ SET_BIT_FIELD(smidc_temp, SMIDC_REQW, settings->dma_write_thresh); -+ SET_BIT_FIELD(smidc_temp, SMIDC_PANICR, -+ settings->dma_panic_read_thresh); -+ SET_BIT_FIELD(smidc_temp, SMIDC_PANICW, -+ settings->dma_panic_write_thresh); -+ if (settings->dma_passthrough_enable) { -+ smidc_temp |= SMIDC_DMAP; -+ smidsr_temp |= SMIDSR_RDREQ; -+ write_smi_reg(inst, smidsr_temp, SMIDSR0); -+ smidsw_temp |= SMIDSW_WDREQ; -+ write_smi_reg(inst, smidsw_temp, SMIDSW0); -+ } else -+ smidc_temp &= ~SMIDC_DMAP; -+ if (settings->dma_enable) -+ smidc_temp |= SMIDC_DMAEN; -+ else -+ smidc_temp &= ~SMIDC_DMAEN; -+ -+ write_smi_reg(inst, smidc_temp, SMIDC); -+ -+ /* re-enable (if was previously enabled) */ -+ write_smi_reg(inst, smics_temp, SMICS); -+ write_smi_reg(inst, smidcs_temp, SMIDCS); -+ -+ spin_unlock(&inst->transaction_lock); -+} -+EXPORT_SYMBOL(bcm2835_smi_set_regs_from_settings); -+ -+struct smi_settings *bcm2835_smi_get_settings_from_regs -+ (struct bcm2835_smi_instance *inst) -+{ -+ struct smi_settings *settings = &inst->settings; -+ int smidsr, smidsw, smidc; -+ -+ spin_lock(&inst->transaction_lock); -+ -+ smidsr = read_smi_reg(inst, SMIDSR0); -+ smidsw = read_smi_reg(inst, SMIDSW0); -+ smidc = read_smi_reg(inst, SMIDC); -+ -+ settings->pack_data = (read_smi_reg(inst, SMICS) & SMICS_PXLDAT) ? -+ true : false; -+ -+ settings->data_width = GET_BIT_FIELD(smidsr, SMIDSR_RWIDTH); -+ settings->read_setup_time = GET_BIT_FIELD(smidsr, SMIDSR_RSETUP); -+ settings->read_hold_time = GET_BIT_FIELD(smidsr, SMIDSR_RHOLD); -+ settings->read_pace_time = GET_BIT_FIELD(smidsr, SMIDSR_RPACE); -+ settings->read_strobe_time = GET_BIT_FIELD(smidsr, SMIDSR_RSTROBE); -+ -+ settings->write_setup_time = GET_BIT_FIELD(smidsw, SMIDSW_WSETUP); -+ settings->write_hold_time = GET_BIT_FIELD(smidsw, SMIDSW_WHOLD); -+ settings->write_pace_time = GET_BIT_FIELD(smidsw, SMIDSW_WPACE); -+ settings->write_strobe_time = GET_BIT_FIELD(smidsw, SMIDSW_WSTROBE); -+ -+ settings->dma_read_thresh = GET_BIT_FIELD(smidc, SMIDC_REQR); -+ settings->dma_write_thresh = GET_BIT_FIELD(smidc, SMIDC_REQW); -+ settings->dma_panic_read_thresh = GET_BIT_FIELD(smidc, SMIDC_PANICR); -+ settings->dma_panic_write_thresh = GET_BIT_FIELD(smidc, SMIDC_PANICW); -+ settings->dma_passthrough_enable = (smidc & SMIDC_DMAP) ? true : false; -+ settings->dma_enable = (smidc & SMIDC_DMAEN) ? true : false; -+ -+ spin_unlock(&inst->transaction_lock); -+ -+ return settings; -+} -+EXPORT_SYMBOL(bcm2835_smi_get_settings_from_regs); -+ -+static inline void smi_set_address(struct bcm2835_smi_instance *inst, -+ unsigned int address) -+{ -+ int smia_temp = 0, smida_temp = 0; -+ -+ SET_BIT_FIELD(smia_temp, SMIA_ADDR, address); -+ SET_BIT_FIELD(smida_temp, SMIDA_ADDR, address); -+ -+ /* Write to both address registers - user doesn't care whether we're -+ doing programmed or direct transfers. */ -+ write_smi_reg(inst, smia_temp, SMIA); -+ write_smi_reg(inst, smida_temp, SMIDA); -+} -+ -+static void smi_setup_regs(struct bcm2835_smi_instance *inst) -+{ -+ -+ dev_dbg(inst->dev, "Initialising SMI registers..."); -+ /* Disable the peripheral if already enabled */ -+ write_smi_reg(inst, 0, SMICS); -+ write_smi_reg(inst, 0, SMIDCS); -+ -+ smi_get_default_settings(inst); -+ bcm2835_smi_set_regs_from_settings(inst); -+ smi_set_address(inst, 0); -+ -+ write_smi_reg(inst, read_smi_reg(inst, SMICS) | SMICS_ENABLE, SMICS); -+ write_smi_reg(inst, read_smi_reg(inst, SMIDCS) | SMIDCS_ENABLE, -+ SMIDCS); -+} -+ -+/**************************************************************************** -+* -+* Low-level SMI access functions -+* Other modules should use the exported higher-level functions e.g. -+* bcm2835_smi_write_buf() unless they have a good reason to use these -+* -+***************************************************************************/ -+ -+static inline uint32_t smi_read_single_word(struct bcm2835_smi_instance *inst) -+{ -+ int timeout = 0; -+ -+ write_smi_reg(inst, SMIDCS_ENABLE, SMIDCS); -+ write_smi_reg(inst, SMIDCS_ENABLE | SMIDCS_START, SMIDCS); -+ /* Make sure things happen in the right order...*/ -+ mb(); -+ while (!(read_smi_reg(inst, SMIDCS) & SMIDCS_DONE) && -+ ++timeout < 10000) -+ ; -+ if (timeout < 10000) -+ return read_smi_reg(inst, SMIDD); -+ -+ dev_err(inst->dev, -+ "SMI direct read timed out (is the clock set up correctly?)"); -+ return 0; -+} -+ -+static inline void smi_write_single_word(struct bcm2835_smi_instance *inst, -+ uint32_t data) -+{ -+ int timeout = 0; -+ -+ write_smi_reg(inst, SMIDCS_ENABLE | SMIDCS_WRITE, SMIDCS); -+ write_smi_reg(inst, data, SMIDD); -+ write_smi_reg(inst, SMIDCS_ENABLE | SMIDCS_WRITE | SMIDCS_START, -+ SMIDCS); -+ -+ while (!(read_smi_reg(inst, SMIDCS) & SMIDCS_DONE) && -+ ++timeout < 10000) -+ ; -+ if (timeout >= 10000) -+ dev_err(inst->dev, -+ "SMI direct write timed out (is the clock set up correctly?)"); -+} -+ -+/* Initiates a programmed read into the read FIFO. It is up to the caller to -+ * read data from the FIFO - either via paced DMA transfer, -+ * or polling SMICS_RXD to check whether data is available. -+ * SMICS_ACTIVE will go low upon completion. */ -+static void smi_init_programmed_read(struct bcm2835_smi_instance *inst, -+ int num_transfers) -+{ -+ int smics_temp; -+ -+ /* Disable the peripheral: */ -+ smics_temp = read_smi_reg(inst, SMICS) & ~(SMICS_ENABLE | SMICS_WRITE); -+ write_smi_reg(inst, smics_temp, SMICS); -+ while (read_smi_reg(inst, SMICS) & SMICS_ENABLE) -+ ; -+ -+ /* Program the transfer count: */ -+ write_smi_reg(inst, num_transfers, SMIL); -+ -+ /* re-enable and start: */ -+ smics_temp |= SMICS_ENABLE; -+ write_smi_reg(inst, smics_temp, SMICS); -+ smics_temp |= SMICS_CLEAR; -+ /* Just to be certain: */ -+ mb(); -+ while (read_smi_reg(inst, SMICS) & SMICS_ACTIVE) -+ ; -+ write_smi_reg(inst, smics_temp, SMICS); -+ smics_temp |= SMICS_START; -+ write_smi_reg(inst, smics_temp, SMICS); -+} -+ -+/* Initiates a programmed write sequence, using data from the write FIFO. -+ * It is up to the caller to initiate a DMA transfer before calling, -+ * or use another method to keep the write FIFO topped up. -+ * SMICS_ACTIVE will go low upon completion. -+ */ -+static void smi_init_programmed_write(struct bcm2835_smi_instance *inst, -+ int num_transfers) -+{ -+ int smics_temp; -+ -+ /* Disable the peripheral: */ -+ smics_temp = read_smi_reg(inst, SMICS) & ~SMICS_ENABLE; -+ write_smi_reg(inst, smics_temp, SMICS); -+ while (read_smi_reg(inst, SMICS) & SMICS_ENABLE) -+ ; -+ -+ /* Program the transfer count: */ -+ write_smi_reg(inst, num_transfers, SMIL); -+ -+ /* setup, re-enable and start: */ -+ smics_temp |= SMICS_WRITE | SMICS_ENABLE; -+ write_smi_reg(inst, smics_temp, SMICS); -+ smics_temp |= SMICS_START; -+ write_smi_reg(inst, smics_temp, SMICS); -+} -+ -+/* Initiate a read and then poll FIFO for data, reading out as it appears. */ -+static void smi_read_fifo(struct bcm2835_smi_instance *inst, -+ uint32_t *dest, int n_bytes) -+{ -+ if (read_smi_reg(inst, SMICS) & SMICS_RXD) { -+ smi_dump_context_labelled(inst, -+ "WARNING: read FIFO not empty at start of read call."); -+ while (read_smi_reg(inst, SMICS)) -+ ; -+ } -+ -+ /* Dispatch the read: */ -+ if (inst->settings.data_width == SMI_WIDTH_8BIT) -+ smi_init_programmed_read(inst, n_bytes); -+ else if (inst->settings.data_width == SMI_WIDTH_16BIT) -+ smi_init_programmed_read(inst, n_bytes / 2); -+ else { -+ dev_err(inst->dev, "Unsupported data width for read."); -+ return; -+ } -+ -+ /* Poll FIFO to keep it empty */ -+ while (!(read_smi_reg(inst, SMICS) & SMICS_DONE)) -+ if (read_smi_reg(inst, SMICS) & SMICS_RXD) -+ *dest++ = read_smi_reg(inst, SMID); -+ -+ /* Ensure that the FIFO is emptied */ -+ if (read_smi_reg(inst, SMICS) & SMICS_RXD) { -+ int fifo_count; -+ -+ fifo_count = GET_BIT_FIELD(read_smi_reg(inst, SMIFD), -+ SMIFD_FCNT); -+ while (fifo_count--) -+ *dest++ = read_smi_reg(inst, SMID); -+ } -+ -+ if (!(read_smi_reg(inst, SMICS) & SMICS_DONE)) -+ smi_dump_context_labelled(inst, -+ "WARNING: transaction finished but done bit not set."); -+ -+ if (read_smi_reg(inst, SMICS) & SMICS_RXD) -+ smi_dump_context_labelled(inst, -+ "WARNING: read FIFO not empty at end of read call."); -+ -+} -+ -+/* Initiate a write, and then keep the FIFO topped up. */ -+static void smi_write_fifo(struct bcm2835_smi_instance *inst, -+ uint32_t *src, int n_bytes) -+{ -+ int i, timeout = 0; -+ -+ /* Empty FIFOs if not already so */ -+ if (!(read_smi_reg(inst, SMICS) & SMICS_TXE)) { -+ smi_dump_context_labelled(inst, -+ "WARNING: write fifo not empty at start of write call."); -+ write_smi_reg(inst, read_smi_reg(inst, SMICS) | SMICS_CLEAR, -+ SMICS); -+ } -+ -+ /* Initiate the transfer */ -+ if (inst->settings.data_width == SMI_WIDTH_8BIT) -+ smi_init_programmed_write(inst, n_bytes); -+ else if (inst->settings.data_width == SMI_WIDTH_16BIT) -+ smi_init_programmed_write(inst, n_bytes / 2); -+ else { -+ dev_err(inst->dev, "Unsupported data width for write."); -+ return; -+ } -+ /* Fill the FIFO: */ -+ for (i = 0; i < (n_bytes - 1) / 4 + 1; ++i) { -+ while (!(read_smi_reg(inst, SMICS) & SMICS_TXD)) -+ ; -+ write_smi_reg(inst, *src++, SMID); -+ } -+ /* Busy wait... */ -+ while (!(read_smi_reg(inst, SMICS) & SMICS_DONE) && ++timeout < -+ 1000000) -+ ; -+ if (timeout >= 1000000) -+ smi_dump_context_labelled(inst, -+ "Timed out on write operation!"); -+ if (!(read_smi_reg(inst, SMICS) & SMICS_TXE)) -+ smi_dump_context_labelled(inst, -+ "WARNING: FIFO not empty at end of write operation."); -+} -+ -+/**************************************************************************** -+* -+* SMI DMA operations -+* -+***************************************************************************/ -+ -+/* Disable SMI and put it into the correct direction before doing DMA setup. -+ Stops spurious DREQs during setup. Peripheral is re-enabled by init_*() */ -+static void smi_disable(struct bcm2835_smi_instance *inst, -+ enum dma_transfer_direction direction) -+{ -+ int smics_temp = read_smi_reg(inst, SMICS) & ~SMICS_ENABLE; -+ -+ if (direction == DMA_DEV_TO_MEM) -+ smics_temp &= ~SMICS_WRITE; -+ else -+ smics_temp |= SMICS_WRITE; -+ write_smi_reg(inst, smics_temp, SMICS); -+ while (read_smi_reg(inst, SMICS) & SMICS_ACTIVE) -+ ; -+} -+ -+static struct scatterlist *smi_scatterlist_from_buffer( -+ struct bcm2835_smi_instance *inst, -+ dma_addr_t buf, -+ size_t len, -+ struct scatterlist *sg) -+{ -+ sg_init_table(sg, 1); -+ sg_dma_address(sg) = buf; -+ sg_dma_len(sg) = len; -+ return sg; -+} -+ -+static void smi_dma_callback_user_copy(void *param) -+{ -+ /* Notify the bottom half that a chunk is ready for user copy */ -+ struct bcm2835_smi_instance *inst = -+ (struct bcm2835_smi_instance *)param; -+ -+ up(&inst->bounce.callback_sem); -+} -+ -+/* Creates a descriptor, assigns the given callback, and submits the -+ descriptor to dmaengine. Does not block - can queue up multiple -+ descriptors and then wait for them all to complete. -+ sg_len is the number of control blocks, NOT the number of bytes. -+ dir can be DMA_MEM_TO_DEV or DMA_DEV_TO_MEM. -+ callback can be NULL - in this case it is not called. */ -+static inline struct dma_async_tx_descriptor *smi_dma_submit_sgl( -+ struct bcm2835_smi_instance *inst, -+ struct scatterlist *sgl, -+ size_t sg_len, -+ enum dma_transfer_direction dir, -+ dma_async_tx_callback callback) -+{ -+ struct dma_async_tx_descriptor *desc; -+ -+ desc = dmaengine_prep_slave_sg(inst->dma_chan, -+ sgl, -+ sg_len, -+ dir, -+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK | -+ DMA_PREP_FENCE); -+ if (!desc) { -+ dev_err(inst->dev, "read_sgl: dma slave preparation failed!"); -+ write_smi_reg(inst, read_smi_reg(inst, SMICS) & ~SMICS_ACTIVE, -+ SMICS); -+ while (read_smi_reg(inst, SMICS) & SMICS_ACTIVE) -+ cpu_relax(); -+ write_smi_reg(inst, read_smi_reg(inst, SMICS) | SMICS_ACTIVE, -+ SMICS); -+ return NULL; -+ } -+ desc->callback = callback; -+ desc->callback_param = inst; -+ if (dmaengine_submit(desc) < 0) -+ return NULL; -+ return desc; -+} -+ -+/* NB this function blocks until the transfer is complete */ -+static void -+smi_dma_read_sgl(struct bcm2835_smi_instance *inst, -+ struct scatterlist *sgl, size_t sg_len, size_t n_bytes) -+{ -+ struct dma_async_tx_descriptor *desc; -+ -+ /* Disable SMI and set to read before dispatching DMA - if SMI is in -+ * write mode and TX fifo is empty, it will generate a DREQ which may -+ * cause the read DMA to complete before the SMI read command is even -+ * dispatched! We want to dispatch DMA before SMI read so that reading -+ * is gapless, for logic analyser. -+ */ -+ -+ smi_disable(inst, DMA_DEV_TO_MEM); -+ -+ desc = smi_dma_submit_sgl(inst, sgl, sg_len, DMA_DEV_TO_MEM, NULL); -+ dma_async_issue_pending(inst->dma_chan); -+ -+ if (inst->settings.data_width == SMI_WIDTH_8BIT) -+ smi_init_programmed_read(inst, n_bytes); -+ else -+ smi_init_programmed_read(inst, n_bytes / 2); -+ -+ if (dma_wait_for_async_tx(desc) == DMA_ERROR) -+ smi_dump_context_labelled(inst, "DMA timeout!"); -+} -+ -+static void -+smi_dma_write_sgl(struct bcm2835_smi_instance *inst, -+ struct scatterlist *sgl, size_t sg_len, size_t n_bytes) -+{ -+ struct dma_async_tx_descriptor *desc; -+ -+ if (inst->settings.data_width == SMI_WIDTH_8BIT) -+ smi_init_programmed_write(inst, n_bytes); -+ else -+ smi_init_programmed_write(inst, n_bytes / 2); -+ -+ desc = smi_dma_submit_sgl(inst, sgl, sg_len, DMA_MEM_TO_DEV, NULL); -+ dma_async_issue_pending(inst->dma_chan); -+ -+ if (dma_wait_for_async_tx(desc) == DMA_ERROR) -+ smi_dump_context_labelled(inst, "DMA timeout!"); -+ else -+ /* Wait for SMI to finish our writes */ -+ while (!(read_smi_reg(inst, SMICS) & SMICS_DONE)) -+ cpu_relax(); -+} -+ -+ssize_t bcm2835_smi_user_dma( -+ struct bcm2835_smi_instance *inst, -+ enum dma_transfer_direction dma_dir, -+ char __user *user_ptr, size_t count, -+ struct bcm2835_smi_bounce_info **bounce) -+{ -+ int chunk_no = 0, chunk_size, count_left = count; -+ struct scatterlist *sgl; -+ void (*init_trans_func)(struct bcm2835_smi_instance *, int); -+ -+ spin_lock(&inst->transaction_lock); -+ -+ if (dma_dir == DMA_DEV_TO_MEM) -+ init_trans_func = smi_init_programmed_read; -+ else -+ init_trans_func = smi_init_programmed_write; -+ -+ smi_disable(inst, dma_dir); -+ -+ sema_init(&inst->bounce.callback_sem, 0); -+ if (bounce) -+ *bounce = &inst->bounce; -+ while (count_left) { -+ chunk_size = count_left > DMA_BOUNCE_BUFFER_SIZE ? -+ DMA_BOUNCE_BUFFER_SIZE : count_left; -+ if (chunk_size == DMA_BOUNCE_BUFFER_SIZE) { -+ sgl = -+ &inst->bounce.sgl[chunk_no % DMA_BOUNCE_BUFFER_COUNT]; -+ } else { -+ sgl = smi_scatterlist_from_buffer( -+ inst, -+ inst->bounce.phys[ -+ chunk_no % DMA_BOUNCE_BUFFER_COUNT], -+ chunk_size, -+ &inst->buffer_sgl); -+ } -+ -+ if (!smi_dma_submit_sgl(inst, sgl, 1, dma_dir, -+ smi_dma_callback_user_copy -+ )) { -+ dev_err(inst->dev, "sgl submit failed"); -+ count = 0; -+ goto out; -+ } -+ count_left -= chunk_size; -+ chunk_no++; -+ } -+ dma_async_issue_pending(inst->dma_chan); -+ -+ if (inst->settings.data_width == SMI_WIDTH_8BIT) -+ init_trans_func(inst, count); -+ else if (inst->settings.data_width == SMI_WIDTH_16BIT) -+ init_trans_func(inst, count / 2); -+out: -+ spin_unlock(&inst->transaction_lock); -+ return count; -+} -+EXPORT_SYMBOL(bcm2835_smi_user_dma); -+ -+ -+/**************************************************************************** -+* -+* High level buffer transfer functions - for use by other drivers -+* -+***************************************************************************/ -+ -+/* Buffer must be physically contiguous - i.e. kmalloc, not vmalloc! */ -+void bcm2835_smi_write_buf( -+ struct bcm2835_smi_instance *inst, -+ const void *buf, size_t n_bytes) -+{ -+ int odd_bytes = n_bytes & 0x3; -+ -+ n_bytes -= odd_bytes; -+ -+ spin_lock(&inst->transaction_lock); -+ -+ if (n_bytes > DMA_THRESHOLD_BYTES) { -+ dma_addr_t phy_addr = dma_map_single( -+ inst->dev, -+ (void *)buf, -+ n_bytes, -+ DMA_MEM_TO_DEV); -+ struct scatterlist *sgl = -+ smi_scatterlist_from_buffer(inst, phy_addr, n_bytes, -+ &inst->buffer_sgl); -+ -+ if (!sgl) { -+ smi_dump_context_labelled(inst, -+ "Error: could not create scatterlist for write!"); -+ goto out; -+ } -+ smi_dma_write_sgl(inst, sgl, 1, n_bytes); -+ -+ dma_unmap_single -+ (inst->dev, phy_addr, n_bytes, DMA_MEM_TO_DEV); -+ } else if (n_bytes) { -+ smi_write_fifo(inst, (uint32_t *) buf, n_bytes); -+ } -+ buf += n_bytes; -+ -+ if (inst->settings.data_width == SMI_WIDTH_8BIT) { -+ while (odd_bytes--) -+ smi_write_single_word(inst, *(uint8_t *) (buf++)); -+ } else { -+ while (odd_bytes >= 2) { -+ smi_write_single_word(inst, *(uint16_t *)buf); -+ buf += 2; -+ odd_bytes -= 2; -+ } -+ if (odd_bytes) { -+ /* Reading an odd number of bytes on a 16 bit bus is -+ a user bug. It's kinder to fail early and tell them -+ than to e.g. transparently give them the bottom byte -+ of a 16 bit transfer. */ -+ dev_err(inst->dev, -+ "WARNING: odd number of bytes specified for wide transfer."); -+ dev_err(inst->dev, -+ "At least one byte dropped as a result."); -+ dump_stack(); -+ } -+ } -+out: -+ spin_unlock(&inst->transaction_lock); -+} -+EXPORT_SYMBOL(bcm2835_smi_write_buf); -+ -+void bcm2835_smi_read_buf(struct bcm2835_smi_instance *inst, -+ void *buf, size_t n_bytes) -+{ -+ -+ /* SMI is inherently 32-bit, which causes surprising amounts of mess -+ for bytes % 4 != 0. Easiest to avoid this mess altogether -+ by handling remainder separately. */ -+ int odd_bytes = n_bytes & 0x3; -+ -+ spin_lock(&inst->transaction_lock); -+ n_bytes -= odd_bytes; -+ if (n_bytes > DMA_THRESHOLD_BYTES) { -+ dma_addr_t phy_addr = dma_map_single(inst->dev, -+ buf, n_bytes, -+ DMA_DEV_TO_MEM); -+ struct scatterlist *sgl = smi_scatterlist_from_buffer( -+ inst, phy_addr, n_bytes, -+ &inst->buffer_sgl); -+ if (!sgl) { -+ smi_dump_context_labelled(inst, -+ "Error: could not create scatterlist for read!"); -+ goto out; -+ } -+ smi_dma_read_sgl(inst, sgl, 1, n_bytes); -+ dma_unmap_single(inst->dev, phy_addr, n_bytes, DMA_DEV_TO_MEM); -+ } else if (n_bytes) { -+ smi_read_fifo(inst, (uint32_t *)buf, n_bytes); -+ } -+ buf += n_bytes; -+ -+ if (inst->settings.data_width == SMI_WIDTH_8BIT) { -+ while (odd_bytes--) -+ *((uint8_t *) (buf++)) = smi_read_single_word(inst); -+ } else { -+ while (odd_bytes >= 2) { -+ *(uint16_t *) buf = smi_read_single_word(inst); -+ buf += 2; -+ odd_bytes -= 2; -+ } -+ if (odd_bytes) { -+ dev_err(inst->dev, -+ "WARNING: odd number of bytes specified for wide transfer."); -+ dev_err(inst->dev, -+ "At least one byte dropped as a result."); -+ dump_stack(); -+ } -+ } -+out: -+ spin_unlock(&inst->transaction_lock); -+} -+EXPORT_SYMBOL(bcm2835_smi_read_buf); -+ -+void bcm2835_smi_set_address(struct bcm2835_smi_instance *inst, -+ unsigned int address) -+{ -+ spin_lock(&inst->transaction_lock); -+ smi_set_address(inst, address); -+ spin_unlock(&inst->transaction_lock); -+} -+EXPORT_SYMBOL(bcm2835_smi_set_address); -+ -+struct bcm2835_smi_instance *bcm2835_smi_get(struct device_node *node) -+{ -+ struct platform_device *pdev; -+ -+ if (!node) -+ return NULL; -+ -+ pdev = of_find_device_by_node(node); -+ if (!pdev) -+ return NULL; -+ -+ return platform_get_drvdata(pdev); -+} -+EXPORT_SYMBOL(bcm2835_smi_get); -+ -+/**************************************************************************** -+* -+* bcm2835_smi_probe - called when the driver is loaded. -+* -+***************************************************************************/ -+ -+static int bcm2835_smi_dma_setup(struct bcm2835_smi_instance *inst) -+{ -+ int i, rv = 0; -+ -+ inst->dma_chan = dma_request_slave_channel(inst->dev, "rx-tx"); -+ -+ inst->dma_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; -+ inst->dma_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; -+ inst->dma_config.src_addr = inst->smi_regs_busaddr + SMID; -+ inst->dma_config.dst_addr = inst->dma_config.src_addr; -+ /* Direction unimportant - always overridden by prep_slave_sg */ -+ inst->dma_config.direction = DMA_DEV_TO_MEM; -+ dmaengine_slave_config(inst->dma_chan, &inst->dma_config); -+ /* Alloc and map bounce buffers */ -+ for (i = 0; i < DMA_BOUNCE_BUFFER_COUNT; ++i) { -+ inst->bounce.buffer[i] = -+ dmam_alloc_coherent(inst->dev, DMA_BOUNCE_BUFFER_SIZE, -+ &inst->bounce.phys[i], -+ GFP_KERNEL); -+ if (!inst->bounce.buffer[i]) { -+ dev_err(inst->dev, "Could not allocate buffer!"); -+ rv = -ENOMEM; -+ break; -+ } -+ smi_scatterlist_from_buffer( -+ inst, -+ inst->bounce.phys[i], -+ DMA_BOUNCE_BUFFER_SIZE, -+ &inst->bounce.sgl[i] -+ ); -+ } -+ -+ return rv; -+} -+ -+static int bcm2835_smi_probe(struct platform_device *pdev) -+{ -+ int err; -+ struct device *dev = &pdev->dev; -+ struct device_node *node = dev->of_node; -+ struct resource *ioresource; -+ struct bcm2835_smi_instance *inst; -+ -+ /* Allocate buffers and instance data */ -+ -+ inst = devm_kzalloc(dev, sizeof(struct bcm2835_smi_instance), -+ GFP_KERNEL); -+ -+ if (!inst) -+ return -ENOMEM; -+ -+ inst->dev = dev; -+ spin_lock_init(&inst->transaction_lock); -+ -+ /* We require device tree support */ -+ if (!node) -+ return -EINVAL; -+ -+ ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ inst->smi_regs_ptr = devm_ioremap_resource(dev, ioresource); -+ ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 1); -+ inst->cm_smi_regs_ptr = devm_ioremap_resource(dev, ioresource); -+ inst->smi_regs_busaddr = be32_to_cpu( -+ *of_get_address(node, 0, NULL, NULL)); -+ of_property_read_u32(node, -+ "brcm,smi-clock-source", -+ &inst->clock_source); -+ of_property_read_u32(node, -+ "brcm,smi-clock-divisor", -+ &inst->clock_divisor); -+ -+ err = bcm2835_smi_dma_setup(inst); -+ if (err) -+ return err; -+ -+ /* Finally, do peripheral setup */ -+ -+ smi_setup_clock(inst); -+ smi_setup_regs(inst); -+ -+ platform_set_drvdata(pdev, inst); -+ -+ dev_info(inst->dev, "initialised"); -+ -+ return 0; -+} -+ -+/**************************************************************************** -+* -+* bcm2835_smi_remove - called when the driver is unloaded. -+* -+***************************************************************************/ -+ -+static int bcm2835_smi_remove(struct platform_device *pdev) -+{ -+ struct bcm2835_smi_instance *inst = platform_get_drvdata(pdev); -+ struct device *dev = inst->dev; -+ -+ dev_info(dev, "SMI device removed - OK"); -+ return 0; -+} -+ -+/**************************************************************************** -+* -+* Register the driver with device tree -+* -+***************************************************************************/ -+ -+static const struct of_device_id bcm2835_smi_of_match[] = { -+ {.compatible = "brcm,bcm2835-smi",}, -+ { /* sentinel */ }, -+}; -+ -+MODULE_DEVICE_TABLE(of, bcm2835_smi_of_match); -+ -+static struct platform_driver bcm2835_smi_driver = { -+ .probe = bcm2835_smi_probe, -+ .remove = bcm2835_smi_remove, -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ .of_match_table = bcm2835_smi_of_match, -+ }, -+}; -+ -+module_platform_driver(bcm2835_smi_driver); -+ -+MODULE_ALIAS("platform:smi-bcm2835"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Device driver for BCM2835's secondary memory interface"); -+MODULE_AUTHOR("Luke Wren "); ---- /dev/null -+++ b/include/linux/broadcom/bcm2835_smi.h -@@ -0,0 +1,391 @@ -+/** -+ * Declarations and definitions for Broadcom's Secondary Memory Interface -+ * -+ * Written by Luke Wren -+ * Copyright (c) 2015, Raspberry Pi (Trading) Ltd. -+ * 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 BCM2835_SMI_H -+#define BCM2835_SMI_H -+ -+#include -+ -+#ifndef __KERNEL__ -+#include -+#include -+#endif -+ -+#define BCM2835_SMI_IOC_MAGIC 0x1 -+#define BCM2835_SMI_INVALID_HANDLE (~0) -+ -+/* IOCTLs 0x100...0x1ff are not device-specific - we can use them */ -+#define BCM2835_SMI_IOC_GET_SETTINGS _IO(BCM2835_SMI_IOC_MAGIC, 0) -+#define BCM2835_SMI_IOC_WRITE_SETTINGS _IO(BCM2835_SMI_IOC_MAGIC, 1) -+#define BCM2835_SMI_IOC_ADDRESS _IO(BCM2835_SMI_IOC_MAGIC, 2) -+#define BCM2835_SMI_IOC_MAX 2 -+ -+#define SMI_WIDTH_8BIT 0 -+#define SMI_WIDTH_16BIT 1 -+#define SMI_WIDTH_9BIT 2 -+#define SMI_WIDTH_18BIT 3 -+ -+/* max number of bytes where DMA will not be used */ -+#define DMA_THRESHOLD_BYTES 128 -+#define DMA_BOUNCE_BUFFER_SIZE (1024 * 1024 / 2) -+#define DMA_BOUNCE_BUFFER_COUNT 3 -+ -+ -+struct smi_settings { -+ int data_width; -+ /* Whether or not to pack multiple SMI transfers into a -+ single 32 bit FIFO word */ -+ bool pack_data; -+ -+ /* Timing for reads (writes the same but for WE) -+ * -+ * OE ----------+ +-------------------- -+ * | | -+ * +----------+ -+ * SD -<==============================>----------- -+ * SA -<=========================================>- -+ * <-setup-> <-strobe -> <-hold -> <- pace -> -+ */ -+ -+ int read_setup_time; -+ int read_hold_time; -+ int read_pace_time; -+ int read_strobe_time; -+ -+ int write_setup_time; -+ int write_hold_time; -+ int write_pace_time; -+ int write_strobe_time; -+ -+ bool dma_enable; /* DREQs */ -+ bool dma_passthrough_enable; /* External DREQs */ -+ int dma_read_thresh; -+ int dma_write_thresh; -+ int dma_panic_read_thresh; -+ int dma_panic_write_thresh; -+}; -+ -+/**************************************************************************** -+* -+* Declare exported SMI functions -+* -+***************************************************************************/ -+ -+#ifdef __KERNEL__ -+ -+#include /* for enum dma_transfer_direction */ -+#include -+#include -+ -+struct bcm2835_smi_instance; -+ -+struct bcm2835_smi_bounce_info { -+ struct semaphore callback_sem; -+ void *buffer[DMA_BOUNCE_BUFFER_COUNT]; -+ dma_addr_t phys[DMA_BOUNCE_BUFFER_COUNT]; -+ struct scatterlist sgl[DMA_BOUNCE_BUFFER_COUNT]; -+}; -+ -+ -+void bcm2835_smi_set_regs_from_settings(struct bcm2835_smi_instance *); -+ -+struct smi_settings *bcm2835_smi_get_settings_from_regs( -+ struct bcm2835_smi_instance *inst); -+ -+void bcm2835_smi_write_buf( -+ struct bcm2835_smi_instance *inst, -+ const void *buf, -+ size_t n_bytes); -+ -+void bcm2835_smi_read_buf( -+ struct bcm2835_smi_instance *inst, -+ void *buf, -+ size_t n_bytes); -+ -+void bcm2835_smi_set_address(struct bcm2835_smi_instance *inst, -+ unsigned int address); -+ -+ssize_t bcm2835_smi_user_dma( -+ struct bcm2835_smi_instance *inst, -+ enum dma_transfer_direction dma_dir, -+ char __user *user_ptr, -+ size_t count, -+ struct bcm2835_smi_bounce_info **bounce); -+ -+struct bcm2835_smi_instance *bcm2835_smi_get(struct device_node *node); -+ -+#endif /* __KERNEL__ */ -+ -+/**************************************************************** -+* -+* Implementation-only declarations -+* -+****************************************************************/ -+ -+#ifdef BCM2835_SMI_IMPLEMENTATION -+ -+/* Clock manager registers for SMI clock: */ -+#define CM_SMI_BASE_ADDRESS ((BCM2708_PERI_BASE) + 0x1010b0) -+/* Clock manager "password" to protect registers from spurious writes */ -+#define CM_PWD (0x5a << 24) -+ -+#define CM_SMI_CTL 0x00 -+#define CM_SMI_DIV 0x04 -+ -+#define CM_SMI_CTL_FLIP (1 << 8) -+#define CM_SMI_CTL_BUSY (1 << 7) -+#define CM_SMI_CTL_KILL (1 << 5) -+#define CM_SMI_CTL_ENAB (1 << 4) -+#define CM_SMI_CTL_SRC_MASK (0xf) -+#define CM_SMI_CTL_SRC_OFFS (0) -+ -+#define CM_SMI_DIV_DIVI_MASK (0xf << 12) -+#define CM_SMI_DIV_DIVI_OFFS (12) -+#define CM_SMI_DIV_DIVF_MASK (0xff << 4) -+#define CM_SMI_DIV_DIVF_OFFS (4) -+ -+/* SMI register mapping:*/ -+#define SMI_BASE_ADDRESS ((BCM2708_PERI_BASE) + 0x600000) -+ -+#define SMICS 0x00 /* control + status register */ -+#define SMIL 0x04 /* length/count (n external txfers) */ -+#define SMIA 0x08 /* address register */ -+#define SMID 0x0c /* data register */ -+#define SMIDSR0 0x10 /* device 0 read settings */ -+#define SMIDSW0 0x14 /* device 0 write settings */ -+#define SMIDSR1 0x18 /* device 1 read settings */ -+#define SMIDSW1 0x1c /* device 1 write settings */ -+#define SMIDSR2 0x20 /* device 2 read settings */ -+#define SMIDSW2 0x24 /* device 2 write settings */ -+#define SMIDSR3 0x28 /* device 3 read settings */ -+#define SMIDSW3 0x2c /* device 3 write settings */ -+#define SMIDC 0x30 /* DMA control registers */ -+#define SMIDCS 0x34 /* direct control/status register */ -+#define SMIDA 0x38 /* direct address register */ -+#define SMIDD 0x3c /* direct data registers */ -+#define SMIFD 0x40 /* FIFO debug register */ -+ -+ -+ -+/* Control and Status register bits: -+ * SMICS_RXF : RX fifo full: 1 when RX fifo is full -+ * SMICS_TXE : TX fifo empty: 1 when empty. -+ * SMICS_RXD : RX fifo contains data: 1 when there is data. -+ * SMICS_TXD : TX fifo can accept data: 1 when true. -+ * SMICS_RXR : RX fifo needs reading: 1 when fifo more than 3/4 full, or -+ * when "DONE" and fifo not emptied. -+ * SMICS_TXW : TX fifo needs writing: 1 when less than 1/4 full. -+ * SMICS_AFERR : AXI FIFO error: 1 when fifo read when empty or written -+ * when full. Write 1 to clear. -+ * SMICS_EDREQ : 1 when external DREQ received. -+ * SMICS_PXLDAT : Pixel data: write 1 to enable pixel transfer modes. -+ * SMICS_SETERR : 1 if there was an error writing to setup regs (e.g. -+ * tx was in progress). Write 1 to clear. -+ * SMICS_PVMODE : Set to 1 to enable pixel valve mode. -+ * SMICS_INTR : Set to 1 to enable interrupt on RX. -+ * SMICS_INTT : Set to 1 to enable interrupt on TX. -+ * SMICS_INTD : Set to 1 to enable interrupt on DONE condition. -+ * SMICS_TEEN : Tear effect mode enabled: Programmed transfers will wait -+ * for a TE trigger before writing. -+ * SMICS_PAD1 : Padding settings for external transfers. For writes: the -+ * number of bytes initially written to the TX fifo that -+ * SMICS_PAD0 : should be ignored. For reads: the number of bytes that will -+ * be read before the data, and should be dropped. -+ * SMICS_WRITE : Transfer direction: 1 = write to external device, 0 = read -+ * SMICS_CLEAR : Write 1 to clear the FIFOs. -+ * SMICS_START : Write 1 to start the programmed transfer. -+ * SMICS_ACTIVE : Reads as 1 when a programmed transfer is underway. -+ * SMICS_DONE : Reads as 1 when transfer finished. For RX, not set until -+ * FIFO emptied. -+ * SMICS_ENABLE : Set to 1 to enable the SMI peripheral, 0 to disable. -+ */ -+ -+#define SMICS_RXF (1 << 31) -+#define SMICS_TXE (1 << 30) -+#define SMICS_RXD (1 << 29) -+#define SMICS_TXD (1 << 28) -+#define SMICS_RXR (1 << 27) -+#define SMICS_TXW (1 << 26) -+#define SMICS_AFERR (1 << 25) -+#define SMICS_EDREQ (1 << 15) -+#define SMICS_PXLDAT (1 << 14) -+#define SMICS_SETERR (1 << 13) -+#define SMICS_PVMODE (1 << 12) -+#define SMICS_INTR (1 << 11) -+#define SMICS_INTT (1 << 10) -+#define SMICS_INTD (1 << 9) -+#define SMICS_TEEN (1 << 8) -+#define SMICS_PAD1 (1 << 7) -+#define SMICS_PAD0 (1 << 6) -+#define SMICS_WRITE (1 << 5) -+#define SMICS_CLEAR (1 << 4) -+#define SMICS_START (1 << 3) -+#define SMICS_ACTIVE (1 << 2) -+#define SMICS_DONE (1 << 1) -+#define SMICS_ENABLE (1 << 0) -+ -+/* Address register bits: */ -+ -+#define SMIA_DEVICE_MASK ((1 << 9) | (1 << 8)) -+#define SMIA_DEVICE_OFFS (8) -+#define SMIA_ADDR_MASK (0x3f) /* bits 5 -> 0 */ -+#define SMIA_ADDR_OFFS (0) -+ -+/* DMA control register bits: -+ * SMIDC_DMAEN : DMA enable: set 1: DMA requests will be issued. -+ * SMIDC_DMAP : DMA passthrough: when set to 0, top two data pins are used by -+ * SMI as usual. When set to 1, the top two pins are used for -+ * external DREQs: pin 16 read request, 17 write. -+ * SMIDC_PANIC* : Threshold at which DMA will panic during read/write. -+ * SMIDC_REQ* : Threshold at which DMA will generate a DREQ. -+ */ -+ -+#define SMIDC_DMAEN (1 << 28) -+#define SMIDC_DMAP (1 << 24) -+#define SMIDC_PANICR_MASK (0x3f << 18) -+#define SMIDC_PANICR_OFFS (18) -+#define SMIDC_PANICW_MASK (0x3f << 12) -+#define SMIDC_PANICW_OFFS (12) -+#define SMIDC_REQR_MASK (0x3f << 6) -+#define SMIDC_REQR_OFFS (6) -+#define SMIDC_REQW_MASK (0x3f) -+#define SMIDC_REQW_OFFS (0) -+ -+/* Device settings register bits: same for all 4 (or 3?) device register sets. -+ * Device read settings: -+ * SMIDSR_RWIDTH : Read transfer width. 00 = 8bit, 01 = 16bit, -+ * 10 = 18bit, 11 = 9bit. -+ * SMIDSR_RSETUP : Read setup time: number of core cycles between chip -+ * select/address and read strobe. Min 1, max 64. -+ * SMIDSR_MODE68 : 1 for System 68 mode (i.e. enable + direction pins, -+ * rather than OE + WE pin) -+ * SMIDSR_FSETUP : If set to 1, setup time only applies to first -+ * transfer after address change. -+ * SMIDSR_RHOLD : Number of core cycles between read strobe going -+ * inactive and CS/address going inactive. Min 1, max 64 -+ * SMIDSR_RPACEALL : When set to 1, this device's RPACE value will always -+ * be used for the next transaction, even if it is not -+ * to this device. -+ * SMIDSR_RPACE : Number of core cycles spent waiting between CS -+ * deassert and start of next transfer. Min 1, max 128 -+ * SMIDSR_RDREQ : 1 = use external DMA request on SD16 to pace reads -+ * from device. Must also set DMAP in SMICS. -+ * SMIDSR_RSTROBE : Number of cycles to assert the read strobe. -+ * min 1, max 128. -+ */ -+#define SMIDSR_RWIDTH_MASK ((1<<31)|(1<<30)) -+#define SMIDSR_RWIDTH_OFFS (30) -+#define SMIDSR_RSETUP_MASK (0x3f << 24) -+#define SMIDSR_RSETUP_OFFS (24) -+#define SMIDSR_MODE68 (1 << 23) -+#define SMIDSR_FSETUP (1 << 22) -+#define SMIDSR_RHOLD_MASK (0x3f << 16) -+#define SMIDSR_RHOLD_OFFS (16) -+#define SMIDSR_RPACEALL (1 << 15) -+#define SMIDSR_RPACE_MASK (0x7f << 8) -+#define SMIDSR_RPACE_OFFS (8) -+#define SMIDSR_RDREQ (1 << 7) -+#define SMIDSR_RSTROBE_MASK (0x7f) -+#define SMIDSR_RSTROBE_OFFS (0) -+ -+/* Device write settings: -+ * SMIDSW_WWIDTH : Write transfer width. 00 = 8bit, 01 = 16bit, -+ * 10= 18bit, 11 = 9bit. -+ * SMIDSW_WSETUP : Number of cycles between CS assert and write strobe. -+ * Min 1, max 64. -+ * SMIDSW_WFORMAT : Pixel format of input. 0 = 16bit RGB 565, -+ * 1 = 32bit RGBA 8888 -+ * SMIDSW_WSWAP : 1 = swap pixel data bits. (Use with SMICS_PXLDAT) -+ * SMIDSW_WHOLD : Time between WE deassert and CS deassert. 1 to 64 -+ * SMIDSW_WPACEALL : 1: this device's WPACE will be used for the next -+ * transfer, regardless of that transfer's device. -+ * SMIDSW_WPACE : Cycles between CS deassert and next CS assert. -+ * Min 1, max 128 -+ * SMIDSW_WDREQ : Use external DREQ on pin 17 to pace writes. DMAP must -+ * be set in SMICS. -+ * SMIDSW_WSTROBE : Number of cycles to assert the write strobe. -+ * Min 1, max 128 -+ */ -+#define SMIDSW_WWIDTH_MASK ((1<<31)|(1<<30)) -+#define SMIDSW_WWIDTH_OFFS (30) -+#define SMIDSW_WSETUP_MASK (0x3f << 24) -+#define SMIDSW_WSETUP_OFFS (24) -+#define SMIDSW_WFORMAT (1 << 23) -+#define SMIDSW_WSWAP (1 << 22) -+#define SMIDSW_WHOLD_MASK (0x3f << 16) -+#define SMIDSW_WHOLD_OFFS (16) -+#define SMIDSW_WPACEALL (1 << 15) -+#define SMIDSW_WPACE_MASK (0x7f << 8) -+#define SMIDSW_WPACE_OFFS (8) -+#define SMIDSW_WDREQ (1 << 7) -+#define SMIDSW_WSTROBE_MASK (0x7f) -+#define SMIDSW_WSTROBE_OFFS (0) -+ -+/* Direct transfer control + status register -+ * SMIDCS_WRITE : Direction of transfer: 1 -> write, 0 -> read -+ * SMIDCS_DONE : 1 when a transfer has finished. Write 1 to clear. -+ * SMIDCS_START : Write 1 to start a transfer, if one is not already underway. -+ * SMIDCE_ENABLE: Write 1 to enable SMI in direct mode. -+ */ -+ -+#define SMIDCS_WRITE (1 << 3) -+#define SMIDCS_DONE (1 << 2) -+#define SMIDCS_START (1 << 1) -+#define SMIDCS_ENABLE (1 << 0) -+ -+/* Direct transfer address register -+ * SMIDA_DEVICE : Indicates which of the device settings banks should be used. -+ * SMIDA_ADDR : The value to be asserted on the address pins. -+ */ -+ -+#define SMIDA_DEVICE_MASK ((1<<9)|(1<<8)) -+#define SMIDA_DEVICE_OFFS (8) -+#define SMIDA_ADDR_MASK (0x3f) -+#define SMIDA_ADDR_OFFS (0) -+ -+/* FIFO debug register -+ * SMIFD_FLVL : The high-tide mark of FIFO count during the most recent txfer -+ * SMIFD_FCNT : The current FIFO count. -+ */ -+#define SMIFD_FLVL_MASK (0x3f << 8) -+#define SMIFD_FLVL_OFFS (8) -+#define SMIFD_FCNT_MASK (0x3f) -+#define SMIFD_FCNT_OFFS (0) -+ -+#endif /* BCM2835_SMI_IMPLEMENTATION */ -+ -+#endif /* BCM2835_SMI_H */ diff --git a/target/linux/brcm2708/patches-4.4/0042-Add-SMI-NAND-driver.patch b/target/linux/brcm2708/patches-4.4/0042-Add-SMI-NAND-driver.patch deleted file mode 100644 index edf2c93e05..0000000000 --- a/target/linux/brcm2708/patches-4.4/0042-Add-SMI-NAND-driver.patch +++ /dev/null @@ -1,358 +0,0 @@ -From f4d44b2c447c401791645627e88d0ef9830c1239 Mon Sep 17 00:00:00 2001 -From: Luke Wren -Date: Sat, 5 Sep 2015 01:16:10 +0100 -Subject: [PATCH 042/170] Add SMI NAND driver - -Signed-off-by: Luke Wren ---- - .../bindings/mtd/brcm,bcm2835-smi-nand.txt | 42 ++++ - drivers/mtd/nand/Kconfig | 7 + - drivers/mtd/nand/Makefile | 1 + - drivers/mtd/nand/bcm2835_smi_nand.c | 268 +++++++++++++++++++++ - 4 files changed, 318 insertions(+) - create mode 100644 Documentation/devicetree/bindings/mtd/brcm,bcm2835-smi-nand.txt - create mode 100644 drivers/mtd/nand/bcm2835_smi_nand.c - ---- /dev/null -+++ b/Documentation/devicetree/bindings/mtd/brcm,bcm2835-smi-nand.txt -@@ -0,0 +1,42 @@ -+* BCM2835 SMI NAND flash -+ -+This driver is a shim between the BCM2835 SMI driver (SMI is a peripheral for -+talking to parallel register interfaces) and Linux's MTD layer. -+ -+Required properties: -+- compatible: "brcm,bcm2835-smi-nand" -+- status: "okay" -+ -+Optional properties: -+- partition@n, where n is an integer from a consecutive sequence starting at 0 -+ - Difficult to store partition table on NAND device - normally put it -+ in the source code, kernel bootparams, or device tree (the best way!) -+ - Sub-properties: -+ - label: the partition name, as shown by mtdinfo /dev/mtd* -+ - reg: the size and offset of this partition. -+ - (optional) read-only: an empty property flagging as read only -+ -+Example: -+ -+nand: flash@0 { -+ compatible = "brcm,bcm2835-smi-nand"; -+ status = "okay"; -+ -+ partition@0 { -+ label = "stage2"; -+ // 128k -+ reg = <0 0x20000>; -+ read-only; -+ }; -+ partition@1 { -+ label = "firmware"; -+ // 16M -+ reg = <0x20000 0x1000000>; -+ read-only; -+ }; -+ partition@2 { -+ label = "root"; -+ // 2G -+ reg = <0x1020000 0x80000000>; -+ }; -+}; -\ No newline at end of file ---- a/drivers/mtd/nand/Kconfig -+++ b/drivers/mtd/nand/Kconfig -@@ -41,6 +41,13 @@ config MTD_SM_COMMON - tristate - default n - -+config MTD_NAND_BCM2835_SMI -+ tristate "Use Broadcom's Secondary Memory Interface as a NAND controller (BCM283x)" -+ depends on (MACH_BCM2708 || MACH_BCM2709 || ARCH_BCM2835) && BCM2835_SMI && MTD_NAND -+ default m -+ help -+ Uses the BCM2835's SMI peripheral as a NAND controller. -+ - config MTD_NAND_DENALI - tristate - ---- a/drivers/mtd/nand/Makefile -+++ b/drivers/mtd/nand/Makefile -@@ -14,6 +14,7 @@ obj-$(CONFIG_MTD_NAND_DENALI) += denali - obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o - obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o - obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o -+obj-$(CONFIG_MTD_NAND_BCM2835_SMI) += bcm2835_smi_nand.o - obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o - obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o - obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o ---- /dev/null -+++ b/drivers/mtd/nand/bcm2835_smi_nand.c -@@ -0,0 +1,268 @@ -+/** -+ * NAND flash driver for Broadcom Secondary Memory Interface -+ * -+ * Written by Luke Wren -+ * Copyright (c) 2015, 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 -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#define DEVICE_NAME "bcm2835-smi-nand" -+#define DRIVER_NAME "smi-nand-bcm2835" -+ -+struct bcm2835_smi_nand_host { -+ struct bcm2835_smi_instance *smi_inst; -+ struct nand_chip nand_chip; -+ struct mtd_info mtd; -+ struct device *dev; -+}; -+ -+/**************************************************************************** -+* -+* NAND functionality implementation -+* -+****************************************************************************/ -+ -+#define SMI_NAND_CLE_PIN 0x01 -+#define SMI_NAND_ALE_PIN 0x02 -+ -+static inline void bcm2835_smi_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, -+ unsigned int ctrl) -+{ -+ uint32_t cmd32 = cmd; -+ uint32_t addr = ~(SMI_NAND_CLE_PIN | SMI_NAND_ALE_PIN); -+ struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent); -+ struct bcm2835_smi_instance *inst = host->smi_inst; -+ -+ if (ctrl & NAND_CLE) -+ addr |= SMI_NAND_CLE_PIN; -+ if (ctrl & NAND_ALE) -+ addr |= SMI_NAND_ALE_PIN; -+ /* Lower ALL the CS pins! */ -+ if (ctrl & NAND_NCE) -+ addr &= (SMI_NAND_CLE_PIN | SMI_NAND_ALE_PIN); -+ -+ bcm2835_smi_set_address(inst, addr); -+ -+ if (cmd != NAND_CMD_NONE) -+ bcm2835_smi_write_buf(inst, &cmd32, 1); -+} -+ -+static inline uint8_t bcm2835_smi_nand_read_byte(struct mtd_info *mtd) -+{ -+ uint8_t byte; -+ struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent); -+ struct bcm2835_smi_instance *inst = host->smi_inst; -+ -+ bcm2835_smi_read_buf(inst, &byte, 1); -+ return byte; -+} -+ -+static inline void bcm2835_smi_nand_write_byte(struct mtd_info *mtd, -+ uint8_t byte) -+{ -+ struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent); -+ struct bcm2835_smi_instance *inst = host->smi_inst; -+ -+ bcm2835_smi_write_buf(inst, &byte, 1); -+} -+ -+static inline void bcm2835_smi_nand_write_buf(struct mtd_info *mtd, -+ const uint8_t *buf, int len) -+{ -+ struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent); -+ struct bcm2835_smi_instance *inst = host->smi_inst; -+ -+ bcm2835_smi_write_buf(inst, buf, len); -+} -+ -+static inline void bcm2835_smi_nand_read_buf(struct mtd_info *mtd, -+ uint8_t *buf, int len) -+{ -+ struct bcm2835_smi_nand_host *host = dev_get_drvdata(mtd->dev.parent); -+ struct bcm2835_smi_instance *inst = host->smi_inst; -+ -+ bcm2835_smi_read_buf(inst, buf, len); -+} -+ -+/**************************************************************************** -+* -+* Probe and remove functions -+* -+***************************************************************************/ -+ -+static int bcm2835_smi_nand_probe(struct platform_device *pdev) -+{ -+ struct bcm2835_smi_nand_host *host; -+ struct nand_chip *this; -+ struct mtd_info *mtd; -+ struct device *dev = &pdev->dev; -+ struct device_node *node = dev->of_node, *smi_node; -+ struct mtd_part_parser_data ppdata; -+ struct smi_settings *smi_settings; -+ struct bcm2835_smi_instance *smi_inst; -+ int ret = -ENXIO; -+ -+ if (!node) { -+ dev_err(dev, "No device tree node supplied!"); -+ return -EINVAL; -+ } -+ -+ smi_node = of_parse_phandle(node, "smi_handle", 0); -+ -+ /* Request use of SMI peripheral: */ -+ smi_inst = bcm2835_smi_get(smi_node); -+ -+ if (!smi_inst) { -+ dev_err(dev, "Could not register with SMI."); -+ return -EPROBE_DEFER; -+ } -+ -+ /* Set SMI timing and bus width */ -+ -+ smi_settings = bcm2835_smi_get_settings_from_regs(smi_inst); -+ -+ smi_settings->data_width = SMI_WIDTH_8BIT; -+ smi_settings->read_setup_time = 2; -+ smi_settings->read_hold_time = 1; -+ smi_settings->read_pace_time = 1; -+ smi_settings->read_strobe_time = 3; -+ -+ smi_settings->write_setup_time = 2; -+ smi_settings->write_hold_time = 1; -+ smi_settings->write_pace_time = 1; -+ smi_settings->write_strobe_time = 3; -+ -+ bcm2835_smi_set_regs_from_settings(smi_inst); -+ -+ host = devm_kzalloc(dev, sizeof(struct bcm2835_smi_nand_host), -+ GFP_KERNEL); -+ if (!host) -+ return -ENOMEM; -+ -+ host->dev = dev; -+ host->smi_inst = smi_inst; -+ -+ platform_set_drvdata(pdev, host); -+ -+ /* Link the structures together */ -+ -+ this = &host->nand_chip; -+ mtd = &host->mtd; -+ mtd->priv = this; -+ mtd->owner = THIS_MODULE; -+ mtd->dev.parent = dev; -+ mtd->name = DRIVER_NAME; -+ ppdata.of_node = node; -+ -+ /* 20 us command delay time... */ -+ this->chip_delay = 20; -+ -+ this->priv = host; -+ this->cmd_ctrl = bcm2835_smi_nand_cmd_ctrl; -+ this->read_byte = bcm2835_smi_nand_read_byte; -+ this->write_byte = bcm2835_smi_nand_write_byte; -+ this->write_buf = bcm2835_smi_nand_write_buf; -+ this->read_buf = bcm2835_smi_nand_read_buf; -+ -+ this->ecc.mode = NAND_ECC_SOFT; -+ -+ /* Should never be accessed directly: */ -+ -+ this->IO_ADDR_R = (void *)0xdeadbeef; -+ this->IO_ADDR_W = (void *)0xdeadbeef; -+ -+ /* First scan to find the device and get the page size */ -+ -+ if (nand_scan_ident(mtd, 1, NULL)) -+ return -ENXIO; -+ -+ /* Second phase scan */ -+ -+ if (nand_scan_tail(mtd)) -+ return -ENXIO; -+ -+ ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); -+ if (!ret) -+ return 0; -+ -+ nand_release(mtd); -+ return -EINVAL; -+} -+ -+static int bcm2835_smi_nand_remove(struct platform_device *pdev) -+{ -+ struct bcm2835_smi_nand_host *host = platform_get_drvdata(pdev); -+ -+ nand_release(&host->mtd); -+ -+ return 0; -+} -+ -+/**************************************************************************** -+* -+* Register the driver with device tree -+* -+***************************************************************************/ -+ -+static const struct of_device_id bcm2835_smi_nand_of_match[] = { -+ {.compatible = "brcm,bcm2835-smi-nand",}, -+ { /* sentinel */ } -+}; -+ -+MODULE_DEVICE_TABLE(of, bcm2835_smi_nand_of_match); -+ -+static struct platform_driver bcm2835_smi_nand_driver = { -+ .probe = bcm2835_smi_nand_probe, -+ .remove = bcm2835_smi_nand_remove, -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ .of_match_table = bcm2835_smi_nand_of_match, -+ }, -+}; -+ -+module_platform_driver(bcm2835_smi_nand_driver); -+ -+MODULE_ALIAS("platform:smi-nand-bcm2835"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION -+ ("Driver for NAND chips using Broadcom Secondary Memory Interface"); -+MODULE_AUTHOR("Luke Wren "); diff --git a/target/linux/brcm2708/patches-4.4/0042-lirc-added-support-for-RaspberryPi-GPIO.patch b/target/linux/brcm2708/patches-4.4/0042-lirc-added-support-for-RaspberryPi-GPIO.patch new file mode 100644 index 0000000000..59f8832448 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0042-lirc-added-support-for-RaspberryPi-GPIO.patch @@ -0,0 +1,841 @@ +From 0bc00c67aea43098c7b066e7b123379b260cc7de Mon Sep 17 00:00:00 2001 +From: Aron Szabo +Date: Sat, 16 Jun 2012 12:15:55 +0200 +Subject: [PATCH 042/232] 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 + +lirc-rpi: Add device tree support, and a suitable overlay + +The overlay supports DT parameters that match the old module +parameters, except that gpio_in_pull should be set using the +strings "up", "down" or "off". + +lirc-rpi: Also support pinctrl-bcm2835 in non-DT mode +--- + drivers/staging/media/lirc/Kconfig | 6 + + drivers/staging/media/lirc/Makefile | 1 + + drivers/staging/media/lirc/lirc_rpi.c | 730 ++++++++++++++++++++++++++++++++++ + include/linux/platform_data/bcm2708.h | 23 ++ + 4 files changed, 760 insertions(+) + create mode 100644 drivers/staging/media/lirc/lirc_rpi.c + create mode 100644 include/linux/platform_data/bcm2708.h + +--- 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 +--- 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 +--- /dev/null ++++ b/drivers/staging/media/lirc/lirc_rpi.c +@@ -0,0 +1,730 @@ ++/* ++ * 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 inline int read_bool_property(const struct device_node *np, ++ const char *propname, ++ bool *out_value) ++{ ++ u32 value = 0; ++ int err = of_property_read_u32(np, propname, &value); ++ if (err == 0) ++ *out_value = (value != 0); ++ return err; ++} ++ ++static void read_pin_settings(struct device_node *node) ++{ ++ u32 pin; ++ int index; ++ ++ for (index = 0; ++ of_property_read_u32_index( ++ node, ++ "brcm,pins", ++ index, ++ &pin) == 0; ++ index++) { ++ u32 function; ++ int err; ++ err = of_property_read_u32_index( ++ node, ++ "brcm,function", ++ index, ++ &function); ++ if (err == 0) { ++ if (function == 1) /* Output */ ++ gpio_out_pin = pin; ++ else if (function == 0) /* Input */ ++ gpio_in_pin = pin; ++ } ++ } ++} ++ ++static int init_port(void) ++{ ++ int i, nlow, nhigh; ++ struct device_node *node; ++ ++ node = lirc_rpi_dev->dev.of_node; ++ ++ gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip); ++ ++ /* ++ * Because of the lack of a setpull function, only support ++ * pinctrl-bcm2835 if using device tree. ++ */ ++ if (!gpiochip && node) ++ gpiochip = gpiochip_find("pinctrl-bcm2835", is_right_chip); ++ ++ if (!gpiochip) { ++ pr_err(LIRC_DRIVER_NAME ": gpio chip not found!\n"); ++ return -ENODEV; ++ } ++ ++ if (node) { ++ struct device_node *pins_node; ++ ++ pins_node = of_parse_phandle(node, "pinctrl-0", 0); ++ if (!pins_node) { ++ printk(KERN_ERR LIRC_DRIVER_NAME ++ ": pinctrl settings not found!\n"); ++ return -EINVAL; ++ } ++ ++ read_pin_settings(pins_node); ++ ++ of_property_read_u32(node, "rpi,sense", &sense); ++ ++ read_bool_property(node, "rpi,softcarrier", &softcarrier); ++ ++ read_bool_property(node, "rpi,invert", &invert); ++ ++ read_bool_property(node, "rpi,debug", &debug); ++ ++ } else { ++ return -EINVAL; ++ } ++ ++ 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; ++} ++ ++// 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 const struct of_device_id lirc_rpi_of_match[] = { ++ { .compatible = "rpi,lirc-rpi", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, lirc_rpi_of_match); ++ ++static struct platform_driver lirc_rpi_driver = { ++ .driver = { ++ .name = LIRC_DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(lirc_rpi_of_match), ++ }, ++}; ++ ++static int __init lirc_rpi_init(void) ++{ ++ struct device_node *node; ++ 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; ++ } ++ ++ node = of_find_compatible_node(NULL, NULL, ++ lirc_rpi_of_match[0].compatible); ++ ++ if (node) { ++ /* DT-enabled */ ++ lirc_rpi_dev = of_find_device_by_node(node); ++ WARN_ON(lirc_rpi_dev->dev.of_node != node); ++ of_node_put(node); ++ } ++ else { ++ 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; ++ ++ 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"); +--- /dev/null ++++ b/include/linux/platform_data/bcm2708.h +@@ -0,0 +1,23 @@ ++/* ++ * include/linux/platform_data/bcm2708.h ++ * ++ * 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. ++ * ++ * (C) 2014 Julian Scheel ++ * ++ */ ++#ifndef __BCM2708_H_ ++#define __BCM2708_H_ ++ ++typedef enum { ++ BCM2708_PULL_OFF, ++ BCM2708_PULL_UP, ++ BCM2708_PULL_DOWN ++} bcm2708_gpio_pull_t; ++ ++extern int bcm2708_gpio_setpull(struct gpio_chip *gc, unsigned offset, ++ bcm2708_gpio_pull_t value); ++ ++#endif diff --git a/target/linux/brcm2708/patches-4.4/0043-Add-cpufreq-driver.patch b/target/linux/brcm2708/patches-4.4/0043-Add-cpufreq-driver.patch new file mode 100644 index 0000000000..26915a10c3 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0043-Add-cpufreq-driver.patch @@ -0,0 +1,257 @@ +From 007ef35b2cf5d8a0197e81e712b38fb72cebae2e Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 3 Jul 2013 00:49:20 +0100 +Subject: [PATCH 043/232] Add cpufreq driver + +Signed-off-by: popcornmix +--- + drivers/cpufreq/Kconfig.arm | 9 ++ + drivers/cpufreq/Makefile | 1 + + drivers/cpufreq/bcm2835-cpufreq.c | 213 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 223 insertions(+) + create mode 100644 drivers/cpufreq/bcm2835-cpufreq.c + +--- a/drivers/cpufreq/Kconfig.arm ++++ b/drivers/cpufreq/Kconfig.arm +@@ -217,6 +217,15 @@ config ARM_SPEAR_CPUFREQ + help + This adds the CPUFreq driver support for SPEAr SOCs. + ++config ARM_BCM2835_CPUFREQ ++ depends on RASPBERRYPI_FIRMWARE ++ bool "BCM2835 Driver" ++ default y ++ help ++ This adds the CPUFreq driver for BCM2835 ++ ++ If in doubt, say N. ++ + config ARM_TEGRA20_CPUFREQ + bool "Tegra20 CPUFreq support" + depends on ARCH_TEGRA +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -73,6 +73,7 @@ obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa11 + obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o + obj-$(CONFIG_ARM_SCPI_CPUFREQ) += scpi-cpufreq.o + obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o ++obj-$(CONFIG_ARM_BCM2835_CPUFREQ) += bcm2835-cpufreq.o + obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o + obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o + obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o +--- /dev/null ++++ b/drivers/cpufreq/bcm2835-cpufreq.c +@@ -0,0 +1,213 @@ ++/***************************************************************************** ++* 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__) ++ ++/* ---------- 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 int bcm2835_cpufreq_clock_property(u32 tag, u32 id, u32 *val) ++{ ++ struct rpi_firmware *fw = rpi_firmware_get(NULL); ++ struct { ++ u32 id; ++ u32 val; ++ } packet; ++ int ret; ++ ++ packet.id = id; ++ packet.val = *val; ++ ret = rpi_firmware_property(fw, tag, &packet, sizeof(packet)); ++ if (ret) ++ return ret; ++ ++ *val = packet.val; ++ ++ return 0; ++} ++ ++static uint32_t bcm2835_cpufreq_set_clock(int cur_rate, int arm_rate) ++{ ++ u32 rate = arm_rate * 1000; ++ int ret; ++ ++ ret = bcm2835_cpufreq_clock_property(RPI_FIRMWARE_SET_CLOCK_RATE, VCMSG_ID_ARM_CLOCK, &rate); ++ if (ret) { ++ print_err("Failed to set clock: %d (%d)\n", arm_rate, ret); ++ return 0; ++ } ++ ++ rate /= 1000; ++ print_debug("Setting new frequency = %d -> %d (actual %d)\n", cur_rate, arm_rate, rate); ++ ++ return rate; ++} ++ ++static uint32_t bcm2835_cpufreq_get_clock(int tag) ++{ ++ u32 rate; ++ int ret; ++ ++ ret = bcm2835_cpufreq_clock_property(tag, VCMSG_ID_ARM_CLOCK, &rate); ++ if (ret) { ++ print_err("Failed to get clock (%d)\n", ret); ++ return 0; ++ } ++ ++ rate /= 1000; ++ print_debug("%s frequency = %u\n", ++ tag == RPI_FIRMWARE_GET_CLOCK_RATE ? "Current": ++ tag == RPI_FIRMWARE_GET_MIN_CLOCK_RATE ? "Min": ++ tag == RPI_FIRMWARE_GET_MAX_CLOCK_RATE ? "Max": ++ "Unexpected", rate); ++ ++ return 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 */ ++ ++ if (!rpi_firmware_get(NULL)) { ++ print_err("Firmware is not available\n"); ++ return -ENODEV; ++ } ++ ++ /* now find out what the maximum and minimum frequencies are */ ++ bcm2835_freq_table[0].frequency = bcm2835_cpufreq_get_clock(RPI_FIRMWARE_GET_MIN_CLOCK_RATE); ++ bcm2835_freq_table[1].frequency = bcm2835_cpufreq_get_clock(RPI_FIRMWARE_GET_MAX_CLOCK_RATE); ++ ++ 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(RPI_FIRMWARE_GET_CLOCK_RATE); ++ print_debug("cpu%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); diff --git a/target/linux/brcm2708/patches-4.4/0043-lirc-added-support-for-RaspberryPi-GPIO.patch b/target/linux/brcm2708/patches-4.4/0043-lirc-added-support-for-RaspberryPi-GPIO.patch deleted file mode 100644 index a46d3af154..0000000000 --- a/target/linux/brcm2708/patches-4.4/0043-lirc-added-support-for-RaspberryPi-GPIO.patch +++ /dev/null @@ -1,841 +0,0 @@ -From 2faad8f6644fa53418c5a7b80da8d5400b72df3f Mon Sep 17 00:00:00 2001 -From: Aron Szabo -Date: Sat, 16 Jun 2012 12:15:55 +0200 -Subject: [PATCH 043/170] 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 - -lirc-rpi: Add device tree support, and a suitable overlay - -The overlay supports DT parameters that match the old module -parameters, except that gpio_in_pull should be set using the -strings "up", "down" or "off". - -lirc-rpi: Also support pinctrl-bcm2835 in non-DT mode ---- - drivers/staging/media/lirc/Kconfig | 6 + - drivers/staging/media/lirc/Makefile | 1 + - drivers/staging/media/lirc/lirc_rpi.c | 730 ++++++++++++++++++++++++++++++++++ - include/linux/platform_data/bcm2708.h | 23 ++ - 4 files changed, 760 insertions(+) - create mode 100644 drivers/staging/media/lirc/lirc_rpi.c - create mode 100644 include/linux/platform_data/bcm2708.h - ---- 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 ---- 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 ---- /dev/null -+++ b/drivers/staging/media/lirc/lirc_rpi.c -@@ -0,0 +1,730 @@ -+/* -+ * 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 inline int read_bool_property(const struct device_node *np, -+ const char *propname, -+ bool *out_value) -+{ -+ u32 value = 0; -+ int err = of_property_read_u32(np, propname, &value); -+ if (err == 0) -+ *out_value = (value != 0); -+ return err; -+} -+ -+static void read_pin_settings(struct device_node *node) -+{ -+ u32 pin; -+ int index; -+ -+ for (index = 0; -+ of_property_read_u32_index( -+ node, -+ "brcm,pins", -+ index, -+ &pin) == 0; -+ index++) { -+ u32 function; -+ int err; -+ err = of_property_read_u32_index( -+ node, -+ "brcm,function", -+ index, -+ &function); -+ if (err == 0) { -+ if (function == 1) /* Output */ -+ gpio_out_pin = pin; -+ else if (function == 0) /* Input */ -+ gpio_in_pin = pin; -+ } -+ } -+} -+ -+static int init_port(void) -+{ -+ int i, nlow, nhigh; -+ struct device_node *node; -+ -+ node = lirc_rpi_dev->dev.of_node; -+ -+ gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip); -+ -+ /* -+ * Because of the lack of a setpull function, only support -+ * pinctrl-bcm2835 if using device tree. -+ */ -+ if (!gpiochip && node) -+ gpiochip = gpiochip_find("pinctrl-bcm2835", is_right_chip); -+ -+ if (!gpiochip) { -+ pr_err(LIRC_DRIVER_NAME ": gpio chip not found!\n"); -+ return -ENODEV; -+ } -+ -+ if (node) { -+ struct device_node *pins_node; -+ -+ pins_node = of_parse_phandle(node, "pinctrl-0", 0); -+ if (!pins_node) { -+ printk(KERN_ERR LIRC_DRIVER_NAME -+ ": pinctrl settings not found!\n"); -+ return -EINVAL; -+ } -+ -+ read_pin_settings(pins_node); -+ -+ of_property_read_u32(node, "rpi,sense", &sense); -+ -+ read_bool_property(node, "rpi,softcarrier", &softcarrier); -+ -+ read_bool_property(node, "rpi,invert", &invert); -+ -+ read_bool_property(node, "rpi,debug", &debug); -+ -+ } else { -+ return -EINVAL; -+ } -+ -+ 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; -+} -+ -+// 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 const struct of_device_id lirc_rpi_of_match[] = { -+ { .compatible = "rpi,lirc-rpi", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, lirc_rpi_of_match); -+ -+static struct platform_driver lirc_rpi_driver = { -+ .driver = { -+ .name = LIRC_DRIVER_NAME, -+ .owner = THIS_MODULE, -+ .of_match_table = of_match_ptr(lirc_rpi_of_match), -+ }, -+}; -+ -+static int __init lirc_rpi_init(void) -+{ -+ struct device_node *node; -+ 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; -+ } -+ -+ node = of_find_compatible_node(NULL, NULL, -+ lirc_rpi_of_match[0].compatible); -+ -+ if (node) { -+ /* DT-enabled */ -+ lirc_rpi_dev = of_find_device_by_node(node); -+ WARN_ON(lirc_rpi_dev->dev.of_node != node); -+ of_node_put(node); -+ } -+ else { -+ 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; -+ -+ 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"); ---- /dev/null -+++ b/include/linux/platform_data/bcm2708.h -@@ -0,0 +1,23 @@ -+/* -+ * include/linux/platform_data/bcm2708.h -+ * -+ * 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. -+ * -+ * (C) 2014 Julian Scheel -+ * -+ */ -+#ifndef __BCM2708_H_ -+#define __BCM2708_H_ -+ -+typedef enum { -+ BCM2708_PULL_OFF, -+ BCM2708_PULL_UP, -+ BCM2708_PULL_DOWN -+} bcm2708_gpio_pull_t; -+ -+extern int bcm2708_gpio_setpull(struct gpio_chip *gc, unsigned offset, -+ bcm2708_gpio_pull_t value); -+ -+#endif diff --git a/target/linux/brcm2708/patches-4.4/0044-Add-cpufreq-driver.patch b/target/linux/brcm2708/patches-4.4/0044-Add-cpufreq-driver.patch deleted file mode 100644 index f1ec5da74f..0000000000 --- a/target/linux/brcm2708/patches-4.4/0044-Add-cpufreq-driver.patch +++ /dev/null @@ -1,257 +0,0 @@ -From 3c7ea37319a4974cbf6b89a84c109a53f4d6b408 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 3 Jul 2013 00:49:20 +0100 -Subject: [PATCH 044/170] Add cpufreq driver - -Signed-off-by: popcornmix ---- - drivers/cpufreq/Kconfig.arm | 9 ++ - drivers/cpufreq/Makefile | 1 + - drivers/cpufreq/bcm2835-cpufreq.c | 213 ++++++++++++++++++++++++++++++++++++++ - 3 files changed, 223 insertions(+) - create mode 100644 drivers/cpufreq/bcm2835-cpufreq.c - ---- a/drivers/cpufreq/Kconfig.arm -+++ b/drivers/cpufreq/Kconfig.arm -@@ -217,6 +217,15 @@ config ARM_SPEAR_CPUFREQ - help - This adds the CPUFreq driver support for SPEAr SOCs. - -+config ARM_BCM2835_CPUFREQ -+ depends on RASPBERRYPI_FIRMWARE -+ bool "BCM2835 Driver" -+ default y -+ help -+ This adds the CPUFreq driver for BCM2835 -+ -+ If in doubt, say N. -+ - config ARM_TEGRA20_CPUFREQ - bool "Tegra20 CPUFreq support" - depends on ARCH_TEGRA ---- a/drivers/cpufreq/Makefile -+++ b/drivers/cpufreq/Makefile -@@ -73,6 +73,7 @@ obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa11 - obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o - obj-$(CONFIG_ARM_SCPI_CPUFREQ) += scpi-cpufreq.o - obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o -+obj-$(CONFIG_ARM_BCM2835_CPUFREQ) += bcm2835-cpufreq.o - obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o - obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o - obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o ---- /dev/null -+++ b/drivers/cpufreq/bcm2835-cpufreq.c -@@ -0,0 +1,213 @@ -+/***************************************************************************** -+* 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__) -+ -+/* ---------- 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 int bcm2835_cpufreq_clock_property(u32 tag, u32 id, u32 *val) -+{ -+ struct rpi_firmware *fw = rpi_firmware_get(NULL); -+ struct { -+ u32 id; -+ u32 val; -+ } packet; -+ int ret; -+ -+ packet.id = id; -+ packet.val = *val; -+ ret = rpi_firmware_property(fw, tag, &packet, sizeof(packet)); -+ if (ret) -+ return ret; -+ -+ *val = packet.val; -+ -+ return 0; -+} -+ -+static uint32_t bcm2835_cpufreq_set_clock(int cur_rate, int arm_rate) -+{ -+ u32 rate = arm_rate * 1000; -+ int ret; -+ -+ ret = bcm2835_cpufreq_clock_property(RPI_FIRMWARE_SET_CLOCK_RATE, VCMSG_ID_ARM_CLOCK, &rate); -+ if (ret) { -+ print_err("Failed to set clock: %d (%d)\n", arm_rate, ret); -+ return 0; -+ } -+ -+ rate /= 1000; -+ print_debug("Setting new frequency = %d -> %d (actual %d)\n", cur_rate, arm_rate, rate); -+ -+ return rate; -+} -+ -+static uint32_t bcm2835_cpufreq_get_clock(int tag) -+{ -+ u32 rate; -+ int ret; -+ -+ ret = bcm2835_cpufreq_clock_property(tag, VCMSG_ID_ARM_CLOCK, &rate); -+ if (ret) { -+ print_err("Failed to get clock (%d)\n", ret); -+ return 0; -+ } -+ -+ rate /= 1000; -+ print_debug("%s frequency = %u\n", -+ tag == RPI_FIRMWARE_GET_CLOCK_RATE ? "Current": -+ tag == RPI_FIRMWARE_GET_MIN_CLOCK_RATE ? "Min": -+ tag == RPI_FIRMWARE_GET_MAX_CLOCK_RATE ? "Max": -+ "Unexpected", rate); -+ -+ return 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 */ -+ -+ if (!rpi_firmware_get(NULL)) { -+ print_err("Firmware is not available\n"); -+ return -ENODEV; -+ } -+ -+ /* now find out what the maximum and minimum frequencies are */ -+ bcm2835_freq_table[0].frequency = bcm2835_cpufreq_get_clock(RPI_FIRMWARE_GET_MIN_CLOCK_RATE); -+ bcm2835_freq_table[1].frequency = bcm2835_cpufreq_get_clock(RPI_FIRMWARE_GET_MAX_CLOCK_RATE); -+ -+ 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(RPI_FIRMWARE_GET_CLOCK_RATE); -+ print_debug("cpu%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); diff --git a/target/linux/brcm2708/patches-4.4/0044-Added-hwmon-thermal-driver-for-reporting-core-temper.patch b/target/linux/brcm2708/patches-4.4/0044-Added-hwmon-thermal-driver-for-reporting-core-temper.patch new file mode 100644 index 0000000000..0834763e24 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0044-Added-hwmon-thermal-driver-for-reporting-core-temper.patch @@ -0,0 +1,193 @@ +From 9f251fdbaaf3bd895c2901413748679d68a0c644 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Tue, 26 Mar 2013 19:24:24 +0000 +Subject: [PATCH 044/232] Added hwmon/thermal driver for reporting core + temperature. Thanks Dorian +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +BCM270x: Move thermal sensor to Device Tree + +Add Device Tree support to bcm2835-thermal driver. +Add thermal sensor device to Device Tree. +Don't add platform device when booting in DT mode. + +Signed-off-by: Noralf Trønnes +--- + drivers/thermal/Kconfig | 7 ++ + drivers/thermal/Makefile | 1 + + drivers/thermal/bcm2835-thermal.c | 141 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 149 insertions(+) + create mode 100644 drivers/thermal/bcm2835-thermal.c + +--- a/drivers/thermal/Kconfig ++++ b/drivers/thermal/Kconfig +@@ -285,6 +285,13 @@ 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 ++ depends on RASPBERRYPI_FIRMWARE ++ 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 +--- a/drivers/thermal/Makefile ++++ b/drivers/thermal/Makefile +@@ -38,6 +38,7 @@ obj-$(CONFIG_ARMADA_THERMAL) += armada_t + 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_IOSF_CORE) += intel_soc_dts_iosf.o + obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o +--- /dev/null ++++ b/drivers/thermal/bcm2835-thermal.c +@@ -0,0 +1,141 @@ ++/***************************************************************************** ++* 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 ++ ++static int bcm2835_thermal_get_property(struct thermal_zone_device *tz, ++ int *temp, u32 tag) ++{ ++ struct rpi_firmware *fw = tz->devdata; ++ struct { ++ u32 id; ++ u32 val; ++ } packet; ++ int ret; ++ ++ *temp = 0; ++ packet.id = 0; ++ ret = rpi_firmware_property(fw, tag, &packet, sizeof(packet)); ++ if (ret) { ++ dev_err(&tz->device, "Failed to get temperature\n"); ++ return ret; ++ } ++ ++ *temp = packet.val; ++ dev_dbg(&tz->device, "%stemp=%d\n", ++ tag == RPI_FIRMWARE_GET_MAX_TEMPERATURE ? "max" : "", *temp); ++ ++ return 0; ++} ++ ++static int bcm2835_thermal_get_temp(struct thermal_zone_device *tz, ++ int *temp) ++{ ++ return bcm2835_thermal_get_property(tz, temp, ++ RPI_FIRMWARE_GET_TEMPERATURE); ++} ++ ++static int bcm2835_thermal_get_max_temp(struct thermal_zone_device *tz, ++ int trip, int *temp) ++{ ++ /* ++ * The maximum safe temperature of the SoC. ++ * Overclock may be disabled above this temperature. ++ */ ++ return bcm2835_thermal_get_property(tz, temp, ++ RPI_FIRMWARE_GET_MAX_TEMPERATURE); ++} ++ ++static int bcm2835_thermal_get_trip_type(struct thermal_zone_device *tz, ++ int trip, enum thermal_trip_type *type) ++{ ++ *type = THERMAL_TRIP_HOT; ++ ++ return 0; ++} ++ ++static int bcm2835_thermal_get_mode(struct thermal_zone_device *tz, ++ enum thermal_device_mode *mode) ++{ ++ *mode = THERMAL_DEVICE_ENABLED; ++ ++ return 0; ++} ++ ++static struct thermal_zone_device_ops ops = { ++ .get_temp = bcm2835_thermal_get_temp, ++ .get_trip_temp = bcm2835_thermal_get_max_temp, ++ .get_trip_type = bcm2835_thermal_get_trip_type, ++ .get_mode = bcm2835_thermal_get_mode, ++}; ++ ++static int bcm2835_thermal_probe(struct platform_device *pdev) ++{ ++ struct device_node *fw_np; ++ struct rpi_firmware *fw; ++ struct thermal_zone_device *tz; ++ ++ fw_np = of_parse_phandle(pdev->dev.of_node, "firmware", 0); ++/* Remove comment when booting without Device Tree is no longer supported ++ if (!fw_np) { ++ dev_err(&pdev->dev, "Missing firmware node\n"); ++ return -ENOENT; ++ } ++*/ ++ fw = rpi_firmware_get(fw_np); ++ if (!fw) ++ return -EPROBE_DEFER; ++ ++ tz = thermal_zone_device_register("bcm2835_thermal", 1, 0, fw, &ops, ++ NULL, 0, 0); ++ if (IS_ERR(tz)) { ++ dev_err(&pdev->dev, "Failed to register the thermal device\n"); ++ return PTR_ERR(tz); ++ } ++ ++ platform_set_drvdata(pdev, tz); ++ ++ return 0; ++} ++ ++static int bcm2835_thermal_remove(struct platform_device *pdev) ++{ ++ thermal_zone_device_unregister(platform_get_drvdata(pdev)); ++ ++ return 0; ++} ++ ++static const struct of_device_id bcm2835_thermal_of_match_table[] = { ++ { .compatible = "brcm,bcm2835-thermal", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, bcm2835_thermal_of_match_table); ++ ++static struct platform_driver bcm2835_thermal_driver = { ++ .probe = bcm2835_thermal_probe, ++ .remove = bcm2835_thermal_remove, ++ .driver = { ++ .name = "bcm2835_thermal", ++ .of_match_table = bcm2835_thermal_of_match_table, ++ }, ++}; ++module_platform_driver(bcm2835_thermal_driver); ++ ++MODULE_AUTHOR("Dorian Peake"); ++MODULE_AUTHOR("Noralf Trønnes"); ++MODULE_DESCRIPTION("Thermal driver for bcm2835 chip"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/brcm2708/patches-4.4/0045-Add-Chris-Boot-s-i2c-driver.patch b/target/linux/brcm2708/patches-4.4/0045-Add-Chris-Boot-s-i2c-driver.patch new file mode 100644 index 0000000000..6e5ae8bb27 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0045-Add-Chris-Boot-s-i2c-driver.patch @@ -0,0 +1,635 @@ +From 28a4399bf03eeb3ec5b7b56c9445b296e1427665 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 17 Jun 2015 15:44:08 +0100 +Subject: [PATCH 045/232] Add Chris Boot's i2c driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +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 + +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: Only register the I2C device for the current board revision + +i2c_bcm2708: Fix clock reference counting + +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: + /* poll for transfer start bit (should only take 1-20 polls) */ + This implies that the setup function can now fail so account for this everywhere it's called +- Removed the clk_get_rate call from inside the setup function as it locks a mutex and that's not ok since we call it from under a spin lock. + +i2c-bcm2708: When using DT, leave the GPIO setup to pinctrl + +i2c-bcm2708: Increase timeouts to allow larger transfers + +Use the timeout value provided by the I2C_TIMEOUT ioctl when waiting +for completion. The default timeout is 1 second. + +See: https://github.com/raspberrypi/linux/issues/260 + +i2c-bcm2708/BCM270X_DT: Add support for I2C2 + +The third I2C bus (I2C2) is normally reserved for HDMI use. Careless +use of this bus can break an attached display - use with caution. + +It is recommended to disable accesses by VideoCore by setting +hdmi_ignore_edid=1 or hdmi_edid_file=1 in config.txt. + +The interface is disabled by default - enable using the +i2c2_iknowwhatimdoing DT parameter. + +bcm2708-spi: Don't use static pin configuration with DT + +Also remove superfluous error checking - the SPI framework ensures the +validity of the chip_select value. + +i2c-bcm2708: Remove non-DT support + +Signed-off-by: Noralf Trønnes +--- + drivers/i2c/busses/Kconfig | 21 +- + drivers/i2c/busses/Makefile | 2 + + drivers/i2c/busses/i2c-bcm2708.c | 493 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 515 insertions(+), 1 deletion(-) + create mode 100644 drivers/i2c/busses/i2c-bcm2708.c + +--- 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 || MACH_BCM2709 || ARCH_BCM2835 ++ 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 +@@ -365,7 +384,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. +--- 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 + +--- /dev/null ++++ b/drivers/i2c/busses/i2c-bcm2708.c +@@ -0,0 +1,493 @@ ++/* ++ * 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 ++#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_WAIT_LOOP_COUNT 200 ++ ++#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; ++ u32 cdiv; ++ ++ struct completion done; ++ ++ struct i2c_msg *msg; ++ int pos; ++ int nmsgs; ++ bool error; ++}; ++ ++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 int bcm2708_bsc_setup(struct bcm2708_i2c *bi) ++{ ++ u32 cdiv, s; ++ u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1; ++ int wait_loops = I2C_WAIT_LOOP_COUNT; ++ ++ /* Can't call clk_get_rate as it locks a mutex and here we are spinlocked. ++ * Use the value that we cached in the probe. ++ */ ++ cdiv = bi->cdiv; ++ ++ 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)) && --wait_loops >= 0); ++ ++ /* did we time out or some error occured? */ ++ if (wait_loops < 0 || (s & (BSC_S_ERR | BSC_S_CLKT))) { ++ return -1; ++ } ++ ++ /* 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); ++ ++ return 0; ++} ++ ++static irqreturn_t bcm2708_i2c_interrupt(int irq, void *dev_id) ++{ ++ struct bcm2708_i2c *bi = dev_id; ++ bool handled = true; ++ u32 s; ++ int ret; ++ ++ 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; ++ ++ bi->msg = 0; /* to inform the that all work is done */ ++ bi->nmsgs = 0; ++ /* 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; ++ ret = bcm2708_bsc_setup(bi); ++ if (ret < 0) { ++ bcm2708_bsc_reset(bi); ++ bi->error = true; ++ bi->msg = 0; /* to inform the that all work is done */ ++ bi->nmsgs = 0; ++ /* wake up our bh */ ++ complete(&bi->done); ++ goto early_exit; ++ } ++ } else { ++ bi->msg = 0; /* to inform the that all work is done */ ++ bi->nmsgs = 0; ++ /* 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; ++ ++ ret = bcm2708_bsc_setup(bi); ++ ++ spin_unlock_irqrestore(&bi->lock, flags); ++ ++ /* check the result of the setup */ ++ if (ret < 0) ++ { ++ dev_err(&adap->dev, "transfer setup timed out\n"); ++ goto error_timeout; ++ } ++ ++ ret = wait_for_completion_timeout(&bi->done, adap->timeout); ++ if (ret == 0) { ++ dev_err(&adap->dev, "transfer timed out\n"); ++ goto error_timeout; ++ } ++ ++ ret = bi->error ? -EIO : num; ++ return ret; ++ ++error_timeout: ++ spin_lock_irqsave(&bi->lock, flags); ++ bcm2708_bsc_reset(bi); ++ bi->msg = 0; /* to inform the interrupt handler that there's nothing else to be done */ ++ bi->nmsgs = 0; ++ spin_unlock_irqrestore(&bi->lock, flags); ++ return -ETIMEDOUT; ++} ++ ++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; ++ ++ 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"); ++ 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); ++ } ++ ++ err = clk_prepare_enable(clk); ++ if (err) { ++ dev_err(&pdev->dev, "could not enable clk: %d\n", err); ++ goto out_clk_put; ++ } ++ ++ bi = kzalloc(sizeof(*bi), GFP_KERNEL); ++ if (!bi) ++ goto out_clk_disable; ++ ++ 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)); ++ adap->dev.of_node = pdev->dev.of_node; ++ ++ switch (pdev->id) { ++ case 0: ++ adap->class = I2C_CLASS_HWMON; ++ break; ++ case 1: ++ adap->class = I2C_CLASS_DDC; ++ break; ++ case 2: ++ adap->class = I2C_CLASS_DDC; ++ break; ++ default: ++ dev_err(&pdev->dev, "can only bind to BSC 0, 1 or 2\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; ++ } ++ bi->cdiv = 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_disable: ++ clk_disable_unprepare(clk); ++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_unprepare(bi->clk); ++ clk_put(bi->clk); ++ kfree(bi); ++ ++ 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, ++}; ++ ++// 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/target/linux/brcm2708/patches-4.4/0045-Added-hwmon-thermal-driver-for-reporting-core-temper.patch b/target/linux/brcm2708/patches-4.4/0045-Added-hwmon-thermal-driver-for-reporting-core-temper.patch deleted file mode 100644 index 06046a69f5..0000000000 --- a/target/linux/brcm2708/patches-4.4/0045-Added-hwmon-thermal-driver-for-reporting-core-temper.patch +++ /dev/null @@ -1,193 +0,0 @@ -From 776e87ffc8b137ea465e7b0076e56cd71d9c56d1 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 26 Mar 2013 19:24:24 +0000 -Subject: [PATCH 045/170] Added hwmon/thermal driver for reporting core - temperature. Thanks Dorian -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -BCM270x: Move thermal sensor to Device Tree - -Add Device Tree support to bcm2835-thermal driver. -Add thermal sensor device to Device Tree. -Don't add platform device when booting in DT mode. - -Signed-off-by: Noralf Trønnes ---- - drivers/thermal/Kconfig | 7 ++ - drivers/thermal/Makefile | 1 + - drivers/thermal/bcm2835-thermal.c | 141 ++++++++++++++++++++++++++++++++++++++ - 3 files changed, 149 insertions(+) - create mode 100644 drivers/thermal/bcm2835-thermal.c - ---- a/drivers/thermal/Kconfig -+++ b/drivers/thermal/Kconfig -@@ -285,6 +285,13 @@ 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 -+ depends on RASPBERRYPI_FIRMWARE -+ 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 ---- a/drivers/thermal/Makefile -+++ b/drivers/thermal/Makefile -@@ -38,6 +38,7 @@ obj-$(CONFIG_ARMADA_THERMAL) += armada_t - 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_IOSF_CORE) += intel_soc_dts_iosf.o - obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o ---- /dev/null -+++ b/drivers/thermal/bcm2835-thermal.c -@@ -0,0 +1,141 @@ -+/***************************************************************************** -+* 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 -+ -+static int bcm2835_thermal_get_property(struct thermal_zone_device *tz, -+ int *temp, u32 tag) -+{ -+ struct rpi_firmware *fw = tz->devdata; -+ struct { -+ u32 id; -+ u32 val; -+ } packet; -+ int ret; -+ -+ *temp = 0; -+ packet.id = 0; -+ ret = rpi_firmware_property(fw, tag, &packet, sizeof(packet)); -+ if (ret) { -+ dev_err(&tz->device, "Failed to get temperature\n"); -+ return ret; -+ } -+ -+ *temp = packet.val; -+ dev_dbg(&tz->device, "%stemp=%d\n", -+ tag == RPI_FIRMWARE_GET_MAX_TEMPERATURE ? "max" : "", *temp); -+ -+ return 0; -+} -+ -+static int bcm2835_thermal_get_temp(struct thermal_zone_device *tz, -+ int *temp) -+{ -+ return bcm2835_thermal_get_property(tz, temp, -+ RPI_FIRMWARE_GET_TEMPERATURE); -+} -+ -+static int bcm2835_thermal_get_max_temp(struct thermal_zone_device *tz, -+ int trip, int *temp) -+{ -+ /* -+ * The maximum safe temperature of the SoC. -+ * Overclock may be disabled above this temperature. -+ */ -+ return bcm2835_thermal_get_property(tz, temp, -+ RPI_FIRMWARE_GET_MAX_TEMPERATURE); -+} -+ -+static int bcm2835_thermal_get_trip_type(struct thermal_zone_device *tz, -+ int trip, enum thermal_trip_type *type) -+{ -+ *type = THERMAL_TRIP_HOT; -+ -+ return 0; -+} -+ -+static int bcm2835_thermal_get_mode(struct thermal_zone_device *tz, -+ enum thermal_device_mode *mode) -+{ -+ *mode = THERMAL_DEVICE_ENABLED; -+ -+ return 0; -+} -+ -+static struct thermal_zone_device_ops ops = { -+ .get_temp = bcm2835_thermal_get_temp, -+ .get_trip_temp = bcm2835_thermal_get_max_temp, -+ .get_trip_type = bcm2835_thermal_get_trip_type, -+ .get_mode = bcm2835_thermal_get_mode, -+}; -+ -+static int bcm2835_thermal_probe(struct platform_device *pdev) -+{ -+ struct device_node *fw_np; -+ struct rpi_firmware *fw; -+ struct thermal_zone_device *tz; -+ -+ fw_np = of_parse_phandle(pdev->dev.of_node, "firmware", 0); -+/* Remove comment when booting without Device Tree is no longer supported -+ if (!fw_np) { -+ dev_err(&pdev->dev, "Missing firmware node\n"); -+ return -ENOENT; -+ } -+*/ -+ fw = rpi_firmware_get(fw_np); -+ if (!fw) -+ return -EPROBE_DEFER; -+ -+ tz = thermal_zone_device_register("bcm2835_thermal", 1, 0, fw, &ops, -+ NULL, 0, 0); -+ if (IS_ERR(tz)) { -+ dev_err(&pdev->dev, "Failed to register the thermal device\n"); -+ return PTR_ERR(tz); -+ } -+ -+ platform_set_drvdata(pdev, tz); -+ -+ return 0; -+} -+ -+static int bcm2835_thermal_remove(struct platform_device *pdev) -+{ -+ thermal_zone_device_unregister(platform_get_drvdata(pdev)); -+ -+ return 0; -+} -+ -+static const struct of_device_id bcm2835_thermal_of_match_table[] = { -+ { .compatible = "brcm,bcm2835-thermal", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, bcm2835_thermal_of_match_table); -+ -+static struct platform_driver bcm2835_thermal_driver = { -+ .probe = bcm2835_thermal_probe, -+ .remove = bcm2835_thermal_remove, -+ .driver = { -+ .name = "bcm2835_thermal", -+ .of_match_table = bcm2835_thermal_of_match_table, -+ }, -+}; -+module_platform_driver(bcm2835_thermal_driver); -+ -+MODULE_AUTHOR("Dorian Peake"); -+MODULE_AUTHOR("Noralf Trønnes"); -+MODULE_DESCRIPTION("Thermal driver for bcm2835 chip"); -+MODULE_LICENSE("GPL"); diff --git a/target/linux/brcm2708/patches-4.4/0046-Add-Chris-Boot-s-i2c-driver.patch b/target/linux/brcm2708/patches-4.4/0046-Add-Chris-Boot-s-i2c-driver.patch deleted file mode 100644 index 6730ee6fd8..0000000000 --- a/target/linux/brcm2708/patches-4.4/0046-Add-Chris-Boot-s-i2c-driver.patch +++ /dev/null @@ -1,635 +0,0 @@ -From 72181304010d3aa74cd2b98db592554a541ce4c2 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 17 Jun 2015 15:44:08 +0100 -Subject: [PATCH 046/170] Add Chris Boot's i2c driver -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -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 - -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: Only register the I2C device for the current board revision - -i2c_bcm2708: Fix clock reference counting - -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: - /* poll for transfer start bit (should only take 1-20 polls) */ - This implies that the setup function can now fail so account for this everywhere it's called -- Removed the clk_get_rate call from inside the setup function as it locks a mutex and that's not ok since we call it from under a spin lock. - -i2c-bcm2708: When using DT, leave the GPIO setup to pinctrl - -i2c-bcm2708: Increase timeouts to allow larger transfers - -Use the timeout value provided by the I2C_TIMEOUT ioctl when waiting -for completion. The default timeout is 1 second. - -See: https://github.com/raspberrypi/linux/issues/260 - -i2c-bcm2708/BCM270X_DT: Add support for I2C2 - -The third I2C bus (I2C2) is normally reserved for HDMI use. Careless -use of this bus can break an attached display - use with caution. - -It is recommended to disable accesses by VideoCore by setting -hdmi_ignore_edid=1 or hdmi_edid_file=1 in config.txt. - -The interface is disabled by default - enable using the -i2c2_iknowwhatimdoing DT parameter. - -bcm2708-spi: Don't use static pin configuration with DT - -Also remove superfluous error checking - the SPI framework ensures the -validity of the chip_select value. - -i2c-bcm2708: Remove non-DT support - -Signed-off-by: Noralf Trønnes ---- - drivers/i2c/busses/Kconfig | 21 +- - drivers/i2c/busses/Makefile | 2 + - drivers/i2c/busses/i2c-bcm2708.c | 493 +++++++++++++++++++++++++++++++++++++++ - 3 files changed, 515 insertions(+), 1 deletion(-) - create mode 100644 drivers/i2c/busses/i2c-bcm2708.c - ---- 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 || MACH_BCM2709 || ARCH_BCM2835 -+ 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 -@@ -365,7 +384,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. ---- 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 - ---- /dev/null -+++ b/drivers/i2c/busses/i2c-bcm2708.c -@@ -0,0 +1,493 @@ -+/* -+ * 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 -+#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_WAIT_LOOP_COUNT 200 -+ -+#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; -+ u32 cdiv; -+ -+ struct completion done; -+ -+ struct i2c_msg *msg; -+ int pos; -+ int nmsgs; -+ bool error; -+}; -+ -+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 int bcm2708_bsc_setup(struct bcm2708_i2c *bi) -+{ -+ u32 cdiv, s; -+ u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1; -+ int wait_loops = I2C_WAIT_LOOP_COUNT; -+ -+ /* Can't call clk_get_rate as it locks a mutex and here we are spinlocked. -+ * Use the value that we cached in the probe. -+ */ -+ cdiv = bi->cdiv; -+ -+ 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)) && --wait_loops >= 0); -+ -+ /* did we time out or some error occured? */ -+ if (wait_loops < 0 || (s & (BSC_S_ERR | BSC_S_CLKT))) { -+ return -1; -+ } -+ -+ /* 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); -+ -+ return 0; -+} -+ -+static irqreturn_t bcm2708_i2c_interrupt(int irq, void *dev_id) -+{ -+ struct bcm2708_i2c *bi = dev_id; -+ bool handled = true; -+ u32 s; -+ int ret; -+ -+ 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; -+ -+ bi->msg = 0; /* to inform the that all work is done */ -+ bi->nmsgs = 0; -+ /* 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; -+ ret = bcm2708_bsc_setup(bi); -+ if (ret < 0) { -+ bcm2708_bsc_reset(bi); -+ bi->error = true; -+ bi->msg = 0; /* to inform the that all work is done */ -+ bi->nmsgs = 0; -+ /* wake up our bh */ -+ complete(&bi->done); -+ goto early_exit; -+ } -+ } else { -+ bi->msg = 0; /* to inform the that all work is done */ -+ bi->nmsgs = 0; -+ /* 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; -+ -+ ret = bcm2708_bsc_setup(bi); -+ -+ spin_unlock_irqrestore(&bi->lock, flags); -+ -+ /* check the result of the setup */ -+ if (ret < 0) -+ { -+ dev_err(&adap->dev, "transfer setup timed out\n"); -+ goto error_timeout; -+ } -+ -+ ret = wait_for_completion_timeout(&bi->done, adap->timeout); -+ if (ret == 0) { -+ dev_err(&adap->dev, "transfer timed out\n"); -+ goto error_timeout; -+ } -+ -+ ret = bi->error ? -EIO : num; -+ return ret; -+ -+error_timeout: -+ spin_lock_irqsave(&bi->lock, flags); -+ bcm2708_bsc_reset(bi); -+ bi->msg = 0; /* to inform the interrupt handler that there's nothing else to be done */ -+ bi->nmsgs = 0; -+ spin_unlock_irqrestore(&bi->lock, flags); -+ return -ETIMEDOUT; -+} -+ -+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; -+ -+ 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"); -+ 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); -+ } -+ -+ err = clk_prepare_enable(clk); -+ if (err) { -+ dev_err(&pdev->dev, "could not enable clk: %d\n", err); -+ goto out_clk_put; -+ } -+ -+ bi = kzalloc(sizeof(*bi), GFP_KERNEL); -+ if (!bi) -+ goto out_clk_disable; -+ -+ 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)); -+ adap->dev.of_node = pdev->dev.of_node; -+ -+ switch (pdev->id) { -+ case 0: -+ adap->class = I2C_CLASS_HWMON; -+ break; -+ case 1: -+ adap->class = I2C_CLASS_DDC; -+ break; -+ case 2: -+ adap->class = I2C_CLASS_DDC; -+ break; -+ default: -+ dev_err(&pdev->dev, "can only bind to BSC 0, 1 or 2\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; -+ } -+ bi->cdiv = 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_disable: -+ clk_disable_unprepare(clk); -+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_unprepare(bi->clk); -+ clk_put(bi->clk); -+ kfree(bi); -+ -+ 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, -+}; -+ -+// 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/target/linux/brcm2708/patches-4.4/0046-char-broadcom-Add-vcio-module.patch b/target/linux/brcm2708/patches-4.4/0046-char-broadcom-Add-vcio-module.patch new file mode 100644 index 0000000000..6070610d38 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0046-char-broadcom-Add-vcio-module.patch @@ -0,0 +1,221 @@ +From 75804edaed6c45d813dcee866d39445130d82539 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= +Date: Fri, 26 Jun 2015 14:27:06 +0200 +Subject: [PATCH 046/232] char: broadcom: Add vcio module +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add module for accessing the mailbox property channel through +/dev/vcio. Was previously in bcm2708-vcio. + +Signed-off-by: Noralf Trønnes +--- + drivers/char/broadcom/Kconfig | 6 ++ + drivers/char/broadcom/Makefile | 1 + + drivers/char/broadcom/vcio.c | 175 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 182 insertions(+) + create mode 100644 drivers/char/broadcom/vcio.c + +--- a/drivers/char/broadcom/Kconfig ++++ b/drivers/char/broadcom/Kconfig +@@ -22,6 +22,12 @@ config BCM2708_VCMEM + help + Helper for videocore memory access and total size allocation. + ++config BCM_VCIO ++ tristate "Mailbox userspace access" ++ depends on BCM2835_MBOX ++ help ++ Gives access to the mailbox property channel from userspace. ++ + endif + + config BCM_VC_SM +--- a/drivers/char/broadcom/Makefile ++++ b/drivers/char/broadcom/Makefile +@@ -1,5 +1,6 @@ + obj-$(CONFIG_BCM_VC_CMA) += vc_cma/ + obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o ++obj-$(CONFIG_BCM_VCIO) += vcio.o + obj-$(CONFIG_BCM_VC_SM) += vc_sm/ + + obj-$(CONFIG_BCM2835_DEVGPIOMEM)+= bcm2835-gpiomem.o +--- /dev/null ++++ b/drivers/char/broadcom/vcio.c +@@ -0,0 +1,175 @@ ++/* ++ * Copyright (C) 2010 Broadcom ++ * Copyright (C) 2015 Noralf Trønnes ++ * ++ * 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. ++ * ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MBOX_CHAN_PROPERTY 8 ++ ++#define VCIO_IOC_MAGIC 100 ++#define IOCTL_MBOX_PROPERTY _IOWR(VCIO_IOC_MAGIC, 0, char *) ++ ++static struct { ++ dev_t devt; ++ struct cdev cdev; ++ struct class *class; ++ struct rpi_firmware *fw; ++} vcio; ++ ++static int vcio_user_property_list(void *user) ++{ ++ u32 *buf, size; ++ int ret; ++ ++ /* The first 32-bit is the size of the buffer */ ++ if (copy_from_user(&size, user, sizeof(size))) ++ return -EFAULT; ++ ++ buf = kmalloc(size, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, user, size)) { ++ kfree(buf); ++ return -EFAULT; ++ } ++ ++ /* Strip off protocol encapsulation */ ++ ret = rpi_firmware_property_list(vcio.fw, &buf[2], size - 12); ++ if (ret) { ++ kfree(buf); ++ return ret; ++ } ++ ++ buf[1] = RPI_FIRMWARE_STATUS_SUCCESS; ++ if (copy_to_user(user, buf, size)) ++ ret = -EFAULT; ++ ++ kfree(buf); ++ ++ return ret; ++} ++ ++static int vcio_device_open(struct inode *inode, struct file *file) ++{ ++ try_module_get(THIS_MODULE); ++ ++ return 0; ++} ++ ++static int vcio_device_release(struct inode *inode, struct file *file) ++{ ++ module_put(THIS_MODULE); ++ ++ return 0; ++} ++ ++static long vcio_device_ioctl(struct file *file, unsigned int ioctl_num, ++ unsigned long ioctl_param) ++{ ++ switch (ioctl_num) { ++ case IOCTL_MBOX_PROPERTY: ++ return vcio_user_property_list((void *)ioctl_param); ++ default: ++ pr_err("unknown ioctl: %d\n", ioctl_num); ++ return -EINVAL; ++ } ++} ++ ++const struct file_operations vcio_fops = { ++ .unlocked_ioctl = vcio_device_ioctl, ++ .open = vcio_device_open, ++ .release = vcio_device_release, ++}; ++ ++static int __init vcio_init(void) ++{ ++ struct device_node *np; ++ static struct device *dev; ++ int ret; ++ ++ np = of_find_compatible_node(NULL, NULL, ++ "raspberrypi,bcm2835-firmware"); ++/* Uncomment this when we only boot with Device Tree ++ if (!of_device_is_available(np)) ++ return -ENODEV; ++*/ ++ vcio.fw = rpi_firmware_get(np); ++ if (!vcio.fw) ++ return -ENODEV; ++ ++ ret = alloc_chrdev_region(&vcio.devt, 0, 1, "vcio"); ++ if (ret) { ++ pr_err("failed to allocate device number\n"); ++ return ret; ++ } ++ ++ cdev_init(&vcio.cdev, &vcio_fops); ++ vcio.cdev.owner = THIS_MODULE; ++ ret = cdev_add(&vcio.cdev, vcio.devt, 1); ++ if (ret) { ++ pr_err("failed to register device\n"); ++ goto err_unregister_chardev; ++ } ++ ++ /* ++ * Create sysfs entries ++ * 'bcm2708_vcio' is used for backwards compatibility so we don't break ++ * userspace. Raspian has a udev rule that changes the permissions. ++ */ ++ vcio.class = class_create(THIS_MODULE, "bcm2708_vcio"); ++ if (IS_ERR(vcio.class)) { ++ ret = PTR_ERR(vcio.class); ++ pr_err("failed to create class\n"); ++ goto err_cdev_del; ++ } ++ ++ dev = device_create(vcio.class, NULL, vcio.devt, NULL, "vcio"); ++ if (IS_ERR(dev)) { ++ ret = PTR_ERR(dev); ++ pr_err("failed to create device\n"); ++ goto err_class_destroy; ++ } ++ ++ return 0; ++ ++err_class_destroy: ++ class_destroy(vcio.class); ++err_cdev_del: ++ cdev_del(&vcio.cdev); ++err_unregister_chardev: ++ unregister_chrdev_region(vcio.devt, 1); ++ ++ return ret; ++} ++module_init(vcio_init); ++ ++static void __exit vcio_exit(void) ++{ ++ device_destroy(vcio.class, vcio.devt); ++ class_destroy(vcio.class); ++ cdev_del(&vcio.cdev); ++ unregister_chrdev_region(vcio.devt, 1); ++} ++module_exit(vcio_exit); ++ ++MODULE_AUTHOR("Gray Girling"); ++MODULE_AUTHOR("Noralf Trønnes"); ++MODULE_DESCRIPTION("Mailbox userspace access"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/brcm2708/patches-4.4/0047-char-broadcom-Add-vcio-module.patch b/target/linux/brcm2708/patches-4.4/0047-char-broadcom-Add-vcio-module.patch deleted file mode 100644 index 4ef967b62f..0000000000 --- a/target/linux/brcm2708/patches-4.4/0047-char-broadcom-Add-vcio-module.patch +++ /dev/null @@ -1,221 +0,0 @@ -From abcf04d54ccb3d0897b1f7a95ddf9d9ccf63dc76 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= -Date: Fri, 26 Jun 2015 14:27:06 +0200 -Subject: [PATCH 047/170] char: broadcom: Add vcio module -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add module for accessing the mailbox property channel through -/dev/vcio. Was previously in bcm2708-vcio. - -Signed-off-by: Noralf Trønnes ---- - drivers/char/broadcom/Kconfig | 6 ++ - drivers/char/broadcom/Makefile | 1 + - drivers/char/broadcom/vcio.c | 175 +++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 182 insertions(+) - create mode 100644 drivers/char/broadcom/vcio.c - ---- a/drivers/char/broadcom/Kconfig -+++ b/drivers/char/broadcom/Kconfig -@@ -22,6 +22,12 @@ config BCM2708_VCMEM - help - Helper for videocore memory access and total size allocation. - -+config BCM_VCIO -+ tristate "Mailbox userspace access" -+ depends on BCM2835_MBOX -+ help -+ Gives access to the mailbox property channel from userspace. -+ - endif - - config BCM_VC_SM ---- a/drivers/char/broadcom/Makefile -+++ b/drivers/char/broadcom/Makefile -@@ -1,5 +1,6 @@ - obj-$(CONFIG_BCM_VC_CMA) += vc_cma/ - obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o -+obj-$(CONFIG_BCM_VCIO) += vcio.o - obj-$(CONFIG_BCM_VC_SM) += vc_sm/ - - obj-$(CONFIG_BCM2835_DEVGPIOMEM)+= bcm2835-gpiomem.o ---- /dev/null -+++ b/drivers/char/broadcom/vcio.c -@@ -0,0 +1,175 @@ -+/* -+ * Copyright (C) 2010 Broadcom -+ * Copyright (C) 2015 Noralf Trønnes -+ * -+ * 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. -+ * -+ */ -+ -+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define MBOX_CHAN_PROPERTY 8 -+ -+#define VCIO_IOC_MAGIC 100 -+#define IOCTL_MBOX_PROPERTY _IOWR(VCIO_IOC_MAGIC, 0, char *) -+ -+static struct { -+ dev_t devt; -+ struct cdev cdev; -+ struct class *class; -+ struct rpi_firmware *fw; -+} vcio; -+ -+static int vcio_user_property_list(void *user) -+{ -+ u32 *buf, size; -+ int ret; -+ -+ /* The first 32-bit is the size of the buffer */ -+ if (copy_from_user(&size, user, sizeof(size))) -+ return -EFAULT; -+ -+ buf = kmalloc(size, GFP_KERNEL); -+ if (!buf) -+ return -ENOMEM; -+ -+ if (copy_from_user(buf, user, size)) { -+ kfree(buf); -+ return -EFAULT; -+ } -+ -+ /* Strip off protocol encapsulation */ -+ ret = rpi_firmware_property_list(vcio.fw, &buf[2], size - 12); -+ if (ret) { -+ kfree(buf); -+ return ret; -+ } -+ -+ buf[1] = RPI_FIRMWARE_STATUS_SUCCESS; -+ if (copy_to_user(user, buf, size)) -+ ret = -EFAULT; -+ -+ kfree(buf); -+ -+ return ret; -+} -+ -+static int vcio_device_open(struct inode *inode, struct file *file) -+{ -+ try_module_get(THIS_MODULE); -+ -+ return 0; -+} -+ -+static int vcio_device_release(struct inode *inode, struct file *file) -+{ -+ module_put(THIS_MODULE); -+ -+ return 0; -+} -+ -+static long vcio_device_ioctl(struct file *file, unsigned int ioctl_num, -+ unsigned long ioctl_param) -+{ -+ switch (ioctl_num) { -+ case IOCTL_MBOX_PROPERTY: -+ return vcio_user_property_list((void *)ioctl_param); -+ default: -+ pr_err("unknown ioctl: %d\n", ioctl_num); -+ return -EINVAL; -+ } -+} -+ -+const struct file_operations vcio_fops = { -+ .unlocked_ioctl = vcio_device_ioctl, -+ .open = vcio_device_open, -+ .release = vcio_device_release, -+}; -+ -+static int __init vcio_init(void) -+{ -+ struct device_node *np; -+ static struct device *dev; -+ int ret; -+ -+ np = of_find_compatible_node(NULL, NULL, -+ "raspberrypi,bcm2835-firmware"); -+/* Uncomment this when we only boot with Device Tree -+ if (!of_device_is_available(np)) -+ return -ENODEV; -+*/ -+ vcio.fw = rpi_firmware_get(np); -+ if (!vcio.fw) -+ return -ENODEV; -+ -+ ret = alloc_chrdev_region(&vcio.devt, 0, 1, "vcio"); -+ if (ret) { -+ pr_err("failed to allocate device number\n"); -+ return ret; -+ } -+ -+ cdev_init(&vcio.cdev, &vcio_fops); -+ vcio.cdev.owner = THIS_MODULE; -+ ret = cdev_add(&vcio.cdev, vcio.devt, 1); -+ if (ret) { -+ pr_err("failed to register device\n"); -+ goto err_unregister_chardev; -+ } -+ -+ /* -+ * Create sysfs entries -+ * 'bcm2708_vcio' is used for backwards compatibility so we don't break -+ * userspace. Raspian has a udev rule that changes the permissions. -+ */ -+ vcio.class = class_create(THIS_MODULE, "bcm2708_vcio"); -+ if (IS_ERR(vcio.class)) { -+ ret = PTR_ERR(vcio.class); -+ pr_err("failed to create class\n"); -+ goto err_cdev_del; -+ } -+ -+ dev = device_create(vcio.class, NULL, vcio.devt, NULL, "vcio"); -+ if (IS_ERR(dev)) { -+ ret = PTR_ERR(dev); -+ pr_err("failed to create device\n"); -+ goto err_class_destroy; -+ } -+ -+ return 0; -+ -+err_class_destroy: -+ class_destroy(vcio.class); -+err_cdev_del: -+ cdev_del(&vcio.cdev); -+err_unregister_chardev: -+ unregister_chrdev_region(vcio.devt, 1); -+ -+ return ret; -+} -+module_init(vcio_init); -+ -+static void __exit vcio_exit(void) -+{ -+ device_destroy(vcio.class, vcio.devt); -+ class_destroy(vcio.class); -+ cdev_del(&vcio.cdev); -+ unregister_chrdev_region(vcio.devt, 1); -+} -+module_exit(vcio_exit); -+ -+MODULE_AUTHOR("Gray Girling"); -+MODULE_AUTHOR("Noralf Trønnes"); -+MODULE_DESCRIPTION("Mailbox userspace access"); -+MODULE_LICENSE("GPL"); diff --git a/target/linux/brcm2708/patches-4.4/0047-firmware-bcm2835-Support-ARCH_BCM270x.patch b/target/linux/brcm2708/patches-4.4/0047-firmware-bcm2835-Support-ARCH_BCM270x.patch new file mode 100644 index 0000000000..243803a9cd --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0047-firmware-bcm2835-Support-ARCH_BCM270x.patch @@ -0,0 +1,106 @@ +From 80b7c5e2cc3d633b3d3a1afbdb537e2ccfe906c7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= +Date: Fri, 26 Jun 2015 14:25:01 +0200 +Subject: [PATCH 047/232] firmware: bcm2835: Support ARCH_BCM270x +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Support booting without Device Tree. +Turn on USB power. +Load driver early because of lacking support for deferred probing +in many drivers. + +Signed-off-by: Noralf Trønnes +--- + drivers/firmware/raspberrypi.c | 41 +++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 39 insertions(+), 2 deletions(-) + +--- a/drivers/firmware/raspberrypi.c ++++ b/drivers/firmware/raspberrypi.c +@@ -28,6 +28,8 @@ struct rpi_firmware { + u32 enabled; + }; + ++static struct platform_device *g_pdev; ++ + static DEFINE_MUTEX(transaction_lock); + + static void response_callback(struct mbox_client *cl, void *msg) +@@ -183,6 +185,25 @@ rpi_firmware_print_firmware_revision(str + } + } + ++static int raspberrypi_firmware_set_power(struct rpi_firmware *fw, ++ u32 domain, bool on) ++{ ++ struct { ++ u32 domain; ++ u32 on; ++ } packet; ++ int ret; ++ ++ packet.domain = domain; ++ packet.on = on; ++ ret = rpi_firmware_property(fw, RPI_FIRMWARE_SET_POWER_STATE, ++ &packet, sizeof(packet)); ++ if (!ret && packet.on != on) ++ ret = -EINVAL; ++ ++ return ret; ++} ++ + static int rpi_firmware_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -207,9 +228,13 @@ static int rpi_firmware_probe(struct pla + init_completion(&fw->c); + + platform_set_drvdata(pdev, fw); ++ g_pdev = pdev; + + rpi_firmware_print_firmware_revision(fw); + ++ if (raspberrypi_firmware_set_power(fw, 3, true)) ++ dev_err(dev, "failed to turn on USB power\n"); ++ + return 0; + } + +@@ -218,6 +243,7 @@ static int rpi_firmware_remove(struct pl + struct rpi_firmware *fw = platform_get_drvdata(pdev); + + mbox_free_channel(fw->chan); ++ g_pdev = NULL; + + return 0; + } +@@ -230,7 +256,7 @@ static int rpi_firmware_remove(struct pl + */ + struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node) + { +- struct platform_device *pdev = of_find_device_by_node(firmware_node); ++ struct platform_device *pdev = g_pdev; + + if (!pdev) + return NULL; +@@ -253,7 +279,18 @@ static struct platform_driver rpi_firmwa + .probe = rpi_firmware_probe, + .remove = rpi_firmware_remove, + }; +-module_platform_driver(rpi_firmware_driver); ++ ++static int __init rpi_firmware_init(void) ++{ ++ return platform_driver_register(&rpi_firmware_driver); ++} ++subsys_initcall(rpi_firmware_init); ++ ++static void __init rpi_firmware_exit(void) ++{ ++ platform_driver_unregister(&rpi_firmware_driver); ++} ++module_exit(rpi_firmware_exit); + + MODULE_AUTHOR("Eric Anholt "); + MODULE_DESCRIPTION("Raspberry Pi firmware driver"); diff --git a/target/linux/brcm2708/patches-4.4/0048-bcm2835-add-v4l2-camera-device.patch b/target/linux/brcm2708/patches-4.4/0048-bcm2835-add-v4l2-camera-device.patch new file mode 100644 index 0000000000..17515c70b4 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0048-bcm2835-add-v4l2-camera-device.patch @@ -0,0 +1,7338 @@ +From e15fddd4332bd3a762d0feef48ff2b5e3df10af0 Mon Sep 17 00:00:00 2001 +From: Vincent Sanders +Date: Wed, 30 Jan 2013 12:45:18 +0000 +Subject: [PATCH 048/232] 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. + +bcm2835 camera planar/packed stride length + +Added a field to the mmal_fmt struct used to compute the bytes per line +when using a particular format. This results in the correct stride being +calculated even when the format is planar. + +Signed-off-by: Garrett Wilson + +bcm2835: camera: check for scene not being found + +static analysis by cppcheck detected some potential NULL pointer +dereference issues: + +[drivers/media/platform/bcm2835/controls.c:854]: (error) Possible null + pointer dereference: scene + (and lines 858, 859 too) + +it is possible that scene is not found because of an invalue ctrl->val +and is therefore NULL and hence causing a null pointer dereference. + +Signed-off-by: Colin Ian King + +bcm2835: memcpy port data to m rather than rmsg + +static analysis by cppcheck detected a memcpy to rmsg which is +not actually initialized at that point. The memcpy should be copying +to variable m instead. + +Signed-off-by: Colin Ian King + +BCM2835-V4L2: Return buffers to videobuf2 on shutdown + +https://github.com/raspberrypi/linux/issues/817 +Fixes the kernel warning from videobuf2 as buffers +are now returned as they are being flushed on +stop_streaming. + +squash: Fixup bcm2835-camera for changes in kernel 4.4 api +--- + 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 | 1844 +++++++++++++++++++++ + drivers/media/platform/bcm2835/bcm2835-camera.h | 126 ++ + drivers/media/platform/bcm2835/controls.c | 1324 +++++++++++++++ + drivers/media/platform/bcm2835/mmal-common.h | 53 + + 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, 6960 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 + +--- /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 +--- 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 +--- 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 + +--- /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 || ARCH_BCM2835) ++ ---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 +--- /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 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.c +@@ -0,0 +1,1844 @@ ++/* ++ * 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, planar, YUV", ++ .fourcc = V4L2_PIX_FMT_YUV420, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_I420, ++ .depth = 12, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ .ybbp = 1, ++ }, ++ { ++ .name = "4:2:2, packed, YUYV", ++ .fourcc = V4L2_PIX_FMT_YUYV, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_YUYV, ++ .depth = 16, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ .ybbp = 2, ++ }, ++ { ++ .name = "RGB24 (LE)", ++ .fourcc = V4L2_PIX_FMT_RGB24, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_BGR24, ++ .depth = 24, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ .ybbp = 3, ++ }, ++ { ++ .name = "JPEG", ++ .fourcc = V4L2_PIX_FMT_JPEG, ++ .flags = V4L2_FMT_FLAG_COMPRESSED, ++ .mmal = MMAL_ENCODING_JPEG, ++ .depth = 8, ++ .mmal_component = MMAL_COMPONENT_IMAGE_ENCODE, ++ .ybbp = 0, ++ }, ++ { ++ .name = "H264", ++ .fourcc = V4L2_PIX_FMT_H264, ++ .flags = V4L2_FMT_FLAG_COMPRESSED, ++ .mmal = MMAL_ENCODING_H264, ++ .depth = 8, ++ .mmal_component = MMAL_COMPONENT_VIDEO_ENCODE, ++ .ybbp = 0, ++ }, ++ { ++ .name = "MJPEG", ++ .fourcc = V4L2_PIX_FMT_MJPEG, ++ .flags = V4L2_FMT_FLAG_COMPRESSED, ++ .mmal = MMAL_ENCODING_MJPEG, ++ .depth = 8, ++ .mmal_component = MMAL_COMPONENT_VIDEO_ENCODE, ++ .ybbp = 0, ++ }, ++ { ++ .name = "4:2:2, packed, YVYU", ++ .fourcc = V4L2_PIX_FMT_YVYU, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_YVYU, ++ .depth = 16, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ .ybbp = 2, ++ }, ++ { ++ .name = "4:2:2, packed, VYUY", ++ .fourcc = V4L2_PIX_FMT_VYUY, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_VYUY, ++ .depth = 16, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ .ybbp = 2, ++ }, ++ { ++ .name = "4:2:2, packed, UYVY", ++ .fourcc = V4L2_PIX_FMT_UYVY, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_UYVY, ++ .depth = 16, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ .ybbp = 2, ++ }, ++ { ++ .name = "4:2:0, planar, NV12", ++ .fourcc = V4L2_PIX_FMT_NV12, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_NV12, ++ .depth = 12, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ .ybbp = 1, ++ }, ++ { ++ .name = "RGB24 (BE)", ++ .fourcc = V4L2_PIX_FMT_BGR24, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_RGB24, ++ .depth = 24, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ .ybbp = 3, ++ }, ++ { ++ .name = "4:2:0, planar, YVU", ++ .fourcc = V4L2_PIX_FMT_YVU420, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_YV12, ++ .depth = 12, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ .ybbp = 1, ++ }, ++ { ++ .name = "4:2:0, planar, NV21", ++ .fourcc = V4L2_PIX_FMT_NV21, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_NV21, ++ .depth = 12, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ .ybbp = 1, ++ }, ++ { ++ .name = "RGB32 (BE)", ++ .fourcc = V4L2_PIX_FMT_BGR32, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_BGRA, ++ .depth = 32, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ .ybbp = 4, ++ }, ++}; ++ ++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 void *parg, ++ 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, 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, 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.timestamp.tv_sec = ++ dev->capture.kernel_start_ts.tv_sec - 1 + ++ div; ++ buf->vb.timestamp.tv_usec = ++ dev->capture.kernel_start_ts.tv_usec + rem; ++ ++ if (buf->vb.timestamp.tv_usec >= ++ USEC_PER_SEC) { ++ buf->vb.timestamp.tv_sec++; ++ buf->vb.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.timestamp.tv_sec, ++ (int)buf->vb.timestamp. ++ tv_usec); ++ } else { ++ v4l2_get_timestamp(&buf->vb.timestamp); ++ } ++ ++ vb2_set_plane_payload(&buf->vb.vb2_buf, 0, length); ++ vb2_buffer_done(&buf->vb.vb2_buf, 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 */ ++ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); ++ 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 vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb); ++ struct mmal_buffer *buf = container_of(vb2, 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.vb2_buf, 0); ++ buf->buffer_size = vb2_plane_size(&buf->vb.vb2_buf, 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; ++ 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->ybbp; ++ ++ /* 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); +--- /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); \ ++} +--- /dev/null ++++ b/drivers/media/platform/bcm2835/controls.c +@@ -0,0 +1,1324 @@ ++/* ++ * 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 (!scene) ++ return -EINVAL; ++ 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; ++} +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-common.h +@@ -0,0 +1,53 @@ ++/* ++ * 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 */ ++ u32 ybbp; /* depth of first Y plane for planar formats */ ++}; ++ ++/* buffer for one video frame */ ++struct mmal_buffer { ++ /* v4l buffer data -- must be first */ ++ struct vb2_v4l2_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; ++}; +--- /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 */ +--- /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 */ +--- /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 */ +--- /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. ++ */ ++ ++}; +--- /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; ++}; +--- /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]; ++}; +--- /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(&m.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; ++} +--- /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 */ diff --git a/target/linux/brcm2708/patches-4.4/0048-firmware-bcm2835-Support-ARCH_BCM270x.patch b/target/linux/brcm2708/patches-4.4/0048-firmware-bcm2835-Support-ARCH_BCM270x.patch deleted file mode 100644 index 93c24d893e..0000000000 --- a/target/linux/brcm2708/patches-4.4/0048-firmware-bcm2835-Support-ARCH_BCM270x.patch +++ /dev/null @@ -1,106 +0,0 @@ -From a205e934b63b6bf3f5f74bc39974ec6ff09df596 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= -Date: Fri, 26 Jun 2015 14:25:01 +0200 -Subject: [PATCH 048/170] firmware: bcm2835: Support ARCH_BCM270x -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Support booting without Device Tree. -Turn on USB power. -Load driver early because of lacking support for deferred probing -in many drivers. - -Signed-off-by: Noralf Trønnes ---- - drivers/firmware/raspberrypi.c | 41 +++++++++++++++++++++++++++++++++++++++-- - 1 file changed, 39 insertions(+), 2 deletions(-) - ---- a/drivers/firmware/raspberrypi.c -+++ b/drivers/firmware/raspberrypi.c -@@ -28,6 +28,8 @@ struct rpi_firmware { - u32 enabled; - }; - -+static struct platform_device *g_pdev; -+ - static DEFINE_MUTEX(transaction_lock); - - static void response_callback(struct mbox_client *cl, void *msg) -@@ -183,6 +185,25 @@ rpi_firmware_print_firmware_revision(str - } - } - -+static int raspberrypi_firmware_set_power(struct rpi_firmware *fw, -+ u32 domain, bool on) -+{ -+ struct { -+ u32 domain; -+ u32 on; -+ } packet; -+ int ret; -+ -+ packet.domain = domain; -+ packet.on = on; -+ ret = rpi_firmware_property(fw, RPI_FIRMWARE_SET_POWER_STATE, -+ &packet, sizeof(packet)); -+ if (!ret && packet.on != on) -+ ret = -EINVAL; -+ -+ return ret; -+} -+ - static int rpi_firmware_probe(struct platform_device *pdev) - { - struct device *dev = &pdev->dev; -@@ -207,9 +228,13 @@ static int rpi_firmware_probe(struct pla - init_completion(&fw->c); - - platform_set_drvdata(pdev, fw); -+ g_pdev = pdev; - - rpi_firmware_print_firmware_revision(fw); - -+ if (raspberrypi_firmware_set_power(fw, 3, true)) -+ dev_err(dev, "failed to turn on USB power\n"); -+ - return 0; - } - -@@ -218,6 +243,7 @@ static int rpi_firmware_remove(struct pl - struct rpi_firmware *fw = platform_get_drvdata(pdev); - - mbox_free_channel(fw->chan); -+ g_pdev = NULL; - - return 0; - } -@@ -230,7 +256,7 @@ static int rpi_firmware_remove(struct pl - */ - struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node) - { -- struct platform_device *pdev = of_find_device_by_node(firmware_node); -+ struct platform_device *pdev = g_pdev; - - if (!pdev) - return NULL; -@@ -253,7 +279,18 @@ static struct platform_driver rpi_firmwa - .probe = rpi_firmware_probe, - .remove = rpi_firmware_remove, - }; --module_platform_driver(rpi_firmware_driver); -+ -+static int __init rpi_firmware_init(void) -+{ -+ return platform_driver_register(&rpi_firmware_driver); -+} -+subsys_initcall(rpi_firmware_init); -+ -+static void __init rpi_firmware_exit(void) -+{ -+ platform_driver_unregister(&rpi_firmware_driver); -+} -+module_exit(rpi_firmware_exit); - - MODULE_AUTHOR("Eric Anholt "); - MODULE_DESCRIPTION("Raspberry Pi firmware driver"); diff --git a/target/linux/brcm2708/patches-4.4/0049-bcm2835-add-v4l2-camera-device.patch b/target/linux/brcm2708/patches-4.4/0049-bcm2835-add-v4l2-camera-device.patch deleted file mode 100644 index a464881a16..0000000000 --- a/target/linux/brcm2708/patches-4.4/0049-bcm2835-add-v4l2-camera-device.patch +++ /dev/null @@ -1,7338 +0,0 @@ -From 4d74ee3d433df319ae3fc36bd16fd96b4bbff865 Mon Sep 17 00:00:00 2001 -From: Vincent Sanders -Date: Wed, 30 Jan 2013 12:45:18 +0000 -Subject: [PATCH 049/170] 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. - -bcm2835 camera planar/packed stride length - -Added a field to the mmal_fmt struct used to compute the bytes per line -when using a particular format. This results in the correct stride being -calculated even when the format is planar. - -Signed-off-by: Garrett Wilson - -bcm2835: camera: check for scene not being found - -static analysis by cppcheck detected some potential NULL pointer -dereference issues: - -[drivers/media/platform/bcm2835/controls.c:854]: (error) Possible null - pointer dereference: scene - (and lines 858, 859 too) - -it is possible that scene is not found because of an invalue ctrl->val -and is therefore NULL and hence causing a null pointer dereference. - -Signed-off-by: Colin Ian King - -bcm2835: memcpy port data to m rather than rmsg - -static analysis by cppcheck detected a memcpy to rmsg which is -not actually initialized at that point. The memcpy should be copying -to variable m instead. - -Signed-off-by: Colin Ian King - -BCM2835-V4L2: Return buffers to videobuf2 on shutdown - -https://github.com/raspberrypi/linux/issues/817 -Fixes the kernel warning from videobuf2 as buffers -are now returned as they are being flushed on -stop_streaming. - -squash: Fixup bcm2835-camera for changes in kernel 4.4 api ---- - 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 | 1844 +++++++++++++++++++++ - drivers/media/platform/bcm2835/bcm2835-camera.h | 126 ++ - drivers/media/platform/bcm2835/controls.c | 1324 +++++++++++++++ - drivers/media/platform/bcm2835/mmal-common.h | 53 + - 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, 6960 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 - ---- /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 ---- 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 ---- 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 - ---- /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 || ARCH_BCM2835) -+ ---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 ---- /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 ---- /dev/null -+++ b/drivers/media/platform/bcm2835/bcm2835-camera.c -@@ -0,0 +1,1844 @@ -+/* -+ * 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, planar, YUV", -+ .fourcc = V4L2_PIX_FMT_YUV420, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_I420, -+ .depth = 12, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ .ybbp = 1, -+ }, -+ { -+ .name = "4:2:2, packed, YUYV", -+ .fourcc = V4L2_PIX_FMT_YUYV, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_YUYV, -+ .depth = 16, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ .ybbp = 2, -+ }, -+ { -+ .name = "RGB24 (LE)", -+ .fourcc = V4L2_PIX_FMT_RGB24, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_BGR24, -+ .depth = 24, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ .ybbp = 3, -+ }, -+ { -+ .name = "JPEG", -+ .fourcc = V4L2_PIX_FMT_JPEG, -+ .flags = V4L2_FMT_FLAG_COMPRESSED, -+ .mmal = MMAL_ENCODING_JPEG, -+ .depth = 8, -+ .mmal_component = MMAL_COMPONENT_IMAGE_ENCODE, -+ .ybbp = 0, -+ }, -+ { -+ .name = "H264", -+ .fourcc = V4L2_PIX_FMT_H264, -+ .flags = V4L2_FMT_FLAG_COMPRESSED, -+ .mmal = MMAL_ENCODING_H264, -+ .depth = 8, -+ .mmal_component = MMAL_COMPONENT_VIDEO_ENCODE, -+ .ybbp = 0, -+ }, -+ { -+ .name = "MJPEG", -+ .fourcc = V4L2_PIX_FMT_MJPEG, -+ .flags = V4L2_FMT_FLAG_COMPRESSED, -+ .mmal = MMAL_ENCODING_MJPEG, -+ .depth = 8, -+ .mmal_component = MMAL_COMPONENT_VIDEO_ENCODE, -+ .ybbp = 0, -+ }, -+ { -+ .name = "4:2:2, packed, YVYU", -+ .fourcc = V4L2_PIX_FMT_YVYU, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_YVYU, -+ .depth = 16, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ .ybbp = 2, -+ }, -+ { -+ .name = "4:2:2, packed, VYUY", -+ .fourcc = V4L2_PIX_FMT_VYUY, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_VYUY, -+ .depth = 16, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ .ybbp = 2, -+ }, -+ { -+ .name = "4:2:2, packed, UYVY", -+ .fourcc = V4L2_PIX_FMT_UYVY, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_UYVY, -+ .depth = 16, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ .ybbp = 2, -+ }, -+ { -+ .name = "4:2:0, planar, NV12", -+ .fourcc = V4L2_PIX_FMT_NV12, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_NV12, -+ .depth = 12, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ .ybbp = 1, -+ }, -+ { -+ .name = "RGB24 (BE)", -+ .fourcc = V4L2_PIX_FMT_BGR24, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_RGB24, -+ .depth = 24, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ .ybbp = 3, -+ }, -+ { -+ .name = "4:2:0, planar, YVU", -+ .fourcc = V4L2_PIX_FMT_YVU420, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_YV12, -+ .depth = 12, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ .ybbp = 1, -+ }, -+ { -+ .name = "4:2:0, planar, NV21", -+ .fourcc = V4L2_PIX_FMT_NV21, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_NV21, -+ .depth = 12, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ .ybbp = 1, -+ }, -+ { -+ .name = "RGB32 (BE)", -+ .fourcc = V4L2_PIX_FMT_BGR32, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_BGRA, -+ .depth = 32, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ .ybbp = 4, -+ }, -+}; -+ -+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 void *parg, -+ 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, 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, 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.timestamp.tv_sec = -+ dev->capture.kernel_start_ts.tv_sec - 1 + -+ div; -+ buf->vb.timestamp.tv_usec = -+ dev->capture.kernel_start_ts.tv_usec + rem; -+ -+ if (buf->vb.timestamp.tv_usec >= -+ USEC_PER_SEC) { -+ buf->vb.timestamp.tv_sec++; -+ buf->vb.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.timestamp.tv_sec, -+ (int)buf->vb.timestamp. -+ tv_usec); -+ } else { -+ v4l2_get_timestamp(&buf->vb.timestamp); -+ } -+ -+ vb2_set_plane_payload(&buf->vb.vb2_buf, 0, length); -+ vb2_buffer_done(&buf->vb.vb2_buf, 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 */ -+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); -+ 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 vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb); -+ struct mmal_buffer *buf = container_of(vb2, 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.vb2_buf, 0); -+ buf->buffer_size = vb2_plane_size(&buf->vb.vb2_buf, 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; -+ 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->ybbp; -+ -+ /* 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); ---- /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); \ -+} ---- /dev/null -+++ b/drivers/media/platform/bcm2835/controls.c -@@ -0,0 +1,1324 @@ -+/* -+ * 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 (!scene) -+ return -EINVAL; -+ 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; -+} ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-common.h -@@ -0,0 +1,53 @@ -+/* -+ * 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 */ -+ u32 ybbp; /* depth of first Y plane for planar formats */ -+}; -+ -+/* buffer for one video frame */ -+struct mmal_buffer { -+ /* v4l buffer data -- must be first */ -+ struct vb2_v4l2_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; -+}; ---- /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 */ ---- /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 */ ---- /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 */ ---- /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. -+ */ -+ -+}; ---- /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; -+}; ---- /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]; -+}; ---- /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(&m.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; -+} ---- /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 */ diff --git a/target/linux/brcm2708/patches-4.4/0049-scripts-Add-mkknlimg-and-knlinfo-scripts-from-tools-.patch b/target/linux/brcm2708/patches-4.4/0049-scripts-Add-mkknlimg-and-knlinfo-scripts-from-tools-.patch new file mode 100644 index 0000000000..60201fe9e5 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0049-scripts-Add-mkknlimg-and-knlinfo-scripts-from-tools-.patch @@ -0,0 +1,461 @@ +From 91e833ab0b220bb0bb810bb9605b0a03cd76baa8 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 11 May 2015 09:00:42 +0100 +Subject: [PATCH 049/232] scripts: Add mkknlimg and knlinfo scripts from tools + repo + +The Raspberry Pi firmware looks for a trailer on the kernel image to +determine whether it was compiled with Device Tree support enabled. +If the firmware finds a kernel without this trailer, or which has a +trailer indicating that it isn't DT-capable, it disables DT support +and reverts to using ATAGs. + +The mkknlimg utility adds that trailer, having first analysed the +image to look for signs of DT support and the kernel version string. + +knlinfo displays the contents of the trailer in the given kernel image. + +scripts/mkknlimg: Add support for ARCH_BCM2835 + +Add a new trailer field indicating whether this is an ARCH_BCM2835 +build, as opposed to MACH_BCM2708/9. If the loader finds this flag +is set it changes the default base dtb file name from bcm270x... +to bcm283y... + +Also update knlinfo to show the status of the field. + +scripts/mkknlimg: Improve ARCH_BCM2835 detection + +The board support code contains sufficient strings to be able to +distinguish 2708 vs. 2835 builds, so remove the check for +bcm2835-pm-wdt which could exist in either. + +Also, since the canned configuration is no longer built in (it's +a module), remove the config string checking. + +See: https://github.com/raspberrypi/linux/issues/1157 +--- + scripts/knlinfo | 168 ++++++++++++++++++++++++++++++++++++++ + scripts/mkknlimg | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 412 insertions(+) + create mode 100755 scripts/knlinfo + create mode 100755 scripts/mkknlimg + +--- /dev/null ++++ b/scripts/knlinfo +@@ -0,0 +1,168 @@ ++#!/usr/bin/env perl ++# ---------------------------------------------------------------------- ++# knlinfo by Phil Elwell for Raspberry Pi ++# ++# (c) 2014,2015 Raspberry Pi (Trading) Limited ++# ++# Licensed under the terms of the GNU General Public License. ++# ---------------------------------------------------------------------- ++ ++use strict; ++use integer; ++ ++use Fcntl ":seek"; ++ ++my $trailer_magic = 'RPTL'; ++ ++my %atom_formats = ++( ++ 'DTOK' => \&format_bool, ++ 'KVer' => \&format_string, ++ '283x' => \&format_bool, ++); ++ ++if (@ARGV != 1) ++{ ++ print ("Usage: knlinfo \n"); ++ exit(1); ++} ++ ++my $kernel_file = $ARGV[0]; ++ ++ ++my ($atoms, $pos) = read_trailer($kernel_file); ++ ++exit(1) if (!$atoms); ++ ++printf("Kernel trailer found at %d/0x%x:\n", $pos, $pos); ++ ++foreach my $atom (@$atoms) ++{ ++ printf(" %s: %s\n", $atom->[0], format_atom($atom)); ++} ++ ++exit(0); ++ ++sub read_trailer ++{ ++ my ($kernel_file) = @_; ++ my $fh; ++ ++ if (!open($fh, '<', $kernel_file)) ++ { ++ print ("* Failed to open '$kernel_file'\n"); ++ return undef; ++ } ++ ++ if (!seek($fh, -12, SEEK_END)) ++ { ++ print ("* seek error in '$kernel_file'\n"); ++ return undef; ++ } ++ ++ my $last_bytes; ++ sysread($fh, $last_bytes, 12); ++ ++ my ($trailer_len, $data_len, $magic) = unpack('VVa4', $last_bytes); ++ ++ if (($magic ne $trailer_magic) || ($data_len != 4)) ++ { ++ print ("* no trailer\n"); ++ return undef; ++ } ++ if (!seek($fh, -12, SEEK_END)) ++ { ++ print ("* seek error in '$kernel_file'\n"); ++ return undef; ++ } ++ ++ $trailer_len -= 12; ++ ++ while ($trailer_len > 0) ++ { ++ if ($trailer_len < 8) ++ { ++ print ("* truncated atom header in trailer\n"); ++ return undef; ++ } ++ if (!seek($fh, -8, SEEK_CUR)) ++ { ++ print ("* seek error in '$kernel_file'\n"); ++ return undef; ++ } ++ $trailer_len -= 8; ++ ++ my $atom_hdr; ++ sysread($fh, $atom_hdr, 8); ++ my ($atom_len, $atom_type) = unpack('Va4', $atom_hdr); ++ ++ if ($trailer_len < $atom_len) ++ { ++ print ("* truncated atom data in trailer\n"); ++ return undef; ++ } ++ ++ my $rounded_len = (($atom_len + 3) & ~3); ++ if (!seek($fh, -(8 + $rounded_len), SEEK_CUR)) ++ { ++ print ("* seek error in '$kernel_file'\n"); ++ return undef; ++ } ++ $trailer_len -= $rounded_len; ++ ++ my $atom_data; ++ sysread($fh, $atom_data, $atom_len); ++ ++ if (!seek($fh, -$atom_len, SEEK_CUR)) ++ { ++ print ("* seek error in '$kernel_file'\n"); ++ return undef; ++ } ++ ++ push @$atoms, [ $atom_type, $atom_data ]; ++ } ++ ++ if (($$atoms[-1][0] eq "\x00\x00\x00\x00") && ++ ($$atoms[-1][1] eq "")) ++ { ++ pop @$atoms; ++ } ++ else ++ { ++ print ("* end marker missing from trailer\n"); ++ } ++ ++ return ($atoms, tell($fh)); ++} ++ ++sub format_atom ++{ ++ my ($atom) = @_; ++ ++ my $format_func = $atom_formats{$atom->[0]} || \&format_hex; ++ return $format_func->($atom->[1]); ++} ++ ++sub format_bool ++{ ++ my ($data) = @_; ++ return unpack('V', $data) ? 'true' : 'false'; ++} ++ ++sub format_int ++{ ++ my ($data) = @_; ++ return unpack('V', $data); ++} ++ ++sub format_string ++{ ++ my ($data) = @_; ++ return '"'.$data.'"'; ++} ++ ++sub format_hex ++{ ++ my ($data) = @_; ++ return unpack('H*', $data); ++} +--- /dev/null ++++ b/scripts/mkknlimg +@@ -0,0 +1,244 @@ ++#!/usr/bin/env perl ++# ---------------------------------------------------------------------- ++# mkknlimg by Phil Elwell for Raspberry Pi ++# based on extract-ikconfig by Dick Streefland ++# ++# (c) 2009,2010 Dick Streefland ++# (c) 2014,2015 Raspberry Pi (Trading) Limited ++# ++# Licensed under the terms of the GNU General Public License. ++# ---------------------------------------------------------------------- ++ ++use strict; ++use warnings; ++use integer; ++ ++my $trailer_magic = 'RPTL'; ++ ++my $tmpfile1 = "/tmp/mkknlimg_$$.1"; ++my $tmpfile2 = "/tmp/mkknlimg_$$.2"; ++ ++my $dtok = 0; ++my $is_283x = 0; ++ ++while (@ARGV && ($ARGV[0] =~ /^-/)) ++{ ++ my $arg = shift(@ARGV); ++ if ($arg eq '--dtok') ++ { ++ $dtok = 1; ++ } ++ elsif ($arg eq '--283x') ++ { ++ $is_283x = 1; ++ } ++ else ++ { ++ print ("* Unknown option '$arg'\n"); ++ usage(); ++ } ++} ++ ++usage() if (@ARGV != 2); ++ ++my $kernel_file = $ARGV[0]; ++my $out_file = $ARGV[1]; ++ ++if (! -r $kernel_file) ++{ ++ print ("* File '$kernel_file' not found\n"); ++ usage(); ++} ++ ++my @wanted_strings = ++( ++ 'bcm2708_fb', ++ 'brcm,bcm2835-mmc', ++ 'brcm,bcm2835-sdhost', ++ 'brcm,bcm2708-pinctrl', ++ 'brcm,bcm2835-gpio', ++ 'brcm,bcm2835', ++ 'brcm,bcm2836' ++); ++ ++my $res = try_extract($kernel_file, $tmpfile1); ++$res = try_decompress('\037\213\010', 'xy', 'gunzip', 0, ++ $kernel_file, $tmpfile1, $tmpfile2) if (!$res); ++$res = try_decompress('\3757zXZ\000', 'abcde', 'unxz --single-stream', -1, ++ $kernel_file, $tmpfile1, $tmpfile2) if (!$res); ++$res = try_decompress('BZh', 'xy', 'bunzip2', 0, ++ $kernel_file, $tmpfile1, $tmpfile2) if (!$res); ++$res = try_decompress('\135\0\0\0', 'xxx', 'unlzma', 0, ++ $kernel_file, $tmpfile1, $tmpfile2) if (!$res); ++$res = try_decompress('\211\114\132', 'xy', 'lzop -d', 0, ++ $kernel_file, $tmpfile1, $tmpfile2) if (!$res); ++$res = try_decompress('\002\041\114\030', 'xy', 'lz4 -d', 1, ++ $kernel_file, $tmpfile1, $tmpfile2) if (!$res); ++ ++my $append_trailer; ++my $trailer; ++my $kver = '?'; ++ ++$append_trailer = $dtok; ++ ++if ($res) ++{ ++ $kver = $res->{''} || '?'; ++ print("Version: $kver\n"); ++ ++ $append_trailer = $dtok; ++ if (!$dtok) ++ { ++ if (config_bool($res, 'bcm2708_fb') || ++ config_bool($res, 'brcm,bcm2835-mmc') || ++ config_bool($res, 'brcm,bcm2835-sdhost')) ++ { ++ $dtok ||= config_bool($res, 'brcm,bcm2708-pinctrl'); ++ $dtok ||= config_bool($res, 'brcm,bcm2835-gpio'); ++ $is_283x ||= config_bool($res, 'brcm,bcm2835'); ++ $is_283x ||= config_bool($res, 'brcm,bcm2836'); ++ $dtok ||= $is_283x; ++ $append_trailer = 1; ++ } ++ else ++ { ++ print ("* This doesn't look like a Raspberry Pi kernel. In pass-through mode.\n"); ++ } ++ } ++} ++elsif (!$dtok) ++{ ++ print ("* Is this a valid kernel? In pass-through mode.\n"); ++} ++ ++if ($append_trailer) ++{ ++ printf("DT: %s\n", $dtok ? "y" : "n"); ++ printf("283x: %s\n", $is_283x ? "y" : "n"); ++ ++ my @atoms; ++ ++ push @atoms, [ $trailer_magic, pack('V', 0) ]; ++ push @atoms, [ 'KVer', $kver ]; ++ push @atoms, [ 'DTOK', pack('V', $dtok) ]; ++ push @atoms, [ '283x', pack('V', $is_283x) ]; ++ ++ $trailer = pack_trailer(\@atoms); ++ $atoms[0]->[1] = pack('V', length($trailer)); ++ ++ $trailer = pack_trailer(\@atoms); ++} ++ ++my $ofh; ++my $total_len = 0; ++ ++if ($out_file eq $kernel_file) ++{ ++ die "* Failed to open '$out_file' for append\n" ++ if (!open($ofh, '>>', $out_file)); ++ $total_len = tell($ofh); ++} ++else ++{ ++ die "* Failed to open '$kernel_file'\n" ++ if (!open(my $ifh, '<', $kernel_file)); ++ die "* Failed to create '$out_file'\n" ++ if (!open($ofh, '>', $out_file)); ++ ++ my $copybuf; ++ while (1) ++ { ++ my $bytes = sysread($ifh, $copybuf, 64*1024); ++ last if (!$bytes); ++ syswrite($ofh, $copybuf, $bytes); ++ $total_len += $bytes; ++ } ++ close($ifh); ++} ++ ++if ($trailer) ++{ ++ # Pad to word-alignment ++ syswrite($ofh, "\x000\x000\x000", (-$total_len & 0x3)); ++ syswrite($ofh, $trailer); ++} ++ ++close($ofh); ++ ++exit($trailer ? 0 : 1); ++ ++END { ++ unlink($tmpfile1) if ($tmpfile1); ++ unlink($tmpfile2) if ($tmpfile2); ++} ++ ++ ++sub usage ++{ ++ print ("Usage: mkknlimg [--dtok] [--283x] \n"); ++ exit(1); ++} ++ ++sub try_extract ++{ ++ my ($knl, $tmp) = @_; ++ ++ my $ver = `strings "$knl" | grep -a -E "^Linux version [1-9]"`; ++ ++ return undef if (!$ver); ++ ++ chomp($ver); ++ ++ my $res = { ''=>$ver }; ++ my $string_pattern = '^('.join('|', @wanted_strings).')$'; ++ ++ my @matches = `strings \"$knl\" | grep -E \"$string_pattern\"`; ++ foreach my $match (@matches) ++ { ++ chomp($match); ++ $res->{$match} = 1; ++ } ++ ++ return $res; ++} ++ ++ ++sub try_decompress ++{ ++ my ($magic, $subst, $zcat, $idx, $knl, $tmp1, $tmp2) = @_; ++ ++ my $pos = `tr "$magic\n$subst" "\n$subst=" < "$knl" | grep -abo "^$subst"`; ++ if ($pos) ++ { ++ chomp($pos); ++ $pos = (split(/[\r\n]+/, $pos))[$idx]; ++ return undef if (!defined($pos)); ++ $pos =~ s/:.*[\r\n]*$//s; ++ my $cmd = "tail -c+$pos \"$knl\" | $zcat > $tmp2 2> /dev/null"; ++ my $err = (system($cmd) >> 8); ++ return undef if (($err != 0) && ($err != 2)); ++ ++ return try_extract($tmp2, $tmp1); ++ } ++ ++ return undef; ++} ++ ++sub pack_trailer ++{ ++ my ($atoms) = @_; ++ my $trailer = pack('VV', 0, 0); ++ for (my $i = $#$atoms; $i>=0; $i--) ++ { ++ my $atom = $atoms->[$i]; ++ $trailer .= pack('a*x!4Va4', $atom->[1], length($atom->[1]), $atom->[0]); ++ } ++ return $trailer; ++} ++ ++sub config_bool ++{ ++ my ($configs, $wanted) = @_; ++ my $val = $configs->{$wanted} || 'n'; ++ return (($val eq 'y') || ($val eq '1')); ++} diff --git a/target/linux/brcm2708/patches-4.4/0050-fdt-Add-support-for-the-CONFIG_CMDLINE_EXTEND-option.patch b/target/linux/brcm2708/patches-4.4/0050-fdt-Add-support-for-the-CONFIG_CMDLINE_EXTEND-option.patch new file mode 100644 index 0000000000..ab07248efe --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0050-fdt-Add-support-for-the-CONFIG_CMDLINE_EXTEND-option.patch @@ -0,0 +1,58 @@ +From 918b9e451bbaebb775725a292fd3b6e3d4cf804f Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 5 Dec 2014 17:26:26 +0000 +Subject: [PATCH 050/232] fdt: Add support for the CONFIG_CMDLINE_EXTEND option + +--- + drivers/of/fdt.c | 29 ++++++++++++++++++++++++----- + 1 file changed, 24 insertions(+), 5 deletions(-) + +--- a/drivers/of/fdt.c ++++ b/drivers/of/fdt.c +@@ -954,22 +954,38 @@ int __init early_init_dt_scan_chosen(uns + + /* Retrieve command line */ + p = of_get_flat_dt_prop(node, "bootargs", &l); +- if (p != NULL && l > 0) +- strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE)); +- p = of_get_flat_dt_prop(node, "bootargs-append", &l); +- if (p != NULL && l > 0) +- strlcat(data, p, min_t(int, strlen(data) + (int)l, COMMAND_LINE_SIZE)); + + /* + * CONFIG_CMDLINE is meant to be a default in case nothing else + * managed to set the command line, unless CONFIG_CMDLINE_FORCE + * is set in which case we override whatever was found earlier. ++ * ++ * However, it can be useful to be able to treat the default as ++ * a starting point to be extended using CONFIG_CMDLINE_EXTEND. + */ ++ ((char *)data)[0] = '\0'; ++ + #ifdef CONFIG_CMDLINE +-#ifndef CONFIG_CMDLINE_FORCE +- if (!((char *)data)[0]) ++ strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); ++ ++ if (p != NULL && l > 0) { ++#if defined(CONFIG_CMDLINE_EXTEND) ++ int len = strlen(data); ++ if (len > 0) { ++ strlcat(data, " ", COMMAND_LINE_SIZE); ++ len++; ++ } ++ strlcpy((char *)data + len, p, min((int)l, COMMAND_LINE_SIZE - len)); ++#elif defined(CONFIG_CMDLINE_FORCE) ++ pr_warning("Ignoring bootargs property (using the default kernel command line)\n"); ++#else ++ /* Neither extend nor force - just override */ ++ strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE)); + #endif +- strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); ++ } ++#else /* CONFIG_CMDLINE */ ++ if (p != NULL && l > 0) ++ strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE)); + #endif /* CONFIG_CMDLINE */ + + pr_debug("Command line is: %s\n", (char*)data); diff --git a/target/linux/brcm2708/patches-4.4/0050-scripts-Add-mkknlimg-and-knlinfo-scripts-from-tools-.patch b/target/linux/brcm2708/patches-4.4/0050-scripts-Add-mkknlimg-and-knlinfo-scripts-from-tools-.patch deleted file mode 100644 index 30cdfe9b0f..0000000000 --- a/target/linux/brcm2708/patches-4.4/0050-scripts-Add-mkknlimg-and-knlinfo-scripts-from-tools-.patch +++ /dev/null @@ -1,461 +0,0 @@ -From 03435989bbadbd333a810644c8246dd7eb0bad07 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 11 May 2015 09:00:42 +0100 -Subject: [PATCH 050/170] scripts: Add mkknlimg and knlinfo scripts from tools - repo - -The Raspberry Pi firmware looks for a trailer on the kernel image to -determine whether it was compiled with Device Tree support enabled. -If the firmware finds a kernel without this trailer, or which has a -trailer indicating that it isn't DT-capable, it disables DT support -and reverts to using ATAGs. - -The mkknlimg utility adds that trailer, having first analysed the -image to look for signs of DT support and the kernel version string. - -knlinfo displays the contents of the trailer in the given kernel image. - -scripts/mkknlimg: Add support for ARCH_BCM2835 - -Add a new trailer field indicating whether this is an ARCH_BCM2835 -build, as opposed to MACH_BCM2708/9. If the loader finds this flag -is set it changes the default base dtb file name from bcm270x... -to bcm283y... - -Also update knlinfo to show the status of the field. - -scripts/mkknlimg: Improve ARCH_BCM2835 detection - -The board support code contains sufficient strings to be able to -distinguish 2708 vs. 2835 builds, so remove the check for -bcm2835-pm-wdt which could exist in either. - -Also, since the canned configuration is no longer built in (it's -a module), remove the config string checking. - -See: https://github.com/raspberrypi/linux/issues/1157 ---- - scripts/knlinfo | 168 ++++++++++++++++++++++++++++++++++++++ - scripts/mkknlimg | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 412 insertions(+) - create mode 100755 scripts/knlinfo - create mode 100755 scripts/mkknlimg - ---- /dev/null -+++ b/scripts/knlinfo -@@ -0,0 +1,168 @@ -+#!/usr/bin/env perl -+# ---------------------------------------------------------------------- -+# knlinfo by Phil Elwell for Raspberry Pi -+# -+# (c) 2014,2015 Raspberry Pi (Trading) Limited -+# -+# Licensed under the terms of the GNU General Public License. -+# ---------------------------------------------------------------------- -+ -+use strict; -+use integer; -+ -+use Fcntl ":seek"; -+ -+my $trailer_magic = 'RPTL'; -+ -+my %atom_formats = -+( -+ 'DTOK' => \&format_bool, -+ 'KVer' => \&format_string, -+ '283x' => \&format_bool, -+); -+ -+if (@ARGV != 1) -+{ -+ print ("Usage: knlinfo \n"); -+ exit(1); -+} -+ -+my $kernel_file = $ARGV[0]; -+ -+ -+my ($atoms, $pos) = read_trailer($kernel_file); -+ -+exit(1) if (!$atoms); -+ -+printf("Kernel trailer found at %d/0x%x:\n", $pos, $pos); -+ -+foreach my $atom (@$atoms) -+{ -+ printf(" %s: %s\n", $atom->[0], format_atom($atom)); -+} -+ -+exit(0); -+ -+sub read_trailer -+{ -+ my ($kernel_file) = @_; -+ my $fh; -+ -+ if (!open($fh, '<', $kernel_file)) -+ { -+ print ("* Failed to open '$kernel_file'\n"); -+ return undef; -+ } -+ -+ if (!seek($fh, -12, SEEK_END)) -+ { -+ print ("* seek error in '$kernel_file'\n"); -+ return undef; -+ } -+ -+ my $last_bytes; -+ sysread($fh, $last_bytes, 12); -+ -+ my ($trailer_len, $data_len, $magic) = unpack('VVa4', $last_bytes); -+ -+ if (($magic ne $trailer_magic) || ($data_len != 4)) -+ { -+ print ("* no trailer\n"); -+ return undef; -+ } -+ if (!seek($fh, -12, SEEK_END)) -+ { -+ print ("* seek error in '$kernel_file'\n"); -+ return undef; -+ } -+ -+ $trailer_len -= 12; -+ -+ while ($trailer_len > 0) -+ { -+ if ($trailer_len < 8) -+ { -+ print ("* truncated atom header in trailer\n"); -+ return undef; -+ } -+ if (!seek($fh, -8, SEEK_CUR)) -+ { -+ print ("* seek error in '$kernel_file'\n"); -+ return undef; -+ } -+ $trailer_len -= 8; -+ -+ my $atom_hdr; -+ sysread($fh, $atom_hdr, 8); -+ my ($atom_len, $atom_type) = unpack('Va4', $atom_hdr); -+ -+ if ($trailer_len < $atom_len) -+ { -+ print ("* truncated atom data in trailer\n"); -+ return undef; -+ } -+ -+ my $rounded_len = (($atom_len + 3) & ~3); -+ if (!seek($fh, -(8 + $rounded_len), SEEK_CUR)) -+ { -+ print ("* seek error in '$kernel_file'\n"); -+ return undef; -+ } -+ $trailer_len -= $rounded_len; -+ -+ my $atom_data; -+ sysread($fh, $atom_data, $atom_len); -+ -+ if (!seek($fh, -$atom_len, SEEK_CUR)) -+ { -+ print ("* seek error in '$kernel_file'\n"); -+ return undef; -+ } -+ -+ push @$atoms, [ $atom_type, $atom_data ]; -+ } -+ -+ if (($$atoms[-1][0] eq "\x00\x00\x00\x00") && -+ ($$atoms[-1][1] eq "")) -+ { -+ pop @$atoms; -+ } -+ else -+ { -+ print ("* end marker missing from trailer\n"); -+ } -+ -+ return ($atoms, tell($fh)); -+} -+ -+sub format_atom -+{ -+ my ($atom) = @_; -+ -+ my $format_func = $atom_formats{$atom->[0]} || \&format_hex; -+ return $format_func->($atom->[1]); -+} -+ -+sub format_bool -+{ -+ my ($data) = @_; -+ return unpack('V', $data) ? 'true' : 'false'; -+} -+ -+sub format_int -+{ -+ my ($data) = @_; -+ return unpack('V', $data); -+} -+ -+sub format_string -+{ -+ my ($data) = @_; -+ return '"'.$data.'"'; -+} -+ -+sub format_hex -+{ -+ my ($data) = @_; -+ return unpack('H*', $data); -+} ---- /dev/null -+++ b/scripts/mkknlimg -@@ -0,0 +1,244 @@ -+#!/usr/bin/env perl -+# ---------------------------------------------------------------------- -+# mkknlimg by Phil Elwell for Raspberry Pi -+# based on extract-ikconfig by Dick Streefland -+# -+# (c) 2009,2010 Dick Streefland -+# (c) 2014,2015 Raspberry Pi (Trading) Limited -+# -+# Licensed under the terms of the GNU General Public License. -+# ---------------------------------------------------------------------- -+ -+use strict; -+use warnings; -+use integer; -+ -+my $trailer_magic = 'RPTL'; -+ -+my $tmpfile1 = "/tmp/mkknlimg_$$.1"; -+my $tmpfile2 = "/tmp/mkknlimg_$$.2"; -+ -+my $dtok = 0; -+my $is_283x = 0; -+ -+while (@ARGV && ($ARGV[0] =~ /^-/)) -+{ -+ my $arg = shift(@ARGV); -+ if ($arg eq '--dtok') -+ { -+ $dtok = 1; -+ } -+ elsif ($arg eq '--283x') -+ { -+ $is_283x = 1; -+ } -+ else -+ { -+ print ("* Unknown option '$arg'\n"); -+ usage(); -+ } -+} -+ -+usage() if (@ARGV != 2); -+ -+my $kernel_file = $ARGV[0]; -+my $out_file = $ARGV[1]; -+ -+if (! -r $kernel_file) -+{ -+ print ("* File '$kernel_file' not found\n"); -+ usage(); -+} -+ -+my @wanted_strings = -+( -+ 'bcm2708_fb', -+ 'brcm,bcm2835-mmc', -+ 'brcm,bcm2835-sdhost', -+ 'brcm,bcm2708-pinctrl', -+ 'brcm,bcm2835-gpio', -+ 'brcm,bcm2835', -+ 'brcm,bcm2836' -+); -+ -+my $res = try_extract($kernel_file, $tmpfile1); -+$res = try_decompress('\037\213\010', 'xy', 'gunzip', 0, -+ $kernel_file, $tmpfile1, $tmpfile2) if (!$res); -+$res = try_decompress('\3757zXZ\000', 'abcde', 'unxz --single-stream', -1, -+ $kernel_file, $tmpfile1, $tmpfile2) if (!$res); -+$res = try_decompress('BZh', 'xy', 'bunzip2', 0, -+ $kernel_file, $tmpfile1, $tmpfile2) if (!$res); -+$res = try_decompress('\135\0\0\0', 'xxx', 'unlzma', 0, -+ $kernel_file, $tmpfile1, $tmpfile2) if (!$res); -+$res = try_decompress('\211\114\132', 'xy', 'lzop -d', 0, -+ $kernel_file, $tmpfile1, $tmpfile2) if (!$res); -+$res = try_decompress('\002\041\114\030', 'xy', 'lz4 -d', 1, -+ $kernel_file, $tmpfile1, $tmpfile2) if (!$res); -+ -+my $append_trailer; -+my $trailer; -+my $kver = '?'; -+ -+$append_trailer = $dtok; -+ -+if ($res) -+{ -+ $kver = $res->{''} || '?'; -+ print("Version: $kver\n"); -+ -+ $append_trailer = $dtok; -+ if (!$dtok) -+ { -+ if (config_bool($res, 'bcm2708_fb') || -+ config_bool($res, 'brcm,bcm2835-mmc') || -+ config_bool($res, 'brcm,bcm2835-sdhost')) -+ { -+ $dtok ||= config_bool($res, 'brcm,bcm2708-pinctrl'); -+ $dtok ||= config_bool($res, 'brcm,bcm2835-gpio'); -+ $is_283x ||= config_bool($res, 'brcm,bcm2835'); -+ $is_283x ||= config_bool($res, 'brcm,bcm2836'); -+ $dtok ||= $is_283x; -+ $append_trailer = 1; -+ } -+ else -+ { -+ print ("* This doesn't look like a Raspberry Pi kernel. In pass-through mode.\n"); -+ } -+ } -+} -+elsif (!$dtok) -+{ -+ print ("* Is this a valid kernel? In pass-through mode.\n"); -+} -+ -+if ($append_trailer) -+{ -+ printf("DT: %s\n", $dtok ? "y" : "n"); -+ printf("283x: %s\n", $is_283x ? "y" : "n"); -+ -+ my @atoms; -+ -+ push @atoms, [ $trailer_magic, pack('V', 0) ]; -+ push @atoms, [ 'KVer', $kver ]; -+ push @atoms, [ 'DTOK', pack('V', $dtok) ]; -+ push @atoms, [ '283x', pack('V', $is_283x) ]; -+ -+ $trailer = pack_trailer(\@atoms); -+ $atoms[0]->[1] = pack('V', length($trailer)); -+ -+ $trailer = pack_trailer(\@atoms); -+} -+ -+my $ofh; -+my $total_len = 0; -+ -+if ($out_file eq $kernel_file) -+{ -+ die "* Failed to open '$out_file' for append\n" -+ if (!open($ofh, '>>', $out_file)); -+ $total_len = tell($ofh); -+} -+else -+{ -+ die "* Failed to open '$kernel_file'\n" -+ if (!open(my $ifh, '<', $kernel_file)); -+ die "* Failed to create '$out_file'\n" -+ if (!open($ofh, '>', $out_file)); -+ -+ my $copybuf; -+ while (1) -+ { -+ my $bytes = sysread($ifh, $copybuf, 64*1024); -+ last if (!$bytes); -+ syswrite($ofh, $copybuf, $bytes); -+ $total_len += $bytes; -+ } -+ close($ifh); -+} -+ -+if ($trailer) -+{ -+ # Pad to word-alignment -+ syswrite($ofh, "\x000\x000\x000", (-$total_len & 0x3)); -+ syswrite($ofh, $trailer); -+} -+ -+close($ofh); -+ -+exit($trailer ? 0 : 1); -+ -+END { -+ unlink($tmpfile1) if ($tmpfile1); -+ unlink($tmpfile2) if ($tmpfile2); -+} -+ -+ -+sub usage -+{ -+ print ("Usage: mkknlimg [--dtok] [--283x] \n"); -+ exit(1); -+} -+ -+sub try_extract -+{ -+ my ($knl, $tmp) = @_; -+ -+ my $ver = `strings "$knl" | grep -a -E "^Linux version [1-9]"`; -+ -+ return undef if (!$ver); -+ -+ chomp($ver); -+ -+ my $res = { ''=>$ver }; -+ my $string_pattern = '^('.join('|', @wanted_strings).')$'; -+ -+ my @matches = `strings \"$knl\" | grep -E \"$string_pattern\"`; -+ foreach my $match (@matches) -+ { -+ chomp($match); -+ $res->{$match} = 1; -+ } -+ -+ return $res; -+} -+ -+ -+sub try_decompress -+{ -+ my ($magic, $subst, $zcat, $idx, $knl, $tmp1, $tmp2) = @_; -+ -+ my $pos = `tr "$magic\n$subst" "\n$subst=" < "$knl" | grep -abo "^$subst"`; -+ if ($pos) -+ { -+ chomp($pos); -+ $pos = (split(/[\r\n]+/, $pos))[$idx]; -+ return undef if (!defined($pos)); -+ $pos =~ s/:.*[\r\n]*$//s; -+ my $cmd = "tail -c+$pos \"$knl\" | $zcat > $tmp2 2> /dev/null"; -+ my $err = (system($cmd) >> 8); -+ return undef if (($err != 0) && ($err != 2)); -+ -+ return try_extract($tmp2, $tmp1); -+ } -+ -+ return undef; -+} -+ -+sub pack_trailer -+{ -+ my ($atoms) = @_; -+ my $trailer = pack('VV', 0, 0); -+ for (my $i = $#$atoms; $i>=0; $i--) -+ { -+ my $atom = $atoms->[$i]; -+ $trailer .= pack('a*x!4Va4', $atom->[1], length($atom->[1]), $atom->[0]); -+ } -+ return $trailer; -+} -+ -+sub config_bool -+{ -+ my ($configs, $wanted) = @_; -+ my $val = $configs->{$wanted} || 'n'; -+ return (($val eq 'y') || ($val eq '1')); -+} diff --git a/target/linux/brcm2708/patches-4.4/0051-BCM2708-Add-core-Device-Tree-support.patch b/target/linux/brcm2708/patches-4.4/0051-BCM2708-Add-core-Device-Tree-support.patch new file mode 100644 index 0000000000..1ff1754877 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0051-BCM2708-Add-core-Device-Tree-support.patch @@ -0,0 +1,4564 @@ +From d1fdaf68df66e82bea1081b490ebeea414d69203 Mon Sep 17 00:00:00 2001 +From: notro +Date: Wed, 9 Jul 2014 14:46:08 +0200 +Subject: [PATCH 051/232] 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 + +Use dts-dirs feature for overlays. + +The kernel makefiles have a dts-dirs target that is for vendor subdirectories. + +Using this fixes the install_dtbs target, which previously did not install the overlays. + +BCM270X_DT: configure I2S DMA channels + +Signed-off-by: Matthias Reichl + +BCM270X_DT: switch to bcm2835-i2s + +I2S soundcard drivers with proper devicetree support (i.e. not linking +to the cpu_dai/platform via name but to cpu/platform via of_node) +will work out of the box without any modifications. + +When the kernel is compiled without devicetree support the platform +code will instantiate the bcm2708-i2s driver and I2S soundcard drivers +will link to it via name, as before. + +Signed-off-by: Matthias Reichl + +SDIO-overlay: add poll_once-boolean parameter + +Add paramter to toggle sdio-device-polling +done every second or once at boot-time. + +Signed-off-by: Patrick Boettcher + +BCM270X_DT: Make mmc overlay compatible with current firmware + +The original DT overlay logic followed a merge-then-patch procedure, +i.e. parameters are applied to the loaded overlay before the overlay +is merged into the base DTB. This sequence has been changed to +patch-then-merge, in order to support parameterised node names, and +to protect against bad overlays. As a result, overrides (parameters) +must only target labels in the overlay, but the overlay can obviously target nodes in the base DTB. + +mmc-overlay.dts (that switches back to the original mmc sdcard +driver) is the only overlay violating that rule, and this patch +fixes it. + +bcm270x_dt: Use the sdhost MMC controller by default + +The "mmc" overlay reverts to using the other controller. + +squash: Add cprman to dt + +BCM270X_DT: Use clk_core for I2C interfaces +--- + arch/arm/boot/dts/Makefile | 30 + + arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 145 +++++ + arch/arm/boot/dts/bcm2708-rpi-b.dts | 135 +++++ + arch/arm/boot/dts/bcm2708-rpi-cm.dts | 102 ++++ + arch/arm/boot/dts/bcm2708-rpi-cm.dtsi | 40 ++ + arch/arm/boot/dts/bcm2708.dtsi | 40 ++ + arch/arm/boot/dts/bcm2708_common.dtsi | 347 +++++++++++ + arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 145 +++++ + arch/arm/boot/dts/bcm2709.dtsi | 102 ++++ + arch/arm/boot/dts/bcm2835-rpi-cm.dts | 93 +++ + arch/arm/boot/dts/bcm2835-rpi-cm.dtsi | 30 + + arch/arm/boot/dts/overlays/Makefile | 69 +++ + arch/arm/boot/dts/overlays/README | 648 +++++++++++++++++++++ + arch/arm/boot/dts/overlays/ads7846-overlay.dts | 83 +++ + .../dts/overlays/bmp085_i2c-sensor-overlay.dts | 23 + + arch/arm/boot/dts/overlays/dht11-overlay.dts | 39 ++ + arch/arm/boot/dts/overlays/enc28j60-overlay.dts | 50 ++ + .../boot/dts/overlays/gpio-poweroff-overlay.dts | 34 ++ + .../boot/dts/overlays/hifiberry-amp-overlay.dts | 39 ++ + .../boot/dts/overlays/hifiberry-dac-overlay.dts | 34 ++ + .../dts/overlays/hifiberry-dacplus-overlay.dts | 39 ++ + .../boot/dts/overlays/hifiberry-digi-overlay.dts | 39 ++ + arch/arm/boot/dts/overlays/hy28a-overlay.dts | 87 +++ + arch/arm/boot/dts/overlays/hy28b-overlay.dts | 142 +++++ + arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts | 55 ++ + arch/arm/boot/dts/overlays/i2s-mmap-overlay.dts | 13 + + arch/arm/boot/dts/overlays/iqaudio-dac-overlay.dts | 39 ++ + .../boot/dts/overlays/iqaudio-dacplus-overlay.dts | 39 ++ + arch/arm/boot/dts/overlays/lirc-rpi-overlay.dts | 57 ++ + .../arm/boot/dts/overlays/mcp2515-can0-overlay.dts | 69 +++ + .../arm/boot/dts/overlays/mcp2515-can1-overlay.dts | 69 +++ + arch/arm/boot/dts/overlays/mmc-overlay.dts | 39 ++ + arch/arm/boot/dts/overlays/mz61581-overlay.dts | 111 ++++ + arch/arm/boot/dts/overlays/piscreen-overlay.dts | 96 +++ + .../dts/overlays/pitft28-resistive-overlay.dts | 115 ++++ + arch/arm/boot/dts/overlays/pps-gpio-overlay.dts | 34 ++ + arch/arm/boot/dts/overlays/pwm-2chan-overlay.dts | 46 ++ + arch/arm/boot/dts/overlays/pwm-overlay.dts | 42 ++ + arch/arm/boot/dts/overlays/raspidac3-overlay.dts | 45 ++ + arch/arm/boot/dts/overlays/rpi-dac-overlay.dts | 34 ++ + arch/arm/boot/dts/overlays/rpi-display-overlay.dts | 82 +++ + arch/arm/boot/dts/overlays/rpi-ft5406-overlay.dts | 17 + + arch/arm/boot/dts/overlays/rpi-proto-overlay.dts | 39 ++ + arch/arm/boot/dts/overlays/rpi-sense-overlay.dts | 47 ++ + arch/arm/boot/dts/overlays/sdhost-overlay.dts | 29 + + arch/arm/boot/dts/overlays/sdio-overlay.dts | 32 + + arch/arm/boot/dts/overlays/smi-dev-overlay.dts | 18 + + arch/arm/boot/dts/overlays/smi-nand-overlay.dts | 69 +++ + arch/arm/boot/dts/overlays/smi-overlay.dts | 37 ++ + .../boot/dts/overlays/spi-gpio35-39-overlay.dts | 31 + + arch/arm/boot/dts/overlays/tinylcd35-overlay.dts | 216 +++++++ + arch/arm/boot/dts/overlays/uart1-overlay.dts | 38 ++ + arch/arm/boot/dts/overlays/vga666-overlay.dts | 30 + + arch/arm/boot/dts/overlays/w1-gpio-overlay.dts | 39 ++ + .../boot/dts/overlays/w1-gpio-pullup-overlay.dts | 41 ++ + 55 files changed, 4203 insertions(+) + 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 100755 arch/arm/boot/dts/bcm2708-rpi-cm.dts + create mode 100644 arch/arm/boot/dts/bcm2708-rpi-cm.dtsi + create mode 100644 arch/arm/boot/dts/bcm2708.dtsi + create mode 100644 arch/arm/boot/dts/bcm2708_common.dtsi + 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/boot/dts/bcm2835-rpi-cm.dts + create mode 100644 arch/arm/boot/dts/bcm2835-rpi-cm.dtsi + create mode 100644 arch/arm/boot/dts/overlays/Makefile + create mode 100644 arch/arm/boot/dts/overlays/README + create mode 100644 arch/arm/boot/dts/overlays/ads7846-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/bmp085_i2c-sensor-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/dht11-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/enc28j60-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/hifiberry-amp-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/hifiberry-dac-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/hifiberry-digi-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/hy28a-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/hy28b-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/i2s-mmap-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/iqaudio-dac-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/iqaudio-dacplus-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/lirc-rpi-overlay.dts + create mode 100755 arch/arm/boot/dts/overlays/mcp2515-can0-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/mcp2515-can1-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/mmc-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/mz61581-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/piscreen-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/pitft28-resistive-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/pps-gpio-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/pwm-2chan-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/pwm-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/raspidac3-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/rpi-dac-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/rpi-display-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/rpi-ft5406-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/rpi-proto-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/rpi-sense-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/sdhost-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/sdio-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/smi-dev-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/smi-nand-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/smi-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/spi-gpio35-39-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/tinylcd35-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/uart1-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/vga666-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/w1-gpio-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/w1-gpio-pullup-overlay.dts + +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -1,5 +1,25 @@ + ifeq ($(CONFIG_OF),y) + ++dtb-$(CONFIG_ARCH_BCM2708) += bcm2708-rpi-b.dtb ++dtb-$(CONFIG_ARCH_BCM2708) += bcm2708-rpi-b-plus.dtb ++dtb-$(CONFIG_ARCH_BCM2708) += bcm2708-rpi-cm.dtb ++dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-cm.dtb ++dtb-$(CONFIG_ARCH_BCM2709) += bcm2709-rpi-2-b.dtb ++ ++# Raspberry Pi ++ifeq ($(CONFIG_ARCH_BCM2708),y) ++ RPI_DT_OVERLAYS=y ++endif ++ifeq ($(CONFIG_ARCH_BCM2709),y) ++ RPI_DT_OVERLAYS=y ++endif ++ifeq ($(CONFIG_ARCH_BCM2835),y) ++ RPI_DT_OVERLAYS=y ++endif ++ifeq ($(RPI_DT_OVERLAYS),y) ++ dts-dirs += overlays ++endif ++ + dtb-$(CONFIG_ARCH_ALPINE) += \ + alpine-db.dtb + dtb-$(CONFIG_MACH_ASM9260) += \ +@@ -777,10 +797,20 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += \ + mt8127-moose.dtb \ + mt8135-evbp1.dtb + dtb-$(CONFIG_ARCH_ZX) += zx296702-ad1.dtb ++ ++targets += dtbs dtbs_install ++targets += $(dtb-y) ++ + endif + + dtstree := $(srctree)/$(src) + dtb-$(CONFIG_OF_ALL_DTBS) := $(patsubst $(dtstree)/%.dts,%.dtb, $(wildcard $(dtstree)/*.dts)) + + always := $(dtb-y) ++subdir-y := $(dts-dirs) + clean-files := *.dtb ++ ++# Enable fixups to support overlays on BCM2708 platforms ++ifeq ($(RPI_DT_OVERLAYS),y) ++ DTC_FLAGS ?= -@ ++endif +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts +@@ -0,0 +1,145 @@ ++/dts-v1/; ++ ++#include "bcm2708.dtsi" ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ model = "Raspberry Pi Model B+"; ++}; ++ ++&gpio { ++ sdhost_pins: sdhost_pins { ++ brcm,pins = <48 49 50 51 52 53>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++ ++ spi0_pins: spi0_pins { ++ brcm,pins = <9 10 11>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++ ++ spi0_cs_pins: spi0_cs_pins { ++ brcm,pins = <8 7>; ++ brcm,function = <1>; /* output */ ++ }; ++ ++ 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 */ ++ }; ++}; ++ ++&sdhost { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdhost_pins>; ++ bus-width = <4>; ++ status = "okay"; ++}; ++ ++&fb { ++ status = "okay"; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&spi0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi0_pins &spi0_cs_pins>; ++ cs-gpios = <&gpio 8 1>, <&gpio 7 1>; ++ ++ 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>; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++}; ++ ++&i2s { ++ #sound-dai-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s_pins>; ++}; ++ ++&random { ++ status = "okay"; ++}; ++ ++&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>; ++ }; ++}; ++ ++/ { ++ __overrides__ { ++ uart0 = <&uart0>,"status"; ++ uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; ++ i2s = <&i2s>,"status"; ++ spi = <&spi0>,"status"; ++ i2c0 = <&i2c0>,"status"; ++ i2c1 = <&i2c1>,"status"; ++ i2c2_iknowwhatimdoing = <&i2c2>,"status"; ++ i2c0_baudrate = <&i2c0>,"clock-frequency:0"; ++ i2c1_baudrate = <&i2c1>,"clock-frequency:0"; ++ i2c2_baudrate = <&i2c2>,"clock-frequency:0"; ++ core_freq = <&clk_core>,"clock-frequency:0"; ++ ++ 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"; ++ ++ audio = <&audio>,"status"; ++ watchdog = <&watchdog>,"status"; ++ random = <&random>,"status"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts +@@ -0,0 +1,135 @@ ++/dts-v1/; ++ ++#include "bcm2708.dtsi" ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ model = "Raspberry Pi Model B"; ++}; ++ ++&gpio { ++ sdhost_pins: sdhost_pins { ++ brcm,pins = <48 49 50 51 52 53>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++ ++ spi0_pins: spi0_pins { ++ brcm,pins = <9 10 11>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++ ++ spi0_cs_pins: spi0_cs_pins { ++ brcm,pins = <8 7>; ++ brcm,function = <1>; /* output */ ++ }; ++ ++ 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 = <6>; /* alt2 */ ++ }; ++}; ++ ++&sdhost { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdhost_pins>; ++ bus-width = <4>; ++ status = "okay"; ++}; ++ ++&fb { ++ status = "okay"; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&spi0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi0_pins &spi0_cs_pins>; ++ cs-gpios = <&gpio 8 1>, <&gpio 7 1>; ++ ++ 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>; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++}; ++ ++&i2s { ++ #sound-dai-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s_pins>; ++}; ++ ++&random { ++ status = "okay"; ++}; ++ ++&leds { ++ act_led: act { ++ label = "led0"; ++ linux,default-trigger = "mmc0"; ++ gpios = <&gpio 16 1>; ++ }; ++}; ++ ++/ { ++ __overrides__ { ++ uart0 = <&uart0>,"status"; ++ uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; ++ i2s = <&i2s>,"status"; ++ spi = <&spi0>,"status"; ++ i2c0 = <&i2c0>,"status"; ++ i2c1 = <&i2c1>,"status"; ++ i2c2_iknowwhatimdoing = <&i2c2>,"status"; ++ i2c0_baudrate = <&i2c0>,"clock-frequency:0"; ++ i2c1_baudrate = <&i2c1>,"clock-frequency:0"; ++ i2c2_baudrate = <&i2c2>,"clock-frequency:0"; ++ core_freq = <&clk_core>,"clock-frequency:0"; ++ ++ act_led_gpio = <&act_led>,"gpios:4"; ++ act_led_activelow = <&act_led>,"gpios:8"; ++ act_led_trigger = <&act_led>,"linux,default-trigger"; ++ ++ audio = <&audio>,"status"; ++ watchdog = <&watchdog>,"status"; ++ random = <&random>,"status"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2708-rpi-cm.dts +@@ -0,0 +1,102 @@ ++/dts-v1/; ++ ++#include "bcm2708-rpi-cm.dtsi" ++ ++/ { ++ model = "Raspberry Pi Compute Module"; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&gpio { ++ spi0_pins: spi0_pins { ++ brcm,pins = <9 10 11>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++ ++ spi0_cs_pins: spi0_cs_pins { ++ brcm,pins = <8 7>; ++ brcm,function = <1>; /* output */ ++ }; ++ ++ 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 &spi0_cs_pins>; ++ cs-gpios = <&gpio 8 1>, <&gpio 7 1>; ++ ++ 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>; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++}; ++ ++&i2s { ++ #sound-dai-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s_pins>; ++}; ++ ++&random { ++ status = "okay"; ++}; ++ ++/ { ++ __overrides__ { ++ uart0 = <&uart0>,"status"; ++ uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; ++ i2s = <&i2s>,"status"; ++ spi = <&spi0>,"status"; ++ i2c0 = <&i2c0>,"status"; ++ i2c1 = <&i2c1>,"status"; ++ i2c2_iknowwhatimdoing = <&i2c2>,"status"; ++ i2c0_baudrate = <&i2c0>,"clock-frequency:0"; ++ i2c1_baudrate = <&i2c1>,"clock-frequency:0"; ++ i2c2_baudrate = <&i2c2>,"clock-frequency:0"; ++ core_freq = <&clk_core>,"clock-frequency:0"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2708-rpi-cm.dtsi +@@ -0,0 +1,40 @@ ++#include "bcm2708.dtsi" ++ ++&gpio { ++ sdhost_pins: sdhost_pins { ++ brcm,pins = <48 49 50 51 52 53>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++}; ++ ++&leds { ++ act_led: act { ++ label = "led0"; ++ linux,default-trigger = "mmc0"; ++ gpios = <&gpio 47 0>; ++ }; ++}; ++ ++&sdhost { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdhost_pins>; ++ bus-width = <4>; ++ non-removable; ++ status = "okay"; ++}; ++ ++&fb { ++ status = "okay"; ++}; ++ ++/ { ++ __overrides__ { ++ act_led_gpio = <&act_led>,"gpios:4"; ++ act_led_activelow = <&act_led>,"gpios:8"; ++ act_led_trigger = <&act_led>,"linux,default-trigger"; ++ ++ audio = <&audio>,"status"; ++ watchdog = <&watchdog>,"status"; ++ random = <&random>,"status"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2708.dtsi +@@ -0,0 +1,40 @@ ++#include "bcm2708_common.dtsi" ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ model = "BCM2708"; ++ ++ chosen { ++ /* No padding required - the boot loader can do that. */ ++ bootargs = ""; ++ }; ++ ++ soc { ++ ranges = <0x7e000000 0x20000000 0x01000000>; ++ ++ timer@7e003000 { ++ compatible = "brcm,bcm2835-system-timer"; ++ reg = <0x7e003000 0x1000>; ++ interrupts = <1 0>, <1 1>, <1 2>, <1 3>; ++ clock-frequency = <1000000>; ++ }; ++ ++ arm-pmu { ++ compatible = "arm,arm1176-pmu"; ++ }; ++ ++ gpiomem { ++ compatible = "brcm,bcm2835-gpiomem"; ++ reg = <0x7e200000 0x1000>; ++ status = "okay"; ++ }; ++ }; ++}; ++ ++&intc { ++ compatible = "brcm,bcm2835-armctrl-ic"; ++}; ++ ++&watchdog { ++ status = "okay"; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2708_common.dtsi +@@ -0,0 +1,347 @@ ++#include "skeleton.dtsi" ++ ++/ { ++ interrupt-parent = <&intc>; ++ ++ aliases { ++ audio = &audio; ++ sound = &sound; ++ soc = &soc; ++ dma = &dma; ++ intc = &intc; ++ watchdog = &watchdog; ++ random = &random; ++ mailbox = &mailbox; ++ gpio = &gpio; ++ uart0 = &uart0; ++ sdhost = &sdhost; ++ i2s = &i2s; ++ spi0 = &spi0; ++ i2c0 = &i2c0; ++ uart1 = &uart1; ++ mmc = &mmc; ++ i2c1 = &i2c1; ++ i2c2 = &i2c2; ++ usb = &usb; ++ leds = &leds; ++ fb = &fb; ++ vchiq = &vchiq; ++ thermal = &thermal; ++ clocks = &clocks; ++ }; ++ ++ /* Onboard audio */ ++ audio: audio { ++ compatible = "brcm,bcm2835-audio"; ++ brcm,pwm-channels = <8>; ++ status = "disabled"; ++ }; ++ ++ /* External sound card */ ++ sound: sound { ++ }; ++ ++ soc: soc { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ dma: dma@7e007000 { ++ compatible = "brcm,bcm2835-dma"; ++ reg = <0x7e007000 0xf00>; ++ interrupts = <1 16>, ++ <1 17>, ++ <1 18>, ++ <1 19>, ++ <1 20>, ++ <1 21>, ++ <1 22>, ++ <1 23>, ++ <1 24>, ++ <1 25>, ++ <1 26>, ++ <1 27>; ++ ++ #dma-cells = <1>; ++ brcm,dma-channel-mask = <0x0f35>; ++ }; ++ ++ intc: interrupt-controller@7e00b200 { ++ compatible = "brcm,bcm2708-armctrl-ic"; ++ reg = <0x7e00b200 0x200>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ mailbox: mailbox@7e00b800 { ++ compatible = "brcm,bcm2835-mbox"; ++ reg = <0x7e00b880 0x40>; ++ interrupts = <0 1>; ++ #mbox-cells = <0>; ++ }; ++ ++ watchdog: watchdog@7e100000 { ++ compatible = "brcm,bcm2835-pm-wdt"; ++ reg = <0x7e100000 0x28>; ++ status = "disabled"; ++ }; ++ ++ cprman: cprman@7e101000 { ++ compatible = "brcm,bcm2835-cprman"; ++ #clock-cells = <1>; ++ reg = <0x7e101000 0x2000>; ++ ++ /* CPRMAN derives everything from the platform's ++ * oscillator. ++ */ ++ clocks = <&clk_osc>; ++ status = "disabled"; ++ }; ++ ++ random: rng@7e104000 { ++ compatible = "brcm,bcm2835-rng"; ++ reg = <0x7e104000 0x10>; ++ status = "disabled"; ++ }; ++ ++ gpio: gpio@7e200000 { ++ compatible = "brcm,bcm2835-gpio"; ++ reg = <0x7e200000 0xb4>; ++ interrupts = <2 17>, <2 18>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ uart0: uart@7e201000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x7e201000 0x1000>; ++ interrupts = <2 25>; ++ clocks = <&clk_uart0 &clk_apb_p>; ++ clock-names = "uartclk","apb_pclk"; ++ arm,primecell-periphid = <0x00241011>; // For an explanation, see ++ // https://github.com/raspberrypi/linux/commit/13731d862cf5219216533a3b0de052cee4cc5038 ++ status = "disabled"; ++ }; ++ ++ sdhost: sdhost@7e202000 { ++ compatible = "brcm,bcm2835-sdhost"; ++ reg = <0x7e202000 0x100>; ++ interrupts = <2 24>; ++ clocks = <&clk_core>; ++ dmas = <&dma 13>, ++ <&dma 13>; ++ dma-names = "tx", "rx"; ++ brcm,pio-limit = <1>; ++ status = "disabled"; ++ }; ++ ++ i2s: i2s@7e203000 { ++ compatible = "brcm,bcm2835-i2s"; ++ reg = <0x7e203000 0x24>, ++ <0x7e101098 0x08>; ++ ++ dmas = <&dma 2>, <&dma 3>; ++ dma-names = "tx", "rx"; ++ status = "disabled"; ++ }; ++ ++ spi0: spi@7e204000 { ++ compatible = "brcm,bcm2835-spi"; ++ reg = <0x7e204000 0x1000>; ++ interrupts = <2 22>; ++ clocks = <&clk_core>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ /* the dma channels */ ++ dmas = <&dma 6>, <&dma 7>; ++ dma-names = "tx", "rx"; ++ /* the chipselects used - <0> means native GPIO ++ * add more gpios if necessary as <&gpio 6 1> ++ * (but do not forget to make them output!) ++ */ ++ cs-gpios = <0>, <0>; ++ }; ++ ++ i2c0: i2c@7e205000 { ++ compatible = "brcm,bcm2708-i2c"; ++ reg = <0x7e205000 0x1000>; ++ interrupts = <2 21>; ++ clocks = <&clk_core>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ pwm: pwm@7e20c000 { ++ compatible = "brcm,bcm2835-pwm"; ++ reg = <0x7e20c000 0x28>; ++ clocks = <&clk_pwm>; ++ #pwm-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ uart1: uart@7e215040 { ++ compatible = "brcm,bcm2835-aux-uart", "ns16550"; ++ reg = <0x7e215040 0x40>; ++ interrupts = <1 29>; ++ clocks = <&clk_uart1>; ++ reg-shift = <2>; ++ no-loopback-test; ++ status = "disabled"; ++ }; ++ ++ 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"; ++ }; ++ ++ i2c1: i2c@7e804000 { ++ compatible = "brcm,bcm2708-i2c"; ++ reg = <0x7e804000 0x1000>; ++ interrupts = <2 21>; ++ clocks = <&clk_core>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2c2: i2c@7e805000 { ++ // Beware - this is shared with the HDMI module. ++ // Careless use may break (really) your display. ++ // Caveat emptor. ++ compatible = "brcm,bcm2708-i2c"; ++ reg = <0x7e805000 0x1000>; ++ interrupts = <2 21>; ++ clocks = <&clk_core>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ smi: smi@7e600000 { ++ compatible = "brcm,bcm2835-smi"; ++ reg = <0x7e600000 0x44>, <0x7e1010b0 0x8>; ++ interrupts = <2 16>; ++ brcm,smi-clock-source = <6>; ++ brcm,smi-clock-divisor = <4>; ++ dmas = <&dma 4>; ++ dma-names = "rx-tx"; ++ status = "disabled"; ++ }; ++ ++ usb: usb@7e980000 { ++ compatible = "brcm,bcm2708-usb"; ++ reg = <0x7e980000 0x10000>, ++ <0x7e006000 0x1000>; ++ interrupts = <2 0>, ++ <1 9>; ++ }; ++ ++ firmware: firmware { ++ compatible = "raspberrypi,bcm2835-firmware"; ++ mboxes = <&mailbox>; ++ }; ++ ++ leds: leds { ++ compatible = "gpio-leds"; ++ }; ++ ++ fb: fb { ++ compatible = "brcm,bcm2708-fb"; ++ firmware = <&firmware>; ++ status = "disabled"; ++ }; ++ ++ vchiq: vchiq { ++ compatible = "brcm,bcm2835-vchiq"; ++ reg = <0x7e00b840 0xf>; ++ interrupts = <0 2>; ++ cache-line-size = <32>; ++ firmware = <&firmware>; ++ }; ++ ++ thermal: thermal { ++ compatible = "brcm,bcm2835-thermal"; ++ firmware = <&firmware>; ++ }; ++ }; ++ ++ clocks: clocks { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ clk_core: clock@0 { ++ compatible = "fixed-clock"; ++ reg = <0>; ++ #clock-cells = <0>; ++ clock-output-names = "core"; ++ clock-frequency = <250000000>; ++ }; ++ ++ clk_mmc: clock@1 { ++ compatible = "fixed-clock"; ++ reg = <1>; ++ #clock-cells = <0>; ++ clock-output-names = "mmc"; ++ clock-frequency = <250000000>; ++ }; ++ ++ clk_uart0: clock@2 { ++ compatible = "fixed-clock"; ++ reg = <2>; ++ #clock-cells = <0>; ++ clock-output-names = "uart0_pclk"; ++ clock-frequency = <3000000>; ++ }; ++ ++ clk_apb_p: clock@3 { ++ compatible = "fixed-clock"; ++ reg = <3>; ++ #clock-cells = <0>; ++ clock-output-names = "apb_pclk"; ++ clock-frequency = <126000000>; ++ }; ++ ++ clk_pwm: clock@4 { ++ compatible = "fixed-clock"; ++ reg = <4>; ++ #clock-cells = <0>; ++ clock-output-names = "pwm"; ++ clock-frequency = <100000000>; ++ }; ++ ++ clk_uart1: clock@5 { ++ compatible = "fixed-factor-clock"; ++ reg = <5>; ++ clocks = <&clk_core>; ++ #clock-cells = <0>; ++ clock-div = <1>; ++ clock-mult = <2>; ++ }; ++ ++ /* The oscillator is the root of the clock tree. */ ++ clk_osc: clock@6 { ++ compatible = "fixed-clock"; ++ reg = <6>; ++ #clock-cells = <0>; ++ clock-output-names = "osc"; ++ clock-frequency = <19200000>; ++ }; ++ }; ++ ++ __overrides__ { ++ cache_line_size = <&vchiq>, "cache-line-size:0"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts +@@ -0,0 +1,145 @@ ++/dts-v1/; ++ ++#include "bcm2709.dtsi" ++ ++/ { ++ compatible = "brcm,bcm2709"; ++ model = "Raspberry Pi 2 Model B"; ++}; ++ ++&gpio { ++ sdhost_pins: sdhost_pins { ++ brcm,pins = <48 49 50 51 52 53>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++ ++ spi0_pins: spi0_pins { ++ brcm,pins = <9 10 11>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++ ++ spi0_cs_pins: spi0_cs_pins { ++ brcm,pins = <8 7>; ++ brcm,function = <1>; /* output */ ++ }; ++ ++ 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 */ ++ }; ++}; ++ ++&sdhost { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdhost_pins>; ++ bus-width = <4>; ++ status = "okay"; ++}; ++ ++&fb { ++ status = "okay"; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&spi0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi0_pins &spi0_cs_pins>; ++ cs-gpios = <&gpio 8 1>, <&gpio 7 1>; ++ ++ 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>; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++}; ++ ++&i2s { ++ #sound-dai-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s_pins>; ++}; ++ ++&random { ++ status = "okay"; ++}; ++ ++&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>; ++ }; ++}; ++ ++/ { ++ __overrides__ { ++ uart0 = <&uart0>,"status"; ++ uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; ++ i2s = <&i2s>,"status"; ++ spi = <&spi0>,"status"; ++ i2c0 = <&i2c0>,"status"; ++ i2c1 = <&i2c1>,"status"; ++ i2c2_iknowwhatimdoing = <&i2c2>,"status"; ++ i2c0_baudrate = <&i2c0>,"clock-frequency:0"; ++ i2c1_baudrate = <&i2c1>,"clock-frequency:0"; ++ i2c2_baudrate = <&i2c2>,"clock-frequency:0"; ++ core_freq = <&clk_core>,"clock-frequency:0"; ++ ++ 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"; ++ ++ audio = <&audio>,"status"; ++ watchdog = <&watchdog>,"status"; ++ random = <&random>,"status"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2709.dtsi +@@ -0,0 +1,102 @@ ++#include "bcm2708_common.dtsi" ++ ++/ { ++ compatible = "brcm,bcm2709"; ++ model = "BCM2709"; ++ ++ chosen { ++ /* No padding required - the boot loader can do that. */ ++ bootargs = ""; ++ }; ++ ++ soc { ++ ranges = <0x7e000000 0x3f000000 0x01000000>, ++ <0x40000000 0x40000000 0x00040000>; ++ ++ local_intc: local_intc { ++ compatible = "brcm,bcm2836-l1-intc"; ++ reg = <0x40000000 0x100>; ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ interrupt-parent = <&local_intc>; ++ }; ++ ++ arm-pmu { ++ compatible = "arm,cortex-a7-pmu"; ++ interrupt-parent = <&local_intc>; ++ interrupts = <9>; ++ }; ++ ++ gpiomem { ++ compatible = "brcm,bcm2835-gpiomem"; ++ reg = <0x7e200000 0x1000>; ++ status = "okay"; ++ }; ++ ++ timer { ++ compatible = "arm,armv7-timer"; ++ clock-frequency = <19200000>; ++ interrupt-parent = <&local_intc>; ++ interrupts = <0>, // PHYS_SECURE_PPI ++ <1>, // PHYS_NONSECURE_PPI ++ <3>, // VIRT_PPI ++ <2>; // HYP_PPI ++ always-on; ++ }; ++ ++ syscon@40000000 { ++ compatible = "brcm,bcm2836-arm-local", "syscon"; ++ reg = <0x40000000 0x100>; ++ }; ++ }; ++ ++ 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"; ++ }; ++}; ++ ++&watchdog { ++ status = "okay"; ++}; ++ ++&intc { ++ compatible = "brcm,bcm2836-armctrl-ic"; ++ interrupt-parent = <&local_intc>; ++ interrupts = <8>; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2835-rpi-cm.dts +@@ -0,0 +1,93 @@ ++/dts-v1/; ++ ++#include "bcm2835-rpi-cm.dtsi" ++ ++/ { ++ model = "Raspberry Pi Compute Module"; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&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>; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++}; ++ ++&i2s { ++ #sound-dai-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s_pins>; ++}; ++ ++/ { ++ __overrides__ { ++ uart0 = <&uart0>,"status"; ++ uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; ++ uart1_clkrate = <&uart1>,"clock-frequency:0"; ++ i2s = <&i2s>,"status"; ++ spi = <&spi0>,"status"; ++ i2c0 = <&i2c0>,"status"; ++ i2c1 = <&i2c1>,"status"; ++ i2c2_iknowwhatimdoing = <&i2c2>,"status"; ++ i2c0_baudrate = <&i2c0>,"clock-frequency:0"; ++ i2c1_baudrate = <&i2c1>,"clock-frequency:0"; ++ i2c2_baudrate = <&i2c2>,"clock-frequency:0"; ++ core_freq = <&clk_core>,"clock-frequency:0"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2835-rpi-cm.dtsi +@@ -0,0 +1,30 @@ ++#include "bcm2835.dtsi" ++ ++&leds { ++ act_led: act { ++ label = "led0"; ++ linux,default-trigger = "mmc0"; ++ gpios = <&gpio 47 0>; ++ }; ++}; ++ ++&mmc { ++ status = "okay"; ++ bus-width = <4>; ++}; ++ ++&fb { ++ status = "okay"; ++}; ++ ++/ { ++ __overrides__ { ++ act_led_gpio = <&act_led>,"gpios:4"; ++ act_led_activelow = <&act_led>,"gpios:8"; ++ act_led_trigger = <&act_led>,"linux,default-trigger"; ++ ++ audio = <&audio>,"status"; ++ watchdog = <&watchdog>,"status"; ++ random = <&random>,"status"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -0,0 +1,69 @@ ++ifeq ($(CONFIG_OF),y) ++ ++# Overlays for the Raspberry Pi platform ++ ++ifeq ($(CONFIG_ARCH_BCM2708),y) ++ RPI_DT_OVERLAYS=y ++endif ++ifeq ($(CONFIG_ARCH_BCM2709),y) ++ RPI_DT_OVERLAYS=y ++endif ++ifeq ($(CONFIG_ARCH_BCM2835),y) ++ RPI_DT_OVERLAYS=y ++endif ++ ++dtb-$(RPI_DT_OVERLAYS) += ads7846-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += bmp085_i2c-sensor-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += dht11-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += enc28j60-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += gpio-poweroff-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-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) += hy28a-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += hy28b-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += i2c-rtc-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += i2s-mmap-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) += mcp2515-can0-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += mcp2515-can1-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += mmc-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += mz61581-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += piscreen-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += pitft28-resistive-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += pwm-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += pwm-2chan-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += raspidac3-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += rpi-dac-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += rpi-display-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += rpi-ft5406-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += rpi-proto-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += rpi-sense-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += sdhost-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += sdio-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += smi-dev-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += smi-nand-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += smi-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += spi-gpio35-39-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += tinylcd35-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += uart1-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += vga666-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += w1-gpio-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += w1-gpio-pullup-overlay.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 +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/README +@@ -0,0 +1,648 @@ ++Introduction ++============ ++ ++This directory contains Device Tree overlays. Device Tree makes it possible ++to support many hardware configurations with a single kernel and without the ++need to explicitly load or blacklist kernel modules. Note that this isn't a ++"pure" Device Tree configuration (c.f. MACH_BCM2835) - some on-board devices ++are still configured by the board support code, but the intention is to ++eventually reach that goal. ++ ++On Raspberry Pi, Device Tree usage is controlled from /boot/config.txt. By ++default, the Raspberry Pi kernel boots with device tree enabled. You can ++completely disable DT usage (for now) by adding: ++ ++ device_tree= ++ ++to your config.txt, which should cause your Pi to revert to the old way of ++doing things after a reboot. ++ ++In /boot you will find a .dtb for each base platform. This describes the ++hardware that is part of the Raspberry Pi board. The loader (start.elf and its ++siblings) selects the .dtb file appropriate for the platform by name, and reads ++it into memory. At this point, all of the optional interfaces (i2c, i2s, spi) ++are disabled, but they can be enabled using Device Tree parameters: ++ ++ dtparam=i2c=on,i2s=on,spi=on ++ ++However, this shouldn't be necessary in many use cases because loading an ++overlay that requires one of those interfaces will cause it to be enabled ++automatically, and it is advisable to only enable interfaces if they are ++needed. ++ ++Configuring additional, optional hardware is done using Device Tree overlays ++(see below). ++ ++raspi-config ++============ ++ ++The Advanced Options section of the raspi-config utility can enable and disable ++Device Tree use, as well as toggling the I2C and SPI interfaces. Note that it ++is possible to both enable an interface and blacklist the driver, if for some ++reason you should want to defer the loading. ++ ++Modules ++======= ++ ++As well as describing the hardware, Device Tree also gives enough information ++to allow suitable driver modules to be located and loaded, with the corollary ++that unneeded modules are not loaded. As a result it should be possible to ++remove lines from /etc/modules, and /etc/modprobe.d/raspi-blacklist.conf can ++have its contents deleted (or commented out). ++ ++Using Overlays ++============== ++ ++Overlays are loaded using the "dtoverlay" directive. As an example, consider the ++popular lirc-rpi module, the Linux Infrared Remote Control driver. In the ++pre-DT world this would be loaded from /etc/modules, with an explicit ++"modprobe lirc-rpi" command, or programmatically by lircd. With DT enabled, ++this becomes a line in config.txt: ++ ++ dtoverlay=lirc-rpi ++ ++This causes the file /boot/overlays/lirc-rpi-overlay.dtb to be loaded. By ++default it will use GPIOs 17 (out) and 18 (in), but this can be modified using ++DT parameters: ++ ++ dtoverlay=lirc-rpi,gpio_out_pin=17,gpio_in_pin=13 ++ ++Parameters always have default values, although in some cases (e.g. "w1-gpio") ++it is necessary to provided multiple overlays in order to get the desired ++behaviour. See the list of overlays below for a description of the parameters and their defaults. ++ ++The Overlay and Parameter Reference ++=================================== ++ ++N.B. When editing this file, please preserve the indentation levels to make it simple to parse ++programmatically. NO HARD TABS. ++ ++ ++Name: ++Info: Configures the base Raspberry Pi hardware ++Load: ++Params: ++ audio Set to "on" to enable the onboard ALSA audio ++ interface (default "off") ++ ++ i2c_arm Set to "on" to enable the ARM's i2c interface ++ (default "off") ++ ++ i2c_vc Set to "on" to enable the i2c interface ++ usually reserved for the VideoCore processor ++ (default "off") ++ ++ i2c An alias for i2c_arm ++ ++ i2c_arm_baudrate Set the baudrate of the ARM's i2c interface ++ (default "100000") ++ ++ i2c_vc_baudrate Set the baudrate of the VideoCore i2c interface ++ (default "100000") ++ ++ i2c_baudrate An alias for i2c_arm_baudrate ++ ++ i2s Set to "on" to enable the i2s interface ++ (default "off") ++ ++ spi Set to "on" to enable the spi interfaces ++ (default "off") ++ ++ random Set to "on" to enable the hardware random ++ number generator (default "on") ++ ++ uart0 Set to "off" to disable uart0 (default "on") ++ ++ watchdog Set to "on" to enable the hardware watchdog ++ (default "off") ++ ++ act_led_trigger Choose which activity the LED tracks. ++ Use "heartbeat" for a nice load indicator. ++ (default "mmc") ++ ++ act_led_activelow Set to "on" to invert the sense of the LED ++ (default "off") ++ ++ act_led_gpio Set which GPIO to use for the activity LED ++ (in case you want to connect it to an external ++ device) ++ (default "16" on a non-Plus board, "47" on a ++ Plus or Pi 2) ++ ++ pwr_led_trigger ++ pwr_led_activelow ++ pwr_led_gpio ++ As for act_led_*, but using the PWR LED. ++ Not available on Model A/B boards. ++ ++ N.B. It is recommended to only enable those interfaces that are needed. ++ Leaving all interfaces enabled can lead to unwanted behaviour (i2c_vc ++ interfering with Pi Camera, I2S and SPI hogging GPIO pins, etc.) ++ Note also that i2c, i2c_arm and i2c_vc are aliases for the physical ++ interfaces i2c0 and i2c1. Use of the numeric variants is still possible ++ but deprecated because the ARM/VC assignments differ between board ++ revisions. The same board-specific mapping applies to i2c_baudrate, ++ and the other i2c baudrate parameters. ++ ++ ++Name: ads7846 ++Info: ADS7846 Touch controller ++Load: dtoverlay=ads7846,= ++Params: cs SPI bus Chip Select (default 1) ++ speed SPI bus speed (default 2Mhz, max 3.25MHz) ++ penirq GPIO used for PENIRQ. REQUIRED ++ penirq_pull Set GPIO pull (default 0=none, 2=pullup) ++ swapxy Swap x and y axis ++ xmin Minimum value on the X axis (default 0) ++ ymin Minimum value on the Y axis (default 0) ++ xmax Maximum value on the X axis (default 4095) ++ ymax Maximum value on the Y axis (default 4095) ++ pmin Minimum reported pressure value (default 0) ++ pmax Maximum reported pressure value (default 65535) ++ xohms Touchpanel sensitivity (X-plate resistance) ++ (default 400) ++ ++ penirq is required and usually xohms (60-100) has to be set as well. ++ Apart from that, pmax (255) and swapxy are also common. ++ The rest of the calibration can be done with xinput-calibrator. ++ See: github.com/notro/fbtft/wiki/FBTFT-on-Raspian ++ Device Tree binding document: ++ www.kernel.org/doc/Documentation/devicetree/bindings/input/ads7846.txt ++ ++ ++Name: bmp085_i2c-sensor ++Info: Configures the BMP085/BMP180 digital barometric pressure and temperature ++ sensors from Bosch Sensortec ++Load: dtoverlay=bmp085_i2c-sensor ++Params: ++ ++ ++Name: dht11 ++Info: Overlay for the DHT11/DHT21/DHT22 humidity/temperature sensors ++ Also sometimes found with the part number(s) AM230x. ++Load: dtoverlay=dht11,= ++Params: gpiopin GPIO connected to the sensor's DATA output. ++ (default 4) ++ ++ ++[ The ds1307-rtc overlay has been deleted. See i2c-rtc. ] ++ ++ ++Name: enc28j60 ++Info: Overlay for the Microchip ENC28J60 Ethernet Controller (SPI) ++Load: dtoverlay=enc28j60,= ++Params: int_pin GPIO used for INT (default 25) ++ ++ speed SPI bus speed (default 12000000) ++ ++ ++Name: gpio-poweroff ++Info: Drives a GPIO high or low on reboot ++Load: dtoverlay=gpio-poweroff,= ++Params: gpiopin GPIO for signalling (default 26) ++ ++ active_low Set if the power control device requires a ++ high->low transition to trigger a power-down. ++ Note that this will require the support of a ++ custom dt-blob.bin to prevent a power-down ++ during the boot process, and that a reboot ++ will also cause the pin to go low. ++ ++ ++Name: hifiberry-amp ++Info: Configures the HifiBerry Amp and Amp+ audio cards ++Load: dtoverlay=hifiberry-amp ++Params: ++ ++ ++Name: hifiberry-dac ++Info: Configures the HifiBerry DAC audio card ++Load: dtoverlay=hifiberry-dac ++Params: ++ ++ ++Name: hifiberry-dacplus ++Info: Configures the HifiBerry DAC+ audio card ++Load: dtoverlay=hifiberry-dacplus ++Params: ++ ++ ++Name: hifiberry-digi ++Info: Configures the HifiBerry Digi audio card ++Load: dtoverlay=hifiberry-digi ++Params: ++ ++ ++Name: hy28a ++Info: HY28A - 2.8" TFT LCD Display Module by HAOYU Electronics ++ Default values match Texy's display shield ++Load: dtoverlay=hy28a,= ++Params: speed Display SPI bus speed ++ ++ rotate Display rotation {0,90,180,270} ++ ++ fps Delay between frame updates ++ ++ debug Debug output level {0-7} ++ ++ xohms Touchpanel sensitivity (X-plate resistance) ++ ++ resetgpio GPIO used to reset controller ++ ++ ledgpio GPIO used to control backlight ++ ++ ++Name: hy28b ++Info: HY28B - 2.8" TFT LCD Display Module by HAOYU Electronics ++ Default values match Texy's display shield ++Load: dtoverlay=hy28b,= ++Params: speed Display SPI bus speed ++ ++ rotate Display rotation {0,90,180,270} ++ ++ fps Delay between frame updates ++ ++ debug Debug output level {0-7} ++ ++ xohms Touchpanel sensitivity (X-plate resistance) ++ ++ resetgpio GPIO used to reset controller ++ ++ ledgpio GPIO used to control backlight ++ ++ ++Name: i2c-rtc ++Info: Adds support for a number of I2C Real Time Clock devices ++Load: dtoverlay=i2c-rtc, ++Params: ds1307 Select the DS1307 device ++ ++ ds3231 Select the DS3231 device ++ ++ mcp7941x Select the MCP7941x device ++ ++ pcf2127 Select the PCF2127 device ++ ++ pcf8523 Select the PCF8523 device ++ ++ pcf8563 Select the PCF8563 device ++ ++ ++Name: i2s-mmap ++Info: Enables mmap support in the bcm2708-i2s driver ++Load: dtoverlay=i2s-mmap ++Params: ++ ++ ++Name: iqaudio-dac ++Info: Configures the IQaudio DAC audio card ++Load: dtoverlay=iqaudio-dac ++Params: ++ ++ ++Name: iqaudio-dacplus ++Info: Configures the IQaudio DAC+ audio card ++Load: dtoverlay=iqaudio-dacplus ++Params: ++ ++ ++Name: lirc-rpi ++Info: Configures lirc-rpi (Linux Infrared Remote Control for Raspberry Pi) ++ Consult the module documentation for more details. ++Load: dtoverlay=lirc-rpi,=,... ++Params: gpio_out_pin GPIO for output (default "17") ++ ++ gpio_in_pin GPIO for input (default "18") ++ ++ gpio_in_pull Pull up/down/off on the input pin ++ (default "down") ++ ++ sense Override the IR receive auto-detection logic: ++ "0" = force active-high ++ "1" = force active-low ++ "-1" = use auto-detection ++ (default "-1") ++ ++ softcarrier Turn the software carrier "on" or "off" ++ (default "on") ++ ++ invert "on" = invert the output pin (default "off") ++ ++ debug "on" = enable additional debug messages ++ (default "off") ++ ++ ++Name: mcp2515-can0 ++Info: Configures the MCP2515 CAN controller on spi0.0 ++Load: dtoverlay=mcp2515-can0,= ++Params: oscillator Clock frequency for the CAN controller (Hz) ++ ++ spimaxfrequency Maximum SPI frequence (Hz) ++ ++ interrupt GPIO for interrupt signal ++ ++ ++Name: mcp2515-can1 ++Info: Configures the MCP2515 CAN controller on spi0.1 ++Load: dtoverlay=mcp2515-can1,= ++Params: oscillator Clock frequency for the CAN controller (Hz) ++ ++ spimaxfrequency Maximum SPI frequence (Hz) ++ ++ interrupt GPIO for interrupt signal ++ ++ ++Name: mmc ++Info: Selects the bcm2835-mmc SD/MMC driver, optionally with overclock ++Load: dtoverlay=mmc,= ++Params: overclock_50 Clock (in MHz) to use when the MMC framework ++ requests 50MHz ++ force_pio Disable DMA support ++ ++ ++Name: mz61581 ++Info: MZ61581 display by Tontec ++Load: dtoverlay=mz61581,= ++Params: speed Display SPI bus speed ++ ++ rotate Display rotation {0,90,180,270} ++ ++ fps Delay between frame updates ++ ++ txbuflen Transmit buffer length (default 32768) ++ ++ debug Debug output level {0-7} ++ ++ xohms Touchpanel sensitivity (X-plate resistance) ++ ++ ++[ The pcf2127-rtc overlay has been deleted. See i2c-rtc. ] ++ ++ ++[ The pcf8523-rtc overlay has been deleted. See i2c-rtc. ] ++ ++ ++[ The pcf8563-rtc overlay has been deleted. See i2c-rtc. ] ++ ++ ++Name: piscreen ++Info: PiScreen display by OzzMaker.com ++Load: dtoverlay=piscreen,= ++Params: speed Display SPI bus speed ++ ++ rotate Display rotation {0,90,180,270} ++ ++ fps Delay between frame updates ++ ++ debug Debug output level {0-7} ++ ++ xohms Touchpanel sensitivity (X-plate resistance) ++ ++ ++Name: pitft28-resistive ++Info: Adafruit PiTFT 2.8" resistive touch screen ++Load: dtoverlay=pitft28-resistive,= ++Params: speed Display SPI bus speed ++ ++ rotate Display rotation {0,90,180,270} ++ ++ fps Delay between frame updates ++ ++ debug Debug output level {0-7} ++ ++ ++Name: pps-gpio ++Info: Configures the pps-gpio (pulse-per-second time signal via GPIO). ++Load: dtoverlay=pps-gpio,= ++Params: gpiopin Input GPIO (default "18") ++ ++ ++Name: pwm ++Info: Configures a single PWM channel ++ Legal pin,function combinations for each channel: ++ PWM0: 12,4(Alt0) 18,2(Alt5) 40,4(Alt0) 52,5(Alt1) ++ PWM1: 13,4(Alt0) 19,2(Alt5) 41,4(Alt0) 45,4(Alt0) 53,5(Alt1) ++ N.B.: ++ 1) Pin 18 is the only one available on all platforms, and ++ it is the one used by the I2S audio interface. ++ Pins 12 and 13 might be better choices on an A+, B+ or Pi2. ++ 2) The onboard analogue audio output uses both PWM channels. ++ 3) So be careful mixing audio and PWM. ++ 4) Currently the clock must have been enabled and configured ++ by other means. ++Load: dtoverlay=pwm,= ++Params: pin Output pin (default 18) - see table ++ func Pin function (default 2 = Alt5) - see above ++ clock PWM clock frequency (informational) ++ ++ ++Name: pwm-2chan ++Info: Configures both PWM channels ++ Legal pin,function combinations for each channel: ++ PWM0: 12,4(Alt0) 18,2(Alt5) 40,4(Alt0) 52,5(Alt1) ++ PWM1: 13,4(Alt0) 19,2(Alt5) 41,4(Alt0) 45,4(Alt0) 53,5(Alt1) ++ N.B.: ++ 1) Pin 18 is the only one available on all platforms, and ++ it is the one used by the I2S audio interface. ++ Pins 12 and 13 might be better choices on an A+, B+ or Pi2. ++ 2) The onboard analogue audio output uses both PWM channels. ++ 3) So be careful mixing audio and PWM. ++ 4) Currently the clock must have been enabled and configured ++ by other means. ++Load: dtoverlay=pwm-2chan,= ++Params: pin Output pin (default 18) - see table ++ pin2 Output pin for other channel (default 19) ++ func Pin function (default 2 = Alt5) - see above ++ func2 Function for pin2 (default 2 = Alt5) ++ clock PWM clock frequency (informational) ++ ++ ++Name: raspidac3 ++Info: Configures the RaspiDAV Rev.3x audio card ++Load: dtoverlay=raspidac3 ++Params: ++ ++ ++Name: rpi-dac ++Info: Configures the RPi DAC audio card ++Load: dtoverlay=rpi-dac ++Params: ++ ++ ++Name: rpi-display ++Info: RPi-Display - 2.8" Touch Display by Watterott ++Load: dtoverlay=rpi-display,= ++Params: speed Display SPI bus speed ++ ++ rotate Display rotation {0,90,180,270} ++ ++ fps Delay between frame updates ++ ++ debug Debug output level {0-7} ++ ++ xohms Touchpanel sensitivity (X-plate resistance) ++ ++ ++Name: rpi-ft5406 ++Info: Official Raspberry Pi display touchscreen ++Load: dtoverlay=rpi-ft5406 ++Params: ++ ++ ++Name: rpi-proto ++Info: Configures the RPi Proto audio card ++Load: dtoverlay=rpi-proto ++Params: ++ ++ ++Name: rpi-sense ++Info: Raspberry Pi Sense HAT ++Load: dtoverlay=rpi-sense ++Params: ++ ++ ++Name: sdhost ++Info: Selects the bcm2835-sdhost SD/MMC driver, optionally with overclock ++Load: dtoverlay=sdhost,= ++Params: overclock_50 Clock (in MHz) to use when the MMC framework ++ requests 50MHz ++ ++ force_pio Disable DMA support (default off) ++ ++ pio_limit Number of blocks above which to use DMA ++ (default 1) ++ ++ debug Enable debug output (default off) ++ ++ ++Name: sdio ++Info: Selects the bcm2835-sdhost SD/MMC driver, optionally with overclock, ++ and enables SDIO via GPIOs 22-27. ++Load: dtoverlay=sdio,= ++Params: overclock_50 Clock (in MHz) to use when the MMC framework ++ requests 50MHz ++ ++ force_pio Disable DMA support (default off) ++ ++ pio_limit Number of blocks above which to use DMA ++ (default 1) ++ ++ debug Enable debug output (default off) ++ ++ poll_once Disable SDIO-device polling every second ++ (default on: polling once at boot-time) ++ ++ ++Name: smi ++Info: Enables the Secondary Memory Interface peripheral. Uses GPIOs 2-25! ++Load: dtoverlay=smi ++Params: ++ ++ ++Name: smi-dev ++Info: Enables the userspace interface for the SMI driver ++Load: dtoverlay=smi-dev ++Params: ++ ++ ++Name: smi-nand ++Info: Enables access to NAND flash via the SMI interface ++Load: dtoverlay=smi-nand ++Params: ++ ++ ++Name: spi-gpio35-39 ++Info: move SPI function block to GPIO 35 to 39 ++Load: dtoverlay=spi-gpio35-39 ++Params: ++ ++ ++Name: tinylcd35 ++Info: 3.5" Color TFT Display by www.tinylcd.com ++ Options: Touch, RTC, keypad ++Load: dtoverlay=tinylcd35,= ++Params: speed Display SPI bus speed ++ ++ rotate Display rotation {0,90,180,270} ++ ++ fps Delay between frame updates ++ ++ debug Debug output level {0-7} ++ ++ touch Enable touch panel ++ ++ touchgpio Touch controller IRQ GPIO ++ ++ xohms Touchpanel: Resistance of X-plate in ohms ++ ++ rtc-pcf PCF8563 Real Time Clock ++ ++ rtc-ds DS1307 Real Time Clock ++ ++ keypad Enable keypad ++ ++ Examples: ++ Display with touchpanel, PCF8563 RTC and keypad: ++ dtoverlay=tinylcd35,touch,rtc-pcf,keypad ++ Old touch display: ++ dtoverlay=tinylcd35,touch,touchgpio=3 ++ ++ ++Name: uart1 ++Info: Enable uart1 in place of uart0 ++Load: dtoverlay=uart1,= ++Params: txd1_pin GPIO pin for TXD1 (14, 32 or 40 - default 14) ++ ++ rxd1_pin GPIO pin for RXD1 (15, 33 or 41 - default 15) ++ ++ ++Name: vga666 ++Info: Overlay for the Fen Logic VGA666 board ++ This uses GPIOs 2-21 (so no I2C), and activates the output 2-3 seconds ++ after the kernel has started. ++Load: dtoverlay=vga666 ++Params: ++ ++ ++Name: w1-gpio ++Info: Configures the w1-gpio Onewire interface module. ++ Use this overlay if you *don't* need a GPIO to drive an external pullup. ++Load: dtoverlay=w1-gpio,= ++Params: gpiopin GPIO for I/O (default "4") ++ ++ pullup Non-zero, "on", or "y" to enable the parasitic ++ power (2-wire, power-on-data) feature ++ ++ ++Name: w1-gpio-pullup ++Info: Configures the w1-gpio Onewire interface module. ++ Use this overlay if you *do* need a GPIO to drive an external pullup. ++Load: dtoverlay=w1-gpio-pullup,= ++Params: gpiopin GPIO for I/O (default "4") ++ ++ pullup Non-zero, "on", or "y" to enable the parasitic ++ power (2-wire, power-on-data) feature ++ ++ extpullup GPIO for external pullup (default "5") ++ ++ ++Troubleshooting ++=============== ++ ++If you are experiencing problems that you think are DT-related, enable DT ++diagnostic output by adding this to /boot/config.txt: ++ ++ dtdebug=on ++ ++and rebooting. Then run: ++ ++ sudo vcdbg log msg ++ ++and look for relevant messages. ++ ++Further reading ++=============== ++ ++This is only meant to be a quick introduction to the subject of Device Tree on ++Raspberry Pi. There is a more complete explanation here: ++ ++http://www.raspberrypi.org/documentation/configuration/device-tree.md +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/ads7846-overlay.dts +@@ -0,0 +1,83 @@ ++/* ++ * Generic Device Tree overlay for the ADS7846 touch controller ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ ads7846_pins: ads7846_pins { ++ brcm,pins = <255>; /* illegal default value */ ++ brcm,function = <0>; /* in */ ++ brcm,pull = <0>; /* none */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ ads7846: ads7846@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&ads7846_pins>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <255 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 255 0>; ++ ++ /* driver defaults */ ++ ti,x-min = /bits/ 16 <0>; ++ ti,y-min = /bits/ 16 <0>; ++ ti,x-max = /bits/ 16 <0x0FFF>; ++ ti,y-max = /bits/ 16 <0x0FFF>; ++ ti,pressure-min = /bits/ 16 <0>; ++ ti,pressure-max = /bits/ 16 <0xFFFF>; ++ ti,x-plate-ohms = /bits/ 16 <400>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ cs = <&ads7846>,"reg:0"; ++ speed = <&ads7846>,"spi-max-frequency:0"; ++ penirq = <&ads7846_pins>,"brcm,pins:0", /* REQUIRED */ ++ <&ads7846>,"interrupts:0", ++ <&ads7846>,"pendown-gpio:4"; ++ penirq_pull = <&ads7846_pins>,"brcm,pull:0"; ++ swapxy = <&ads7846>,"ti,swap-xy?"; ++ xmin = <&ads7846>,"ti,x-min;0"; ++ ymin = <&ads7846>,"ti,y-min;0"; ++ xmax = <&ads7846>,"ti,x-max;0"; ++ ymax = <&ads7846>,"ti,y-max;0"; ++ pmin = <&ads7846>,"ti,pressure-min;0"; ++ pmax = <&ads7846>,"ti,pressure-max;0"; ++ xohms = <&ads7846>,"ti,x-plate-ohms;0"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/bmp085_i2c-sensor-overlay.dts +@@ -0,0 +1,23 @@ ++// Definitions for BMP085/BMP180 digital barometric pressure and temperature sensors from Bosch Sensortec ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&i2c_arm>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ bmp085@77 { ++ compatible = "bosch,bmp085"; ++ reg = <0x77>; ++ default-oversampling = <3>; ++ status = "okay"; ++ }; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/dht11-overlay.dts +@@ -0,0 +1,39 @@ ++/* ++ * Overlay for the DHT11/21/22 humidity/temperature sensor modules. ++ */ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target-path = "/"; ++ __overlay__ { ++ ++ dht11: dht11@0 { ++ compatible = "dht11"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&dht11_pins>; ++ gpios = <&gpio 4 0>; ++ status = "okay"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ dht11_pins: dht11_pins { ++ brcm,pins = <4>; ++ brcm,function = <0>; // in ++ brcm,pull = <0>; // off ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ gpiopin = <&dht11_pins>,"brcm,pins:0", ++ <&dht11>,"gpios:4"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/enc28j60-overlay.dts +@@ -0,0 +1,50 @@ ++// Overlay for the Microchip ENC28J60 Ethernet Controller ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ eth1: enc28j60@0{ ++ compatible = "microchip,enc28j60"; ++ reg = <0>; /* CE0 */ ++ pinctrl-names = "default"; ++ pinctrl-0 = <ð1_pins>; ++ interrupt-parent = <&gpio>; ++ interrupts = <25 0x2>; /* falling edge */ ++ spi-max-frequency = <12000000>; ++ status = "okay"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ eth1_pins: eth1_pins { ++ brcm,pins = <25>; ++ brcm,function = <0>; /* in */ ++ brcm,pull = <0>; /* none */ ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ int_pin = <ð1>, "interrupts:0", ++ <ð1_pins>, "brcm,pins:0"; ++ speed = <ð1>, "spi-max-frequency:0"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts +@@ -0,0 +1,34 @@ ++// Definitions for gpio-poweroff module ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target-path = "/"; ++ __overlay__ { ++ power_ctrl: power_ctrl { ++ compatible = "gpio-poweroff"; ++ gpios = <&gpio 26 0>; ++ force; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ power_ctrl_pins: power_ctrl_pins { ++ brcm,pins = <26>; ++ brcm,function = <1>; // out ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ gpiopin = <&power_ctrl>,"gpios:4", ++ <&power_ctrl_pins>,"brcm,pins:0"; ++ active_low = <&power_ctrl>,"gpios:8"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/hifiberry-amp-overlay.dts +@@ -0,0 +1,39 @@ ++// Definitions for HiFiBerry Amp/Amp+ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&sound>; ++ __overlay__ { ++ compatible = "hifiberry,hifiberry-amp"; ++ 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"; ++ ++ tas5713@1b { ++ #sound-dai-cells = <0>; ++ compatible = "ti,tas5713"; ++ reg = <0x1b>; ++ status = "okay"; ++ }; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/hifiberry-dac-overlay.dts +@@ -0,0 +1,34 @@ ++// Definitions for HiFiBerry DAC ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&sound>; ++ __overlay__ { ++ compatible = "hifiberry,hifiberry-dac"; ++ i2s-controller = <&i2s>; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&i2s>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@2 { ++ target-path = "/"; ++ __overlay__ { ++ pcm5102a-codec { ++ #sound-dai-cells = <0>; ++ compatible = "ti,pcm5102a"; ++ status = "okay"; ++ }; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts +@@ -0,0 +1,39 @@ ++// Definitions for HiFiBerry DAC+ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&sound>; ++ __overlay__ { ++ compatible = "hifiberry,hifiberry-dacplus"; ++ 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"; ++ ++ pcm5122@4d { ++ #sound-dai-cells = <0>; ++ compatible = "ti,pcm5122"; ++ reg = <0x4d>; ++ status = "okay"; ++ }; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/hifiberry-digi-overlay.dts +@@ -0,0 +1,39 @@ ++// Definitions for HiFiBerry Digi ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&sound>; ++ __overlay__ { ++ compatible = "hifiberry,hifiberry-digi"; ++ 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"; ++ ++ wm8804@3b { ++ #sound-dai-cells = <0>; ++ compatible = "wlf,wm8804"; ++ reg = <0x3b>; ++ status = "okay"; ++ }; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/hy28a-overlay.dts +@@ -0,0 +1,87 @@ ++/* ++ * Device Tree overlay for HY28A display ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ hy28a_pins: hy28a_pins { ++ brcm,pins = <17 25 18>; ++ brcm,function = <0 1 1>; /* in out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hy28a: hy28a@0{ ++ compatible = "ilitek,ili9320"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hy28a_pins>; ++ ++ spi-max-frequency = <32000000>; ++ spi-cpol; ++ spi-cpha; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ startbyte = <0x70>; ++ reset-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ hy28a_ts: hy28a-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <17 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&hy28a>,"spi-max-frequency:0"; ++ rotate = <&hy28a>,"rotate:0"; ++ fps = <&hy28a>,"fps:0"; ++ debug = <&hy28a>,"debug:0"; ++ xohms = <&hy28a_ts>,"ti,x-plate-ohms;0"; ++ resetgpio = <&hy28a>,"reset-gpios:4", ++ <&hy28a_pins>, "brcm,pins:1"; ++ ledgpio = <&hy28a>,"led-gpios:4", ++ <&hy28a_pins>, "brcm,pins:2"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/hy28b-overlay.dts +@@ -0,0 +1,142 @@ ++/* ++ * Device Tree overlay for HY28b display shield by Texy ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ hy28b_pins: hy28b_pins { ++ brcm,pins = <17 25 18>; ++ brcm,function = <0 1 1>; /* in out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hy28b: hy28b@0{ ++ compatible = "ilitek,ili9325"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hy28b_pins>; ++ ++ spi-max-frequency = <48000000>; ++ spi-cpol; ++ spi-cpha; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ startbyte = <0x70>; ++ reset-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 1>; ++ ++ gamma = "04 1F 4 7 7 0 7 7 6 0\n0F 00 1 7 4 0 0 0 6 7"; ++ ++ init = <0x10000e7 0x0010 ++ 0x1000000 0x0001 ++ 0x1000001 0x0100 ++ 0x1000002 0x0700 ++ 0x1000003 0x1030 ++ 0x1000004 0x0000 ++ 0x1000008 0x0207 ++ 0x1000009 0x0000 ++ 0x100000a 0x0000 ++ 0x100000c 0x0001 ++ 0x100000d 0x0000 ++ 0x100000f 0x0000 ++ 0x1000010 0x0000 ++ 0x1000011 0x0007 ++ 0x1000012 0x0000 ++ 0x1000013 0x0000 ++ 0x2000032 ++ 0x1000010 0x1590 ++ 0x1000011 0x0227 ++ 0x2000032 ++ 0x1000012 0x009c ++ 0x2000032 ++ 0x1000013 0x1900 ++ 0x1000029 0x0023 ++ 0x100002b 0x000e ++ 0x2000032 ++ 0x1000020 0x0000 ++ 0x1000021 0x0000 ++ 0x2000032 ++ 0x1000050 0x0000 ++ 0x1000051 0x00ef ++ 0x1000052 0x0000 ++ 0x1000053 0x013f ++ 0x1000060 0xa700 ++ 0x1000061 0x0001 ++ 0x100006a 0x0000 ++ 0x1000080 0x0000 ++ 0x1000081 0x0000 ++ 0x1000082 0x0000 ++ 0x1000083 0x0000 ++ 0x1000084 0x0000 ++ 0x1000085 0x0000 ++ 0x1000090 0x0010 ++ 0x1000092 0x0000 ++ 0x1000093 0x0003 ++ 0x1000095 0x0110 ++ 0x1000097 0x0000 ++ 0x1000098 0x0000 ++ 0x1000007 0x0133 ++ 0x1000020 0x0000 ++ 0x1000021 0x0000 ++ 0x2000064>; ++ debug = <0>; ++ }; ++ ++ hy28b_ts: hy28b-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <17 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&hy28b>,"spi-max-frequency:0"; ++ rotate = <&hy28b>,"rotate:0"; ++ fps = <&hy28b>,"fps:0"; ++ debug = <&hy28b>,"debug:0"; ++ xohms = <&hy28b_ts>,"ti,x-plate-ohms;0"; ++ resetgpio = <&hy28b>,"reset-gpios:4", ++ <&hy28b_pins>, "brcm,pins:1"; ++ ledgpio = <&hy28b>,"led-gpios:4", ++ <&hy28b_pins>, "brcm,pins:2"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts +@@ -0,0 +1,55 @@ ++// Definitions for several I2C based Real Time Clocks ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&i2c_arm>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ ds1307: ds1307@68 { ++ compatible = "maxim,ds1307"; ++ reg = <0x68>; ++ status = "disable"; ++ }; ++ mcp7941x: mcp7941x@6f { ++ compatible = "microchip,mcp7941x"; ++ reg = <0x6f>; ++ status = "disable"; ++ }; ++ ds3231: ds3231@68 { ++ compatible = "maxim,ds3231"; ++ reg = <0x68>; ++ status = "disable"; ++ }; ++ pcf2127: pcf2127@51 { ++ compatible = "nxp,pcf2127"; ++ reg = <0x51>; ++ status = "disable"; ++ }; ++ pcf8523: pcf8523@68 { ++ compatible = "nxp,pcf8523"; ++ reg = <0x68>; ++ status = "disable"; ++ }; ++ pcf8563: pcf8563@51 { ++ compatible = "nxp,pcf8563"; ++ reg = <0x51>; ++ status = "disable"; ++ }; ++ }; ++ }; ++ __overrides__ { ++ ds1307 = <&ds1307>,"status"; ++ ds3231 = <&ds3231>,"status"; ++ mcp7941x = <&mcp7941x>,"status"; ++ pcf2127 = <&pcf2127>,"status"; ++ pcf8523 = <&pcf8523>,"status"; ++ pcf8563 = <&pcf8563>,"status"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/i2s-mmap-overlay.dts +@@ -0,0 +1,13 @@ ++/dts-v1/; ++/plugin/; ++ ++/{ ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&i2s>; ++ __overlay__ { ++ brcm,enable-mmap; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/iqaudio-dac-overlay.dts +@@ -0,0 +1,39 @@ ++// Definitions for IQaudIO DAC ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&sound>; ++ __overlay__ { ++ compatible = "iqaudio,iqaudio-dac"; ++ 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"; ++ ++ pcm5122@4c { ++ #sound-dai-cells = <0>; ++ compatible = "ti,pcm5122"; ++ reg = <0x4c>; ++ status = "okay"; ++ }; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/iqaudio-dacplus-overlay.dts +@@ -0,0 +1,39 @@ ++// Definitions for IQaudIO DAC+ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&sound>; ++ __overlay__ { ++ compatible = "iqaudio,iqaudio-dac"; ++ 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"; ++ ++ pcm5122@4c { ++ #sound-dai-cells = <0>; ++ compatible = "ti,pcm5122"; ++ reg = <0x4c>; ++ status = "okay"; ++ }; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/lirc-rpi-overlay.dts +@@ -0,0 +1,57 @@ ++// Definitions for lirc-rpi module ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target-path = "/"; ++ __overlay__ { ++ lirc_rpi: lirc_rpi { ++ compatible = "rpi,lirc-rpi"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&lirc_pins>; ++ status = "okay"; ++ ++ // Override autodetection of IR receiver circuit ++ // (0 = active high, 1 = active low, -1 = no override ) ++ rpi,sense = <0xffffffff>; ++ ++ // Software carrier ++ // (0 = off, 1 = on) ++ rpi,softcarrier = <1>; ++ ++ // Invert output ++ // (0 = off, 1 = on) ++ rpi,invert = <0>; ++ ++ // Enable debugging messages ++ // (0 = off, 1 = on) ++ rpi,debug = <0>; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ lirc_pins: lirc_pins { ++ brcm,pins = <17 18>; ++ brcm,function = <1 0>; // out in ++ brcm,pull = <0 1>; // off down ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ gpio_out_pin = <&lirc_pins>,"brcm,pins:0"; ++ gpio_in_pin = <&lirc_pins>,"brcm,pins:4"; ++ gpio_in_pull = <&lirc_pins>,"brcm,pull:4"; ++ ++ sense = <&lirc_rpi>,"rpi,sense:0"; ++ softcarrier = <&lirc_rpi>,"rpi,softcarrier:0"; ++ invert = <&lirc_rpi>,"rpi,invert:0"; ++ debug = <&lirc_rpi>,"rpi,debug:0"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/mcp2515-can0-overlay.dts +@@ -0,0 +1,69 @@ ++/* ++ * Device tree overlay for mcp251x/can0 on spi0.0 ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2836", "brcm,bcm2708", "brcm,bcm2709"; ++ /* disable spi-dev for spi0.0 */ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ spidev@0{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ /* the interrupt pin of the can-controller */ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ can0_pins: can0_pins { ++ brcm,pins = <25>; ++ brcm,function = <0>; /* input */ ++ }; ++ }; ++ }; ++ ++ /* the clock/oscillator of the can-controller */ ++ fragment@2 { ++ target-path = "/clocks"; ++ __overlay__ { ++ /* external oscillator of mcp2515 on SPI0.0 */ ++ can0_osc: can0_osc { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <16000000>; ++ }; ++ }; ++ }; ++ ++ /* the spi config of the can-controller itself binding everything together */ ++ fragment@3 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ can0: mcp2515@0 { ++ reg = <0>; ++ compatible = "microchip,mcp2515"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&can0_pins>; ++ spi-max-frequency = <10000000>; ++ interrupt-parent = <&gpio>; ++ interrupts = <25 0x2>; ++ clocks = <&can0_osc>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ oscillator = <&can0_osc>,"clock-frequency:0"; ++ spimaxfrequency = <&can0>,"spi-max-frequency:0"; ++ interrupt = <&can0_pins>,"brcm,pins:0",<&can0>,"interrupts:0"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/mcp2515-can1-overlay.dts +@@ -0,0 +1,69 @@ ++/* ++ * Device tree overlay for mcp251x/can1 on spi0.1 edited by petit_miner ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2836", "brcm,bcm2708", "brcm,bcm2709"; ++ /* disable spi-dev for spi0.1 */ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ /* the interrupt pin of the can-controller */ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ can1_pins: can1_pins { ++ brcm,pins = <25>; ++ brcm,function = <0>; /* input */ ++ }; ++ }; ++ }; ++ ++ /* the clock/oscillator of the can-controller */ ++ fragment@2 { ++ target-path = "/clocks"; ++ __overlay__ { ++ /* external oscillator of mcp2515 on spi0.1 */ ++ can1_osc: can1_osc { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <16000000>; ++ }; ++ }; ++ }; ++ ++ /* the spi config of the can-controller itself binding everything together */ ++ fragment@3 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ can1: mcp2515@1 { ++ reg = <1>; ++ compatible = "microchip,mcp2515"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&can1_pins>; ++ spi-max-frequency = <10000000>; ++ interrupt-parent = <&gpio>; ++ interrupts = <25 0x2>; ++ clocks = <&can1_osc>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ oscillator = <&can1_osc>,"clock-frequency:0"; ++ spimaxfrequency = <&can1>,"spi-max-frequency:0"; ++ interrupt = <&can1_pins>,"brcm,pins:0",<&can1>,"interrupts:0"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/mmc-overlay.dts +@@ -0,0 +1,39 @@ ++/dts-v1/; ++/plugin/; ++ ++/{ ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&mmc>; ++ frag0: __overlay__ { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&mmc_pins>; ++ bus-width = <4>; ++ brcm,overclock-50 = <0>; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ mmc_pins: mmc_pins { ++ brcm,pins = <48 49 50 51 52 53>; ++ brcm,function = <7>; /* alt3 */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&sdhost>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ ++ __overrides__ { ++ overclock_50 = <&frag0>,"brcm,overclock-50:0"; ++ force_pio = <&frag0>,"brcm,force-pio?"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/mz61581-overlay.dts +@@ -0,0 +1,111 @@ ++/* ++ * Device Tree overlay for MZ61581-PI-EXT 2014.12.28 by Tontec ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ mz61581_pins: mz61581_pins { ++ brcm,pins = <4 15 18 25>; ++ brcm,function = <0 1 1 1>; /* in out out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ mz61581: mz61581@0{ ++ compatible = "samsung,s6d02a1"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&mz61581_pins>; ++ ++ spi-max-frequency = <128000000>; ++ spi-cpol; ++ spi-cpha; ++ ++ width = <320>; ++ height = <480>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ txbuflen = <32768>; ++ ++ reset-gpios = <&gpio 15 0>; ++ dc-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 0>; ++ ++ init = <0x10000b0 00 ++ 0x1000011 ++ 0x20000ff ++ 0x10000b3 0x02 0x00 0x00 0x00 ++ 0x10000c0 0x13 0x3b 0x00 0x02 0x00 0x01 0x00 0x43 ++ 0x10000c1 0x08 0x16 0x08 0x08 ++ 0x10000c4 0x11 0x07 0x03 0x03 ++ 0x10000c6 0x00 ++ 0x10000c8 0x03 0x03 0x13 0x5c 0x03 0x07 0x14 0x08 0x00 0x21 0x08 0x14 0x07 0x53 0x0c 0x13 0x03 0x03 0x21 0x00 ++ 0x1000035 0x00 ++ 0x1000036 0xa0 ++ 0x100003a 0x55 ++ 0x1000044 0x00 0x01 ++ 0x10000d0 0x07 0x07 0x1d 0x03 ++ 0x10000d1 0x03 0x30 0x10 ++ 0x10000d2 0x03 0x14 0x04 ++ 0x1000029 ++ 0x100002c>; ++ ++ /* This is a workaround to make sure the init sequence slows down and doesn't fail */ ++ debug = <3>; ++ }; ++ ++ mz61581_ts: mz61581_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <4 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 4 0>; ++ ++ ti,x-plate-ohms = /bits/ 16 <60>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&mz61581>, "spi-max-frequency:0"; ++ rotate = <&mz61581>, "rotate:0"; ++ fps = <&mz61581>, "fps:0"; ++ txbuflen = <&mz61581>, "txbuflen:0"; ++ debug = <&mz61581>, "debug:0"; ++ xohms = <&mz61581_ts>,"ti,x-plate-ohms;0"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/piscreen-overlay.dts +@@ -0,0 +1,96 @@ ++/* ++ * Device Tree overlay for PiScreen 3.5" display shield by Ozzmaker ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ piscreen_pins: piscreen_pins { ++ brcm,pins = <17 25 24 22>; ++ brcm,function = <0 1 1 1>; /* in out out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ piscreen: piscreen@0{ ++ compatible = "ilitek,ili9486"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&piscreen_pins>; ++ ++ spi-max-frequency = <24000000>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ regwidth = <16>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 22 1>; ++ debug = <0>; ++ ++ init = <0x10000b0 0x00 ++ 0x1000011 ++ 0x20000ff ++ 0x100003a 0x55 ++ 0x1000036 0x28 ++ 0x10000c2 0x44 ++ 0x10000c5 0x00 0x00 0x00 0x00 ++ 0x10000e0 0x0f 0x1f 0x1c 0x0c 0x0f 0x08 0x48 0x98 0x37 0x0a 0x13 0x04 0x11 0x0d 0x00 ++ 0x10000e1 0x0f 0x32 0x2e 0x0b 0x0d 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 ++ 0x10000e2 0x0f 0x32 0x2e 0x0b 0x0d 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 ++ 0x1000011 ++ 0x1000029>; ++ }; ++ ++ piscreen_ts: piscreen-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <17 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,swap-xy; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&piscreen>,"spi-max-frequency:0"; ++ rotate = <&piscreen>,"rotate:0"; ++ fps = <&piscreen>,"fps:0"; ++ debug = <&piscreen>,"debug:0"; ++ xohms = <&piscreen_ts>,"ti,x-plate-ohms;0"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/pitft28-resistive-overlay.dts +@@ -0,0 +1,115 @@ ++/* ++ * Device Tree overlay for Adafruit PiTFT 2.8" resistive touch screen ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ pitft_pins: pitft_pins { ++ brcm,pins = <24 25>; ++ brcm,function = <0 1>; /* in out */ ++ brcm,pull = <2 0>; /* pullup none */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ pitft: pitft@0{ ++ compatible = "ilitek,ili9340"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pitft_pins>; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <90>; ++ fps = <25>; ++ bgr; ++ buswidth = <8>; ++ dc-gpios = <&gpio 25 0>; ++ debug = <0>; ++ }; ++ ++ pitft_ts@1 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "st,stmpe610"; ++ reg = <1>; ++ ++ spi-max-frequency = <500000>; ++ irq-gpio = <&gpio 24 0x2>; /* IRQF_TRIGGER_FALLING */ ++ interrupts = <24 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ interrupt-controller; ++ ++ stmpe_touchscreen { ++ compatible = "st,stmpe-ts"; ++ st,sample-time = <4>; ++ st,mod-12b = <1>; ++ st,ref-sel = <0>; ++ st,adc-freq = <2>; ++ st,ave-ctrl = <3>; ++ st,touch-det-delay = <4>; ++ st,settling = <2>; ++ st,fraction-z = <7>; ++ st,i-drive = <0>; ++ }; ++ ++ stmpe_gpio: stmpe_gpio { ++ #gpio-cells = <2>; ++ compatible = "st,stmpe-gpio"; ++ /* ++ * only GPIO2 is wired/available ++ * and it is wired to the backlight ++ */ ++ st,norequest-mask = <0x7b>; ++ }; ++ }; ++ }; ++ }; ++ ++ fragment@3 { ++ target-path = "/soc"; ++ __overlay__ { ++ backlight { ++ compatible = "gpio-backlight"; ++ gpios = <&stmpe_gpio 2 0>; ++ default-on; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ speed = <&pitft>,"spi-max-frequency:0"; ++ rotate = <&pitft>,"rotate:0"; ++ fps = <&pitft>,"fps:0"; ++ debug = <&pitft>,"debug:0"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/pps-gpio-overlay.dts +@@ -0,0 +1,34 @@ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ fragment@0 { ++ target-path = "/"; ++ __overlay__ { ++ pps: pps { ++ compatible = "pps-gpio"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pps_pins>; ++ gpios = <&gpio 18 0>; ++ status = "okay"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ pps_pins: pps_pins { ++ brcm,pins = <18>; ++ brcm,function = <0>; // in ++ brcm,pull = <0>; // off ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ gpiopin = <&pps>,"gpios:4", ++ <&pps_pins>,"brcm,pins:0"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/pwm-2chan-overlay.dts +@@ -0,0 +1,46 @@ ++/dts-v1/; ++/plugin/; ++ ++/* ++This is the 2-channel overlay - only use it if you need both channels. ++ ++Legal pin,function combinations for each channel: ++ PWM0: 12,4(Alt0) 18,2(Alt5) 40,4(Alt0) 52,5(Alt1) ++ PWM1: 13,4(Alt0) 19,2(Alt5) 41,4(Alt0) 45,4(Alt0) 53,5(Alt1) ++ ++N.B.: ++ 1) Pin 18 is the only one available on all platforms, and ++ it is the one used by the I2S audio interface. ++ Pins 12 and 13 might be better choices on an A+, B+ or Pi2. ++ 2) The onboard analogue audio output uses both PWM channels. ++ 3) So be careful mixing audio and PWM. ++*/ ++ ++/ { ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ pwm_pins: pwm_pins { ++ brcm,pins = <18 19>; ++ brcm,function = <2 2>; /* Alt5 */ ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&pwm>; ++ __overlay__ { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_pins>; ++ status = "okay"; ++ }; ++ }; ++ ++ __overrides__ { ++ pin = <&pwm_pins>,"brcm,pins:0"; ++ pin2 = <&pwm_pins>,"brcm,pins:4"; ++ func = <&pwm_pins>,"brcm,function:0"; ++ func2 = <&pwm_pins>,"brcm,function:4"; ++ clock = <&clk_pwm>,"clock-frequency:0"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/pwm-overlay.dts +@@ -0,0 +1,42 @@ ++/dts-v1/; ++/plugin/; ++ ++/* ++Legal pin,function combinations for each channel: ++ PWM0: 12,4(Alt0) 18,2(Alt5) 40,4(Alt0) 52,5(Alt1) ++ PWM1: 13,4(Alt0) 19,2(Alt5) 41,4(Alt0) 45,4(Alt0) 53,5(Alt1) ++ ++N.B.: ++ 1) Pin 18 is the only one available on all platforms, and ++ it is the one used by the I2S audio interface. ++ Pins 12 and 13 might be better choices on an A+, B+ or Pi2. ++ 2) The onboard analogue audio output uses both PWM channels. ++ 3) So be careful mixing audio and PWM. ++*/ ++ ++/ { ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ pwm_pins: pwm_pins { ++ brcm,pins = <18>; ++ brcm,function = <2>; /* Alt5 */ ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&pwm>; ++ __overlay__ { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_pins>; ++ status = "okay"; ++ }; ++ }; ++ ++ __overrides__ { ++ pin = <&pwm_pins>,"brcm,pins:0"; ++ func = <&pwm_pins>,"brcm,function:0"; ++ clock = <&clk_pwm>,"clock-frequency:0"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/raspidac3-overlay.dts +@@ -0,0 +1,45 @@ ++// Definitions for RaspiDACv3 ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&sound>; ++ __overlay__ { ++ compatible = "jg,raspidacv3"; ++ 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"; ++ ++ pcm5122@4c { ++ #sound-dai-cells = <0>; ++ compatible = "ti,pcm5122"; ++ reg = <0x4c>; ++ status = "okay"; ++ }; ++ ++ tpa6130a2: tpa6130a2@60 { ++ compatible = "ti,tpa6130a2"; ++ reg = <0x60>; ++ status = "okay"; ++ }; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/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"; ++ }; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/rpi-display-overlay.dts +@@ -0,0 +1,82 @@ ++/* ++ * Device Tree overlay for rpi-display by Watterott ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ rpi_display_pins: rpi_display_pins { ++ brcm,pins = <18 23 24 25>; ++ brcm,function = <1 1 1 0>; /* out out out in */ ++ brcm,pull = <0 0 0 2>; /* - - - up */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ rpidisplay: rpi-display@0{ ++ compatible = "ilitek,ili9341"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&rpi_display_pins>; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ reset-gpios = <&gpio 23 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ rpidisplay_ts: rpi-display-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <25 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 25 0>; ++ ti,x-plate-ohms = /bits/ 16 <60>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&rpidisplay>,"spi-max-frequency:0"; ++ rotate = <&rpidisplay>,"rotate:0"; ++ fps = <&rpidisplay>,"fps:0"; ++ debug = <&rpidisplay>,"debug:0"; ++ xohms = <&rpidisplay_ts>,"ti,x-plate-ohms;0"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/rpi-ft5406-overlay.dts +@@ -0,0 +1,17 @@ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target-path = "/"; ++ __overlay__ { ++ rpi_ft5406: rpi_ft5406 { ++ compatible = "rpi,rpi-ft5406"; ++ firmware = <&firmware>; ++ status = "okay"; ++ }; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/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"; ++ }; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/rpi-sense-overlay.dts +@@ -0,0 +1,47 @@ ++// rpi-sense HAT ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&i2c1>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ rpi-sense@46 { ++ compatible = "rpi,rpi-sense"; ++ reg = <0x46>; ++ keys-int-gpios = <&gpio 23 1>; ++ status = "okay"; ++ }; ++ ++ lsm9ds1-magn@1c { ++ compatible = "st,lsm9ds1-magn"; ++ reg = <0x1c>; ++ status = "okay"; ++ }; ++ ++ lsm9ds1-accel6a { ++ compatible = "st,lsm9ds1-accel"; ++ reg = <0x6a>; ++ status = "okay"; ++ }; ++ ++ lps25h-press@5c { ++ compatible = "st,lps25h-press"; ++ reg = <0x5c>; ++ status = "okay"; ++ }; ++ ++ hts221-humid@5f { ++ compatible = "st,hts221-humid"; ++ reg = <0x5f>; ++ status = "okay"; ++ }; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/sdhost-overlay.dts +@@ -0,0 +1,29 @@ ++/dts-v1/; ++/plugin/; ++ ++/{ ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&mmc>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&sdhost>; ++ frag1: __overlay__ { ++ brcm,overclock-50 = <0>; ++ brcm,pio-limit = <1>; ++ status = "okay"; ++ }; ++ }; ++ ++ __overrides__ { ++ overclock_50 = <&frag1>,"brcm,overclock-50:0"; ++ force_pio = <&frag1>,"brcm,force-pio?"; ++ pio_limit = <&frag1>,"brcm,pio-limit:0"; ++ debug = <&frag1>,"brcm,debug?"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/sdio-overlay.dts +@@ -0,0 +1,32 @@ ++/* Enable SDIO from MMC interface via GPIOs 22-27. Includes sdhost overlay. */ ++ ++/include/ "sdhost-overlay.dts" ++ ++/{ ++ compatible = "brcm,bcm2708"; ++ ++ fragment@3 { ++ target = <&mmc>; ++ sdio_mmc: __overlay__ { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdio_pins>; ++ non-removable; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@4 { ++ target = <&gpio>; ++ __overlay__ { ++ sdio_pins: sdio_pins { ++ brcm,pins = <22 23 24 25 26 27>; ++ brcm,function = <7 7 7 7 7 7>; /* ALT3 = SD1 */ ++ brcm,pull = <0 2 2 2 2 2>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ poll_once = <&sdio_mmc>,"non-removable?"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/smi-dev-overlay.dts +@@ -0,0 +1,18 @@ ++// Description: Overlay to enable character device interface for SMI. ++// Author: Luke Wren ++ ++/dts-v1/; ++/plugin/; ++ ++/{ ++ fragment@0 { ++ target = <&soc>; ++ __overlay__ { ++ smi_dev { ++ compatible = "brcm,bcm2835-smi-dev"; ++ smi_handle = <&smi>; ++ status = "okay"; ++ }; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/smi-nand-overlay.dts +@@ -0,0 +1,69 @@ ++// Description: Overlay to enable NAND flash through ++// the secondary memory interface ++// Author: Luke Wren ++ ++/dts-v1/; ++/plugin/; ++ ++/{ ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&smi>; ++ __overlay__ { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&smi_pins>; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&soc>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ nand: flash@0 { ++ compatible = "brcm,bcm2835-smi-nand"; ++ smi_handle = <&smi>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ status = "okay"; ++ ++ partition@0 { ++ label = "stage2"; ++ // 128k ++ reg = <0 0x20000>; ++ read-only; ++ }; ++ partition@1 { ++ label = "firmware"; ++ // 16M ++ reg = <0x20000 0x1000000>; ++ read-only; ++ }; ++ partition@2 { ++ label = "root"; ++ // 2G (will need to use 64 bit for >=4G) ++ reg = <0x1020000 0x80000000>; ++ }; ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&gpio>; ++ __overlay__ { ++ smi_pins: smi_pins { ++ brcm,pins = <0 1 2 3 4 5 6 7 8 9 10 11 ++ 12 13 14 15>; ++ /* Alt 1: SMI */ ++ brcm,function = <5 5 5 5 5 5 5 5 5 5 5 ++ 5 5 5 5 5>; ++ /* /CS, /WE and /OE are pulled high, as they are ++ generally active low signals */ ++ brcm,pull = <2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0>; ++ }; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/smi-overlay.dts +@@ -0,0 +1,37 @@ ++// Description: Overlay to enable the secondary memory interface peripheral ++// Author: Luke Wren ++ ++/dts-v1/; ++/plugin/; ++ ++/{ ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&smi>; ++ __overlay__ { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&smi_pins>; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ smi_pins: smi_pins { ++ /* Don't configure the top two address bits, as ++ these are already used as ID_SD and ID_SC */ ++ brcm,pins = <2 3 4 5 6 7 8 9 10 11 12 13 14 15 ++ 16 17 18 19 20 21 22 23 24 25>; ++ /* Alt 0: SMI */ ++ brcm,function = <5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 ++ 5 5 5 5 5 5 5 5 5>; ++ /* /CS, /WE and /OE are pulled high, as they are ++ generally active low signals */ ++ brcm,pull = <2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 ++ 0 0 0 0 0 0 0>; ++ }; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/spi-gpio35-39-overlay.dts +@@ -0,0 +1,31 @@ ++/* ++ * Device tree overlay to move spi0 to gpio 35 to 39 on CM ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2836", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ cs-gpios = <&gpio 36 1>, <&gpio 35 1>; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&spi0_cs_pins>; ++ __overlay__ { ++ bcrm,pins = <36 35>; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0_pins>; ++ __overlay__ { ++ bcrm,pins = <37 38 39>; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/tinylcd35-overlay.dts +@@ -0,0 +1,216 @@ ++/* ++ * tinylcd35-overlay.dts ++ * ++ * ------------------------------------------------- ++ * www.tinlylcd.com ++ * ------------------------------------------------- ++ * Device---Driver-----BUS GPIO's ++ * display tinylcd35 spi0.0 25 24 18 ++ * touch ads7846 spi0.1 5 ++ * rtc ds1307 i2c1-0068 ++ * rtc pcf8563 i2c1-0051 ++ * keypad gpio-keys --------- 17 22 27 23 28 ++ * ++ * ++ * TinyLCD.com 3.5 inch TFT ++ * ++ * Version 001 ++ * 5/3/2015 -- Noralf Trønnes Initial Device tree framework ++ * 10/3/2015 -- tinylcd@gmail.com added ds1307 support. ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ tinylcd35_pins: tinylcd35_pins { ++ brcm,pins = <25 24 18>; ++ brcm,function = <1>; /* out */ ++ }; ++ tinylcd35_ts_pins: tinylcd35_ts_pins { ++ brcm,pins = <5>; ++ brcm,function = <0>; /* in */ ++ }; ++ keypad_pins: keypad_pins { ++ brcm,pins = <4 17 22 23 27>; ++ brcm,function = <0>; /* in */ ++ brcm,pull = <1>; /* down */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ tinylcd35: tinylcd35@0{ ++ compatible = "neosec,tinylcd"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&tinylcd35_pins>, ++ <&tinylcd35_ts_pins>; ++ ++ spi-max-frequency = <48000000>; ++ rotate = <270>; ++ fps = <20>; ++ bgr; ++ buswidth = <8>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ ++ init = <0x10000B0 0x80 ++ 0x10000C0 0x0A 0x0A ++ 0x10000C1 0x01 0x01 ++ 0x10000C2 0x33 ++ 0x10000C5 0x00 0x42 0x80 ++ 0x10000B1 0xD0 0x11 ++ 0x10000B4 0x02 ++ 0x10000B6 0x00 0x22 0x3B ++ 0x10000B7 0x07 ++ 0x1000036 0x58 ++ 0x10000F0 0x36 0xA5 0xD3 ++ 0x10000E5 0x80 ++ 0x10000E5 0x01 ++ 0x10000B3 0x00 ++ 0x10000E5 0x00 ++ 0x10000F0 0x36 0xA5 0x53 ++ 0x10000E0 0x00 0x35 0x33 0x00 0x00 0x00 0x00 0x35 0x33 0x00 0x00 0x00 ++ 0x100003A 0x55 ++ 0x1000011 ++ 0x2000001 ++ 0x1000029>; ++ }; ++ ++ tinylcd35_ts: tinylcd35_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <5 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 5 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ ++ /* RTC */ ++ ++ fragment@3 { ++ target = <&i2c1>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ pcf8563: pcf8563@51 { ++ compatible = "nxp,pcf8563"; ++ reg = <0x51>; ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@4 { ++ target = <&i2c1>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ ds1307: ds1307@68 { ++ compatible = "maxim,ds1307"; ++ reg = <0x68>; ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ /* ++ * Values for input event code is found under the ++ * 'Keys and buttons' heading in include/uapi/linux/input.h ++ */ ++ fragment@5 { ++ target-path = "/soc"; ++ __overlay__ { ++ keypad: keypad { ++ compatible = "gpio-keys"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&keypad_pins>; ++ status = "disabled"; ++ autorepeat; ++ ++ button@17 { ++ label = "GPIO KEY_UP"; ++ linux,code = <103>; ++ gpios = <&gpio 17 0>; ++ }; ++ button@22 { ++ label = "GPIO KEY_DOWN"; ++ linux,code = <108>; ++ gpios = <&gpio 22 0>; ++ }; ++ button@27 { ++ label = "GPIO KEY_LEFT"; ++ linux,code = <105>; ++ gpios = <&gpio 27 0>; ++ }; ++ button@23 { ++ label = "GPIO KEY_RIGHT"; ++ linux,code = <106>; ++ gpios = <&gpio 23 0>; ++ }; ++ button@4 { ++ label = "GPIO KEY_ENTER"; ++ linux,code = <28>; ++ gpios = <&gpio 4 0>; ++ }; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ speed = <&tinylcd35>,"spi-max-frequency:0"; ++ rotate = <&tinylcd35>,"rotate:0"; ++ fps = <&tinylcd35>,"fps:0"; ++ debug = <&tinylcd35>,"debug:0"; ++ touch = <&tinylcd35_ts>,"status"; ++ touchgpio = <&tinylcd35_ts_pins>,"brcm,pins:0", ++ <&tinylcd35_ts>,"interrupts:0", ++ <&tinylcd35_ts>,"pendown-gpio:4"; ++ xohms = <&tinylcd35_ts>,"ti,x-plate-ohms;0"; ++ rtc-pcf = <&i2c1>,"status", ++ <&pcf8563>,"status"; ++ rtc-ds = <&i2c1>,"status", ++ <&ds1307>,"status"; ++ keypad = <&keypad>,"status"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/uart1-overlay.dts +@@ -0,0 +1,38 @@ ++/dts-v1/; ++/plugin/; ++ ++/{ ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&uart1>; ++ __overlay__ { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart1_pins>; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ uart1_pins: uart1_pins { ++ brcm,pins = <14 15>; ++ brcm,function = <2>; /* alt5 */ ++ brcm,pull = <0 2>; ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target-path = "/chosen"; ++ __overlay__ { ++ bootargs = "8250.nr_uarts=1"; ++ }; ++ }; ++ ++ __overrides__ { ++ txd1_pin = <&uart1_pins>,"brcm,pins:0"; ++ rxd1_pin = <&uart1_pins>,"brcm,pins:4"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/vga666-overlay.dts +@@ -0,0 +1,30 @@ ++/dts-v1/; ++/plugin/; ++ ++/{ ++ compatible = "brcm,bcm2708"; ++ ++ // There is no VGA driver module, but we need a platform device ++ // node (that doesn't already use pinctrl) to hang the pinctrl ++ // reference on - leds will do ++ ++ fragment@0 { ++ target = <&leds>; ++ __overlay__ { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&vga666_pins>; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ vga666_pins: vga666_pins { ++ brcm,pins = <2 3 4 5 6 7 8 9 10 11 12 ++ 13 14 15 16 17 18 19 20 21>; ++ brcm,function = <6>; /* alt2 */ ++ brcm,pull = <0>; /* no pull */ ++ }; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/w1-gpio-overlay.dts +@@ -0,0 +1,39 @@ ++// Definitions for w1-gpio module (without external pullup) ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target-path = "/"; ++ __overlay__ { ++ ++ w1: onewire@0 { ++ compatible = "w1-gpio"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&w1_pins>; ++ gpios = <&gpio 4 0>; ++ rpi,parasitic-power = <0>; ++ status = "okay"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ w1_pins: w1_pins { ++ brcm,pins = <4>; ++ brcm,function = <0>; // in (initially) ++ brcm,pull = <0>; // off ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ gpiopin = <&w1>,"gpios:4", ++ <&w1_pins>,"brcm,pins:0"; ++ pullup = <&w1>,"rpi,parasitic-power:0"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/w1-gpio-pullup-overlay.dts +@@ -0,0 +1,41 @@ ++// Definitions for w1-gpio module (with external pullup) ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target-path = "/"; ++ __overlay__ { ++ ++ w1: onewire@0 { ++ compatible = "w1-gpio"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&w1_pins>; ++ gpios = <&gpio 4 0>, <&gpio 5 1>; ++ rpi,parasitic-power = <0>; ++ status = "okay"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ w1_pins: w1_pins { ++ brcm,pins = <4 5>; ++ brcm,function = <0 1>; // in out ++ brcm,pull = <0 0>; // off off ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ gpiopin = <&w1>,"gpios:4", ++ <&w1_pins>,"brcm,pins:0"; ++ extpullup = <&w1>,"gpios:16", ++ <&w1_pins>,"brcm,pins:4"; ++ pullup = <&w1>,"rpi,parasitic-power:0"; ++ }; ++}; diff --git a/target/linux/brcm2708/patches-4.4/0051-fdt-Add-support-for-the-CONFIG_CMDLINE_EXTEND-option.patch b/target/linux/brcm2708/patches-4.4/0051-fdt-Add-support-for-the-CONFIG_CMDLINE_EXTEND-option.patch deleted file mode 100644 index 6103352b51..0000000000 --- a/target/linux/brcm2708/patches-4.4/0051-fdt-Add-support-for-the-CONFIG_CMDLINE_EXTEND-option.patch +++ /dev/null @@ -1,58 +0,0 @@ -From a251bf4a37d8fd468bc3ff853bcebe8ee452a31e Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Fri, 5 Dec 2014 17:26:26 +0000 -Subject: [PATCH 051/170] fdt: Add support for the CONFIG_CMDLINE_EXTEND option - ---- - drivers/of/fdt.c | 29 ++++++++++++++++++++++++----- - 1 file changed, 24 insertions(+), 5 deletions(-) - ---- a/drivers/of/fdt.c -+++ b/drivers/of/fdt.c -@@ -954,22 +954,38 @@ int __init early_init_dt_scan_chosen(uns - - /* Retrieve command line */ - p = of_get_flat_dt_prop(node, "bootargs", &l); -- if (p != NULL && l > 0) -- strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE)); -- p = of_get_flat_dt_prop(node, "bootargs-append", &l); -- if (p != NULL && l > 0) -- strlcat(data, p, min_t(int, strlen(data) + (int)l, COMMAND_LINE_SIZE)); - - /* - * CONFIG_CMDLINE is meant to be a default in case nothing else - * managed to set the command line, unless CONFIG_CMDLINE_FORCE - * is set in which case we override whatever was found earlier. -+ * -+ * However, it can be useful to be able to treat the default as -+ * a starting point to be extended using CONFIG_CMDLINE_EXTEND. - */ -+ ((char *)data)[0] = '\0'; -+ - #ifdef CONFIG_CMDLINE --#ifndef CONFIG_CMDLINE_FORCE -- if (!((char *)data)[0]) -+ strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); -+ -+ if (p != NULL && l > 0) { -+#if defined(CONFIG_CMDLINE_EXTEND) -+ int len = strlen(data); -+ if (len > 0) { -+ strlcat(data, " ", COMMAND_LINE_SIZE); -+ len++; -+ } -+ strlcpy((char *)data + len, p, min((int)l, COMMAND_LINE_SIZE - len)); -+#elif defined(CONFIG_CMDLINE_FORCE) -+ pr_warning("Ignoring bootargs property (using the default kernel command line)\n"); -+#else -+ /* Neither extend nor force - just override */ -+ strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE)); - #endif -- strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); -+ } -+#else /* CONFIG_CMDLINE */ -+ if (p != NULL && l > 0) -+ strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE)); - #endif /* CONFIG_CMDLINE */ - - pr_debug("Command line is: %s\n", (char*)data); diff --git a/target/linux/brcm2708/patches-4.4/0052-BCM2708-Add-core-Device-Tree-support.patch b/target/linux/brcm2708/patches-4.4/0052-BCM2708-Add-core-Device-Tree-support.patch deleted file mode 100644 index 84d2c7ba6f..0000000000 --- a/target/linux/brcm2708/patches-4.4/0052-BCM2708-Add-core-Device-Tree-support.patch +++ /dev/null @@ -1,4564 +0,0 @@ -From 0112c4ce9db6cdc53af67dc7634966c3594de7ff Mon Sep 17 00:00:00 2001 -From: notro -Date: Wed, 9 Jul 2014 14:46:08 +0200 -Subject: [PATCH 052/170] 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 - -Use dts-dirs feature for overlays. - -The kernel makefiles have a dts-dirs target that is for vendor subdirectories. - -Using this fixes the install_dtbs target, which previously did not install the overlays. - -BCM270X_DT: configure I2S DMA channels - -Signed-off-by: Matthias Reichl - -BCM270X_DT: switch to bcm2835-i2s - -I2S soundcard drivers with proper devicetree support (i.e. not linking -to the cpu_dai/platform via name but to cpu/platform via of_node) -will work out of the box without any modifications. - -When the kernel is compiled without devicetree support the platform -code will instantiate the bcm2708-i2s driver and I2S soundcard drivers -will link to it via name, as before. - -Signed-off-by: Matthias Reichl - -SDIO-overlay: add poll_once-boolean parameter - -Add paramter to toggle sdio-device-polling -done every second or once at boot-time. - -Signed-off-by: Patrick Boettcher - -BCM270X_DT: Make mmc overlay compatible with current firmware - -The original DT overlay logic followed a merge-then-patch procedure, -i.e. parameters are applied to the loaded overlay before the overlay -is merged into the base DTB. This sequence has been changed to -patch-then-merge, in order to support parameterised node names, and -to protect against bad overlays. As a result, overrides (parameters) -must only target labels in the overlay, but the overlay can obviously target nodes in the base DTB. - -mmc-overlay.dts (that switches back to the original mmc sdcard -driver) is the only overlay violating that rule, and this patch -fixes it. - -bcm270x_dt: Use the sdhost MMC controller by default - -The "mmc" overlay reverts to using the other controller. - -squash: Add cprman to dt - -BCM270X_DT: Use clk_core for I2C interfaces ---- - arch/arm/boot/dts/Makefile | 30 + - arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 145 +++++ - arch/arm/boot/dts/bcm2708-rpi-b.dts | 135 +++++ - arch/arm/boot/dts/bcm2708-rpi-cm.dts | 102 ++++ - arch/arm/boot/dts/bcm2708-rpi-cm.dtsi | 40 ++ - arch/arm/boot/dts/bcm2708.dtsi | 40 ++ - arch/arm/boot/dts/bcm2708_common.dtsi | 347 +++++++++++ - arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 145 +++++ - arch/arm/boot/dts/bcm2709.dtsi | 102 ++++ - arch/arm/boot/dts/bcm2835-rpi-cm.dts | 93 +++ - arch/arm/boot/dts/bcm2835-rpi-cm.dtsi | 30 + - arch/arm/boot/dts/overlays/Makefile | 69 +++ - arch/arm/boot/dts/overlays/README | 648 +++++++++++++++++++++ - arch/arm/boot/dts/overlays/ads7846-overlay.dts | 83 +++ - .../dts/overlays/bmp085_i2c-sensor-overlay.dts | 23 + - arch/arm/boot/dts/overlays/dht11-overlay.dts | 39 ++ - arch/arm/boot/dts/overlays/enc28j60-overlay.dts | 50 ++ - .../boot/dts/overlays/gpio-poweroff-overlay.dts | 34 ++ - .../boot/dts/overlays/hifiberry-amp-overlay.dts | 39 ++ - .../boot/dts/overlays/hifiberry-dac-overlay.dts | 34 ++ - .../dts/overlays/hifiberry-dacplus-overlay.dts | 39 ++ - .../boot/dts/overlays/hifiberry-digi-overlay.dts | 39 ++ - arch/arm/boot/dts/overlays/hy28a-overlay.dts | 87 +++ - arch/arm/boot/dts/overlays/hy28b-overlay.dts | 142 +++++ - arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts | 55 ++ - arch/arm/boot/dts/overlays/i2s-mmap-overlay.dts | 13 + - arch/arm/boot/dts/overlays/iqaudio-dac-overlay.dts | 39 ++ - .../boot/dts/overlays/iqaudio-dacplus-overlay.dts | 39 ++ - arch/arm/boot/dts/overlays/lirc-rpi-overlay.dts | 57 ++ - .../arm/boot/dts/overlays/mcp2515-can0-overlay.dts | 69 +++ - .../arm/boot/dts/overlays/mcp2515-can1-overlay.dts | 69 +++ - arch/arm/boot/dts/overlays/mmc-overlay.dts | 39 ++ - arch/arm/boot/dts/overlays/mz61581-overlay.dts | 111 ++++ - arch/arm/boot/dts/overlays/piscreen-overlay.dts | 96 +++ - .../dts/overlays/pitft28-resistive-overlay.dts | 115 ++++ - arch/arm/boot/dts/overlays/pps-gpio-overlay.dts | 34 ++ - arch/arm/boot/dts/overlays/pwm-2chan-overlay.dts | 46 ++ - arch/arm/boot/dts/overlays/pwm-overlay.dts | 42 ++ - arch/arm/boot/dts/overlays/raspidac3-overlay.dts | 45 ++ - arch/arm/boot/dts/overlays/rpi-dac-overlay.dts | 34 ++ - arch/arm/boot/dts/overlays/rpi-display-overlay.dts | 82 +++ - arch/arm/boot/dts/overlays/rpi-ft5406-overlay.dts | 17 + - arch/arm/boot/dts/overlays/rpi-proto-overlay.dts | 39 ++ - arch/arm/boot/dts/overlays/rpi-sense-overlay.dts | 47 ++ - arch/arm/boot/dts/overlays/sdhost-overlay.dts | 29 + - arch/arm/boot/dts/overlays/sdio-overlay.dts | 32 + - arch/arm/boot/dts/overlays/smi-dev-overlay.dts | 18 + - arch/arm/boot/dts/overlays/smi-nand-overlay.dts | 69 +++ - arch/arm/boot/dts/overlays/smi-overlay.dts | 37 ++ - .../boot/dts/overlays/spi-gpio35-39-overlay.dts | 31 + - arch/arm/boot/dts/overlays/tinylcd35-overlay.dts | 216 +++++++ - arch/arm/boot/dts/overlays/uart1-overlay.dts | 38 ++ - arch/arm/boot/dts/overlays/vga666-overlay.dts | 30 + - arch/arm/boot/dts/overlays/w1-gpio-overlay.dts | 39 ++ - .../boot/dts/overlays/w1-gpio-pullup-overlay.dts | 41 ++ - 55 files changed, 4203 insertions(+) - 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 100755 arch/arm/boot/dts/bcm2708-rpi-cm.dts - create mode 100644 arch/arm/boot/dts/bcm2708-rpi-cm.dtsi - create mode 100644 arch/arm/boot/dts/bcm2708.dtsi - create mode 100644 arch/arm/boot/dts/bcm2708_common.dtsi - 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/boot/dts/bcm2835-rpi-cm.dts - create mode 100644 arch/arm/boot/dts/bcm2835-rpi-cm.dtsi - create mode 100644 arch/arm/boot/dts/overlays/Makefile - create mode 100644 arch/arm/boot/dts/overlays/README - create mode 100644 arch/arm/boot/dts/overlays/ads7846-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/bmp085_i2c-sensor-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/dht11-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/enc28j60-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/hifiberry-amp-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/hifiberry-dac-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/hifiberry-digi-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/hy28a-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/hy28b-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/i2s-mmap-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/iqaudio-dac-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/iqaudio-dacplus-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/lirc-rpi-overlay.dts - create mode 100755 arch/arm/boot/dts/overlays/mcp2515-can0-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/mcp2515-can1-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/mmc-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/mz61581-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/piscreen-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/pitft28-resistive-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/pps-gpio-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/pwm-2chan-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/pwm-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/raspidac3-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/rpi-dac-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/rpi-display-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/rpi-ft5406-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/rpi-proto-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/rpi-sense-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/sdhost-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/sdio-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/smi-dev-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/smi-nand-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/smi-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/spi-gpio35-39-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/tinylcd35-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/uart1-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/vga666-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/w1-gpio-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/w1-gpio-pullup-overlay.dts - ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -1,5 +1,25 @@ - ifeq ($(CONFIG_OF),y) - -+dtb-$(CONFIG_ARCH_BCM2708) += bcm2708-rpi-b.dtb -+dtb-$(CONFIG_ARCH_BCM2708) += bcm2708-rpi-b-plus.dtb -+dtb-$(CONFIG_ARCH_BCM2708) += bcm2708-rpi-cm.dtb -+dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-cm.dtb -+dtb-$(CONFIG_ARCH_BCM2709) += bcm2709-rpi-2-b.dtb -+ -+# Raspberry Pi -+ifeq ($(CONFIG_ARCH_BCM2708),y) -+ RPI_DT_OVERLAYS=y -+endif -+ifeq ($(CONFIG_ARCH_BCM2709),y) -+ RPI_DT_OVERLAYS=y -+endif -+ifeq ($(CONFIG_ARCH_BCM2835),y) -+ RPI_DT_OVERLAYS=y -+endif -+ifeq ($(RPI_DT_OVERLAYS),y) -+ dts-dirs += overlays -+endif -+ - dtb-$(CONFIG_ARCH_ALPINE) += \ - alpine-db.dtb - dtb-$(CONFIG_MACH_ASM9260) += \ -@@ -777,10 +797,20 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += \ - mt8127-moose.dtb \ - mt8135-evbp1.dtb - dtb-$(CONFIG_ARCH_ZX) += zx296702-ad1.dtb -+ -+targets += dtbs dtbs_install -+targets += $(dtb-y) -+ - endif - - dtstree := $(srctree)/$(src) - dtb-$(CONFIG_OF_ALL_DTBS) := $(patsubst $(dtstree)/%.dts,%.dtb, $(wildcard $(dtstree)/*.dts)) - - always := $(dtb-y) -+subdir-y := $(dts-dirs) - clean-files := *.dtb -+ -+# Enable fixups to support overlays on BCM2708 platforms -+ifeq ($(RPI_DT_OVERLAYS),y) -+ DTC_FLAGS ?= -@ -+endif ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts -@@ -0,0 +1,145 @@ -+/dts-v1/; -+ -+#include "bcm2708.dtsi" -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ model = "Raspberry Pi Model B+"; -+}; -+ -+&gpio { -+ sdhost_pins: sdhost_pins { -+ brcm,pins = <48 49 50 51 52 53>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+ -+ spi0_pins: spi0_pins { -+ brcm,pins = <9 10 11>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+ -+ spi0_cs_pins: spi0_cs_pins { -+ brcm,pins = <8 7>; -+ brcm,function = <1>; /* output */ -+ }; -+ -+ 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 */ -+ }; -+}; -+ -+&sdhost { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&sdhost_pins>; -+ bus-width = <4>; -+ status = "okay"; -+}; -+ -+&fb { -+ status = "okay"; -+}; -+ -+&uart0 { -+ status = "okay"; -+}; -+ -+&spi0 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&spi0_pins &spi0_cs_pins>; -+ cs-gpios = <&gpio 8 1>, <&gpio 7 1>; -+ -+ 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>; -+}; -+ -+&i2c2 { -+ clock-frequency = <100000>; -+}; -+ -+&i2s { -+ #sound-dai-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2s_pins>; -+}; -+ -+&random { -+ status = "okay"; -+}; -+ -+&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>; -+ }; -+}; -+ -+/ { -+ __overrides__ { -+ uart0 = <&uart0>,"status"; -+ uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; -+ i2s = <&i2s>,"status"; -+ spi = <&spi0>,"status"; -+ i2c0 = <&i2c0>,"status"; -+ i2c1 = <&i2c1>,"status"; -+ i2c2_iknowwhatimdoing = <&i2c2>,"status"; -+ i2c0_baudrate = <&i2c0>,"clock-frequency:0"; -+ i2c1_baudrate = <&i2c1>,"clock-frequency:0"; -+ i2c2_baudrate = <&i2c2>,"clock-frequency:0"; -+ core_freq = <&clk_core>,"clock-frequency:0"; -+ -+ 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"; -+ -+ audio = <&audio>,"status"; -+ watchdog = <&watchdog>,"status"; -+ random = <&random>,"status"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts -@@ -0,0 +1,135 @@ -+/dts-v1/; -+ -+#include "bcm2708.dtsi" -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ model = "Raspberry Pi Model B"; -+}; -+ -+&gpio { -+ sdhost_pins: sdhost_pins { -+ brcm,pins = <48 49 50 51 52 53>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+ -+ spi0_pins: spi0_pins { -+ brcm,pins = <9 10 11>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+ -+ spi0_cs_pins: spi0_cs_pins { -+ brcm,pins = <8 7>; -+ brcm,function = <1>; /* output */ -+ }; -+ -+ 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 = <6>; /* alt2 */ -+ }; -+}; -+ -+&sdhost { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&sdhost_pins>; -+ bus-width = <4>; -+ status = "okay"; -+}; -+ -+&fb { -+ status = "okay"; -+}; -+ -+&uart0 { -+ status = "okay"; -+}; -+ -+&spi0 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&spi0_pins &spi0_cs_pins>; -+ cs-gpios = <&gpio 8 1>, <&gpio 7 1>; -+ -+ 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>; -+}; -+ -+&i2c2 { -+ clock-frequency = <100000>; -+}; -+ -+&i2s { -+ #sound-dai-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2s_pins>; -+}; -+ -+&random { -+ status = "okay"; -+}; -+ -+&leds { -+ act_led: act { -+ label = "led0"; -+ linux,default-trigger = "mmc0"; -+ gpios = <&gpio 16 1>; -+ }; -+}; -+ -+/ { -+ __overrides__ { -+ uart0 = <&uart0>,"status"; -+ uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; -+ i2s = <&i2s>,"status"; -+ spi = <&spi0>,"status"; -+ i2c0 = <&i2c0>,"status"; -+ i2c1 = <&i2c1>,"status"; -+ i2c2_iknowwhatimdoing = <&i2c2>,"status"; -+ i2c0_baudrate = <&i2c0>,"clock-frequency:0"; -+ i2c1_baudrate = <&i2c1>,"clock-frequency:0"; -+ i2c2_baudrate = <&i2c2>,"clock-frequency:0"; -+ core_freq = <&clk_core>,"clock-frequency:0"; -+ -+ act_led_gpio = <&act_led>,"gpios:4"; -+ act_led_activelow = <&act_led>,"gpios:8"; -+ act_led_trigger = <&act_led>,"linux,default-trigger"; -+ -+ audio = <&audio>,"status"; -+ watchdog = <&watchdog>,"status"; -+ random = <&random>,"status"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2708-rpi-cm.dts -@@ -0,0 +1,102 @@ -+/dts-v1/; -+ -+#include "bcm2708-rpi-cm.dtsi" -+ -+/ { -+ model = "Raspberry Pi Compute Module"; -+}; -+ -+&uart0 { -+ status = "okay"; -+}; -+ -+&gpio { -+ spi0_pins: spi0_pins { -+ brcm,pins = <9 10 11>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+ -+ spi0_cs_pins: spi0_cs_pins { -+ brcm,pins = <8 7>; -+ brcm,function = <1>; /* output */ -+ }; -+ -+ 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 &spi0_cs_pins>; -+ cs-gpios = <&gpio 8 1>, <&gpio 7 1>; -+ -+ 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>; -+}; -+ -+&i2c2 { -+ clock-frequency = <100000>; -+}; -+ -+&i2s { -+ #sound-dai-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2s_pins>; -+}; -+ -+&random { -+ status = "okay"; -+}; -+ -+/ { -+ __overrides__ { -+ uart0 = <&uart0>,"status"; -+ uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; -+ i2s = <&i2s>,"status"; -+ spi = <&spi0>,"status"; -+ i2c0 = <&i2c0>,"status"; -+ i2c1 = <&i2c1>,"status"; -+ i2c2_iknowwhatimdoing = <&i2c2>,"status"; -+ i2c0_baudrate = <&i2c0>,"clock-frequency:0"; -+ i2c1_baudrate = <&i2c1>,"clock-frequency:0"; -+ i2c2_baudrate = <&i2c2>,"clock-frequency:0"; -+ core_freq = <&clk_core>,"clock-frequency:0"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2708-rpi-cm.dtsi -@@ -0,0 +1,40 @@ -+#include "bcm2708.dtsi" -+ -+&gpio { -+ sdhost_pins: sdhost_pins { -+ brcm,pins = <48 49 50 51 52 53>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+}; -+ -+&leds { -+ act_led: act { -+ label = "led0"; -+ linux,default-trigger = "mmc0"; -+ gpios = <&gpio 47 0>; -+ }; -+}; -+ -+&sdhost { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&sdhost_pins>; -+ bus-width = <4>; -+ non-removable; -+ status = "okay"; -+}; -+ -+&fb { -+ status = "okay"; -+}; -+ -+/ { -+ __overrides__ { -+ act_led_gpio = <&act_led>,"gpios:4"; -+ act_led_activelow = <&act_led>,"gpios:8"; -+ act_led_trigger = <&act_led>,"linux,default-trigger"; -+ -+ audio = <&audio>,"status"; -+ watchdog = <&watchdog>,"status"; -+ random = <&random>,"status"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -0,0 +1,40 @@ -+#include "bcm2708_common.dtsi" -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ model = "BCM2708"; -+ -+ chosen { -+ /* No padding required - the boot loader can do that. */ -+ bootargs = ""; -+ }; -+ -+ soc { -+ ranges = <0x7e000000 0x20000000 0x01000000>; -+ -+ timer@7e003000 { -+ compatible = "brcm,bcm2835-system-timer"; -+ reg = <0x7e003000 0x1000>; -+ interrupts = <1 0>, <1 1>, <1 2>, <1 3>; -+ clock-frequency = <1000000>; -+ }; -+ -+ arm-pmu { -+ compatible = "arm,arm1176-pmu"; -+ }; -+ -+ gpiomem { -+ compatible = "brcm,bcm2835-gpiomem"; -+ reg = <0x7e200000 0x1000>; -+ status = "okay"; -+ }; -+ }; -+}; -+ -+&intc { -+ compatible = "brcm,bcm2835-armctrl-ic"; -+}; -+ -+&watchdog { -+ status = "okay"; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2708_common.dtsi -@@ -0,0 +1,347 @@ -+#include "skeleton.dtsi" -+ -+/ { -+ interrupt-parent = <&intc>; -+ -+ aliases { -+ audio = &audio; -+ sound = &sound; -+ soc = &soc; -+ dma = &dma; -+ intc = &intc; -+ watchdog = &watchdog; -+ random = &random; -+ mailbox = &mailbox; -+ gpio = &gpio; -+ uart0 = &uart0; -+ sdhost = &sdhost; -+ i2s = &i2s; -+ spi0 = &spi0; -+ i2c0 = &i2c0; -+ uart1 = &uart1; -+ mmc = &mmc; -+ i2c1 = &i2c1; -+ i2c2 = &i2c2; -+ usb = &usb; -+ leds = &leds; -+ fb = &fb; -+ vchiq = &vchiq; -+ thermal = &thermal; -+ clocks = &clocks; -+ }; -+ -+ /* Onboard audio */ -+ audio: audio { -+ compatible = "brcm,bcm2835-audio"; -+ brcm,pwm-channels = <8>; -+ status = "disabled"; -+ }; -+ -+ /* External sound card */ -+ sound: sound { -+ }; -+ -+ soc: soc { -+ compatible = "simple-bus"; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ -+ dma: dma@7e007000 { -+ compatible = "brcm,bcm2835-dma"; -+ reg = <0x7e007000 0xf00>; -+ interrupts = <1 16>, -+ <1 17>, -+ <1 18>, -+ <1 19>, -+ <1 20>, -+ <1 21>, -+ <1 22>, -+ <1 23>, -+ <1 24>, -+ <1 25>, -+ <1 26>, -+ <1 27>; -+ -+ #dma-cells = <1>; -+ brcm,dma-channel-mask = <0x0f35>; -+ }; -+ -+ intc: interrupt-controller@7e00b200 { -+ compatible = "brcm,bcm2708-armctrl-ic"; -+ reg = <0x7e00b200 0x200>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ mailbox: mailbox@7e00b800 { -+ compatible = "brcm,bcm2835-mbox"; -+ reg = <0x7e00b880 0x40>; -+ interrupts = <0 1>; -+ #mbox-cells = <0>; -+ }; -+ -+ watchdog: watchdog@7e100000 { -+ compatible = "brcm,bcm2835-pm-wdt"; -+ reg = <0x7e100000 0x28>; -+ status = "disabled"; -+ }; -+ -+ cprman: cprman@7e101000 { -+ compatible = "brcm,bcm2835-cprman"; -+ #clock-cells = <1>; -+ reg = <0x7e101000 0x2000>; -+ -+ /* CPRMAN derives everything from the platform's -+ * oscillator. -+ */ -+ clocks = <&clk_osc>; -+ status = "disabled"; -+ }; -+ -+ random: rng@7e104000 { -+ compatible = "brcm,bcm2835-rng"; -+ reg = <0x7e104000 0x10>; -+ status = "disabled"; -+ }; -+ -+ gpio: gpio@7e200000 { -+ compatible = "brcm,bcm2835-gpio"; -+ reg = <0x7e200000 0xb4>; -+ interrupts = <2 17>, <2 18>; -+ -+ gpio-controller; -+ #gpio-cells = <2>; -+ -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ uart0: uart@7e201000 { -+ compatible = "arm,pl011", "arm,primecell"; -+ reg = <0x7e201000 0x1000>; -+ interrupts = <2 25>; -+ clocks = <&clk_uart0 &clk_apb_p>; -+ clock-names = "uartclk","apb_pclk"; -+ arm,primecell-periphid = <0x00241011>; // For an explanation, see -+ // https://github.com/raspberrypi/linux/commit/13731d862cf5219216533a3b0de052cee4cc5038 -+ status = "disabled"; -+ }; -+ -+ sdhost: sdhost@7e202000 { -+ compatible = "brcm,bcm2835-sdhost"; -+ reg = <0x7e202000 0x100>; -+ interrupts = <2 24>; -+ clocks = <&clk_core>; -+ dmas = <&dma 13>, -+ <&dma 13>; -+ dma-names = "tx", "rx"; -+ brcm,pio-limit = <1>; -+ status = "disabled"; -+ }; -+ -+ i2s: i2s@7e203000 { -+ compatible = "brcm,bcm2835-i2s"; -+ reg = <0x7e203000 0x24>, -+ <0x7e101098 0x08>; -+ -+ dmas = <&dma 2>, <&dma 3>; -+ dma-names = "tx", "rx"; -+ status = "disabled"; -+ }; -+ -+ spi0: spi@7e204000 { -+ compatible = "brcm,bcm2835-spi"; -+ reg = <0x7e204000 0x1000>; -+ interrupts = <2 22>; -+ clocks = <&clk_core>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ /* the dma channels */ -+ dmas = <&dma 6>, <&dma 7>; -+ dma-names = "tx", "rx"; -+ /* the chipselects used - <0> means native GPIO -+ * add more gpios if necessary as <&gpio 6 1> -+ * (but do not forget to make them output!) -+ */ -+ cs-gpios = <0>, <0>; -+ }; -+ -+ i2c0: i2c@7e205000 { -+ compatible = "brcm,bcm2708-i2c"; -+ reg = <0x7e205000 0x1000>; -+ interrupts = <2 21>; -+ clocks = <&clk_core>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; -+ -+ pwm: pwm@7e20c000 { -+ compatible = "brcm,bcm2835-pwm"; -+ reg = <0x7e20c000 0x28>; -+ clocks = <&clk_pwm>; -+ #pwm-cells = <2>; -+ status = "disabled"; -+ }; -+ -+ uart1: uart@7e215040 { -+ compatible = "brcm,bcm2835-aux-uart", "ns16550"; -+ reg = <0x7e215040 0x40>; -+ interrupts = <1 29>; -+ clocks = <&clk_uart1>; -+ reg-shift = <2>; -+ no-loopback-test; -+ status = "disabled"; -+ }; -+ -+ 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"; -+ }; -+ -+ i2c1: i2c@7e804000 { -+ compatible = "brcm,bcm2708-i2c"; -+ reg = <0x7e804000 0x1000>; -+ interrupts = <2 21>; -+ clocks = <&clk_core>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; -+ -+ i2c2: i2c@7e805000 { -+ // Beware - this is shared with the HDMI module. -+ // Careless use may break (really) your display. -+ // Caveat emptor. -+ compatible = "brcm,bcm2708-i2c"; -+ reg = <0x7e805000 0x1000>; -+ interrupts = <2 21>; -+ clocks = <&clk_core>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; -+ -+ smi: smi@7e600000 { -+ compatible = "brcm,bcm2835-smi"; -+ reg = <0x7e600000 0x44>, <0x7e1010b0 0x8>; -+ interrupts = <2 16>; -+ brcm,smi-clock-source = <6>; -+ brcm,smi-clock-divisor = <4>; -+ dmas = <&dma 4>; -+ dma-names = "rx-tx"; -+ status = "disabled"; -+ }; -+ -+ usb: usb@7e980000 { -+ compatible = "brcm,bcm2708-usb"; -+ reg = <0x7e980000 0x10000>, -+ <0x7e006000 0x1000>; -+ interrupts = <2 0>, -+ <1 9>; -+ }; -+ -+ firmware: firmware { -+ compatible = "raspberrypi,bcm2835-firmware"; -+ mboxes = <&mailbox>; -+ }; -+ -+ leds: leds { -+ compatible = "gpio-leds"; -+ }; -+ -+ fb: fb { -+ compatible = "brcm,bcm2708-fb"; -+ firmware = <&firmware>; -+ status = "disabled"; -+ }; -+ -+ vchiq: vchiq { -+ compatible = "brcm,bcm2835-vchiq"; -+ reg = <0x7e00b840 0xf>; -+ interrupts = <0 2>; -+ cache-line-size = <32>; -+ firmware = <&firmware>; -+ }; -+ -+ thermal: thermal { -+ compatible = "brcm,bcm2835-thermal"; -+ firmware = <&firmware>; -+ }; -+ }; -+ -+ clocks: clocks { -+ compatible = "simple-bus"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ clk_core: clock@0 { -+ compatible = "fixed-clock"; -+ reg = <0>; -+ #clock-cells = <0>; -+ clock-output-names = "core"; -+ clock-frequency = <250000000>; -+ }; -+ -+ clk_mmc: clock@1 { -+ compatible = "fixed-clock"; -+ reg = <1>; -+ #clock-cells = <0>; -+ clock-output-names = "mmc"; -+ clock-frequency = <250000000>; -+ }; -+ -+ clk_uart0: clock@2 { -+ compatible = "fixed-clock"; -+ reg = <2>; -+ #clock-cells = <0>; -+ clock-output-names = "uart0_pclk"; -+ clock-frequency = <3000000>; -+ }; -+ -+ clk_apb_p: clock@3 { -+ compatible = "fixed-clock"; -+ reg = <3>; -+ #clock-cells = <0>; -+ clock-output-names = "apb_pclk"; -+ clock-frequency = <126000000>; -+ }; -+ -+ clk_pwm: clock@4 { -+ compatible = "fixed-clock"; -+ reg = <4>; -+ #clock-cells = <0>; -+ clock-output-names = "pwm"; -+ clock-frequency = <100000000>; -+ }; -+ -+ clk_uart1: clock@5 { -+ compatible = "fixed-factor-clock"; -+ reg = <5>; -+ clocks = <&clk_core>; -+ #clock-cells = <0>; -+ clock-div = <1>; -+ clock-mult = <2>; -+ }; -+ -+ /* The oscillator is the root of the clock tree. */ -+ clk_osc: clock@6 { -+ compatible = "fixed-clock"; -+ reg = <6>; -+ #clock-cells = <0>; -+ clock-output-names = "osc"; -+ clock-frequency = <19200000>; -+ }; -+ }; -+ -+ __overrides__ { -+ cache_line_size = <&vchiq>, "cache-line-size:0"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts -@@ -0,0 +1,145 @@ -+/dts-v1/; -+ -+#include "bcm2709.dtsi" -+ -+/ { -+ compatible = "brcm,bcm2709"; -+ model = "Raspberry Pi 2 Model B"; -+}; -+ -+&gpio { -+ sdhost_pins: sdhost_pins { -+ brcm,pins = <48 49 50 51 52 53>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+ -+ spi0_pins: spi0_pins { -+ brcm,pins = <9 10 11>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+ -+ spi0_cs_pins: spi0_cs_pins { -+ brcm,pins = <8 7>; -+ brcm,function = <1>; /* output */ -+ }; -+ -+ 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 */ -+ }; -+}; -+ -+&sdhost { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&sdhost_pins>; -+ bus-width = <4>; -+ status = "okay"; -+}; -+ -+&fb { -+ status = "okay"; -+}; -+ -+&uart0 { -+ status = "okay"; -+}; -+ -+&spi0 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&spi0_pins &spi0_cs_pins>; -+ cs-gpios = <&gpio 8 1>, <&gpio 7 1>; -+ -+ 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>; -+}; -+ -+&i2c2 { -+ clock-frequency = <100000>; -+}; -+ -+&i2s { -+ #sound-dai-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2s_pins>; -+}; -+ -+&random { -+ status = "okay"; -+}; -+ -+&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>; -+ }; -+}; -+ -+/ { -+ __overrides__ { -+ uart0 = <&uart0>,"status"; -+ uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; -+ i2s = <&i2s>,"status"; -+ spi = <&spi0>,"status"; -+ i2c0 = <&i2c0>,"status"; -+ i2c1 = <&i2c1>,"status"; -+ i2c2_iknowwhatimdoing = <&i2c2>,"status"; -+ i2c0_baudrate = <&i2c0>,"clock-frequency:0"; -+ i2c1_baudrate = <&i2c1>,"clock-frequency:0"; -+ i2c2_baudrate = <&i2c2>,"clock-frequency:0"; -+ core_freq = <&clk_core>,"clock-frequency:0"; -+ -+ 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"; -+ -+ audio = <&audio>,"status"; -+ watchdog = <&watchdog>,"status"; -+ random = <&random>,"status"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2709.dtsi -@@ -0,0 +1,102 @@ -+#include "bcm2708_common.dtsi" -+ -+/ { -+ compatible = "brcm,bcm2709"; -+ model = "BCM2709"; -+ -+ chosen { -+ /* No padding required - the boot loader can do that. */ -+ bootargs = ""; -+ }; -+ -+ soc { -+ ranges = <0x7e000000 0x3f000000 0x01000000>, -+ <0x40000000 0x40000000 0x00040000>; -+ -+ local_intc: local_intc { -+ compatible = "brcm,bcm2836-l1-intc"; -+ reg = <0x40000000 0x100>; -+ interrupt-controller; -+ #interrupt-cells = <1>; -+ interrupt-parent = <&local_intc>; -+ }; -+ -+ arm-pmu { -+ compatible = "arm,cortex-a7-pmu"; -+ interrupt-parent = <&local_intc>; -+ interrupts = <9>; -+ }; -+ -+ gpiomem { -+ compatible = "brcm,bcm2835-gpiomem"; -+ reg = <0x7e200000 0x1000>; -+ status = "okay"; -+ }; -+ -+ timer { -+ compatible = "arm,armv7-timer"; -+ clock-frequency = <19200000>; -+ interrupt-parent = <&local_intc>; -+ interrupts = <0>, // PHYS_SECURE_PPI -+ <1>, // PHYS_NONSECURE_PPI -+ <3>, // VIRT_PPI -+ <2>; // HYP_PPI -+ always-on; -+ }; -+ -+ syscon@40000000 { -+ compatible = "brcm,bcm2836-arm-local", "syscon"; -+ reg = <0x40000000 0x100>; -+ }; -+ }; -+ -+ 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"; -+ }; -+}; -+ -+&watchdog { -+ status = "okay"; -+}; -+ -+&intc { -+ compatible = "brcm,bcm2836-armctrl-ic"; -+ interrupt-parent = <&local_intc>; -+ interrupts = <8>; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2835-rpi-cm.dts -@@ -0,0 +1,93 @@ -+/dts-v1/; -+ -+#include "bcm2835-rpi-cm.dtsi" -+ -+/ { -+ model = "Raspberry Pi Compute Module"; -+}; -+ -+&uart0 { -+ status = "okay"; -+}; -+ -+&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>; -+}; -+ -+&i2c2 { -+ clock-frequency = <100000>; -+}; -+ -+&i2s { -+ #sound-dai-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2s_pins>; -+}; -+ -+/ { -+ __overrides__ { -+ uart0 = <&uart0>,"status"; -+ uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; -+ uart1_clkrate = <&uart1>,"clock-frequency:0"; -+ i2s = <&i2s>,"status"; -+ spi = <&spi0>,"status"; -+ i2c0 = <&i2c0>,"status"; -+ i2c1 = <&i2c1>,"status"; -+ i2c2_iknowwhatimdoing = <&i2c2>,"status"; -+ i2c0_baudrate = <&i2c0>,"clock-frequency:0"; -+ i2c1_baudrate = <&i2c1>,"clock-frequency:0"; -+ i2c2_baudrate = <&i2c2>,"clock-frequency:0"; -+ core_freq = <&clk_core>,"clock-frequency:0"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2835-rpi-cm.dtsi -@@ -0,0 +1,30 @@ -+#include "bcm2835.dtsi" -+ -+&leds { -+ act_led: act { -+ label = "led0"; -+ linux,default-trigger = "mmc0"; -+ gpios = <&gpio 47 0>; -+ }; -+}; -+ -+&mmc { -+ status = "okay"; -+ bus-width = <4>; -+}; -+ -+&fb { -+ status = "okay"; -+}; -+ -+/ { -+ __overrides__ { -+ act_led_gpio = <&act_led>,"gpios:4"; -+ act_led_activelow = <&act_led>,"gpios:8"; -+ act_led_trigger = <&act_led>,"linux,default-trigger"; -+ -+ audio = <&audio>,"status"; -+ watchdog = <&watchdog>,"status"; -+ random = <&random>,"status"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/Makefile -@@ -0,0 +1,69 @@ -+ifeq ($(CONFIG_OF),y) -+ -+# Overlays for the Raspberry Pi platform -+ -+ifeq ($(CONFIG_ARCH_BCM2708),y) -+ RPI_DT_OVERLAYS=y -+endif -+ifeq ($(CONFIG_ARCH_BCM2709),y) -+ RPI_DT_OVERLAYS=y -+endif -+ifeq ($(CONFIG_ARCH_BCM2835),y) -+ RPI_DT_OVERLAYS=y -+endif -+ -+dtb-$(RPI_DT_OVERLAYS) += ads7846-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += bmp085_i2c-sensor-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += dht11-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += enc28j60-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += gpio-poweroff-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-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) += hy28a-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += hy28b-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += i2c-rtc-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += i2s-mmap-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) += mcp2515-can0-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += mcp2515-can1-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += mmc-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += mz61581-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += piscreen-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += pitft28-resistive-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += pwm-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += pwm-2chan-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += raspidac3-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += rpi-dac-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += rpi-display-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += rpi-ft5406-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += rpi-proto-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += rpi-sense-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += sdhost-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += sdio-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += smi-dev-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += smi-nand-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += smi-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += spi-gpio35-39-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += tinylcd35-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += uart1-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += vga666-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += w1-gpio-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += w1-gpio-pullup-overlay.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 ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/README -@@ -0,0 +1,648 @@ -+Introduction -+============ -+ -+This directory contains Device Tree overlays. Device Tree makes it possible -+to support many hardware configurations with a single kernel and without the -+need to explicitly load or blacklist kernel modules. Note that this isn't a -+"pure" Device Tree configuration (c.f. MACH_BCM2835) - some on-board devices -+are still configured by the board support code, but the intention is to -+eventually reach that goal. -+ -+On Raspberry Pi, Device Tree usage is controlled from /boot/config.txt. By -+default, the Raspberry Pi kernel boots with device tree enabled. You can -+completely disable DT usage (for now) by adding: -+ -+ device_tree= -+ -+to your config.txt, which should cause your Pi to revert to the old way of -+doing things after a reboot. -+ -+In /boot you will find a .dtb for each base platform. This describes the -+hardware that is part of the Raspberry Pi board. The loader (start.elf and its -+siblings) selects the .dtb file appropriate for the platform by name, and reads -+it into memory. At this point, all of the optional interfaces (i2c, i2s, spi) -+are disabled, but they can be enabled using Device Tree parameters: -+ -+ dtparam=i2c=on,i2s=on,spi=on -+ -+However, this shouldn't be necessary in many use cases because loading an -+overlay that requires one of those interfaces will cause it to be enabled -+automatically, and it is advisable to only enable interfaces if they are -+needed. -+ -+Configuring additional, optional hardware is done using Device Tree overlays -+(see below). -+ -+raspi-config -+============ -+ -+The Advanced Options section of the raspi-config utility can enable and disable -+Device Tree use, as well as toggling the I2C and SPI interfaces. Note that it -+is possible to both enable an interface and blacklist the driver, if for some -+reason you should want to defer the loading. -+ -+Modules -+======= -+ -+As well as describing the hardware, Device Tree also gives enough information -+to allow suitable driver modules to be located and loaded, with the corollary -+that unneeded modules are not loaded. As a result it should be possible to -+remove lines from /etc/modules, and /etc/modprobe.d/raspi-blacklist.conf can -+have its contents deleted (or commented out). -+ -+Using Overlays -+============== -+ -+Overlays are loaded using the "dtoverlay" directive. As an example, consider the -+popular lirc-rpi module, the Linux Infrared Remote Control driver. In the -+pre-DT world this would be loaded from /etc/modules, with an explicit -+"modprobe lirc-rpi" command, or programmatically by lircd. With DT enabled, -+this becomes a line in config.txt: -+ -+ dtoverlay=lirc-rpi -+ -+This causes the file /boot/overlays/lirc-rpi-overlay.dtb to be loaded. By -+default it will use GPIOs 17 (out) and 18 (in), but this can be modified using -+DT parameters: -+ -+ dtoverlay=lirc-rpi,gpio_out_pin=17,gpio_in_pin=13 -+ -+Parameters always have default values, although in some cases (e.g. "w1-gpio") -+it is necessary to provided multiple overlays in order to get the desired -+behaviour. See the list of overlays below for a description of the parameters and their defaults. -+ -+The Overlay and Parameter Reference -+=================================== -+ -+N.B. When editing this file, please preserve the indentation levels to make it simple to parse -+programmatically. NO HARD TABS. -+ -+ -+Name: -+Info: Configures the base Raspberry Pi hardware -+Load: -+Params: -+ audio Set to "on" to enable the onboard ALSA audio -+ interface (default "off") -+ -+ i2c_arm Set to "on" to enable the ARM's i2c interface -+ (default "off") -+ -+ i2c_vc Set to "on" to enable the i2c interface -+ usually reserved for the VideoCore processor -+ (default "off") -+ -+ i2c An alias for i2c_arm -+ -+ i2c_arm_baudrate Set the baudrate of the ARM's i2c interface -+ (default "100000") -+ -+ i2c_vc_baudrate Set the baudrate of the VideoCore i2c interface -+ (default "100000") -+ -+ i2c_baudrate An alias for i2c_arm_baudrate -+ -+ i2s Set to "on" to enable the i2s interface -+ (default "off") -+ -+ spi Set to "on" to enable the spi interfaces -+ (default "off") -+ -+ random Set to "on" to enable the hardware random -+ number generator (default "off") -+ -+ uart0 Set to "off" to disable uart0 (default "on") -+ -+ watchdog Set to "on" to enable the hardware watchdog -+ (default "off") -+ -+ act_led_trigger Choose which activity the LED tracks. -+ Use "heartbeat" for a nice load indicator. -+ (default "mmc") -+ -+ act_led_activelow Set to "on" to invert the sense of the LED -+ (default "off") -+ -+ act_led_gpio Set which GPIO to use for the activity LED -+ (in case you want to connect it to an external -+ device) -+ (default "16" on a non-Plus board, "47" on a -+ Plus or Pi 2) -+ -+ pwr_led_trigger -+ pwr_led_activelow -+ pwr_led_gpio -+ As for act_led_*, but using the PWR LED. -+ Not available on Model A/B boards. -+ -+ N.B. It is recommended to only enable those interfaces that are needed. -+ Leaving all interfaces enabled can lead to unwanted behaviour (i2c_vc -+ interfering with Pi Camera, I2S and SPI hogging GPIO pins, etc.) -+ Note also that i2c, i2c_arm and i2c_vc are aliases for the physical -+ interfaces i2c0 and i2c1. Use of the numeric variants is still possible -+ but deprecated because the ARM/VC assignments differ between board -+ revisions. The same board-specific mapping applies to i2c_baudrate, -+ and the other i2c baudrate parameters. -+ -+ -+Name: ads7846 -+Info: ADS7846 Touch controller -+Load: dtoverlay=ads7846,= -+Params: cs SPI bus Chip Select (default 1) -+ speed SPI bus speed (default 2Mhz, max 3.25MHz) -+ penirq GPIO used for PENIRQ. REQUIRED -+ penirq_pull Set GPIO pull (default 0=none, 2=pullup) -+ swapxy Swap x and y axis -+ xmin Minimum value on the X axis (default 0) -+ ymin Minimum value on the Y axis (default 0) -+ xmax Maximum value on the X axis (default 4095) -+ ymax Maximum value on the Y axis (default 4095) -+ pmin Minimum reported pressure value (default 0) -+ pmax Maximum reported pressure value (default 65535) -+ xohms Touchpanel sensitivity (X-plate resistance) -+ (default 400) -+ -+ penirq is required and usually xohms (60-100) has to be set as well. -+ Apart from that, pmax (255) and swapxy are also common. -+ The rest of the calibration can be done with xinput-calibrator. -+ See: github.com/notro/fbtft/wiki/FBTFT-on-Raspian -+ Device Tree binding document: -+ www.kernel.org/doc/Documentation/devicetree/bindings/input/ads7846.txt -+ -+ -+Name: bmp085_i2c-sensor -+Info: Configures the BMP085/BMP180 digital barometric pressure and temperature -+ sensors from Bosch Sensortec -+Load: dtoverlay=bmp085_i2c-sensor -+Params: -+ -+ -+Name: dht11 -+Info: Overlay for the DHT11/DHT21/DHT22 humidity/temperature sensors -+ Also sometimes found with the part number(s) AM230x. -+Load: dtoverlay=dht11,= -+Params: gpiopin GPIO connected to the sensor's DATA output. -+ (default 4) -+ -+ -+[ The ds1307-rtc overlay has been deleted. See i2c-rtc. ] -+ -+ -+Name: enc28j60 -+Info: Overlay for the Microchip ENC28J60 Ethernet Controller (SPI) -+Load: dtoverlay=enc28j60,= -+Params: int_pin GPIO used for INT (default 25) -+ -+ speed SPI bus speed (default 12000000) -+ -+ -+Name: gpio-poweroff -+Info: Drives a GPIO high or low on reboot -+Load: dtoverlay=gpio-poweroff,= -+Params: gpiopin GPIO for signalling (default 26) -+ -+ active_low Set if the power control device requires a -+ high->low transition to trigger a power-down. -+ Note that this will require the support of a -+ custom dt-blob.bin to prevent a power-down -+ during the boot process, and that a reboot -+ will also cause the pin to go low. -+ -+ -+Name: hifiberry-amp -+Info: Configures the HifiBerry Amp and Amp+ audio cards -+Load: dtoverlay=hifiberry-amp -+Params: -+ -+ -+Name: hifiberry-dac -+Info: Configures the HifiBerry DAC audio card -+Load: dtoverlay=hifiberry-dac -+Params: -+ -+ -+Name: hifiberry-dacplus -+Info: Configures the HifiBerry DAC+ audio card -+Load: dtoverlay=hifiberry-dacplus -+Params: -+ -+ -+Name: hifiberry-digi -+Info: Configures the HifiBerry Digi audio card -+Load: dtoverlay=hifiberry-digi -+Params: -+ -+ -+Name: hy28a -+Info: HY28A - 2.8" TFT LCD Display Module by HAOYU Electronics -+ Default values match Texy's display shield -+Load: dtoverlay=hy28a,= -+Params: speed Display SPI bus speed -+ -+ rotate Display rotation {0,90,180,270} -+ -+ fps Delay between frame updates -+ -+ debug Debug output level {0-7} -+ -+ xohms Touchpanel sensitivity (X-plate resistance) -+ -+ resetgpio GPIO used to reset controller -+ -+ ledgpio GPIO used to control backlight -+ -+ -+Name: hy28b -+Info: HY28B - 2.8" TFT LCD Display Module by HAOYU Electronics -+ Default values match Texy's display shield -+Load: dtoverlay=hy28b,= -+Params: speed Display SPI bus speed -+ -+ rotate Display rotation {0,90,180,270} -+ -+ fps Delay between frame updates -+ -+ debug Debug output level {0-7} -+ -+ xohms Touchpanel sensitivity (X-plate resistance) -+ -+ resetgpio GPIO used to reset controller -+ -+ ledgpio GPIO used to control backlight -+ -+ -+Name: i2c-rtc -+Info: Adds support for a number of I2C Real Time Clock devices -+Load: dtoverlay=i2c-rtc, -+Params: ds1307 Select the DS1307 device -+ -+ ds3231 Select the DS3231 device -+ -+ mcp7941x Select the MCP7941x device -+ -+ pcf2127 Select the PCF2127 device -+ -+ pcf8523 Select the PCF8523 device -+ -+ pcf8563 Select the PCF8563 device -+ -+ -+Name: i2s-mmap -+Info: Enables mmap support in the bcm2708-i2s driver -+Load: dtoverlay=i2s-mmap -+Params: -+ -+ -+Name: iqaudio-dac -+Info: Configures the IQaudio DAC audio card -+Load: dtoverlay=iqaudio-dac -+Params: -+ -+ -+Name: iqaudio-dacplus -+Info: Configures the IQaudio DAC+ audio card -+Load: dtoverlay=iqaudio-dacplus -+Params: -+ -+ -+Name: lirc-rpi -+Info: Configures lirc-rpi (Linux Infrared Remote Control for Raspberry Pi) -+ Consult the module documentation for more details. -+Load: dtoverlay=lirc-rpi,=,... -+Params: gpio_out_pin GPIO for output (default "17") -+ -+ gpio_in_pin GPIO for input (default "18") -+ -+ gpio_in_pull Pull up/down/off on the input pin -+ (default "down") -+ -+ sense Override the IR receive auto-detection logic: -+ "0" = force active-high -+ "1" = force active-low -+ "-1" = use auto-detection -+ (default "-1") -+ -+ softcarrier Turn the software carrier "on" or "off" -+ (default "on") -+ -+ invert "on" = invert the output pin (default "off") -+ -+ debug "on" = enable additional debug messages -+ (default "off") -+ -+ -+Name: mcp2515-can0 -+Info: Configures the MCP2515 CAN controller on spi0.0 -+Load: dtoverlay=mcp2515-can0,= -+Params: oscillator Clock frequency for the CAN controller (Hz) -+ -+ spimaxfrequency Maximum SPI frequence (Hz) -+ -+ interrupt GPIO for interrupt signal -+ -+ -+Name: mcp2515-can1 -+Info: Configures the MCP2515 CAN controller on spi0.1 -+Load: dtoverlay=mcp2515-can1,= -+Params: oscillator Clock frequency for the CAN controller (Hz) -+ -+ spimaxfrequency Maximum SPI frequence (Hz) -+ -+ interrupt GPIO for interrupt signal -+ -+ -+Name: mmc -+Info: Selects the bcm2835-mmc SD/MMC driver, optionally with overclock -+Load: dtoverlay=mmc,= -+Params: overclock_50 Clock (in MHz) to use when the MMC framework -+ requests 50MHz -+ force_pio Disable DMA support -+ -+ -+Name: mz61581 -+Info: MZ61581 display by Tontec -+Load: dtoverlay=mz61581,= -+Params: speed Display SPI bus speed -+ -+ rotate Display rotation {0,90,180,270} -+ -+ fps Delay between frame updates -+ -+ txbuflen Transmit buffer length (default 32768) -+ -+ debug Debug output level {0-7} -+ -+ xohms Touchpanel sensitivity (X-plate resistance) -+ -+ -+[ The pcf2127-rtc overlay has been deleted. See i2c-rtc. ] -+ -+ -+[ The pcf8523-rtc overlay has been deleted. See i2c-rtc. ] -+ -+ -+[ The pcf8563-rtc overlay has been deleted. See i2c-rtc. ] -+ -+ -+Name: piscreen -+Info: PiScreen display by OzzMaker.com -+Load: dtoverlay=piscreen,= -+Params: speed Display SPI bus speed -+ -+ rotate Display rotation {0,90,180,270} -+ -+ fps Delay between frame updates -+ -+ debug Debug output level {0-7} -+ -+ xohms Touchpanel sensitivity (X-plate resistance) -+ -+ -+Name: pitft28-resistive -+Info: Adafruit PiTFT 2.8" resistive touch screen -+Load: dtoverlay=pitft28-resistive,= -+Params: speed Display SPI bus speed -+ -+ rotate Display rotation {0,90,180,270} -+ -+ fps Delay between frame updates -+ -+ debug Debug output level {0-7} -+ -+ -+Name: pps-gpio -+Info: Configures the pps-gpio (pulse-per-second time signal via GPIO). -+Load: dtoverlay=pps-gpio,= -+Params: gpiopin Input GPIO (default "18") -+ -+ -+Name: pwm -+Info: Configures a single PWM channel -+ Legal pin,function combinations for each channel: -+ PWM0: 12,4(Alt0) 18,2(Alt5) 40,4(Alt0) 52,5(Alt1) -+ PWM1: 13,4(Alt0) 19,2(Alt5) 41,4(Alt0) 45,4(Alt0) 53,5(Alt1) -+ N.B.: -+ 1) Pin 18 is the only one available on all platforms, and -+ it is the one used by the I2S audio interface. -+ Pins 12 and 13 might be better choices on an A+, B+ or Pi2. -+ 2) The onboard analogue audio output uses both PWM channels. -+ 3) So be careful mixing audio and PWM. -+ 4) Currently the clock must have been enabled and configured -+ by other means. -+Load: dtoverlay=pwm,= -+Params: pin Output pin (default 18) - see table -+ func Pin function (default 2 = Alt5) - see above -+ clock PWM clock frequency (informational) -+ -+ -+Name: pwm-2chan -+Info: Configures both PWM channels -+ Legal pin,function combinations for each channel: -+ PWM0: 12,4(Alt0) 18,2(Alt5) 40,4(Alt0) 52,5(Alt1) -+ PWM1: 13,4(Alt0) 19,2(Alt5) 41,4(Alt0) 45,4(Alt0) 53,5(Alt1) -+ N.B.: -+ 1) Pin 18 is the only one available on all platforms, and -+ it is the one used by the I2S audio interface. -+ Pins 12 and 13 might be better choices on an A+, B+ or Pi2. -+ 2) The onboard analogue audio output uses both PWM channels. -+ 3) So be careful mixing audio and PWM. -+ 4) Currently the clock must have been enabled and configured -+ by other means. -+Load: dtoverlay=pwm-2chan,= -+Params: pin Output pin (default 18) - see table -+ pin2 Output pin for other channel (default 19) -+ func Pin function (default 2 = Alt5) - see above -+ func2 Function for pin2 (default 2 = Alt5) -+ clock PWM clock frequency (informational) -+ -+ -+Name: raspidac3 -+Info: Configures the RaspiDAV Rev.3x audio card -+Load: dtoverlay=raspidac3 -+Params: -+ -+ -+Name: rpi-dac -+Info: Configures the RPi DAC audio card -+Load: dtoverlay=rpi-dac -+Params: -+ -+ -+Name: rpi-display -+Info: RPi-Display - 2.8" Touch Display by Watterott -+Load: dtoverlay=rpi-display,= -+Params: speed Display SPI bus speed -+ -+ rotate Display rotation {0,90,180,270} -+ -+ fps Delay between frame updates -+ -+ debug Debug output level {0-7} -+ -+ xohms Touchpanel sensitivity (X-plate resistance) -+ -+ -+Name: rpi-ft5406 -+Info: Official Raspberry Pi display touchscreen -+Load: dtoverlay=rpi-ft5406 -+Params: -+ -+ -+Name: rpi-proto -+Info: Configures the RPi Proto audio card -+Load: dtoverlay=rpi-proto -+Params: -+ -+ -+Name: rpi-sense -+Info: Raspberry Pi Sense HAT -+Load: dtoverlay=rpi-sense -+Params: -+ -+ -+Name: sdhost -+Info: Selects the bcm2835-sdhost SD/MMC driver, optionally with overclock -+Load: dtoverlay=sdhost,= -+Params: overclock_50 Clock (in MHz) to use when the MMC framework -+ requests 50MHz -+ -+ force_pio Disable DMA support (default off) -+ -+ pio_limit Number of blocks above which to use DMA -+ (default 1) -+ -+ debug Enable debug output (default off) -+ -+ -+Name: sdio -+Info: Selects the bcm2835-sdhost SD/MMC driver, optionally with overclock, -+ and enables SDIO via GPIOs 22-27. -+Load: dtoverlay=sdio,= -+Params: overclock_50 Clock (in MHz) to use when the MMC framework -+ requests 50MHz -+ -+ force_pio Disable DMA support (default off) -+ -+ pio_limit Number of blocks above which to use DMA -+ (default 1) -+ -+ debug Enable debug output (default off) -+ -+ poll_once Disable SDIO-device polling every second -+ (default on: polling once at boot-time) -+ -+ -+Name: smi -+Info: Enables the Secondary Memory Interface peripheral. Uses GPIOs 2-25! -+Load: dtoverlay=smi -+Params: -+ -+ -+Name: smi-dev -+Info: Enables the userspace interface for the SMI driver -+Load: dtoverlay=smi-dev -+Params: -+ -+ -+Name: smi-nand -+Info: Enables access to NAND flash via the SMI interface -+Load: dtoverlay=smi-nand -+Params: -+ -+ -+Name: spi-gpio35-39 -+Info: move SPI function block to GPIO 35 to 39 -+Load: dtoverlay=spi-gpio35-39 -+Params: -+ -+ -+Name: tinylcd35 -+Info: 3.5" Color TFT Display by www.tinylcd.com -+ Options: Touch, RTC, keypad -+Load: dtoverlay=tinylcd35,= -+Params: speed Display SPI bus speed -+ -+ rotate Display rotation {0,90,180,270} -+ -+ fps Delay between frame updates -+ -+ debug Debug output level {0-7} -+ -+ touch Enable touch panel -+ -+ touchgpio Touch controller IRQ GPIO -+ -+ xohms Touchpanel: Resistance of X-plate in ohms -+ -+ rtc-pcf PCF8563 Real Time Clock -+ -+ rtc-ds DS1307 Real Time Clock -+ -+ keypad Enable keypad -+ -+ Examples: -+ Display with touchpanel, PCF8563 RTC and keypad: -+ dtoverlay=tinylcd35,touch,rtc-pcf,keypad -+ Old touch display: -+ dtoverlay=tinylcd35,touch,touchgpio=3 -+ -+ -+Name: uart1 -+Info: Enable uart1 in place of uart0 -+Load: dtoverlay=uart1,= -+Params: txd1_pin GPIO pin for TXD1 (14, 32 or 40 - default 14) -+ -+ rxd1_pin GPIO pin for RXD1 (15, 33 or 41 - default 15) -+ -+ -+Name: vga666 -+Info: Overlay for the Fen Logic VGA666 board -+ This uses GPIOs 2-21 (so no I2C), and activates the output 2-3 seconds -+ after the kernel has started. -+Load: dtoverlay=vga666 -+Params: -+ -+ -+Name: w1-gpio -+Info: Configures the w1-gpio Onewire interface module. -+ Use this overlay if you *don't* need a GPIO to drive an external pullup. -+Load: dtoverlay=w1-gpio,= -+Params: gpiopin GPIO for I/O (default "4") -+ -+ pullup Non-zero, "on", or "y" to enable the parasitic -+ power (2-wire, power-on-data) feature -+ -+ -+Name: w1-gpio-pullup -+Info: Configures the w1-gpio Onewire interface module. -+ Use this overlay if you *do* need a GPIO to drive an external pullup. -+Load: dtoverlay=w1-gpio-pullup,= -+Params: gpiopin GPIO for I/O (default "4") -+ -+ pullup Non-zero, "on", or "y" to enable the parasitic -+ power (2-wire, power-on-data) feature -+ -+ extpullup GPIO for external pullup (default "5") -+ -+ -+Troubleshooting -+=============== -+ -+If you are experiencing problems that you think are DT-related, enable DT -+diagnostic output by adding this to /boot/config.txt: -+ -+ dtdebug=on -+ -+and rebooting. Then run: -+ -+ sudo vcdbg log msg -+ -+and look for relevant messages. -+ -+Further reading -+=============== -+ -+This is only meant to be a quick introduction to the subject of Device Tree on -+Raspberry Pi. There is a more complete explanation here: -+ -+http://www.raspberrypi.org/documentation/configuration/device-tree.md ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/ads7846-overlay.dts -@@ -0,0 +1,83 @@ -+/* -+ * Generic Device Tree overlay for the ADS7846 touch controller -+ * -+ */ -+ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&spi0>; -+ __overlay__ { -+ status = "okay"; -+ -+ spidev@0{ -+ status = "disabled"; -+ }; -+ -+ spidev@1{ -+ status = "disabled"; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ ads7846_pins: ads7846_pins { -+ brcm,pins = <255>; /* illegal default value */ -+ brcm,function = <0>; /* in */ -+ brcm,pull = <0>; /* none */ -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&spi0>; -+ __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ ads7846: ads7846@1 { -+ compatible = "ti,ads7846"; -+ reg = <1>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&ads7846_pins>; -+ -+ spi-max-frequency = <2000000>; -+ interrupts = <255 2>; /* high-to-low edge triggered */ -+ interrupt-parent = <&gpio>; -+ pendown-gpio = <&gpio 255 0>; -+ -+ /* driver defaults */ -+ ti,x-min = /bits/ 16 <0>; -+ ti,y-min = /bits/ 16 <0>; -+ ti,x-max = /bits/ 16 <0x0FFF>; -+ ti,y-max = /bits/ 16 <0x0FFF>; -+ ti,pressure-min = /bits/ 16 <0>; -+ ti,pressure-max = /bits/ 16 <0xFFFF>; -+ ti,x-plate-ohms = /bits/ 16 <400>; -+ }; -+ }; -+ }; -+ __overrides__ { -+ cs = <&ads7846>,"reg:0"; -+ speed = <&ads7846>,"spi-max-frequency:0"; -+ penirq = <&ads7846_pins>,"brcm,pins:0", /* REQUIRED */ -+ <&ads7846>,"interrupts:0", -+ <&ads7846>,"pendown-gpio:4"; -+ penirq_pull = <&ads7846_pins>,"brcm,pull:0"; -+ swapxy = <&ads7846>,"ti,swap-xy?"; -+ xmin = <&ads7846>,"ti,x-min;0"; -+ ymin = <&ads7846>,"ti,y-min;0"; -+ xmax = <&ads7846>,"ti,x-max;0"; -+ ymax = <&ads7846>,"ti,y-max;0"; -+ pmin = <&ads7846>,"ti,pressure-min;0"; -+ pmax = <&ads7846>,"ti,pressure-max;0"; -+ xohms = <&ads7846>,"ti,x-plate-ohms;0"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/bmp085_i2c-sensor-overlay.dts -@@ -0,0 +1,23 @@ -+// Definitions for BMP085/BMP180 digital barometric pressure and temperature sensors from Bosch Sensortec -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&i2c_arm>; -+ __overlay__ { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "okay"; -+ -+ bmp085@77 { -+ compatible = "bosch,bmp085"; -+ reg = <0x77>; -+ default-oversampling = <3>; -+ status = "okay"; -+ }; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/dht11-overlay.dts -@@ -0,0 +1,39 @@ -+/* -+ * Overlay for the DHT11/21/22 humidity/temperature sensor modules. -+ */ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target-path = "/"; -+ __overlay__ { -+ -+ dht11: dht11@0 { -+ compatible = "dht11"; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&dht11_pins>; -+ gpios = <&gpio 4 0>; -+ status = "okay"; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ dht11_pins: dht11_pins { -+ brcm,pins = <4>; -+ brcm,function = <0>; // in -+ brcm,pull = <0>; // off -+ }; -+ }; -+ }; -+ -+ __overrides__ { -+ gpiopin = <&dht11_pins>,"brcm,pins:0", -+ <&dht11>,"gpios:4"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/enc28j60-overlay.dts -@@ -0,0 +1,50 @@ -+// Overlay for the Microchip ENC28J60 Ethernet Controller -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&spi0>; -+ __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ status = "okay"; -+ -+ spidev@0{ -+ status = "disabled"; -+ }; -+ -+ eth1: enc28j60@0{ -+ compatible = "microchip,enc28j60"; -+ reg = <0>; /* CE0 */ -+ pinctrl-names = "default"; -+ pinctrl-0 = <ð1_pins>; -+ interrupt-parent = <&gpio>; -+ interrupts = <25 0x2>; /* falling edge */ -+ spi-max-frequency = <12000000>; -+ status = "okay"; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ eth1_pins: eth1_pins { -+ brcm,pins = <25>; -+ brcm,function = <0>; /* in */ -+ brcm,pull = <0>; /* none */ -+ }; -+ }; -+ }; -+ -+ __overrides__ { -+ int_pin = <ð1>, "interrupts:0", -+ <ð1_pins>, "brcm,pins:0"; -+ speed = <ð1>, "spi-max-frequency:0"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts -@@ -0,0 +1,34 @@ -+// Definitions for gpio-poweroff module -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target-path = "/"; -+ __overlay__ { -+ power_ctrl: power_ctrl { -+ compatible = "gpio-poweroff"; -+ gpios = <&gpio 26 0>; -+ force; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ power_ctrl_pins: power_ctrl_pins { -+ brcm,pins = <26>; -+ brcm,function = <1>; // out -+ }; -+ }; -+ }; -+ -+ __overrides__ { -+ gpiopin = <&power_ctrl>,"gpios:4", -+ <&power_ctrl_pins>,"brcm,pins:0"; -+ active_low = <&power_ctrl>,"gpios:8"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/hifiberry-amp-overlay.dts -@@ -0,0 +1,39 @@ -+// Definitions for HiFiBerry Amp/Amp+ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&sound>; -+ __overlay__ { -+ compatible = "hifiberry,hifiberry-amp"; -+ 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"; -+ -+ tas5713@1b { -+ #sound-dai-cells = <0>; -+ compatible = "ti,tas5713"; -+ reg = <0x1b>; -+ status = "okay"; -+ }; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/hifiberry-dac-overlay.dts -@@ -0,0 +1,34 @@ -+// Definitions for HiFiBerry DAC -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&sound>; -+ __overlay__ { -+ compatible = "hifiberry,hifiberry-dac"; -+ i2s-controller = <&i2s>; -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&i2s>; -+ __overlay__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@2 { -+ target-path = "/"; -+ __overlay__ { -+ pcm5102a-codec { -+ #sound-dai-cells = <0>; -+ compatible = "ti,pcm5102a"; -+ status = "okay"; -+ }; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts -@@ -0,0 +1,39 @@ -+// Definitions for HiFiBerry DAC+ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&sound>; -+ __overlay__ { -+ compatible = "hifiberry,hifiberry-dacplus"; -+ 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"; -+ -+ pcm5122@4d { -+ #sound-dai-cells = <0>; -+ compatible = "ti,pcm5122"; -+ reg = <0x4d>; -+ status = "okay"; -+ }; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/hifiberry-digi-overlay.dts -@@ -0,0 +1,39 @@ -+// Definitions for HiFiBerry Digi -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&sound>; -+ __overlay__ { -+ compatible = "hifiberry,hifiberry-digi"; -+ 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"; -+ -+ wm8804@3b { -+ #sound-dai-cells = <0>; -+ compatible = "wlf,wm8804"; -+ reg = <0x3b>; -+ status = "okay"; -+ }; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/hy28a-overlay.dts -@@ -0,0 +1,87 @@ -+/* -+ * Device Tree overlay for HY28A display -+ * -+ */ -+ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&spi0>; -+ __overlay__ { -+ status = "okay"; -+ -+ spidev@0{ -+ status = "disabled"; -+ }; -+ -+ spidev@1{ -+ status = "disabled"; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ hy28a_pins: hy28a_pins { -+ brcm,pins = <17 25 18>; -+ brcm,function = <0 1 1>; /* in out out */ -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&spi0>; -+ __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ hy28a: hy28a@0{ -+ compatible = "ilitek,ili9320"; -+ reg = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&hy28a_pins>; -+ -+ spi-max-frequency = <32000000>; -+ spi-cpol; -+ spi-cpha; -+ rotate = <270>; -+ bgr; -+ fps = <50>; -+ buswidth = <8>; -+ startbyte = <0x70>; -+ reset-gpios = <&gpio 25 0>; -+ led-gpios = <&gpio 18 1>; -+ debug = <0>; -+ }; -+ -+ hy28a_ts: hy28a-ts@1 { -+ compatible = "ti,ads7846"; -+ reg = <1>; -+ -+ spi-max-frequency = <2000000>; -+ interrupts = <17 2>; /* high-to-low edge triggered */ -+ interrupt-parent = <&gpio>; -+ pendown-gpio = <&gpio 17 0>; -+ ti,x-plate-ohms = /bits/ 16 <100>; -+ ti,pressure-max = /bits/ 16 <255>; -+ }; -+ }; -+ }; -+ __overrides__ { -+ speed = <&hy28a>,"spi-max-frequency:0"; -+ rotate = <&hy28a>,"rotate:0"; -+ fps = <&hy28a>,"fps:0"; -+ debug = <&hy28a>,"debug:0"; -+ xohms = <&hy28a_ts>,"ti,x-plate-ohms;0"; -+ resetgpio = <&hy28a>,"reset-gpios:4", -+ <&hy28a_pins>, "brcm,pins:1"; -+ ledgpio = <&hy28a>,"led-gpios:4", -+ <&hy28a_pins>, "brcm,pins:2"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/hy28b-overlay.dts -@@ -0,0 +1,142 @@ -+/* -+ * Device Tree overlay for HY28b display shield by Texy -+ * -+ */ -+ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&spi0>; -+ __overlay__ { -+ status = "okay"; -+ -+ spidev@0{ -+ status = "disabled"; -+ }; -+ -+ spidev@1{ -+ status = "disabled"; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ hy28b_pins: hy28b_pins { -+ brcm,pins = <17 25 18>; -+ brcm,function = <0 1 1>; /* in out out */ -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&spi0>; -+ __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ hy28b: hy28b@0{ -+ compatible = "ilitek,ili9325"; -+ reg = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&hy28b_pins>; -+ -+ spi-max-frequency = <48000000>; -+ spi-cpol; -+ spi-cpha; -+ rotate = <270>; -+ bgr; -+ fps = <50>; -+ buswidth = <8>; -+ startbyte = <0x70>; -+ reset-gpios = <&gpio 25 0>; -+ led-gpios = <&gpio 18 1>; -+ -+ gamma = "04 1F 4 7 7 0 7 7 6 0\n0F 00 1 7 4 0 0 0 6 7"; -+ -+ init = <0x10000e7 0x0010 -+ 0x1000000 0x0001 -+ 0x1000001 0x0100 -+ 0x1000002 0x0700 -+ 0x1000003 0x1030 -+ 0x1000004 0x0000 -+ 0x1000008 0x0207 -+ 0x1000009 0x0000 -+ 0x100000a 0x0000 -+ 0x100000c 0x0001 -+ 0x100000d 0x0000 -+ 0x100000f 0x0000 -+ 0x1000010 0x0000 -+ 0x1000011 0x0007 -+ 0x1000012 0x0000 -+ 0x1000013 0x0000 -+ 0x2000032 -+ 0x1000010 0x1590 -+ 0x1000011 0x0227 -+ 0x2000032 -+ 0x1000012 0x009c -+ 0x2000032 -+ 0x1000013 0x1900 -+ 0x1000029 0x0023 -+ 0x100002b 0x000e -+ 0x2000032 -+ 0x1000020 0x0000 -+ 0x1000021 0x0000 -+ 0x2000032 -+ 0x1000050 0x0000 -+ 0x1000051 0x00ef -+ 0x1000052 0x0000 -+ 0x1000053 0x013f -+ 0x1000060 0xa700 -+ 0x1000061 0x0001 -+ 0x100006a 0x0000 -+ 0x1000080 0x0000 -+ 0x1000081 0x0000 -+ 0x1000082 0x0000 -+ 0x1000083 0x0000 -+ 0x1000084 0x0000 -+ 0x1000085 0x0000 -+ 0x1000090 0x0010 -+ 0x1000092 0x0000 -+ 0x1000093 0x0003 -+ 0x1000095 0x0110 -+ 0x1000097 0x0000 -+ 0x1000098 0x0000 -+ 0x1000007 0x0133 -+ 0x1000020 0x0000 -+ 0x1000021 0x0000 -+ 0x2000064>; -+ debug = <0>; -+ }; -+ -+ hy28b_ts: hy28b-ts@1 { -+ compatible = "ti,ads7846"; -+ reg = <1>; -+ -+ spi-max-frequency = <2000000>; -+ interrupts = <17 2>; /* high-to-low edge triggered */ -+ interrupt-parent = <&gpio>; -+ pendown-gpio = <&gpio 17 0>; -+ ti,x-plate-ohms = /bits/ 16 <100>; -+ ti,pressure-max = /bits/ 16 <255>; -+ }; -+ }; -+ }; -+ __overrides__ { -+ speed = <&hy28b>,"spi-max-frequency:0"; -+ rotate = <&hy28b>,"rotate:0"; -+ fps = <&hy28b>,"fps:0"; -+ debug = <&hy28b>,"debug:0"; -+ xohms = <&hy28b_ts>,"ti,x-plate-ohms;0"; -+ resetgpio = <&hy28b>,"reset-gpios:4", -+ <&hy28b_pins>, "brcm,pins:1"; -+ ledgpio = <&hy28b>,"led-gpios:4", -+ <&hy28b_pins>, "brcm,pins:2"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts -@@ -0,0 +1,55 @@ -+// Definitions for several I2C based Real Time Clocks -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&i2c_arm>; -+ __overlay__ { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "okay"; -+ -+ ds1307: ds1307@68 { -+ compatible = "maxim,ds1307"; -+ reg = <0x68>; -+ status = "disable"; -+ }; -+ mcp7941x: mcp7941x@6f { -+ compatible = "microchip,mcp7941x"; -+ reg = <0x6f>; -+ status = "disable"; -+ }; -+ ds3231: ds3231@68 { -+ compatible = "maxim,ds3231"; -+ reg = <0x68>; -+ status = "disable"; -+ }; -+ pcf2127: pcf2127@51 { -+ compatible = "nxp,pcf2127"; -+ reg = <0x51>; -+ status = "disable"; -+ }; -+ pcf8523: pcf8523@68 { -+ compatible = "nxp,pcf8523"; -+ reg = <0x68>; -+ status = "disable"; -+ }; -+ pcf8563: pcf8563@51 { -+ compatible = "nxp,pcf8563"; -+ reg = <0x51>; -+ status = "disable"; -+ }; -+ }; -+ }; -+ __overrides__ { -+ ds1307 = <&ds1307>,"status"; -+ ds3231 = <&ds3231>,"status"; -+ mcp7941x = <&mcp7941x>,"status"; -+ pcf2127 = <&pcf2127>,"status"; -+ pcf8523 = <&pcf8523>,"status"; -+ pcf8563 = <&pcf8563>,"status"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/i2s-mmap-overlay.dts -@@ -0,0 +1,13 @@ -+/dts-v1/; -+/plugin/; -+ -+/{ -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&i2s>; -+ __overlay__ { -+ brcm,enable-mmap; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/iqaudio-dac-overlay.dts -@@ -0,0 +1,39 @@ -+// Definitions for IQaudIO DAC -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&sound>; -+ __overlay__ { -+ compatible = "iqaudio,iqaudio-dac"; -+ 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"; -+ -+ pcm5122@4c { -+ #sound-dai-cells = <0>; -+ compatible = "ti,pcm5122"; -+ reg = <0x4c>; -+ status = "okay"; -+ }; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/iqaudio-dacplus-overlay.dts -@@ -0,0 +1,39 @@ -+// Definitions for IQaudIO DAC+ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&sound>; -+ __overlay__ { -+ compatible = "iqaudio,iqaudio-dac"; -+ 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"; -+ -+ pcm5122@4c { -+ #sound-dai-cells = <0>; -+ compatible = "ti,pcm5122"; -+ reg = <0x4c>; -+ status = "okay"; -+ }; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/lirc-rpi-overlay.dts -@@ -0,0 +1,57 @@ -+// Definitions for lirc-rpi module -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target-path = "/"; -+ __overlay__ { -+ lirc_rpi: lirc_rpi { -+ compatible = "rpi,lirc-rpi"; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&lirc_pins>; -+ status = "okay"; -+ -+ // Override autodetection of IR receiver circuit -+ // (0 = active high, 1 = active low, -1 = no override ) -+ rpi,sense = <0xffffffff>; -+ -+ // Software carrier -+ // (0 = off, 1 = on) -+ rpi,softcarrier = <1>; -+ -+ // Invert output -+ // (0 = off, 1 = on) -+ rpi,invert = <0>; -+ -+ // Enable debugging messages -+ // (0 = off, 1 = on) -+ rpi,debug = <0>; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ lirc_pins: lirc_pins { -+ brcm,pins = <17 18>; -+ brcm,function = <1 0>; // out in -+ brcm,pull = <0 1>; // off down -+ }; -+ }; -+ }; -+ -+ __overrides__ { -+ gpio_out_pin = <&lirc_pins>,"brcm,pins:0"; -+ gpio_in_pin = <&lirc_pins>,"brcm,pins:4"; -+ gpio_in_pull = <&lirc_pins>,"brcm,pull:4"; -+ -+ sense = <&lirc_rpi>,"rpi,sense:0"; -+ softcarrier = <&lirc_rpi>,"rpi,softcarrier:0"; -+ invert = <&lirc_rpi>,"rpi,invert:0"; -+ debug = <&lirc_rpi>,"rpi,debug:0"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/mcp2515-can0-overlay.dts -@@ -0,0 +1,69 @@ -+/* -+ * Device tree overlay for mcp251x/can0 on spi0.0 -+ */ -+ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2836", "brcm,bcm2708", "brcm,bcm2709"; -+ /* disable spi-dev for spi0.0 */ -+ fragment@0 { -+ target = <&spi0>; -+ __overlay__ { -+ status = "okay"; -+ spidev@0{ -+ status = "disabled"; -+ }; -+ }; -+ }; -+ -+ /* the interrupt pin of the can-controller */ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ can0_pins: can0_pins { -+ brcm,pins = <25>; -+ brcm,function = <0>; /* input */ -+ }; -+ }; -+ }; -+ -+ /* the clock/oscillator of the can-controller */ -+ fragment@2 { -+ target-path = "/clocks"; -+ __overlay__ { -+ /* external oscillator of mcp2515 on SPI0.0 */ -+ can0_osc: can0_osc { -+ compatible = "fixed-clock"; -+ #clock-cells = <0>; -+ clock-frequency = <16000000>; -+ }; -+ }; -+ }; -+ -+ /* the spi config of the can-controller itself binding everything together */ -+ fragment@3 { -+ target = <&spi0>; -+ __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ can0: mcp2515@0 { -+ reg = <0>; -+ compatible = "microchip,mcp2515"; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&can0_pins>; -+ spi-max-frequency = <10000000>; -+ interrupt-parent = <&gpio>; -+ interrupts = <25 0x2>; -+ clocks = <&can0_osc>; -+ }; -+ }; -+ }; -+ __overrides__ { -+ oscillator = <&can0_osc>,"clock-frequency:0"; -+ spimaxfrequency = <&can0>,"spi-max-frequency:0"; -+ interrupt = <&can0_pins>,"brcm,pins:0",<&can0>,"interrupts:0"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/mcp2515-can1-overlay.dts -@@ -0,0 +1,69 @@ -+/* -+ * Device tree overlay for mcp251x/can1 on spi0.1 edited by petit_miner -+ */ -+ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2836", "brcm,bcm2708", "brcm,bcm2709"; -+ /* disable spi-dev for spi0.1 */ -+ fragment@0 { -+ target = <&spi0>; -+ __overlay__ { -+ status = "okay"; -+ spidev@1{ -+ status = "disabled"; -+ }; -+ }; -+ }; -+ -+ /* the interrupt pin of the can-controller */ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ can1_pins: can1_pins { -+ brcm,pins = <25>; -+ brcm,function = <0>; /* input */ -+ }; -+ }; -+ }; -+ -+ /* the clock/oscillator of the can-controller */ -+ fragment@2 { -+ target-path = "/clocks"; -+ __overlay__ { -+ /* external oscillator of mcp2515 on spi0.1 */ -+ can1_osc: can1_osc { -+ compatible = "fixed-clock"; -+ #clock-cells = <0>; -+ clock-frequency = <16000000>; -+ }; -+ }; -+ }; -+ -+ /* the spi config of the can-controller itself binding everything together */ -+ fragment@3 { -+ target = <&spi0>; -+ __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ can1: mcp2515@1 { -+ reg = <1>; -+ compatible = "microchip,mcp2515"; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&can1_pins>; -+ spi-max-frequency = <10000000>; -+ interrupt-parent = <&gpio>; -+ interrupts = <25 0x2>; -+ clocks = <&can1_osc>; -+ }; -+ }; -+ }; -+ __overrides__ { -+ oscillator = <&can1_osc>,"clock-frequency:0"; -+ spimaxfrequency = <&can1>,"spi-max-frequency:0"; -+ interrupt = <&can1_pins>,"brcm,pins:0",<&can1>,"interrupts:0"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/mmc-overlay.dts -@@ -0,0 +1,39 @@ -+/dts-v1/; -+/plugin/; -+ -+/{ -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&mmc>; -+ frag0: __overlay__ { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&mmc_pins>; -+ bus-width = <4>; -+ brcm,overclock-50 = <0>; -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ mmc_pins: mmc_pins { -+ brcm,pins = <48 49 50 51 52 53>; -+ brcm,function = <7>; /* alt3 */ -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&sdhost>; -+ __overlay__ { -+ status = "disabled"; -+ }; -+ }; -+ -+ __overrides__ { -+ overclock_50 = <&frag0>,"brcm,overclock-50:0"; -+ force_pio = <&frag0>,"brcm,force-pio?"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/mz61581-overlay.dts -@@ -0,0 +1,111 @@ -+/* -+ * Device Tree overlay for MZ61581-PI-EXT 2014.12.28 by Tontec -+ * -+ */ -+ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&spi0>; -+ __overlay__ { -+ status = "okay"; -+ -+ spidev@0{ -+ status = "disabled"; -+ }; -+ -+ spidev@1{ -+ status = "disabled"; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ mz61581_pins: mz61581_pins { -+ brcm,pins = <4 15 18 25>; -+ brcm,function = <0 1 1 1>; /* in out out out */ -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&spi0>; -+ __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ mz61581: mz61581@0{ -+ compatible = "samsung,s6d02a1"; -+ reg = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&mz61581_pins>; -+ -+ spi-max-frequency = <128000000>; -+ spi-cpol; -+ spi-cpha; -+ -+ width = <320>; -+ height = <480>; -+ rotate = <270>; -+ bgr; -+ fps = <30>; -+ buswidth = <8>; -+ txbuflen = <32768>; -+ -+ reset-gpios = <&gpio 15 0>; -+ dc-gpios = <&gpio 25 0>; -+ led-gpios = <&gpio 18 0>; -+ -+ init = <0x10000b0 00 -+ 0x1000011 -+ 0x20000ff -+ 0x10000b3 0x02 0x00 0x00 0x00 -+ 0x10000c0 0x13 0x3b 0x00 0x02 0x00 0x01 0x00 0x43 -+ 0x10000c1 0x08 0x16 0x08 0x08 -+ 0x10000c4 0x11 0x07 0x03 0x03 -+ 0x10000c6 0x00 -+ 0x10000c8 0x03 0x03 0x13 0x5c 0x03 0x07 0x14 0x08 0x00 0x21 0x08 0x14 0x07 0x53 0x0c 0x13 0x03 0x03 0x21 0x00 -+ 0x1000035 0x00 -+ 0x1000036 0xa0 -+ 0x100003a 0x55 -+ 0x1000044 0x00 0x01 -+ 0x10000d0 0x07 0x07 0x1d 0x03 -+ 0x10000d1 0x03 0x30 0x10 -+ 0x10000d2 0x03 0x14 0x04 -+ 0x1000029 -+ 0x100002c>; -+ -+ /* This is a workaround to make sure the init sequence slows down and doesn't fail */ -+ debug = <3>; -+ }; -+ -+ mz61581_ts: mz61581_ts@1 { -+ compatible = "ti,ads7846"; -+ reg = <1>; -+ -+ spi-max-frequency = <2000000>; -+ interrupts = <4 2>; /* high-to-low edge triggered */ -+ interrupt-parent = <&gpio>; -+ pendown-gpio = <&gpio 4 0>; -+ -+ ti,x-plate-ohms = /bits/ 16 <60>; -+ ti,pressure-max = /bits/ 16 <255>; -+ }; -+ }; -+ }; -+ __overrides__ { -+ speed = <&mz61581>, "spi-max-frequency:0"; -+ rotate = <&mz61581>, "rotate:0"; -+ fps = <&mz61581>, "fps:0"; -+ txbuflen = <&mz61581>, "txbuflen:0"; -+ debug = <&mz61581>, "debug:0"; -+ xohms = <&mz61581_ts>,"ti,x-plate-ohms;0"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/piscreen-overlay.dts -@@ -0,0 +1,96 @@ -+/* -+ * Device Tree overlay for PiScreen 3.5" display shield by Ozzmaker -+ * -+ */ -+ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&spi0>; -+ __overlay__ { -+ status = "okay"; -+ -+ spidev@0{ -+ status = "disabled"; -+ }; -+ -+ spidev@1{ -+ status = "disabled"; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ piscreen_pins: piscreen_pins { -+ brcm,pins = <17 25 24 22>; -+ brcm,function = <0 1 1 1>; /* in out out out */ -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&spi0>; -+ __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ piscreen: piscreen@0{ -+ compatible = "ilitek,ili9486"; -+ reg = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&piscreen_pins>; -+ -+ spi-max-frequency = <24000000>; -+ rotate = <270>; -+ bgr; -+ fps = <30>; -+ buswidth = <8>; -+ regwidth = <16>; -+ reset-gpios = <&gpio 25 0>; -+ dc-gpios = <&gpio 24 0>; -+ led-gpios = <&gpio 22 1>; -+ debug = <0>; -+ -+ init = <0x10000b0 0x00 -+ 0x1000011 -+ 0x20000ff -+ 0x100003a 0x55 -+ 0x1000036 0x28 -+ 0x10000c2 0x44 -+ 0x10000c5 0x00 0x00 0x00 0x00 -+ 0x10000e0 0x0f 0x1f 0x1c 0x0c 0x0f 0x08 0x48 0x98 0x37 0x0a 0x13 0x04 0x11 0x0d 0x00 -+ 0x10000e1 0x0f 0x32 0x2e 0x0b 0x0d 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 -+ 0x10000e2 0x0f 0x32 0x2e 0x0b 0x0d 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 -+ 0x1000011 -+ 0x1000029>; -+ }; -+ -+ piscreen_ts: piscreen-ts@1 { -+ compatible = "ti,ads7846"; -+ reg = <1>; -+ -+ spi-max-frequency = <2000000>; -+ interrupts = <17 2>; /* high-to-low edge triggered */ -+ interrupt-parent = <&gpio>; -+ pendown-gpio = <&gpio 17 0>; -+ ti,swap-xy; -+ ti,x-plate-ohms = /bits/ 16 <100>; -+ ti,pressure-max = /bits/ 16 <255>; -+ }; -+ }; -+ }; -+ __overrides__ { -+ speed = <&piscreen>,"spi-max-frequency:0"; -+ rotate = <&piscreen>,"rotate:0"; -+ fps = <&piscreen>,"fps:0"; -+ debug = <&piscreen>,"debug:0"; -+ xohms = <&piscreen_ts>,"ti,x-plate-ohms;0"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/pitft28-resistive-overlay.dts -@@ -0,0 +1,115 @@ -+/* -+ * Device Tree overlay for Adafruit PiTFT 2.8" resistive touch screen -+ * -+ */ -+ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&spi0>; -+ __overlay__ { -+ status = "okay"; -+ -+ spidev@0{ -+ status = "disabled"; -+ }; -+ -+ spidev@1{ -+ status = "disabled"; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ pitft_pins: pitft_pins { -+ brcm,pins = <24 25>; -+ brcm,function = <0 1>; /* in out */ -+ brcm,pull = <2 0>; /* pullup none */ -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&spi0>; -+ __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ pitft: pitft@0{ -+ compatible = "ilitek,ili9340"; -+ reg = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&pitft_pins>; -+ -+ spi-max-frequency = <32000000>; -+ rotate = <90>; -+ fps = <25>; -+ bgr; -+ buswidth = <8>; -+ dc-gpios = <&gpio 25 0>; -+ debug = <0>; -+ }; -+ -+ pitft_ts@1 { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ compatible = "st,stmpe610"; -+ reg = <1>; -+ -+ spi-max-frequency = <500000>; -+ irq-gpio = <&gpio 24 0x2>; /* IRQF_TRIGGER_FALLING */ -+ interrupts = <24 2>; /* high-to-low edge triggered */ -+ interrupt-parent = <&gpio>; -+ interrupt-controller; -+ -+ stmpe_touchscreen { -+ compatible = "st,stmpe-ts"; -+ st,sample-time = <4>; -+ st,mod-12b = <1>; -+ st,ref-sel = <0>; -+ st,adc-freq = <2>; -+ st,ave-ctrl = <3>; -+ st,touch-det-delay = <4>; -+ st,settling = <2>; -+ st,fraction-z = <7>; -+ st,i-drive = <0>; -+ }; -+ -+ stmpe_gpio: stmpe_gpio { -+ #gpio-cells = <2>; -+ compatible = "st,stmpe-gpio"; -+ /* -+ * only GPIO2 is wired/available -+ * and it is wired to the backlight -+ */ -+ st,norequest-mask = <0x7b>; -+ }; -+ }; -+ }; -+ }; -+ -+ fragment@3 { -+ target-path = "/soc"; -+ __overlay__ { -+ backlight { -+ compatible = "gpio-backlight"; -+ gpios = <&stmpe_gpio 2 0>; -+ default-on; -+ }; -+ }; -+ }; -+ -+ __overrides__ { -+ speed = <&pitft>,"spi-max-frequency:0"; -+ rotate = <&pitft>,"rotate:0"; -+ fps = <&pitft>,"fps:0"; -+ debug = <&pitft>,"debug:0"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/pps-gpio-overlay.dts -@@ -0,0 +1,34 @@ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ fragment@0 { -+ target-path = "/"; -+ __overlay__ { -+ pps: pps { -+ compatible = "pps-gpio"; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&pps_pins>; -+ gpios = <&gpio 18 0>; -+ status = "okay"; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ pps_pins: pps_pins { -+ brcm,pins = <18>; -+ brcm,function = <0>; // in -+ brcm,pull = <0>; // off -+ }; -+ }; -+ }; -+ -+ __overrides__ { -+ gpiopin = <&pps>,"gpios:4", -+ <&pps_pins>,"brcm,pins:0"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/pwm-2chan-overlay.dts -@@ -0,0 +1,46 @@ -+/dts-v1/; -+/plugin/; -+ -+/* -+This is the 2-channel overlay - only use it if you need both channels. -+ -+Legal pin,function combinations for each channel: -+ PWM0: 12,4(Alt0) 18,2(Alt5) 40,4(Alt0) 52,5(Alt1) -+ PWM1: 13,4(Alt0) 19,2(Alt5) 41,4(Alt0) 45,4(Alt0) 53,5(Alt1) -+ -+N.B.: -+ 1) Pin 18 is the only one available on all platforms, and -+ it is the one used by the I2S audio interface. -+ Pins 12 and 13 might be better choices on an A+, B+ or Pi2. -+ 2) The onboard analogue audio output uses both PWM channels. -+ 3) So be careful mixing audio and PWM. -+*/ -+ -+/ { -+ fragment@0 { -+ target = <&gpio>; -+ __overlay__ { -+ pwm_pins: pwm_pins { -+ brcm,pins = <18 19>; -+ brcm,function = <2 2>; /* Alt5 */ -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&pwm>; -+ __overlay__ { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&pwm_pins>; -+ status = "okay"; -+ }; -+ }; -+ -+ __overrides__ { -+ pin = <&pwm_pins>,"brcm,pins:0"; -+ pin2 = <&pwm_pins>,"brcm,pins:4"; -+ func = <&pwm_pins>,"brcm,function:0"; -+ func2 = <&pwm_pins>,"brcm,function:4"; -+ clock = <&clk_pwm>,"clock-frequency:0"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/pwm-overlay.dts -@@ -0,0 +1,42 @@ -+/dts-v1/; -+/plugin/; -+ -+/* -+Legal pin,function combinations for each channel: -+ PWM0: 12,4(Alt0) 18,2(Alt5) 40,4(Alt0) 52,5(Alt1) -+ PWM1: 13,4(Alt0) 19,2(Alt5) 41,4(Alt0) 45,4(Alt0) 53,5(Alt1) -+ -+N.B.: -+ 1) Pin 18 is the only one available on all platforms, and -+ it is the one used by the I2S audio interface. -+ Pins 12 and 13 might be better choices on an A+, B+ or Pi2. -+ 2) The onboard analogue audio output uses both PWM channels. -+ 3) So be careful mixing audio and PWM. -+*/ -+ -+/ { -+ fragment@0 { -+ target = <&gpio>; -+ __overlay__ { -+ pwm_pins: pwm_pins { -+ brcm,pins = <18>; -+ brcm,function = <2>; /* Alt5 */ -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&pwm>; -+ __overlay__ { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&pwm_pins>; -+ status = "okay"; -+ }; -+ }; -+ -+ __overrides__ { -+ pin = <&pwm_pins>,"brcm,pins:0"; -+ func = <&pwm_pins>,"brcm,function:0"; -+ clock = <&clk_pwm>,"clock-frequency:0"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/raspidac3-overlay.dts -@@ -0,0 +1,45 @@ -+// Definitions for RaspiDACv3 -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&sound>; -+ __overlay__ { -+ compatible = "jg,raspidacv3"; -+ 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"; -+ -+ pcm5122@4c { -+ #sound-dai-cells = <0>; -+ compatible = "ti,pcm5122"; -+ reg = <0x4c>; -+ status = "okay"; -+ }; -+ -+ tpa6130a2: tpa6130a2@60 { -+ compatible = "ti,tpa6130a2"; -+ reg = <0x60>; -+ status = "okay"; -+ }; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/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"; -+ }; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/rpi-display-overlay.dts -@@ -0,0 +1,82 @@ -+/* -+ * Device Tree overlay for rpi-display by Watterott -+ * -+ */ -+ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&spi0>; -+ __overlay__ { -+ status = "okay"; -+ -+ spidev@0{ -+ status = "disabled"; -+ }; -+ -+ spidev@1{ -+ status = "disabled"; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ rpi_display_pins: rpi_display_pins { -+ brcm,pins = <18 23 24 25>; -+ brcm,function = <1 1 1 0>; /* out out out in */ -+ brcm,pull = <0 0 0 2>; /* - - - up */ -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&spi0>; -+ __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ rpidisplay: rpi-display@0{ -+ compatible = "ilitek,ili9341"; -+ reg = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&rpi_display_pins>; -+ -+ spi-max-frequency = <32000000>; -+ rotate = <270>; -+ bgr; -+ fps = <30>; -+ buswidth = <8>; -+ reset-gpios = <&gpio 23 0>; -+ dc-gpios = <&gpio 24 0>; -+ led-gpios = <&gpio 18 1>; -+ debug = <0>; -+ }; -+ -+ rpidisplay_ts: rpi-display-ts@1 { -+ compatible = "ti,ads7846"; -+ reg = <1>; -+ -+ spi-max-frequency = <2000000>; -+ interrupts = <25 2>; /* high-to-low edge triggered */ -+ interrupt-parent = <&gpio>; -+ pendown-gpio = <&gpio 25 0>; -+ ti,x-plate-ohms = /bits/ 16 <60>; -+ ti,pressure-max = /bits/ 16 <255>; -+ }; -+ }; -+ }; -+ __overrides__ { -+ speed = <&rpidisplay>,"spi-max-frequency:0"; -+ rotate = <&rpidisplay>,"rotate:0"; -+ fps = <&rpidisplay>,"fps:0"; -+ debug = <&rpidisplay>,"debug:0"; -+ xohms = <&rpidisplay_ts>,"ti,x-plate-ohms;0"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/rpi-ft5406-overlay.dts -@@ -0,0 +1,17 @@ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target-path = "/"; -+ __overlay__ { -+ rpi_ft5406: rpi_ft5406 { -+ compatible = "rpi,rpi-ft5406"; -+ firmware = <&firmware>; -+ status = "okay"; -+ }; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/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"; -+ }; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/rpi-sense-overlay.dts -@@ -0,0 +1,47 @@ -+// rpi-sense HAT -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&i2c1>; -+ __overlay__ { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "okay"; -+ -+ rpi-sense@46 { -+ compatible = "rpi,rpi-sense"; -+ reg = <0x46>; -+ keys-int-gpios = <&gpio 23 1>; -+ status = "okay"; -+ }; -+ -+ lsm9ds1-magn@1c { -+ compatible = "st,lsm9ds1-magn"; -+ reg = <0x1c>; -+ status = "okay"; -+ }; -+ -+ lsm9ds1-accel6a { -+ compatible = "st,lsm9ds1-accel"; -+ reg = <0x6a>; -+ status = "okay"; -+ }; -+ -+ lps25h-press@5c { -+ compatible = "st,lps25h-press"; -+ reg = <0x5c>; -+ status = "okay"; -+ }; -+ -+ hts221-humid@5f { -+ compatible = "st,hts221-humid"; -+ reg = <0x5f>; -+ status = "okay"; -+ }; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/sdhost-overlay.dts -@@ -0,0 +1,29 @@ -+/dts-v1/; -+/plugin/; -+ -+/{ -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&mmc>; -+ __overlay__ { -+ status = "disabled"; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&sdhost>; -+ frag1: __overlay__ { -+ brcm,overclock-50 = <0>; -+ brcm,pio-limit = <1>; -+ status = "okay"; -+ }; -+ }; -+ -+ __overrides__ { -+ overclock_50 = <&frag1>,"brcm,overclock-50:0"; -+ force_pio = <&frag1>,"brcm,force-pio?"; -+ pio_limit = <&frag1>,"brcm,pio-limit:0"; -+ debug = <&frag1>,"brcm,debug?"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/sdio-overlay.dts -@@ -0,0 +1,32 @@ -+/* Enable SDIO from MMC interface via GPIOs 22-27. Includes sdhost overlay. */ -+ -+/include/ "sdhost-overlay.dts" -+ -+/{ -+ compatible = "brcm,bcm2708"; -+ -+ fragment@3 { -+ target = <&mmc>; -+ sdio_mmc: __overlay__ { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&sdio_pins>; -+ non-removable; -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@4 { -+ target = <&gpio>; -+ __overlay__ { -+ sdio_pins: sdio_pins { -+ brcm,pins = <22 23 24 25 26 27>; -+ brcm,function = <7 7 7 7 7 7>; /* ALT3 = SD1 */ -+ brcm,pull = <0 2 2 2 2 2>; -+ }; -+ }; -+ }; -+ -+ __overrides__ { -+ poll_once = <&sdio_mmc>,"non-removable?"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/smi-dev-overlay.dts -@@ -0,0 +1,18 @@ -+// Description: Overlay to enable character device interface for SMI. -+// Author: Luke Wren -+ -+/dts-v1/; -+/plugin/; -+ -+/{ -+ fragment@0 { -+ target = <&soc>; -+ __overlay__ { -+ smi_dev { -+ compatible = "brcm,bcm2835-smi-dev"; -+ smi_handle = <&smi>; -+ status = "okay"; -+ }; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/smi-nand-overlay.dts -@@ -0,0 +1,69 @@ -+// Description: Overlay to enable NAND flash through -+// the secondary memory interface -+// Author: Luke Wren -+ -+/dts-v1/; -+/plugin/; -+ -+/{ -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&smi>; -+ __overlay__ { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&smi_pins>; -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&soc>; -+ __overlay__ { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ -+ nand: flash@0 { -+ compatible = "brcm,bcm2835-smi-nand"; -+ smi_handle = <&smi>; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ status = "okay"; -+ -+ partition@0 { -+ label = "stage2"; -+ // 128k -+ reg = <0 0x20000>; -+ read-only; -+ }; -+ partition@1 { -+ label = "firmware"; -+ // 16M -+ reg = <0x20000 0x1000000>; -+ read-only; -+ }; -+ partition@2 { -+ label = "root"; -+ // 2G (will need to use 64 bit for >=4G) -+ reg = <0x1020000 0x80000000>; -+ }; -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&gpio>; -+ __overlay__ { -+ smi_pins: smi_pins { -+ brcm,pins = <0 1 2 3 4 5 6 7 8 9 10 11 -+ 12 13 14 15>; -+ /* Alt 1: SMI */ -+ brcm,function = <5 5 5 5 5 5 5 5 5 5 5 -+ 5 5 5 5 5>; -+ /* /CS, /WE and /OE are pulled high, as they are -+ generally active low signals */ -+ brcm,pull = <2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0>; -+ }; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/smi-overlay.dts -@@ -0,0 +1,37 @@ -+// Description: Overlay to enable the secondary memory interface peripheral -+// Author: Luke Wren -+ -+/dts-v1/; -+/plugin/; -+ -+/{ -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&smi>; -+ __overlay__ { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&smi_pins>; -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ smi_pins: smi_pins { -+ /* Don't configure the top two address bits, as -+ these are already used as ID_SD and ID_SC */ -+ brcm,pins = <2 3 4 5 6 7 8 9 10 11 12 13 14 15 -+ 16 17 18 19 20 21 22 23 24 25>; -+ /* Alt 0: SMI */ -+ brcm,function = <5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 -+ 5 5 5 5 5 5 5 5 5>; -+ /* /CS, /WE and /OE are pulled high, as they are -+ generally active low signals */ -+ brcm,pull = <2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 -+ 0 0 0 0 0 0 0>; -+ }; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/spi-gpio35-39-overlay.dts -@@ -0,0 +1,31 @@ -+/* -+ * Device tree overlay to move spi0 to gpio 35 to 39 on CM -+ */ -+ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2836", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&spi0>; -+ __overlay__ { -+ cs-gpios = <&gpio 36 1>, <&gpio 35 1>; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&spi0_cs_pins>; -+ __overlay__ { -+ bcrm,pins = <36 35>; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&spi0_pins>; -+ __overlay__ { -+ bcrm,pins = <37 38 39>; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/tinylcd35-overlay.dts -@@ -0,0 +1,216 @@ -+/* -+ * tinylcd35-overlay.dts -+ * -+ * ------------------------------------------------- -+ * www.tinlylcd.com -+ * ------------------------------------------------- -+ * Device---Driver-----BUS GPIO's -+ * display tinylcd35 spi0.0 25 24 18 -+ * touch ads7846 spi0.1 5 -+ * rtc ds1307 i2c1-0068 -+ * rtc pcf8563 i2c1-0051 -+ * keypad gpio-keys --------- 17 22 27 23 28 -+ * -+ * -+ * TinyLCD.com 3.5 inch TFT -+ * -+ * Version 001 -+ * 5/3/2015 -- Noralf Trønnes Initial Device tree framework -+ * 10/3/2015 -- tinylcd@gmail.com added ds1307 support. -+ * -+ */ -+ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&spi0>; -+ __overlay__ { -+ status = "okay"; -+ -+ spidev@0{ -+ status = "disabled"; -+ }; -+ -+ spidev@1{ -+ status = "disabled"; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ tinylcd35_pins: tinylcd35_pins { -+ brcm,pins = <25 24 18>; -+ brcm,function = <1>; /* out */ -+ }; -+ tinylcd35_ts_pins: tinylcd35_ts_pins { -+ brcm,pins = <5>; -+ brcm,function = <0>; /* in */ -+ }; -+ keypad_pins: keypad_pins { -+ brcm,pins = <4 17 22 23 27>; -+ brcm,function = <0>; /* in */ -+ brcm,pull = <1>; /* down */ -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&spi0>; -+ __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ tinylcd35: tinylcd35@0{ -+ compatible = "neosec,tinylcd"; -+ reg = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&tinylcd35_pins>, -+ <&tinylcd35_ts_pins>; -+ -+ spi-max-frequency = <48000000>; -+ rotate = <270>; -+ fps = <20>; -+ bgr; -+ buswidth = <8>; -+ reset-gpios = <&gpio 25 0>; -+ dc-gpios = <&gpio 24 0>; -+ led-gpios = <&gpio 18 1>; -+ debug = <0>; -+ -+ init = <0x10000B0 0x80 -+ 0x10000C0 0x0A 0x0A -+ 0x10000C1 0x01 0x01 -+ 0x10000C2 0x33 -+ 0x10000C5 0x00 0x42 0x80 -+ 0x10000B1 0xD0 0x11 -+ 0x10000B4 0x02 -+ 0x10000B6 0x00 0x22 0x3B -+ 0x10000B7 0x07 -+ 0x1000036 0x58 -+ 0x10000F0 0x36 0xA5 0xD3 -+ 0x10000E5 0x80 -+ 0x10000E5 0x01 -+ 0x10000B3 0x00 -+ 0x10000E5 0x00 -+ 0x10000F0 0x36 0xA5 0x53 -+ 0x10000E0 0x00 0x35 0x33 0x00 0x00 0x00 0x00 0x35 0x33 0x00 0x00 0x00 -+ 0x100003A 0x55 -+ 0x1000011 -+ 0x2000001 -+ 0x1000029>; -+ }; -+ -+ tinylcd35_ts: tinylcd35_ts@1 { -+ compatible = "ti,ads7846"; -+ reg = <1>; -+ status = "disabled"; -+ -+ spi-max-frequency = <2000000>; -+ interrupts = <5 2>; /* high-to-low edge triggered */ -+ interrupt-parent = <&gpio>; -+ pendown-gpio = <&gpio 5 0>; -+ ti,x-plate-ohms = /bits/ 16 <100>; -+ ti,pressure-max = /bits/ 16 <255>; -+ }; -+ }; -+ }; -+ -+ /* RTC */ -+ -+ fragment@3 { -+ target = <&i2c1>; -+ __overlay__ { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ pcf8563: pcf8563@51 { -+ compatible = "nxp,pcf8563"; -+ reg = <0x51>; -+ status = "disabled"; -+ }; -+ }; -+ }; -+ -+ fragment@4 { -+ target = <&i2c1>; -+ __overlay__ { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ ds1307: ds1307@68 { -+ compatible = "maxim,ds1307"; -+ reg = <0x68>; -+ status = "disabled"; -+ }; -+ }; -+ }; -+ -+ /* -+ * Values for input event code is found under the -+ * 'Keys and buttons' heading in include/uapi/linux/input.h -+ */ -+ fragment@5 { -+ target-path = "/soc"; -+ __overlay__ { -+ keypad: keypad { -+ compatible = "gpio-keys"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&keypad_pins>; -+ status = "disabled"; -+ autorepeat; -+ -+ button@17 { -+ label = "GPIO KEY_UP"; -+ linux,code = <103>; -+ gpios = <&gpio 17 0>; -+ }; -+ button@22 { -+ label = "GPIO KEY_DOWN"; -+ linux,code = <108>; -+ gpios = <&gpio 22 0>; -+ }; -+ button@27 { -+ label = "GPIO KEY_LEFT"; -+ linux,code = <105>; -+ gpios = <&gpio 27 0>; -+ }; -+ button@23 { -+ label = "GPIO KEY_RIGHT"; -+ linux,code = <106>; -+ gpios = <&gpio 23 0>; -+ }; -+ button@4 { -+ label = "GPIO KEY_ENTER"; -+ linux,code = <28>; -+ gpios = <&gpio 4 0>; -+ }; -+ }; -+ }; -+ }; -+ -+ __overrides__ { -+ speed = <&tinylcd35>,"spi-max-frequency:0"; -+ rotate = <&tinylcd35>,"rotate:0"; -+ fps = <&tinylcd35>,"fps:0"; -+ debug = <&tinylcd35>,"debug:0"; -+ touch = <&tinylcd35_ts>,"status"; -+ touchgpio = <&tinylcd35_ts_pins>,"brcm,pins:0", -+ <&tinylcd35_ts>,"interrupts:0", -+ <&tinylcd35_ts>,"pendown-gpio:4"; -+ xohms = <&tinylcd35_ts>,"ti,x-plate-ohms;0"; -+ rtc-pcf = <&i2c1>,"status", -+ <&pcf8563>,"status"; -+ rtc-ds = <&i2c1>,"status", -+ <&ds1307>,"status"; -+ keypad = <&keypad>,"status"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/uart1-overlay.dts -@@ -0,0 +1,38 @@ -+/dts-v1/; -+/plugin/; -+ -+/{ -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&uart1>; -+ __overlay__ { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&uart1_pins>; -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ uart1_pins: uart1_pins { -+ brcm,pins = <14 15>; -+ brcm,function = <2>; /* alt5 */ -+ brcm,pull = <0 2>; -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target-path = "/chosen"; -+ __overlay__ { -+ bootargs = "8250.nr_uarts=1"; -+ }; -+ }; -+ -+ __overrides__ { -+ txd1_pin = <&uart1_pins>,"brcm,pins:0"; -+ rxd1_pin = <&uart1_pins>,"brcm,pins:4"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/vga666-overlay.dts -@@ -0,0 +1,30 @@ -+/dts-v1/; -+/plugin/; -+ -+/{ -+ compatible = "brcm,bcm2708"; -+ -+ // There is no VGA driver module, but we need a platform device -+ // node (that doesn't already use pinctrl) to hang the pinctrl -+ // reference on - leds will do -+ -+ fragment@0 { -+ target = <&leds>; -+ __overlay__ { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&vga666_pins>; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ vga666_pins: vga666_pins { -+ brcm,pins = <2 3 4 5 6 7 8 9 10 11 12 -+ 13 14 15 16 17 18 19 20 21>; -+ brcm,function = <6>; /* alt2 */ -+ brcm,pull = <0>; /* no pull */ -+ }; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/w1-gpio-overlay.dts -@@ -0,0 +1,39 @@ -+// Definitions for w1-gpio module (without external pullup) -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target-path = "/"; -+ __overlay__ { -+ -+ w1: onewire@0 { -+ compatible = "w1-gpio"; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&w1_pins>; -+ gpios = <&gpio 4 0>; -+ rpi,parasitic-power = <0>; -+ status = "okay"; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ w1_pins: w1_pins { -+ brcm,pins = <4>; -+ brcm,function = <0>; // in (initially) -+ brcm,pull = <0>; // off -+ }; -+ }; -+ }; -+ -+ __overrides__ { -+ gpiopin = <&w1>,"gpios:4", -+ <&w1_pins>,"brcm,pins:0"; -+ pullup = <&w1>,"rpi,parasitic-power:0"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/w1-gpio-pullup-overlay.dts -@@ -0,0 +1,41 @@ -+// Definitions for w1-gpio module (with external pullup) -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target-path = "/"; -+ __overlay__ { -+ -+ w1: onewire@0 { -+ compatible = "w1-gpio"; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&w1_pins>; -+ gpios = <&gpio 4 0>, <&gpio 5 1>; -+ rpi,parasitic-power = <0>; -+ status = "okay"; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ w1_pins: w1_pins { -+ brcm,pins = <4 5>; -+ brcm,function = <0 1>; // in out -+ brcm,pull = <0 0>; // off off -+ }; -+ }; -+ }; -+ -+ __overrides__ { -+ gpiopin = <&w1>,"gpios:4", -+ <&w1_pins>,"brcm,pins:0"; -+ extpullup = <&w1>,"gpios:16", -+ <&w1_pins>,"brcm,pins:4"; -+ pullup = <&w1>,"rpi,parasitic-power:0"; -+ }; -+}; diff --git a/target/linux/brcm2708/patches-4.4/0052-bcm2835-Match-with-BCM2708-Device-Trees.patch b/target/linux/brcm2708/patches-4.4/0052-bcm2835-Match-with-BCM2708-Device-Trees.patch new file mode 100644 index 0000000000..a7966df9d3 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0052-bcm2835-Match-with-BCM2708-Device-Trees.patch @@ -0,0 +1,515 @@ +From da7cdef4979282624e0e7527c3c8334a59f39676 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= +Date: Sat, 15 Aug 2015 20:47:07 +0200 +Subject: [PATCH 052/232] bcm2835: Match with BCM2708 Device Trees +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Noralf Trønnes +--- + arch/arm/boot/dts/bcm2835-rpi-b-plus.dts | 132 ++++++++++++++++++--- + arch/arm/boot/dts/bcm2835-rpi-b.dts | 115 ++++++++++++++++-- + arch/arm/boot/dts/bcm2835.dtsi | 195 +++---------------------------- + 3 files changed, 237 insertions(+), 205 deletions(-) + +--- a/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts +@@ -1,30 +1,128 @@ + /dts-v1/; +-#include "bcm2835-rpi.dtsi" ++#include "bcm2835.dtsi" + + / { + compatible = "raspberrypi,model-b-plus", "brcm,bcm2835"; + model = "Raspberry Pi Model B+"; +- +- leds { +- act { +- gpios = <&gpio 47 0>; +- }; +- +- pwr { +- label = "PWR"; +- gpios = <&gpio 35 0>; +- default-state = "keep"; +- linux,default-trigger = "default-on"; +- }; +- }; + }; + + &gpio { +- pinctrl-0 = <&gpioout &alt0 &i2s_alt0 &alt3>; ++ 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>; ++ }; + +- /* I2S interface */ +- i2s_alt0: i2s_alt0 { ++ i2c1_pins: i2c1 { ++ brcm,pins = <2 3>; ++ brcm,function = <4>; ++ }; ++ ++ i2s_pins: i2s { + brcm,pins = <18 19 20 21>; +- brcm,function = ; ++ brcm,function = <4>; /* alt0 */ ++ }; ++}; ++ ++&mmc { ++ status = "okay"; ++ bus-width = <4>; ++}; ++ ++&fb { ++ status = "okay"; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&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>; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++}; ++ ++&i2s { ++ #sound-dai-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s_pins>; ++}; ++ ++&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>; ++ }; ++}; ++ ++/ { ++ __overrides__ { ++ uart0 = <&uart0>,"status"; ++ uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; ++ uart1_clkrate = <&uart1>,"clock-frequency:0"; ++ i2s = <&i2s>,"status"; ++ spi = <&spi0>,"status"; ++ i2c0 = <&i2c0>,"status"; ++ i2c1 = <&i2c1>,"status"; ++ i2c2_iknowwhatimdoing = <&i2c2>,"status"; ++ i2c0_baudrate = <&i2c0>,"clock-frequency:0"; ++ i2c1_baudrate = <&i2c1>,"clock-frequency:0"; ++ i2c2_baudrate = <&i2c2>,"clock-frequency:0"; ++ core_freq = <&clk_core>,"clock-frequency:0"; ++ ++ 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"; ++ ++ audio = <&audio>,"status"; ++ watchdog = <&watchdog>,"status"; ++ random = <&random>,"status"; + }; + }; +--- a/arch/arm/boot/dts/bcm2835-rpi-b.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-b.dts +@@ -1,17 +1,118 @@ + /dts-v1/; +-#include "bcm2835-rpi.dtsi" ++#include "bcm2835.dtsi" + + / { + compatible = "raspberrypi,model-b", "brcm,bcm2835"; + model = "Raspberry Pi Model B"; ++}; + +- leds { +- act { +- gpios = <&gpio 16 1>; +- }; ++&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 = <6>; /* alt2 */ + }; + }; + +-&gpio { +- pinctrl-0 = <&gpioout &alt0 &alt3>; ++&mmc { ++ status = "okay"; ++ bus-width = <4>; ++}; ++ ++&fb { ++ status = "okay"; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&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>; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++}; ++ ++&i2s { ++ #sound-dai-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s_pins>; ++}; ++ ++&leds { ++ act_led: act { ++ label = "led0"; ++ linux,default-trigger = "mmc0"; ++ gpios = <&gpio 16 1>; ++ }; ++}; ++ ++/ { ++ __overrides__ { ++ uart0 = <&uart0>,"status"; ++ uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; ++ uart1_clkrate = <&uart1>,"clock-frequency:0"; ++ i2s = <&i2s>,"status"; ++ spi = <&spi0>,"status"; ++ i2c0 = <&i2c0>,"status"; ++ i2c1 = <&i2c1>,"status"; ++ i2c2_iknowwhatimdoing = <&i2c2>,"status"; ++ i2c0_baudrate = <&i2c0>,"clock-frequency:0"; ++ i2c1_baudrate = <&i2c1>,"clock-frequency:0"; ++ i2c2_baudrate = <&i2c2>,"clock-frequency:0"; ++ core_freq = <&clk_core>,"clock-frequency:0"; ++ ++ act_led_gpio = <&act_led>,"gpios:4"; ++ act_led_activelow = <&act_led>,"gpios:8"; ++ act_led_trigger = <&act_led>,"linux,default-trigger"; ++ ++ audio = <&audio>,"status"; ++ watchdog = <&watchdog>,"status"; ++ random = <&random>,"status"; ++ }; + }; +--- a/arch/arm/boot/dts/bcm2835.dtsi ++++ b/arch/arm/boot/dts/bcm2835.dtsi +@@ -1,206 +1,39 @@ +-#include +-#include +-#include "skeleton.dtsi" ++#include "bcm2708_common.dtsi" + + / { + compatible = "brcm,bcm2835"; + model = "BCM2835"; +- interrupt-parent = <&intc>; + + chosen { +- bootargs = "earlyprintk console=ttyAMA0"; ++ bootargs = ""; + }; + + soc { +- compatible = "simple-bus"; +- #address-cells = <1>; +- #size-cells = <1>; +- ranges = <0x7e000000 0x20000000 0x02000000>; ++ ranges = <0x7e000000 0x20000000 0x01000000>; + dma-ranges = <0x40000000 0x00000000 0x20000000>; + + timer@7e003000 { + compatible = "brcm,bcm2835-system-timer"; + reg = <0x7e003000 0x1000>; + interrupts = <1 0>, <1 1>, <1 2>, <1 3>; +- /* This could be a reference to BCM2835_CLOCK_TIMER, +- * but we don't have the driver using the common clock +- * support yet. +- */ + clock-frequency = <1000000>; + }; + +- dma: dma@7e007000 { +- compatible = "brcm,bcm2835-dma"; +- reg = <0x7e007000 0xf00>; +- interrupts = <1 16>, +- <1 17>, +- <1 18>, +- <1 19>, +- <1 20>, +- <1 21>, +- <1 22>, +- <1 23>, +- <1 24>, +- <1 25>, +- <1 26>, +- <1 27>, +- <1 28>; +- +- #dma-cells = <1>; +- brcm,dma-channel-mask = <0x7f35>; +- }; +- +- intc: interrupt-controller@7e00b200 { +- compatible = "brcm,bcm2835-armctrl-ic"; +- reg = <0x7e00b200 0x200>; +- interrupt-controller; +- #interrupt-cells = <2>; +- }; +- +- watchdog@7e100000 { +- compatible = "brcm,bcm2835-pm-wdt"; +- reg = <0x7e100000 0x28>; +- }; +- +- clocks: cprman@7e101000 { +- compatible = "brcm,bcm2835-cprman"; +- #clock-cells = <1>; +- reg = <0x7e101000 0x2000>; +- +- /* CPRMAN derives everything from the platform's +- * oscillator. +- */ +- clocks = <&clk_osc>; +- }; +- +- rng@7e104000 { +- compatible = "brcm,bcm2835-rng"; +- reg = <0x7e104000 0x10>; +- }; +- +- mailbox: mailbox@7e00b800 { +- compatible = "brcm,bcm2835-mbox"; +- reg = <0x7e00b880 0x40>; +- interrupts = <0 1>; +- #mbox-cells = <0>; +- }; +- +- gpio: gpio@7e200000 { +- compatible = "brcm,bcm2835-gpio"; +- reg = <0x7e200000 0xb4>; +- /* +- * The GPIO IP block is designed for 3 banks of GPIOs. +- * Each bank has a GPIO interrupt for itself. +- * There is an overall "any bank" interrupt. +- * In order, these are GIC interrupts 17, 18, 19, 20. +- * Since the BCM2835 only has 2 banks, the 2nd bank +- * interrupt output appears to be mirrored onto the +- * 3rd bank's interrupt signal. +- * So, a bank0 interrupt shows up on 17, 20, and +- * a bank1 interrupt shows up on 18, 19, 20! +- */ +- interrupts = <2 17>, <2 18>, <2 19>, <2 20>; +- +- gpio-controller; +- #gpio-cells = <2>; +- +- interrupt-controller; +- #interrupt-cells = <2>; +- }; +- +- uart0: uart@7e201000 { +- compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell"; +- reg = <0x7e201000 0x1000>; +- interrupts = <2 25>; +- clocks = <&clocks BCM2835_CLOCK_UART>, +- <&clocks BCM2835_CLOCK_VPU>; +- clock-names = "uartclk", "apb_pclk"; +- arm,primecell-periphid = <0x00241011>; +- }; +- +- i2s: i2s@7e203000 { +- compatible = "brcm,bcm2835-i2s"; +- reg = <0x7e203000 0x24>, +- <0x7e101098 0x08>; +- +- dmas = <&dma 2>, +- <&dma 3>; +- dma-names = "tx", "rx"; +- status = "disabled"; +- }; +- +- spi: spi@7e204000 { +- compatible = "brcm,bcm2835-spi"; +- reg = <0x7e204000 0x1000>; +- interrupts = <2 22>; +- clocks = <&clocks BCM2835_CLOCK_VPU>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- i2c0: i2c@7e205000 { +- compatible = "brcm,bcm2835-i2c"; +- reg = <0x7e205000 0x1000>; +- interrupts = <2 21>; +- clocks = <&clocks BCM2835_CLOCK_VPU>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- sdhci: sdhci@7e300000 { +- compatible = "brcm,bcm2835-sdhci"; +- reg = <0x7e300000 0x100>; +- interrupts = <2 30>; +- clocks = <&clocks BCM2835_CLOCK_EMMC>; +- status = "disabled"; +- }; +- +- i2c1: i2c@7e804000 { +- compatible = "brcm,bcm2835-i2c"; +- reg = <0x7e804000 0x1000>; +- interrupts = <2 21>; +- clocks = <&clocks BCM2835_CLOCK_VPU>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- i2c2: i2c@7e805000 { +- compatible = "brcm,bcm2835-i2c"; +- reg = <0x7e805000 0x1000>; +- interrupts = <2 21>; +- clocks = <&clocks BCM2835_CLOCK_VPU>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- usb@7e980000 { +- compatible = "brcm,bcm2835-usb"; +- reg = <0x7e980000 0x10000>; +- interrupts = <1 9>; +- }; +- + arm-pmu { + compatible = "arm,arm1176-pmu"; + }; +- }; + +- clocks { +- compatible = "simple-bus"; +- #address-cells = <1>; +- #size-cells = <0>; +- +- /* The oscillator is the root of the clock tree. */ +- clk_osc: clock@3 { +- compatible = "fixed-clock"; +- reg = <3>; +- #clock-cells = <0>; +- clock-output-names = "osc"; +- clock-frequency = <19200000>; ++ aux_enable: aux_enable@0x7e215004 { ++ compatible = "bcrm,bcm2835-aux-enable"; ++ reg = <0x7e215004 0x04>; + }; +- + }; + }; ++ ++&intc { ++ compatible = "brcm,bcm2835-armctrl-ic"; ++}; ++ ++&watchdog { ++ status = "okay"; ++}; diff --git a/target/linux/brcm2708/patches-4.4/0053-bcm2835-Match-with-BCM2708-Device-Trees.patch b/target/linux/brcm2708/patches-4.4/0053-bcm2835-Match-with-BCM2708-Device-Trees.patch deleted file mode 100644 index 23654791a8..0000000000 --- a/target/linux/brcm2708/patches-4.4/0053-bcm2835-Match-with-BCM2708-Device-Trees.patch +++ /dev/null @@ -1,515 +0,0 @@ -From 23eabf2220f0f748df3e88a35e55a82c53242b4a Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= -Date: Sat, 15 Aug 2015 20:47:07 +0200 -Subject: [PATCH 053/170] bcm2835: Match with BCM2708 Device Trees -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Noralf Trønnes ---- - arch/arm/boot/dts/bcm2835-rpi-b-plus.dts | 132 ++++++++++++++++++--- - arch/arm/boot/dts/bcm2835-rpi-b.dts | 115 ++++++++++++++++-- - arch/arm/boot/dts/bcm2835.dtsi | 195 +++---------------------------- - 3 files changed, 237 insertions(+), 205 deletions(-) - ---- a/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts -+++ b/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts -@@ -1,30 +1,128 @@ - /dts-v1/; --#include "bcm2835-rpi.dtsi" -+#include "bcm2835.dtsi" - - / { - compatible = "raspberrypi,model-b-plus", "brcm,bcm2835"; - model = "Raspberry Pi Model B+"; -- -- leds { -- act { -- gpios = <&gpio 47 0>; -- }; -- -- pwr { -- label = "PWR"; -- gpios = <&gpio 35 0>; -- default-state = "keep"; -- linux,default-trigger = "default-on"; -- }; -- }; - }; - - &gpio { -- pinctrl-0 = <&gpioout &alt0 &i2s_alt0 &alt3>; -+ 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>; -+ }; - -- /* I2S interface */ -- i2s_alt0: i2s_alt0 { -+ i2c1_pins: i2c1 { -+ brcm,pins = <2 3>; -+ brcm,function = <4>; -+ }; -+ -+ i2s_pins: i2s { - brcm,pins = <18 19 20 21>; -- brcm,function = ; -+ brcm,function = <4>; /* alt0 */ -+ }; -+}; -+ -+&mmc { -+ status = "okay"; -+ bus-width = <4>; -+}; -+ -+&fb { -+ status = "okay"; -+}; -+ -+&uart0 { -+ status = "okay"; -+}; -+ -+&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>; -+}; -+ -+&i2c2 { -+ clock-frequency = <100000>; -+}; -+ -+&i2s { -+ #sound-dai-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2s_pins>; -+}; -+ -+&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>; -+ }; -+}; -+ -+/ { -+ __overrides__ { -+ uart0 = <&uart0>,"status"; -+ uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; -+ uart1_clkrate = <&uart1>,"clock-frequency:0"; -+ i2s = <&i2s>,"status"; -+ spi = <&spi0>,"status"; -+ i2c0 = <&i2c0>,"status"; -+ i2c1 = <&i2c1>,"status"; -+ i2c2_iknowwhatimdoing = <&i2c2>,"status"; -+ i2c0_baudrate = <&i2c0>,"clock-frequency:0"; -+ i2c1_baudrate = <&i2c1>,"clock-frequency:0"; -+ i2c2_baudrate = <&i2c2>,"clock-frequency:0"; -+ core_freq = <&clk_core>,"clock-frequency:0"; -+ -+ 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"; -+ -+ audio = <&audio>,"status"; -+ watchdog = <&watchdog>,"status"; -+ random = <&random>,"status"; - }; - }; ---- a/arch/arm/boot/dts/bcm2835-rpi-b.dts -+++ b/arch/arm/boot/dts/bcm2835-rpi-b.dts -@@ -1,17 +1,118 @@ - /dts-v1/; --#include "bcm2835-rpi.dtsi" -+#include "bcm2835.dtsi" - - / { - compatible = "raspberrypi,model-b", "brcm,bcm2835"; - model = "Raspberry Pi Model B"; -+}; - -- leds { -- act { -- gpios = <&gpio 16 1>; -- }; -+&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 = <6>; /* alt2 */ - }; - }; - --&gpio { -- pinctrl-0 = <&gpioout &alt0 &alt3>; -+&mmc { -+ status = "okay"; -+ bus-width = <4>; -+}; -+ -+&fb { -+ status = "okay"; -+}; -+ -+&uart0 { -+ status = "okay"; -+}; -+ -+&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>; -+}; -+ -+&i2c2 { -+ clock-frequency = <100000>; -+}; -+ -+&i2s { -+ #sound-dai-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2s_pins>; -+}; -+ -+&leds { -+ act_led: act { -+ label = "led0"; -+ linux,default-trigger = "mmc0"; -+ gpios = <&gpio 16 1>; -+ }; -+}; -+ -+/ { -+ __overrides__ { -+ uart0 = <&uart0>,"status"; -+ uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; -+ uart1_clkrate = <&uart1>,"clock-frequency:0"; -+ i2s = <&i2s>,"status"; -+ spi = <&spi0>,"status"; -+ i2c0 = <&i2c0>,"status"; -+ i2c1 = <&i2c1>,"status"; -+ i2c2_iknowwhatimdoing = <&i2c2>,"status"; -+ i2c0_baudrate = <&i2c0>,"clock-frequency:0"; -+ i2c1_baudrate = <&i2c1>,"clock-frequency:0"; -+ i2c2_baudrate = <&i2c2>,"clock-frequency:0"; -+ core_freq = <&clk_core>,"clock-frequency:0"; -+ -+ act_led_gpio = <&act_led>,"gpios:4"; -+ act_led_activelow = <&act_led>,"gpios:8"; -+ act_led_trigger = <&act_led>,"linux,default-trigger"; -+ -+ audio = <&audio>,"status"; -+ watchdog = <&watchdog>,"status"; -+ random = <&random>,"status"; -+ }; - }; ---- a/arch/arm/boot/dts/bcm2835.dtsi -+++ b/arch/arm/boot/dts/bcm2835.dtsi -@@ -1,206 +1,39 @@ --#include --#include --#include "skeleton.dtsi" -+#include "bcm2708_common.dtsi" - - / { - compatible = "brcm,bcm2835"; - model = "BCM2835"; -- interrupt-parent = <&intc>; - - chosen { -- bootargs = "earlyprintk console=ttyAMA0"; -+ bootargs = ""; - }; - - soc { -- compatible = "simple-bus"; -- #address-cells = <1>; -- #size-cells = <1>; -- ranges = <0x7e000000 0x20000000 0x02000000>; -+ ranges = <0x7e000000 0x20000000 0x01000000>; - dma-ranges = <0x40000000 0x00000000 0x20000000>; - - timer@7e003000 { - compatible = "brcm,bcm2835-system-timer"; - reg = <0x7e003000 0x1000>; - interrupts = <1 0>, <1 1>, <1 2>, <1 3>; -- /* This could be a reference to BCM2835_CLOCK_TIMER, -- * but we don't have the driver using the common clock -- * support yet. -- */ - clock-frequency = <1000000>; - }; - -- dma: dma@7e007000 { -- compatible = "brcm,bcm2835-dma"; -- reg = <0x7e007000 0xf00>; -- interrupts = <1 16>, -- <1 17>, -- <1 18>, -- <1 19>, -- <1 20>, -- <1 21>, -- <1 22>, -- <1 23>, -- <1 24>, -- <1 25>, -- <1 26>, -- <1 27>, -- <1 28>; -- -- #dma-cells = <1>; -- brcm,dma-channel-mask = <0x7f35>; -- }; -- -- intc: interrupt-controller@7e00b200 { -- compatible = "brcm,bcm2835-armctrl-ic"; -- reg = <0x7e00b200 0x200>; -- interrupt-controller; -- #interrupt-cells = <2>; -- }; -- -- watchdog@7e100000 { -- compatible = "brcm,bcm2835-pm-wdt"; -- reg = <0x7e100000 0x28>; -- }; -- -- clocks: cprman@7e101000 { -- compatible = "brcm,bcm2835-cprman"; -- #clock-cells = <1>; -- reg = <0x7e101000 0x2000>; -- -- /* CPRMAN derives everything from the platform's -- * oscillator. -- */ -- clocks = <&clk_osc>; -- }; -- -- rng@7e104000 { -- compatible = "brcm,bcm2835-rng"; -- reg = <0x7e104000 0x10>; -- }; -- -- mailbox: mailbox@7e00b800 { -- compatible = "brcm,bcm2835-mbox"; -- reg = <0x7e00b880 0x40>; -- interrupts = <0 1>; -- #mbox-cells = <0>; -- }; -- -- gpio: gpio@7e200000 { -- compatible = "brcm,bcm2835-gpio"; -- reg = <0x7e200000 0xb4>; -- /* -- * The GPIO IP block is designed for 3 banks of GPIOs. -- * Each bank has a GPIO interrupt for itself. -- * There is an overall "any bank" interrupt. -- * In order, these are GIC interrupts 17, 18, 19, 20. -- * Since the BCM2835 only has 2 banks, the 2nd bank -- * interrupt output appears to be mirrored onto the -- * 3rd bank's interrupt signal. -- * So, a bank0 interrupt shows up on 17, 20, and -- * a bank1 interrupt shows up on 18, 19, 20! -- */ -- interrupts = <2 17>, <2 18>, <2 19>, <2 20>; -- -- gpio-controller; -- #gpio-cells = <2>; -- -- interrupt-controller; -- #interrupt-cells = <2>; -- }; -- -- uart0: uart@7e201000 { -- compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell"; -- reg = <0x7e201000 0x1000>; -- interrupts = <2 25>; -- clocks = <&clocks BCM2835_CLOCK_UART>, -- <&clocks BCM2835_CLOCK_VPU>; -- clock-names = "uartclk", "apb_pclk"; -- arm,primecell-periphid = <0x00241011>; -- }; -- -- i2s: i2s@7e203000 { -- compatible = "brcm,bcm2835-i2s"; -- reg = <0x7e203000 0x24>, -- <0x7e101098 0x08>; -- -- dmas = <&dma 2>, -- <&dma 3>; -- dma-names = "tx", "rx"; -- status = "disabled"; -- }; -- -- spi: spi@7e204000 { -- compatible = "brcm,bcm2835-spi"; -- reg = <0x7e204000 0x1000>; -- interrupts = <2 22>; -- clocks = <&clocks BCM2835_CLOCK_VPU>; -- #address-cells = <1>; -- #size-cells = <0>; -- status = "disabled"; -- }; -- -- i2c0: i2c@7e205000 { -- compatible = "brcm,bcm2835-i2c"; -- reg = <0x7e205000 0x1000>; -- interrupts = <2 21>; -- clocks = <&clocks BCM2835_CLOCK_VPU>; -- #address-cells = <1>; -- #size-cells = <0>; -- status = "disabled"; -- }; -- -- sdhci: sdhci@7e300000 { -- compatible = "brcm,bcm2835-sdhci"; -- reg = <0x7e300000 0x100>; -- interrupts = <2 30>; -- clocks = <&clocks BCM2835_CLOCK_EMMC>; -- status = "disabled"; -- }; -- -- i2c1: i2c@7e804000 { -- compatible = "brcm,bcm2835-i2c"; -- reg = <0x7e804000 0x1000>; -- interrupts = <2 21>; -- clocks = <&clocks BCM2835_CLOCK_VPU>; -- #address-cells = <1>; -- #size-cells = <0>; -- status = "disabled"; -- }; -- -- i2c2: i2c@7e805000 { -- compatible = "brcm,bcm2835-i2c"; -- reg = <0x7e805000 0x1000>; -- interrupts = <2 21>; -- clocks = <&clocks BCM2835_CLOCK_VPU>; -- #address-cells = <1>; -- #size-cells = <0>; -- status = "disabled"; -- }; -- -- usb@7e980000 { -- compatible = "brcm,bcm2835-usb"; -- reg = <0x7e980000 0x10000>; -- interrupts = <1 9>; -- }; -- - arm-pmu { - compatible = "arm,arm1176-pmu"; - }; -- }; - -- clocks { -- compatible = "simple-bus"; -- #address-cells = <1>; -- #size-cells = <0>; -- -- /* The oscillator is the root of the clock tree. */ -- clk_osc: clock@3 { -- compatible = "fixed-clock"; -- reg = <3>; -- #clock-cells = <0>; -- clock-output-names = "osc"; -- clock-frequency = <19200000>; -+ aux_enable: aux_enable@0x7e215004 { -+ compatible = "bcrm,bcm2835-aux-enable"; -+ reg = <0x7e215004 0x04>; - }; -- - }; - }; -+ -+&intc { -+ compatible = "brcm,bcm2835-armctrl-ic"; -+}; -+ -+&watchdog { -+ status = "okay"; -+}; diff --git a/target/linux/brcm2708/patches-4.4/0053-fbdev-add-FBIOCOPYAREA-ioctl.patch b/target/linux/brcm2708/patches-4.4/0053-fbdev-add-FBIOCOPYAREA-ioctl.patch new file mode 100644 index 0000000000..f6416e4766 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0053-fbdev-add-FBIOCOPYAREA-ioctl.patch @@ -0,0 +1,91 @@ +From b28366cb5b514e5c59586f9a5509d3afff5337d5 Mon Sep 17 00:00:00 2001 +From: Siarhei Siamashka +Date: Mon, 17 Jun 2013 13:32:11 +0300 +Subject: [PATCH 053/232] 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(+) + +--- 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 * + 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 * + 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 + 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); +--- 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 */ diff --git a/target/linux/brcm2708/patches-4.4/0054-fbdev-add-FBIOCOPYAREA-ioctl.patch b/target/linux/brcm2708/patches-4.4/0054-fbdev-add-FBIOCOPYAREA-ioctl.patch deleted file mode 100644 index f4808874fb..0000000000 --- a/target/linux/brcm2708/patches-4.4/0054-fbdev-add-FBIOCOPYAREA-ioctl.patch +++ /dev/null @@ -1,91 +0,0 @@ -From f978fb3b816681159b6cc97104954dea31488add Mon Sep 17 00:00:00 2001 -From: Siarhei Siamashka -Date: Mon, 17 Jun 2013 13:32:11 +0300 -Subject: [PATCH 054/170] 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(+) - ---- 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 * - 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 * - 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 - 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); ---- 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 */ diff --git a/target/linux/brcm2708/patches-4.4/0057-Speed-up-console-framebuffer-imageblit-function.patch b/target/linux/brcm2708/patches-4.4/0057-Speed-up-console-framebuffer-imageblit-function.patch new file mode 100644 index 0000000000..b2e9803e6b --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0057-Speed-up-console-framebuffer-imageblit-function.patch @@ -0,0 +1,209 @@ +From dbf03b37592b79d891cb796fda13d8495a6a5234 Mon Sep 17 00:00:00 2001 +From: Harm Hanemaaijer +Date: Thu, 20 Jun 2013 20:21:39 +0200 +Subject: [PATCH 057/232] 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(-) + +--- 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 + } + } + ++/* ++ * 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, co + 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 diff --git a/target/linux/brcm2708/patches-4.4/0058-Allow-mac-address-to-be-set-in-smsc95xx.patch b/target/linux/brcm2708/patches-4.4/0058-Allow-mac-address-to-be-set-in-smsc95xx.patch new file mode 100644 index 0000000000..69be30cfac --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0058-Allow-mac-address-to-be-set-in-smsc95xx.patch @@ -0,0 +1,91 @@ +From cf63ba6ba21e8a1d62b1eb4917af42d6bcfa4445 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Tue, 26 Mar 2013 17:26:38 +0000 +Subject: [PATCH 058/232] Allow mac address to be set in smsc95xx + +Signed-off-by: popcornmix +--- + drivers/net/usb/smsc95xx.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 56 insertions(+) + +--- 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 = false; + 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_dev + 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) { diff --git a/target/linux/brcm2708/patches-4.4/0058-Speed-up-console-framebuffer-imageblit-function.patch b/target/linux/brcm2708/patches-4.4/0058-Speed-up-console-framebuffer-imageblit-function.patch deleted file mode 100644 index 5d5a561a06..0000000000 --- a/target/linux/brcm2708/patches-4.4/0058-Speed-up-console-framebuffer-imageblit-function.patch +++ /dev/null @@ -1,209 +0,0 @@ -From 53bcd6e5a43a353508c7aff48abcdcaae1b35ca8 Mon Sep 17 00:00:00 2001 -From: Harm Hanemaaijer -Date: Thu, 20 Jun 2013 20:21:39 +0200 -Subject: [PATCH 058/170] 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(-) - ---- 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 - } - } - -+/* -+ * 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, co - 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 diff --git a/target/linux/brcm2708/patches-4.4/0059-Allow-mac-address-to-be-set-in-smsc95xx.patch b/target/linux/brcm2708/patches-4.4/0059-Allow-mac-address-to-be-set-in-smsc95xx.patch deleted file mode 100644 index 03a972c8f4..0000000000 --- a/target/linux/brcm2708/patches-4.4/0059-Allow-mac-address-to-be-set-in-smsc95xx.patch +++ /dev/null @@ -1,91 +0,0 @@ -From 6c0a9150827dc58fac38c8e787c050626325842e Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 26 Mar 2013 17:26:38 +0000 -Subject: [PATCH 059/170] Allow mac address to be set in smsc95xx - -Signed-off-by: popcornmix ---- - drivers/net/usb/smsc95xx.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 56 insertions(+) - ---- 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 = false; - 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_dev - 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) { diff --git a/target/linux/brcm2708/patches-4.4/0059-enabling-the-realtime-clock-1-wire-chip-DS1307-and-1.patch b/target/linux/brcm2708/patches-4.4/0059-enabling-the-realtime-clock-1-wire-chip-DS1307-and-1.patch new file mode 100644 index 0000000000..8cf13da6f3 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0059-enabling-the-realtime-clock-1-wire-chip-DS1307-and-1.patch @@ -0,0 +1,242 @@ +From 20f0537c77ac3ef4e446bd0013c37ddd3af3936b Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 8 May 2013 11:46:50 +0100 +Subject: [PATCH 059/232] 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 + +w1-gpio: Sort out the pullup/parasitic power tangle +--- + drivers/w1/masters/w1-gpio.c | 69 ++++++++++++++++++++++++++++++++++++++++---- + drivers/w1/w1.h | 6 ++++ + drivers/w1/w1_int.c | 14 +++++++++ + drivers/w1/w1_io.c | 18 ++++++++++-- + include/linux/w1-gpio.h | 1 + + 5 files changed, 99 insertions(+), 9 deletions(-) + +--- a/drivers/w1/masters/w1-gpio.c ++++ b/drivers/w1/masters/w1-gpio.c +@@ -23,6 +23,19 @@ + #include "../w1.h" + #include "../w1_int.h" + ++static int w1_gpio_pullup = 0; ++static int w1_gpio_pullup_orig = 0; ++module_param_named(pullup, w1_gpio_pullup, int, 0); ++MODULE_PARM_DESC(pullup, "Enable parasitic power (power on data) mode"); ++static int w1_gpio_pullup_pin = -1; ++static int w1_gpio_pullup_pin_orig = -1; ++module_param_named(extpullup, w1_gpio_pullup_pin, int, 0); ++MODULE_PARM_DESC(extpullup, "GPIO external pullup pin 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 +80,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 const struct of_device_id w1_gpio_dt_ids[] = { + { .compatible = "w1-gpio" }, +@@ -80,6 +103,7 @@ static int w1_gpio_probe_dt(struct platf + struct w1_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct device_node *np = pdev->dev.of_node; + int gpio; ++ u32 value; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) +@@ -88,6 +112,9 @@ static int w1_gpio_probe_dt(struct platf + if (of_get_property(np, "linux,open-drain", NULL)) + pdata->is_open_drain = 1; + ++ if (of_property_read_u32(np, "rpi,parasitic-power", &value) == 0) ++ pdata->parasitic_power = (value != 0); ++ + gpio = of_get_gpio(np, 0); + if (gpio < 0) { + if (gpio != -EPROBE_DEFER) +@@ -103,7 +130,7 @@ static int w1_gpio_probe_dt(struct platf + if (gpio == -EPROBE_DEFER) + return gpio; + /* ignore other errors as the pullup gpio is optional */ +- pdata->ext_pullup_enable_pin = gpio; ++ pdata->ext_pullup_enable_pin = (gpio >= 0) ? gpio : -1; + + pdev->dev.platform_data = pdata; + +@@ -113,13 +140,15 @@ static int w1_gpio_probe_dt(struct platf + 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 +165,22 @@ static int w1_gpio_probe(struct platform + return -ENOMEM; + } + ++ w1_gpio_pin_orig = pdata->pin; ++ w1_gpio_pullup_pin_orig = pdata->ext_pullup_enable_pin; ++ w1_gpio_pullup_orig = pdata->parasitic_power; ++ ++ if(gpio_is_valid(w1_gpio_pin)) { ++ pdata->pin = w1_gpio_pin; ++ pdata->ext_pullup_enable_pin = -1; ++ pdata->parasitic_power = -1; ++ } ++ pdata->parasitic_power |= w1_gpio_pullup; ++ if(gpio_is_valid(w1_gpio_pullup_pin)) { ++ pdata->ext_pullup_enable_pin = w1_gpio_pullup_pin; ++ } ++ ++ dev_info(&pdev->dev, "gpio pin %d, external pullup pin %d, parasitic power %d\n", pdata->pin, pdata->ext_pullup_enable_pin, pdata->parasitic_power); ++ + err = devm_gpio_request(&pdev->dev, pdata->pin, "w1"); + if (err) { + dev_err(&pdev->dev, "gpio_request (pin) failed\n"); +@@ -165,6 +210,14 @@ static int w1_gpio_probe(struct platform + master->set_pullup = w1_gpio_set_pullup; + } + ++ if (pdata->parasitic_power) { ++ if (pdata->is_open_drain) ++ printk(KERN_ERR "w1-gpio 'pullup'(parasitic power) " ++ "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 +248,10 @@ static int w1_gpio_remove(struct platfor + + w1_remove_master_device(master); + ++ pdata->pin = w1_gpio_pin_orig; ++ pdata->ext_pullup_enable_pin = w1_gpio_pullup_pin_orig; ++ pdata->parasitic_power = w1_gpio_pullup_orig; ++ + return 0; + } + +--- 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); + }; +--- a/drivers/w1/w1_int.c ++++ b/drivers/w1/w1_int.c +@@ -122,6 +122,20 @@ int w1_add_master_device(struct w1_bus_m + 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). */ +--- a/drivers/w1/w1_io.c ++++ b/drivers/w1/w1_io.c +@@ -134,10 +134,22 @@ static void w1_pre_write(struct w1_maste + 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; + } + } +--- a/include/linux/w1-gpio.h ++++ b/include/linux/w1-gpio.h +@@ -18,6 +18,7 @@ + struct w1_gpio_platform_data { + unsigned int pin; + unsigned int is_open_drain:1; ++ unsigned int parasitic_power:1; + void (*enable_external_pullup)(int enable); + unsigned int ext_pullup_enable_pin; + unsigned int pullup_duration; diff --git a/target/linux/brcm2708/patches-4.4/0060-Added-Device-IDs-for-August-DVB-T-205.patch b/target/linux/brcm2708/patches-4.4/0060-Added-Device-IDs-for-August-DVB-T-205.patch new file mode 100644 index 0000000000..a57f6e0dab --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0060-Added-Device-IDs-for-August-DVB-T-205.patch @@ -0,0 +1,22 @@ +From fbd8454a22771ed12e1905a52934a6a871bc492e Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 3 Jul 2013 00:54:08 +0100 +Subject: [PATCH 060/232] Added Device IDs for August DVB-T 205 + +--- + drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c ++++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +@@ -1900,6 +1900,10 @@ static const struct usb_device_id rtl28x + &rtl28xxu_props, "Compro VideoMate U650F", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd394, + &rtl28xxu_props, "MaxMedia HU394-T", NULL) }, ++ { DVB_USB_DEVICE(USB_VID_GTEK, 0xb803 /*USB_PID_AUGUST_DVBT205*/, ++ &rtl28xxu_props, "August DVB-T 205", NULL) }, ++ { DVB_USB_DEVICE(USB_VID_GTEK, 0xa803 /*USB_PID_AUGUST_DVBT205*/, ++ &rtl28xxu_props, "August DVB-T 205", NULL) }, + { DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6a03, + &rtl28xxu_props, "Leadtek WinFast DTV Dongle mini", NULL) }, + { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_CPYTO_REDI_PC50A, diff --git a/target/linux/brcm2708/patches-4.4/0060-enabling-the-realtime-clock-1-wire-chip-DS1307-and-1.patch b/target/linux/brcm2708/patches-4.4/0060-enabling-the-realtime-clock-1-wire-chip-DS1307-and-1.patch deleted file mode 100644 index eb44deb85e..0000000000 --- a/target/linux/brcm2708/patches-4.4/0060-enabling-the-realtime-clock-1-wire-chip-DS1307-and-1.patch +++ /dev/null @@ -1,242 +0,0 @@ -From e172d31b8402f13aed5679cd7023f0c0935aab05 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 8 May 2013 11:46:50 +0100 -Subject: [PATCH 060/170] 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 - -w1-gpio: Sort out the pullup/parasitic power tangle ---- - drivers/w1/masters/w1-gpio.c | 69 ++++++++++++++++++++++++++++++++++++++++---- - drivers/w1/w1.h | 6 ++++ - drivers/w1/w1_int.c | 14 +++++++++ - drivers/w1/w1_io.c | 18 ++++++++++-- - include/linux/w1-gpio.h | 1 + - 5 files changed, 99 insertions(+), 9 deletions(-) - ---- a/drivers/w1/masters/w1-gpio.c -+++ b/drivers/w1/masters/w1-gpio.c -@@ -23,6 +23,19 @@ - #include "../w1.h" - #include "../w1_int.h" - -+static int w1_gpio_pullup = 0; -+static int w1_gpio_pullup_orig = 0; -+module_param_named(pullup, w1_gpio_pullup, int, 0); -+MODULE_PARM_DESC(pullup, "Enable parasitic power (power on data) mode"); -+static int w1_gpio_pullup_pin = -1; -+static int w1_gpio_pullup_pin_orig = -1; -+module_param_named(extpullup, w1_gpio_pullup_pin, int, 0); -+MODULE_PARM_DESC(extpullup, "GPIO external pullup pin 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 +80,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 const struct of_device_id w1_gpio_dt_ids[] = { - { .compatible = "w1-gpio" }, -@@ -80,6 +103,7 @@ static int w1_gpio_probe_dt(struct platf - struct w1_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct device_node *np = pdev->dev.of_node; - int gpio; -+ u32 value; - - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) -@@ -88,6 +112,9 @@ static int w1_gpio_probe_dt(struct platf - if (of_get_property(np, "linux,open-drain", NULL)) - pdata->is_open_drain = 1; - -+ if (of_property_read_u32(np, "rpi,parasitic-power", &value) == 0) -+ pdata->parasitic_power = (value != 0); -+ - gpio = of_get_gpio(np, 0); - if (gpio < 0) { - if (gpio != -EPROBE_DEFER) -@@ -103,7 +130,7 @@ static int w1_gpio_probe_dt(struct platf - if (gpio == -EPROBE_DEFER) - return gpio; - /* ignore other errors as the pullup gpio is optional */ -- pdata->ext_pullup_enable_pin = gpio; -+ pdata->ext_pullup_enable_pin = (gpio >= 0) ? gpio : -1; - - pdev->dev.platform_data = pdata; - -@@ -113,13 +140,15 @@ static int w1_gpio_probe_dt(struct platf - 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 +165,22 @@ static int w1_gpio_probe(struct platform - return -ENOMEM; - } - -+ w1_gpio_pin_orig = pdata->pin; -+ w1_gpio_pullup_pin_orig = pdata->ext_pullup_enable_pin; -+ w1_gpio_pullup_orig = pdata->parasitic_power; -+ -+ if(gpio_is_valid(w1_gpio_pin)) { -+ pdata->pin = w1_gpio_pin; -+ pdata->ext_pullup_enable_pin = -1; -+ pdata->parasitic_power = -1; -+ } -+ pdata->parasitic_power |= w1_gpio_pullup; -+ if(gpio_is_valid(w1_gpio_pullup_pin)) { -+ pdata->ext_pullup_enable_pin = w1_gpio_pullup_pin; -+ } -+ -+ dev_info(&pdev->dev, "gpio pin %d, external pullup pin %d, parasitic power %d\n", pdata->pin, pdata->ext_pullup_enable_pin, pdata->parasitic_power); -+ - err = devm_gpio_request(&pdev->dev, pdata->pin, "w1"); - if (err) { - dev_err(&pdev->dev, "gpio_request (pin) failed\n"); -@@ -165,6 +210,14 @@ static int w1_gpio_probe(struct platform - master->set_pullup = w1_gpio_set_pullup; - } - -+ if (pdata->parasitic_power) { -+ if (pdata->is_open_drain) -+ printk(KERN_ERR "w1-gpio 'pullup'(parasitic power) " -+ "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 +248,10 @@ static int w1_gpio_remove(struct platfor - - w1_remove_master_device(master); - -+ pdata->pin = w1_gpio_pin_orig; -+ pdata->ext_pullup_enable_pin = w1_gpio_pullup_pin_orig; -+ pdata->parasitic_power = w1_gpio_pullup_orig; -+ - return 0; - } - ---- 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); - }; ---- a/drivers/w1/w1_int.c -+++ b/drivers/w1/w1_int.c -@@ -122,6 +122,20 @@ int w1_add_master_device(struct w1_bus_m - 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). */ ---- a/drivers/w1/w1_io.c -+++ b/drivers/w1/w1_io.c -@@ -134,10 +134,22 @@ static void w1_pre_write(struct w1_maste - 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; - } - } ---- a/include/linux/w1-gpio.h -+++ b/include/linux/w1-gpio.h -@@ -18,6 +18,7 @@ - struct w1_gpio_platform_data { - unsigned int pin; - unsigned int is_open_drain:1; -+ unsigned int parasitic_power:1; - void (*enable_external_pullup)(int enable); - unsigned int ext_pullup_enable_pin; - unsigned int pullup_duration; diff --git a/target/linux/brcm2708/patches-4.4/0061-Added-Device-IDs-for-August-DVB-T-205.patch b/target/linux/brcm2708/patches-4.4/0061-Added-Device-IDs-for-August-DVB-T-205.patch deleted file mode 100644 index 12645bf311..0000000000 --- a/target/linux/brcm2708/patches-4.4/0061-Added-Device-IDs-for-August-DVB-T-205.patch +++ /dev/null @@ -1,22 +0,0 @@ -From 78c49b30c0dcc8b73f383f71b89719798fc89b72 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 3 Jul 2013 00:54:08 +0100 -Subject: [PATCH 061/170] Added Device IDs for August DVB-T 205 - ---- - drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 4 ++++ - 1 file changed, 4 insertions(+) - ---- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c -+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c -@@ -1900,6 +1900,10 @@ static const struct usb_device_id rtl28x - &rtl28xxu_props, "Compro VideoMate U650F", NULL) }, - { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd394, - &rtl28xxu_props, "MaxMedia HU394-T", NULL) }, -+ { DVB_USB_DEVICE(USB_VID_GTEK, 0xb803 /*USB_PID_AUGUST_DVBT205*/, -+ &rtl28xxu_props, "August DVB-T 205", NULL) }, -+ { DVB_USB_DEVICE(USB_VID_GTEK, 0xa803 /*USB_PID_AUGUST_DVBT205*/, -+ &rtl28xxu_props, "August DVB-T 205", NULL) }, - { DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6a03, - &rtl28xxu_props, "Leadtek WinFast DTV Dongle mini", NULL) }, - { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_CPYTO_REDI_PC50A, diff --git a/target/linux/brcm2708/patches-4.4/0061-config-Enable-CONFIG_MEMCG-but-leave-it-disabled-due.patch b/target/linux/brcm2708/patches-4.4/0061-config-Enable-CONFIG_MEMCG-but-leave-it-disabled-due.patch new file mode 100644 index 0000000000..afc9b6be8e --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0061-config-Enable-CONFIG_MEMCG-but-leave-it-disabled-due.patch @@ -0,0 +1,49 @@ +From 6f8e2aeb77e4437eab3a46d74a1a2b9533812c81 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 18 Dec 2013 22:16:19 +0000 +Subject: [PATCH 061/232] config: Enable CONFIG_MEMCG, but leave it disabled + (due to memory cost). Enable with cgroup_enable=memory. + +--- + kernel/cgroup.c | 23 ++++++++++++++++++++++- + 1 file changed, 22 insertions(+), 1 deletion(-) + +--- a/kernel/cgroup.c ++++ b/kernel/cgroup.c +@@ -5302,7 +5302,7 @@ int __init cgroup_init_early(void) + return 0; + } + +-static unsigned long cgroup_disable_mask __initdata; ++static unsigned long cgroup_disable_mask __initdata = 1<<0; + + /** + * cgroup_init - cgroup initialization +@@ -5798,6 +5798,27 @@ static int __init cgroup_disable(char *s + } + __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) && ++ strcmp(token, ss->legacy_name)) ++ continue; ++ cgroup_disable_mask &= ~(1 << i); ++ } ++ } ++ return 1; ++} ++__setup("cgroup_enable=", cgroup_enable); ++ + /** + * css_tryget_online_from_dir - get corresponding css from a cgroup dentry + * @dentry: directory dentry of interest diff --git a/target/linux/brcm2708/patches-4.4/0062-ASoC-Add-support-for-PCM5102A-codec.patch b/target/linux/brcm2708/patches-4.4/0062-ASoC-Add-support-for-PCM5102A-codec.patch new file mode 100644 index 0000000000..8d56af7c7a --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0062-ASoC-Add-support-for-PCM5102A-codec.patch @@ -0,0 +1,128 @@ +From b507ac15b579d88a09bf915f2f3ca8de7f1e4bf7 Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 14:59:51 +0100 +Subject: [PATCH 062/232] 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 | 5 ++++ + sound/soc/codecs/Makefile | 2 ++ + sound/soc/codecs/pcm5102a.c | 70 +++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 77 insertions(+) + create mode 100644 sound/soc/codecs/pcm5102a.c + +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -89,6 +89,7 @@ config SND_SOC_ALL_CODECS + select SND_SOC_PCM512x_SPI if SPI_MASTER + select SND_SOC_RT286 if I2C + select SND_SOC_RT298 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 +@@ -549,6 +550,10 @@ config SND_SOC_RT298 + tristate + depends on I2C + ++config SND_SOC_PCM5102A ++ tristate ++ depends on I2C ++ + config SND_SOC_RT5631 + tristate "Realtek ALC5631/RT5631 CODEC" + depends on I2C +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -85,6 +85,7 @@ snd-soc-rl6231-objs := rl6231.o + snd-soc-rl6347a-objs := rl6347a.o + snd-soc-rt286-objs := rt286.o + snd-soc-rt298-objs := rt298.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 +@@ -280,6 +281,7 @@ obj-$(CONFIG_SND_SOC_RL6231) += snd-soc- + obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o + obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o + obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.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 +--- /dev/null ++++ b/sound/soc/codecs/pcm5102a.c +@@ -0,0 +1,70 @@ ++/* ++ * 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 const struct of_device_id pcm5102a_of_match[] = { ++ { .compatible = "ti,pcm5102a", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, pcm5102a_of_match); ++ ++static struct platform_driver pcm5102a_codec_driver = { ++ .probe = pcm5102a_probe, ++ .remove = pcm5102a_remove, ++ .driver = { ++ .name = "pcm5102a-codec", ++ .owner = THIS_MODULE, ++ .of_match_table = pcm5102a_of_match, ++ }, ++}; ++ ++module_platform_driver(pcm5102a_codec_driver); ++ ++MODULE_DESCRIPTION("ASoC PCM5102A codec driver"); ++MODULE_AUTHOR("Florian Meier "); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/brcm2708/patches-4.4/0062-config-Enable-CONFIG_MEMCG-but-leave-it-disabled-due.patch b/target/linux/brcm2708/patches-4.4/0062-config-Enable-CONFIG_MEMCG-but-leave-it-disabled-due.patch deleted file mode 100644 index 52704d58cb..0000000000 --- a/target/linux/brcm2708/patches-4.4/0062-config-Enable-CONFIG_MEMCG-but-leave-it-disabled-due.patch +++ /dev/null @@ -1,49 +0,0 @@ -From d0f48fe7aab3e4036fd55b491a9be94ea24f377d Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 18 Dec 2013 22:16:19 +0000 -Subject: [PATCH 062/170] config: Enable CONFIG_MEMCG, but leave it disabled - (due to memory cost). Enable with cgroup_enable=memory. - ---- - kernel/cgroup.c | 23 ++++++++++++++++++++++- - 1 file changed, 22 insertions(+), 1 deletion(-) - ---- a/kernel/cgroup.c -+++ b/kernel/cgroup.c -@@ -5302,7 +5302,7 @@ int __init cgroup_init_early(void) - return 0; - } - --static unsigned long cgroup_disable_mask __initdata; -+static unsigned long cgroup_disable_mask __initdata = 1<<0; - - /** - * cgroup_init - cgroup initialization -@@ -5798,6 +5798,27 @@ static int __init cgroup_disable(char *s - } - __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) && -+ strcmp(token, ss->legacy_name)) -+ continue; -+ cgroup_disable_mask &= ~(1 << i); -+ } -+ } -+ return 1; -+} -+__setup("cgroup_enable=", cgroup_enable); -+ - /** - * css_tryget_online_from_dir - get corresponding css from a cgroup dentry - * @dentry: directory dentry of interest diff --git a/target/linux/brcm2708/patches-4.4/0063-ASoC-Add-support-for-HifiBerry-DAC.patch b/target/linux/brcm2708/patches-4.4/0063-ASoC-Add-support-for-HifiBerry-DAC.patch new file mode 100644 index 0000000000..5dc895ead8 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0063-ASoC-Add-support-for-HifiBerry-DAC.patch @@ -0,0 +1,165 @@ +From 9f5e88b201753c68a90fa45a269aa82febc58fc6 Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 19:19:08 +0100 +Subject: [PATCH 063/232] 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 | 4 ++ + sound/soc/bcm/hifiberry_dac.c | 122 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 133 insertions(+) + create mode 100644 sound/soc/bcm/hifiberry_dac.c + +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -7,3 +7,10 @@ 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_HIFIBERRY_DAC ++ tristate "Support for HifiBerry DAC" ++ depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S ++ select SND_SOC_PCM5102A ++ help ++ Say Y or M if you want to add support for HifiBerry DAC. +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -3,3 +3,7 @@ snd-soc-bcm2835-i2s-objs := bcm2835-i2s. + + obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-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 +--- /dev/null ++++ b/sound/soc/bcm/hifiberry_dac.c +@@ -0,0 +1,122 @@ ++/* ++ * 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; ++ ++ if (pdev->dev.of_node) { ++ struct device_node *i2s_node; ++ struct snd_soc_dai_link *dai = &snd_rpi_hifiberry_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_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 const struct of_device_id snd_rpi_hifiberry_dac_of_match[] = { ++ { .compatible = "hifiberry,hifiberry-dac", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, snd_rpi_hifiberry_dac_of_match); ++ ++static struct platform_driver snd_rpi_hifiberry_dac_driver = { ++ .driver = { ++ .name = "snd-hifiberry-dac", ++ .owner = THIS_MODULE, ++ .of_match_table = snd_rpi_hifiberry_dac_of_match, ++ }, ++ .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"); diff --git a/target/linux/brcm2708/patches-4.4/0063-ASoC-Add-support-for-PCM5102A-codec.patch b/target/linux/brcm2708/patches-4.4/0063-ASoC-Add-support-for-PCM5102A-codec.patch deleted file mode 100644 index 2fcca63e11..0000000000 --- a/target/linux/brcm2708/patches-4.4/0063-ASoC-Add-support-for-PCM5102A-codec.patch +++ /dev/null @@ -1,128 +0,0 @@ -From 8d35b3a72d3580ad6f5bc5835bfe13b00cce6086 Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Fri, 22 Nov 2013 14:59:51 +0100 -Subject: [PATCH 063/170] 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 | 5 ++++ - sound/soc/codecs/Makefile | 2 ++ - sound/soc/codecs/pcm5102a.c | 70 +++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 77 insertions(+) - create mode 100644 sound/soc/codecs/pcm5102a.c - ---- a/sound/soc/codecs/Kconfig -+++ b/sound/soc/codecs/Kconfig -@@ -89,6 +89,7 @@ config SND_SOC_ALL_CODECS - select SND_SOC_PCM512x_SPI if SPI_MASTER - select SND_SOC_RT286 if I2C - select SND_SOC_RT298 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 -@@ -549,6 +550,10 @@ config SND_SOC_RT298 - tristate - depends on I2C - -+config SND_SOC_PCM5102A -+ tristate -+ depends on I2C -+ - config SND_SOC_RT5631 - tristate "Realtek ALC5631/RT5631 CODEC" - depends on I2C ---- a/sound/soc/codecs/Makefile -+++ b/sound/soc/codecs/Makefile -@@ -85,6 +85,7 @@ snd-soc-rl6231-objs := rl6231.o - snd-soc-rl6347a-objs := rl6347a.o - snd-soc-rt286-objs := rt286.o - snd-soc-rt298-objs := rt298.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 -@@ -280,6 +281,7 @@ obj-$(CONFIG_SND_SOC_RL6231) += snd-soc- - obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o - obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o - obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.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 ---- /dev/null -+++ b/sound/soc/codecs/pcm5102a.c -@@ -0,0 +1,70 @@ -+/* -+ * 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 const struct of_device_id pcm5102a_of_match[] = { -+ { .compatible = "ti,pcm5102a", }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, pcm5102a_of_match); -+ -+static struct platform_driver pcm5102a_codec_driver = { -+ .probe = pcm5102a_probe, -+ .remove = pcm5102a_remove, -+ .driver = { -+ .name = "pcm5102a-codec", -+ .owner = THIS_MODULE, -+ .of_match_table = pcm5102a_of_match, -+ }, -+}; -+ -+module_platform_driver(pcm5102a_codec_driver); -+ -+MODULE_DESCRIPTION("ASoC PCM5102A codec driver"); -+MODULE_AUTHOR("Florian Meier "); -+MODULE_LICENSE("GPL v2"); diff --git a/target/linux/brcm2708/patches-4.4/0064-ASoC-Add-support-for-HifiBerry-DAC.patch b/target/linux/brcm2708/patches-4.4/0064-ASoC-Add-support-for-HifiBerry-DAC.patch deleted file mode 100644 index 51c1fb1da8..0000000000 --- a/target/linux/brcm2708/patches-4.4/0064-ASoC-Add-support-for-HifiBerry-DAC.patch +++ /dev/null @@ -1,165 +0,0 @@ -From 9ced0e86593de2214ff8a81f71c802d7e7301b05 Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Fri, 22 Nov 2013 19:19:08 +0100 -Subject: [PATCH 064/170] 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 | 4 ++ - sound/soc/bcm/hifiberry_dac.c | 122 ++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 133 insertions(+) - create mode 100644 sound/soc/bcm/hifiberry_dac.c - ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -7,3 +7,10 @@ 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_HIFIBERRY_DAC -+ tristate "Support for HifiBerry DAC" -+ depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S -+ select SND_SOC_PCM5102A -+ help -+ Say Y or M if you want to add support for HifiBerry DAC. ---- a/sound/soc/bcm/Makefile -+++ b/sound/soc/bcm/Makefile -@@ -3,3 +3,7 @@ snd-soc-bcm2835-i2s-objs := bcm2835-i2s. - - obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-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 ---- /dev/null -+++ b/sound/soc/bcm/hifiberry_dac.c -@@ -0,0 +1,122 @@ -+/* -+ * 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; -+ -+ if (pdev->dev.of_node) { -+ struct device_node *i2s_node; -+ struct snd_soc_dai_link *dai = &snd_rpi_hifiberry_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_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 const struct of_device_id snd_rpi_hifiberry_dac_of_match[] = { -+ { .compatible = "hifiberry,hifiberry-dac", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, snd_rpi_hifiberry_dac_of_match); -+ -+static struct platform_driver snd_rpi_hifiberry_dac_driver = { -+ .driver = { -+ .name = "snd-hifiberry-dac", -+ .owner = THIS_MODULE, -+ .of_match_table = snd_rpi_hifiberry_dac_of_match, -+ }, -+ .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"); diff --git a/target/linux/brcm2708/patches-4.4/0064-ASoC-Add-support-for-Rpi-DAC.patch b/target/linux/brcm2708/patches-4.4/0064-ASoC-Add-support-for-Rpi-DAC.patch new file mode 100644 index 0000000000..5c212ce6a0 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0064-ASoC-Add-support-for-Rpi-DAC.patch @@ -0,0 +1,275 @@ +From 95bf71f0e826b97bad98f350e2fee6ba515a38cf Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 19:21:34 +0100 +Subject: [PATCH 064/232] ASoC: Add support for Rpi-DAC + +--- + sound/soc/bcm/Kconfig | 7 +++ + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/rpi-dac.c | 118 ++++++++++++++++++++++++++++++++++++++++++++ + sound/soc/codecs/Kconfig | 9 ++++ + sound/soc/codecs/Makefile | 2 + + sound/soc/codecs/pcm1794a.c | 69 ++++++++++++++++++++++++++ + 6 files changed, 207 insertions(+) + create mode 100644 sound/soc/bcm/rpi-dac.c + create mode 100644 sound/soc/codecs/pcm1794a.c + +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -14,3 +14,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 || SND_BCM2835_SOC_I2S ++ select SND_SOC_PCM1794A ++ help ++ Say Y or M if you want to add support for RPi-DAC. +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -5,5 +5,7 @@ obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd + + # 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 +--- /dev/null ++++ b/sound/soc/bcm/rpi-dac.c +@@ -0,0 +1,118 @@ ++/* ++ * 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; ++ ++ 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); ++ ++ return ret; ++} ++ ++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, ++}; ++ ++module_platform_driver(snd_rpi_rpi_dac_driver); ++ ++MODULE_AUTHOR("Florian Meier "); ++MODULE_DESCRIPTION("ASoC Driver for RPi-DAC"); ++MODULE_LICENSE("GPL v2"); +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -90,6 +90,7 @@ config SND_SOC_ALL_CODECS + select SND_SOC_RT286 if I2C + select SND_SOC_RT298 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 +@@ -550,6 +551,14 @@ config SND_SOC_RT298 + tristate + depends on I2C + ++config SND_SOC_RT298 ++ tristate ++ depends on I2C ++ ++config SND_SOC_PCM1794A ++ tristate ++ depends on I2C ++ + config SND_SOC_PCM5102A + tristate + depends on I2C +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -85,6 +85,7 @@ snd-soc-rl6231-objs := rl6231.o + snd-soc-rl6347a-objs := rl6347a.o + snd-soc-rt286-objs := rt286.o + snd-soc-rt298-objs := rt298.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 +@@ -281,6 +282,7 @@ obj-$(CONFIG_SND_SOC_RL6231) += snd-soc- + obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o + obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o + obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.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 +--- /dev/null ++++ b/sound/soc/codecs/pcm1794a.c +@@ -0,0 +1,69 @@ ++/* ++ * 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 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), ++ }, ++}; ++ ++module_platform_driver(pcm1794a_codec_driver); ++ ++MODULE_DESCRIPTION("ASoC PCM1794A codec driver"); ++MODULE_AUTHOR("Florian Meier "); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/brcm2708/patches-4.4/0065-ASoC-Add-support-for-Rpi-DAC.patch b/target/linux/brcm2708/patches-4.4/0065-ASoC-Add-support-for-Rpi-DAC.patch deleted file mode 100644 index a2d8e5628c..0000000000 --- a/target/linux/brcm2708/patches-4.4/0065-ASoC-Add-support-for-Rpi-DAC.patch +++ /dev/null @@ -1,275 +0,0 @@ -From f16a654f57b024d44fe58d62b660e01de6a7811a Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Fri, 22 Nov 2013 19:21:34 +0100 -Subject: [PATCH 065/170] ASoC: Add support for Rpi-DAC - ---- - sound/soc/bcm/Kconfig | 7 +++ - sound/soc/bcm/Makefile | 2 + - sound/soc/bcm/rpi-dac.c | 118 ++++++++++++++++++++++++++++++++++++++++++++ - sound/soc/codecs/Kconfig | 9 ++++ - sound/soc/codecs/Makefile | 2 + - sound/soc/codecs/pcm1794a.c | 69 ++++++++++++++++++++++++++ - 6 files changed, 207 insertions(+) - create mode 100644 sound/soc/bcm/rpi-dac.c - create mode 100644 sound/soc/codecs/pcm1794a.c - ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -14,3 +14,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 || SND_BCM2835_SOC_I2S -+ select SND_SOC_PCM1794A -+ help -+ Say Y or M if you want to add support for RPi-DAC. ---- a/sound/soc/bcm/Makefile -+++ b/sound/soc/bcm/Makefile -@@ -5,5 +5,7 @@ obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd - - # 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 ---- /dev/null -+++ b/sound/soc/bcm/rpi-dac.c -@@ -0,0 +1,118 @@ -+/* -+ * 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; -+ -+ 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); -+ -+ return ret; -+} -+ -+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, -+}; -+ -+module_platform_driver(snd_rpi_rpi_dac_driver); -+ -+MODULE_AUTHOR("Florian Meier "); -+MODULE_DESCRIPTION("ASoC Driver for RPi-DAC"); -+MODULE_LICENSE("GPL v2"); ---- a/sound/soc/codecs/Kconfig -+++ b/sound/soc/codecs/Kconfig -@@ -90,6 +90,7 @@ config SND_SOC_ALL_CODECS - select SND_SOC_RT286 if I2C - select SND_SOC_RT298 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 -@@ -550,6 +551,14 @@ config SND_SOC_RT298 - tristate - depends on I2C - -+config SND_SOC_RT298 -+ tristate -+ depends on I2C -+ -+config SND_SOC_PCM1794A -+ tristate -+ depends on I2C -+ - config SND_SOC_PCM5102A - tristate - depends on I2C ---- a/sound/soc/codecs/Makefile -+++ b/sound/soc/codecs/Makefile -@@ -85,6 +85,7 @@ snd-soc-rl6231-objs := rl6231.o - snd-soc-rl6347a-objs := rl6347a.o - snd-soc-rt286-objs := rt286.o - snd-soc-rt298-objs := rt298.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 -@@ -281,6 +282,7 @@ obj-$(CONFIG_SND_SOC_RL6231) += snd-soc- - obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o - obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o - obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.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 ---- /dev/null -+++ b/sound/soc/codecs/pcm1794a.c -@@ -0,0 +1,69 @@ -+/* -+ * 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 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), -+ }, -+}; -+ -+module_platform_driver(pcm1794a_codec_driver); -+ -+MODULE_DESCRIPTION("ASoC PCM1794A codec driver"); -+MODULE_AUTHOR("Florian Meier "); -+MODULE_LICENSE("GPL v2"); diff --git a/target/linux/brcm2708/patches-4.4/0065-ASoC-wm8804-Implement-MCLK-configuration-options-add.patch b/target/linux/brcm2708/patches-4.4/0065-ASoC-wm8804-Implement-MCLK-configuration-options-add.patch new file mode 100644 index 0000000000..b668f43750 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0065-ASoC-wm8804-Implement-MCLK-configuration-options-add.patch @@ -0,0 +1,40 @@ +From 455d188d2a22ced675219587856c0dc66efc7256 Mon Sep 17 00:00:00 2001 +From: Daniel Matuschek +Date: Wed, 15 Jan 2014 21:41:23 +0100 +Subject: [PATCH 065/232] 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(-) + +--- a/sound/soc/codecs/wm8804.c ++++ b/sound/soc/codecs/wm8804.c +@@ -304,6 +304,7 @@ static int wm8804_hw_params(struct snd_p + blen = 0x1; + break; + case 24: ++ case 32: + blen = 0x2; + break; + default: +@@ -515,7 +516,7 @@ static const struct snd_soc_dai_ops wm88 + }; + + #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 | \ diff --git a/target/linux/brcm2708/patches-4.4/0066-ASoC-BCM-Add-support-for-HiFiBerry-Digi.-Driver-is-b.patch b/target/linux/brcm2708/patches-4.4/0066-ASoC-BCM-Add-support-for-HiFiBerry-Digi.-Driver-is-b.patch new file mode 100644 index 0000000000..913a7c66a6 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0066-ASoC-BCM-Add-support-for-HiFiBerry-Digi.-Driver-is-b.patch @@ -0,0 +1,282 @@ +From 442de6ae737fbd27bcf2fde8be857beccc59fd1f Mon Sep 17 00:00:00 2001 +From: Daniel Matuschek +Date: Wed, 15 Jan 2014 21:42:08 +0100 +Subject: [PATCH 066/232] ASoC: BCM:Add support for HiFiBerry Digi. Driver is + based on the patched WM8804 driver. + +Signed-off-by: Daniel Matuschek + +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. +Default behaviour of the module is the same, but when auto_shutdown_output +is set to 1, the SPDIF oputput will shutdown if no stream is playing. + +bugfix for 32kHz sample rate, was missing + +HiFiBerry Digi: set SPDIF status bits for sample rate + +The HiFiBerry Digi driver did not signal the sample rate in the SPDIF status bits. +While this is optional, some DACs and receivers do not accept this signal. This patch +adds the sample rate bits in the SPDIF status block. +--- + sound/soc/bcm/Kconfig | 7 ++ + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/hifiberry_digi.c | 223 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 232 insertions(+) + create mode 100644 sound/soc/bcm/hifiberry_digi.c + +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -15,6 +15,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 || SND_BCM2835_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 || SND_BCM2835_SOC_I2S +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -5,7 +5,9 @@ obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd + + # 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 +--- /dev/null ++++ b/sound/soc/bcm/hifiberry_digi.c +@@ -0,0 +1,223 @@ ++/* ++ * 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 short int auto_shutdown_output = 0; ++module_param(auto_shutdown_output, short, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); ++MODULE_PARM_DESC(auto_shutdown_output, "Shutdown SP/DIF output if playback is stopped"); ++ ++ ++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_startup(struct snd_pcm_substream *substream) { ++ /* turn on digital output */ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_codec *codec = rtd->codec; ++ snd_soc_update_bits(codec, WM8804_PWRDN, 0x3c, 0x00); ++ return 0; ++} ++ ++static void snd_rpi_hifiberry_digi_shutdown(struct snd_pcm_substream *substream) { ++ /* turn off output */ ++ if (auto_shutdown_output) { ++ /* turn off output */ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_codec *codec = rtd->codec; ++ snd_soc_update_bits(codec, WM8804_PWRDN, 0x3c, 0x3c); ++ } ++} ++ ++ ++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 sampling_freq=1; ++ ++ int ret; ++ ++ samplerate = params_rate(params); ++ ++ if (samplerate<=96000) { ++ mclk_freq=samplerate*256; ++ mclk_div=WM8804_MCLKDIV_256FS; ++ } else { ++ mclk_freq=samplerate*128; ++ mclk_div=WM8804_MCLKDIV_128FS; ++ } ++ ++ switch (samplerate) { ++ case 32000: ++ sampling_freq=0x03; ++ break; ++ case 44100: ++ sampling_freq=0x00; ++ break; ++ case 48000: ++ sampling_freq=0x02; ++ break; ++ case 88200: ++ sampling_freq=0x08; ++ break; ++ case 96000: ++ sampling_freq=0x0a; ++ break; ++ case 176400: ++ sampling_freq=0x0c; ++ break; ++ case 192000: ++ sampling_freq=0x0e; ++ break; ++ default: ++ dev_err(codec->dev, ++ "Failed to set WM8804 SYSCLK, unsupported samplerate %d\n", ++ samplerate); ++ } ++ ++ 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); ++ ++ /* set sampling frequency status bits */ ++ snd_soc_update_bits(codec, WM8804_SPDTX4, 0x0f, sampling_freq); ++ ++ 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, ++ .startup = snd_rpi_hifiberry_digi_startup, ++ .shutdown = snd_rpi_hifiberry_digi_shutdown, ++}; ++ ++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; ++ ++ if (pdev->dev.of_node) { ++ struct device_node *i2s_node; ++ struct snd_soc_dai_link *dai = &snd_rpi_hifiberry_digi_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_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 const struct of_device_id snd_rpi_hifiberry_digi_of_match[] = { ++ { .compatible = "hifiberry,hifiberry-digi", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, snd_rpi_hifiberry_digi_of_match); ++ ++static struct platform_driver snd_rpi_hifiberry_digi_driver = { ++ .driver = { ++ .name = "snd-hifiberry-digi", ++ .owner = THIS_MODULE, ++ .of_match_table = snd_rpi_hifiberry_digi_of_match, ++ }, ++ .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"); diff --git a/target/linux/brcm2708/patches-4.4/0066-ASoC-wm8804-Implement-MCLK-configuration-options-add.patch b/target/linux/brcm2708/patches-4.4/0066-ASoC-wm8804-Implement-MCLK-configuration-options-add.patch deleted file mode 100644 index 3404a2f08f..0000000000 --- a/target/linux/brcm2708/patches-4.4/0066-ASoC-wm8804-Implement-MCLK-configuration-options-add.patch +++ /dev/null @@ -1,40 +0,0 @@ -From dbdeca565b0850da182d3bd0b3ed41f667ca8a45 Mon Sep 17 00:00:00 2001 -From: Daniel Matuschek -Date: Wed, 15 Jan 2014 21:41:23 +0100 -Subject: [PATCH 066/170] 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(-) - ---- a/sound/soc/codecs/wm8804.c -+++ b/sound/soc/codecs/wm8804.c -@@ -304,6 +304,7 @@ static int wm8804_hw_params(struct snd_p - blen = 0x1; - break; - case 24: -+ case 32: - blen = 0x2; - break; - default: -@@ -515,7 +516,7 @@ static const struct snd_soc_dai_ops wm88 - }; - - #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 | \ diff --git a/target/linux/brcm2708/patches-4.4/0067-ASoC-BCM-Add-support-for-HiFiBerry-Digi.-Driver-is-b.patch b/target/linux/brcm2708/patches-4.4/0067-ASoC-BCM-Add-support-for-HiFiBerry-Digi.-Driver-is-b.patch deleted file mode 100644 index 50e1e1957c..0000000000 --- a/target/linux/brcm2708/patches-4.4/0067-ASoC-BCM-Add-support-for-HiFiBerry-Digi.-Driver-is-b.patch +++ /dev/null @@ -1,282 +0,0 @@ -From 0c2e64d3eda229c2f52ce6ed9d0b1c1572dd9b19 Mon Sep 17 00:00:00 2001 -From: Daniel Matuschek -Date: Wed, 15 Jan 2014 21:42:08 +0100 -Subject: [PATCH 067/170] ASoC: BCM:Add support for HiFiBerry Digi. Driver is - based on the patched WM8804 driver. - -Signed-off-by: Daniel Matuschek - -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. -Default behaviour of the module is the same, but when auto_shutdown_output -is set to 1, the SPDIF oputput will shutdown if no stream is playing. - -bugfix for 32kHz sample rate, was missing - -HiFiBerry Digi: set SPDIF status bits for sample rate - -The HiFiBerry Digi driver did not signal the sample rate in the SPDIF status bits. -While this is optional, some DACs and receivers do not accept this signal. This patch -adds the sample rate bits in the SPDIF status block. ---- - sound/soc/bcm/Kconfig | 7 ++ - sound/soc/bcm/Makefile | 2 + - sound/soc/bcm/hifiberry_digi.c | 223 +++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 232 insertions(+) - create mode 100644 sound/soc/bcm/hifiberry_digi.c - ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -15,6 +15,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 || SND_BCM2835_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 || SND_BCM2835_SOC_I2S ---- a/sound/soc/bcm/Makefile -+++ b/sound/soc/bcm/Makefile -@@ -5,7 +5,9 @@ obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd - - # 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 ---- /dev/null -+++ b/sound/soc/bcm/hifiberry_digi.c -@@ -0,0 +1,223 @@ -+/* -+ * 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 short int auto_shutdown_output = 0; -+module_param(auto_shutdown_output, short, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); -+MODULE_PARM_DESC(auto_shutdown_output, "Shutdown SP/DIF output if playback is stopped"); -+ -+ -+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_startup(struct snd_pcm_substream *substream) { -+ /* turn on digital output */ -+ struct snd_soc_pcm_runtime *rtd = substream->private_data; -+ struct snd_soc_codec *codec = rtd->codec; -+ snd_soc_update_bits(codec, WM8804_PWRDN, 0x3c, 0x00); -+ return 0; -+} -+ -+static void snd_rpi_hifiberry_digi_shutdown(struct snd_pcm_substream *substream) { -+ /* turn off output */ -+ if (auto_shutdown_output) { -+ /* turn off output */ -+ struct snd_soc_pcm_runtime *rtd = substream->private_data; -+ struct snd_soc_codec *codec = rtd->codec; -+ snd_soc_update_bits(codec, WM8804_PWRDN, 0x3c, 0x3c); -+ } -+} -+ -+ -+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 sampling_freq=1; -+ -+ int ret; -+ -+ samplerate = params_rate(params); -+ -+ if (samplerate<=96000) { -+ mclk_freq=samplerate*256; -+ mclk_div=WM8804_MCLKDIV_256FS; -+ } else { -+ mclk_freq=samplerate*128; -+ mclk_div=WM8804_MCLKDIV_128FS; -+ } -+ -+ switch (samplerate) { -+ case 32000: -+ sampling_freq=0x03; -+ break; -+ case 44100: -+ sampling_freq=0x00; -+ break; -+ case 48000: -+ sampling_freq=0x02; -+ break; -+ case 88200: -+ sampling_freq=0x08; -+ break; -+ case 96000: -+ sampling_freq=0x0a; -+ break; -+ case 176400: -+ sampling_freq=0x0c; -+ break; -+ case 192000: -+ sampling_freq=0x0e; -+ break; -+ default: -+ dev_err(codec->dev, -+ "Failed to set WM8804 SYSCLK, unsupported samplerate %d\n", -+ samplerate); -+ } -+ -+ 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); -+ -+ /* set sampling frequency status bits */ -+ snd_soc_update_bits(codec, WM8804_SPDTX4, 0x0f, sampling_freq); -+ -+ 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, -+ .startup = snd_rpi_hifiberry_digi_startup, -+ .shutdown = snd_rpi_hifiberry_digi_shutdown, -+}; -+ -+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; -+ -+ if (pdev->dev.of_node) { -+ struct device_node *i2s_node; -+ struct snd_soc_dai_link *dai = &snd_rpi_hifiberry_digi_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_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 const struct of_device_id snd_rpi_hifiberry_digi_of_match[] = { -+ { .compatible = "hifiberry,hifiberry-digi", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, snd_rpi_hifiberry_digi_of_match); -+ -+static struct platform_driver snd_rpi_hifiberry_digi_driver = { -+ .driver = { -+ .name = "snd-hifiberry-digi", -+ .owner = THIS_MODULE, -+ .of_match_table = snd_rpi_hifiberry_digi_of_match, -+ }, -+ .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"); diff --git a/target/linux/brcm2708/patches-4.4/0067-ASoC-wm8804-Set-idle_bias_off-to-false-Idle-bias-has.patch b/target/linux/brcm2708/patches-4.4/0067-ASoC-wm8804-Set-idle_bias_off-to-false-Idle-bias-has.patch new file mode 100644 index 0000000000..b9f6ea4205 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0067-ASoC-wm8804-Set-idle_bias_off-to-false-Idle-bias-has.patch @@ -0,0 +1,22 @@ +From 836e7d5d46f17115b728986e3077c78c211a7678 Mon Sep 17 00:00:00 2001 +From: Daniel Matuschek +Date: Thu, 16 Jan 2014 07:36:35 +0100 +Subject: [PATCH 067/232] 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(-) + +--- a/sound/soc/codecs/wm8804.c ++++ b/sound/soc/codecs/wm8804.c +@@ -544,7 +544,7 @@ static struct snd_soc_dai_driver wm8804_ + }; + + static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = { +- .idle_bias_off = true, ++ .idle_bias_off = false, + + .dapm_widgets = wm8804_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8804_dapm_widgets), diff --git a/target/linux/brcm2708/patches-4.4/0068-ASoC-wm8804-Set-idle_bias_off-to-false-Idle-bias-has.patch b/target/linux/brcm2708/patches-4.4/0068-ASoC-wm8804-Set-idle_bias_off-to-false-Idle-bias-has.patch deleted file mode 100644 index cc82f7523f..0000000000 --- a/target/linux/brcm2708/patches-4.4/0068-ASoC-wm8804-Set-idle_bias_off-to-false-Idle-bias-has.patch +++ /dev/null @@ -1,22 +0,0 @@ -From f2789622ff7c1297e28f38bb198f5fd24b4602ef Mon Sep 17 00:00:00 2001 -From: Daniel Matuschek -Date: Thu, 16 Jan 2014 07:36:35 +0100 -Subject: [PATCH 068/170] 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(-) - ---- a/sound/soc/codecs/wm8804.c -+++ b/sound/soc/codecs/wm8804.c -@@ -544,7 +544,7 @@ static struct snd_soc_dai_driver wm8804_ - }; - - static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = { -- .idle_bias_off = true, -+ .idle_bias_off = false, - - .dapm_widgets = wm8804_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm8804_dapm_widgets), diff --git a/target/linux/brcm2708/patches-4.4/0068-Add-IQaudIO-Sound-Card-support-for-Raspberry-Pi.patch b/target/linux/brcm2708/patches-4.4/0068-Add-IQaudIO-Sound-Card-support-for-Raspberry-Pi.patch new file mode 100644 index 0000000000..d16dcc4268 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0068-Add-IQaudIO-Sound-Card-support-for-Raspberry-Pi.patch @@ -0,0 +1,178 @@ +From 7ab81054051a41288e4aa2bb21d4740e0d78a4ac Mon Sep 17 00:00:00 2001 +From: Gordon Garrity +Date: Sat, 8 Mar 2014 16:56:57 +0000 +Subject: [PATCH 068/232] Add IQaudIO Sound Card support for Raspberry Pi + +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/Kconfig | 7 +++ + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/iqaudio-dac.c | 132 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 141 insertions(+) + create mode 100644 sound/soc/bcm/iqaudio-dac.c + +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -28,3 +28,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 || SND_BCM2835_SOC_I2S ++ select SND_SOC_PCM512x_I2C ++ help ++ Say Y or M if you want to add support for IQaudIO-DAC. +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -7,7 +7,9 @@ obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd + 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 +--- /dev/null ++++ b/sound/soc/bcm/iqaudio-dac.c +@@ -0,0 +1,132 @@ ++/* ++ * 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) ++{ ++ int ret; ++ struct snd_soc_card *card = rtd->card; ++ ++ ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207); ++ if (ret < 0) ++ dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); ++ ++ 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; ++ ++ if (pdev->dev.of_node) { ++ struct device_node *i2s_node; ++ struct snd_soc_dai_link *dai = &snd_rpi_iqaudio_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_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", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, iqaudio_of_match); ++ ++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"); diff --git a/target/linux/brcm2708/patches-4.4/0069-Add-IQaudIO-Sound-Card-support-for-Raspberry-Pi.patch b/target/linux/brcm2708/patches-4.4/0069-Add-IQaudIO-Sound-Card-support-for-Raspberry-Pi.patch deleted file mode 100644 index 1c2e4e99a5..0000000000 --- a/target/linux/brcm2708/patches-4.4/0069-Add-IQaudIO-Sound-Card-support-for-Raspberry-Pi.patch +++ /dev/null @@ -1,178 +0,0 @@ -From 80fa31f923b4967bc86be20c5af780ef284fc972 Mon Sep 17 00:00:00 2001 -From: Gordon Garrity -Date: Sat, 8 Mar 2014 16:56:57 +0000 -Subject: [PATCH 069/170] Add IQaudIO Sound Card support for Raspberry Pi - -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/Kconfig | 7 +++ - sound/soc/bcm/Makefile | 2 + - sound/soc/bcm/iqaudio-dac.c | 132 ++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 141 insertions(+) - create mode 100644 sound/soc/bcm/iqaudio-dac.c - ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -28,3 +28,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 || SND_BCM2835_SOC_I2S -+ select SND_SOC_PCM512x_I2C -+ help -+ Say Y or M if you want to add support for IQaudIO-DAC. ---- a/sound/soc/bcm/Makefile -+++ b/sound/soc/bcm/Makefile -@@ -7,7 +7,9 @@ obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd - 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 ---- /dev/null -+++ b/sound/soc/bcm/iqaudio-dac.c -@@ -0,0 +1,132 @@ -+/* -+ * 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) -+{ -+ int ret; -+ struct snd_soc_card *card = rtd->card; -+ -+ ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207); -+ if (ret < 0) -+ dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); -+ -+ 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; -+ -+ if (pdev->dev.of_node) { -+ struct device_node *i2s_node; -+ struct snd_soc_dai_link *dai = &snd_rpi_iqaudio_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_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", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, iqaudio_of_match); -+ -+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"); diff --git a/target/linux/brcm2708/patches-4.4/0069-hid-Reduce-default-mouse-polling-interval-to-60Hz.patch b/target/linux/brcm2708/patches-4.4/0069-hid-Reduce-default-mouse-polling-interval-to-60Hz.patch new file mode 100644 index 0000000000..e269150f23 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0069-hid-Reduce-default-mouse-polling-interval-to-60Hz.patch @@ -0,0 +1,36 @@ +From efb984b47e685c966a3b55874de2c1fb5b0b1140 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Mon, 14 Jul 2014 22:02:09 +0100 +Subject: [PATCH 069/232] 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(-) + +--- 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"); + +@@ -1091,8 +1091,12 @@ static int usbhid_start(struct hid_devic + } + + /* 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)) { diff --git a/target/linux/brcm2708/patches-4.4/0070-Added-support-for-HiFiBerry-DAC.patch b/target/linux/brcm2708/patches-4.4/0070-Added-support-for-HiFiBerry-DAC.patch new file mode 100644 index 0000000000..9a0e2033a9 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0070-Added-support-for-HiFiBerry-DAC.patch @@ -0,0 +1,190 @@ +From 72a92f2e40881dcb46e50021c5c5fe2c20d8c461 Mon Sep 17 00:00:00 2001 +From: Daniel Matuschek +Date: Mon, 4 Aug 2014 10:06:56 +0200 +Subject: [PATCH 070/232] 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. +--- + sound/soc/bcm/Kconfig | 7 ++ + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/hifiberry_dacplus.c | 141 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 150 insertions(+) + create mode 100644 sound/soc/bcm/hifiberry_dacplus.c + +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -15,6 +15,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_DACPLUS ++ tristate "Support for HifiBerry DAC+" ++ depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S ++ select SND_SOC_PCM512x ++ 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 || SND_BCM2835_SOC_I2S +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -5,11 +5,13 @@ obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd + + # BCM2708 Machine Support + snd-soc-hifiberry-dac-objs := hifiberry_dac.o ++snd-soc-hifiberry-dacplus-objs := hifiberry_dacplus.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_DACPLUS) += snd-soc-hifiberry-dacplus.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 +--- /dev/null ++++ b/sound/soc/bcm/hifiberry_dacplus.c +@@ -0,0 +1,141 @@ ++/* ++ * ASoC Driver for HiFiBerry DAC+ ++ * ++ * Author: Daniel Matuschek ++ * Copyright 2014 ++ * based on code by Florian Meier ++ * ++ * 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/pcm512x.h" ++ ++static int snd_rpi_hifiberry_dacplus_init(struct snd_soc_pcm_runtime *rtd) ++{ ++ struct snd_soc_codec *codec = rtd->codec; ++ snd_soc_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08); ++ snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0xf, 0x02); ++ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08,0x08); ++ return 0; ++} ++ ++static int snd_rpi_hifiberry_dacplus_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, 64); ++} ++ ++static int snd_rpi_hifiberry_dacplus_startup(struct snd_pcm_substream *substream) { ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_codec *codec = rtd->codec; ++ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08,0x08); ++ return 0; ++} ++ ++static void snd_rpi_hifiberry_dacplus_shutdown(struct snd_pcm_substream *substream) { ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_codec *codec = rtd->codec; ++ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08,0x00); ++} ++ ++/* machine stream operations */ ++static struct snd_soc_ops snd_rpi_hifiberry_dacplus_ops = { ++ .hw_params = snd_rpi_hifiberry_dacplus_hw_params, ++ .startup = snd_rpi_hifiberry_dacplus_startup, ++ .shutdown = snd_rpi_hifiberry_dacplus_shutdown, ++}; ++ ++static struct snd_soc_dai_link snd_rpi_hifiberry_dacplus_dai[] = { ++{ ++ .name = "HiFiBerry DAC+", ++ .stream_name = "HiFiBerry DAC+ HiFi", ++ .cpu_dai_name = "bcm2708-i2s.0", ++ .codec_dai_name = "pcm512x-hifi", ++ .platform_name = "bcm2708-i2s.0", ++ .codec_name = "pcm512x.1-004d", ++ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_CBS_CFS, ++ .ops = &snd_rpi_hifiberry_dacplus_ops, ++ .init = snd_rpi_hifiberry_dacplus_init, ++}, ++}; ++ ++/* audio machine driver */ ++static struct snd_soc_card snd_rpi_hifiberry_dacplus = { ++ .name = "snd_rpi_hifiberry_dacplus", ++ .dai_link = snd_rpi_hifiberry_dacplus_dai, ++ .num_links = ARRAY_SIZE(snd_rpi_hifiberry_dacplus_dai), ++}; ++ ++static int snd_rpi_hifiberry_dacplus_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ snd_rpi_hifiberry_dacplus.dev = &pdev->dev; ++ ++ if (pdev->dev.of_node) { ++ struct device_node *i2s_node; ++ struct snd_soc_dai_link *dai = &snd_rpi_hifiberry_dacplus_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_hifiberry_dacplus); ++ if (ret) ++ dev_err(&pdev->dev, ++ "snd_soc_register_card() failed: %d\n", ret); ++ ++ return ret; ++} ++ ++static int snd_rpi_hifiberry_dacplus_remove(struct platform_device *pdev) ++{ ++ return snd_soc_unregister_card(&snd_rpi_hifiberry_dacplus); ++} ++ ++static const struct of_device_id snd_rpi_hifiberry_dacplus_of_match[] = { ++ { .compatible = "hifiberry,hifiberry-dacplus", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, snd_rpi_hifiberry_dacplus_of_match); ++ ++static struct platform_driver snd_rpi_hifiberry_dacplus_driver = { ++ .driver = { ++ .name = "snd-rpi-hifiberry-dacplus", ++ .owner = THIS_MODULE, ++ .of_match_table = snd_rpi_hifiberry_dacplus_of_match, ++ }, ++ .probe = snd_rpi_hifiberry_dacplus_probe, ++ .remove = snd_rpi_hifiberry_dacplus_remove, ++}; ++ ++module_platform_driver(snd_rpi_hifiberry_dacplus_driver); ++ ++MODULE_AUTHOR("Daniel Matuschek "); ++MODULE_DESCRIPTION("ASoC Driver for HiFiBerry DAC+"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/brcm2708/patches-4.4/0070-hid-Reduce-default-mouse-polling-interval-to-60Hz.patch b/target/linux/brcm2708/patches-4.4/0070-hid-Reduce-default-mouse-polling-interval-to-60Hz.patch deleted file mode 100644 index 471732ce92..0000000000 --- a/target/linux/brcm2708/patches-4.4/0070-hid-Reduce-default-mouse-polling-interval-to-60Hz.patch +++ /dev/null @@ -1,36 +0,0 @@ -From b15f112ddaf2cd20a8f999ac823ddb51d7d513b9 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Mon, 14 Jul 2014 22:02:09 +0100 -Subject: [PATCH 070/170] 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(-) - ---- 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"); - -@@ -1091,8 +1091,12 @@ static int usbhid_start(struct hid_devic - } - - /* 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)) { diff --git a/target/linux/brcm2708/patches-4.4/0071-Added-driver-for-HiFiBerry-Amp-amplifier-add-on-boar.patch b/target/linux/brcm2708/patches-4.4/0071-Added-driver-for-HiFiBerry-Amp-amplifier-add-on-boar.patch new file mode 100644 index 0000000000..b45c47c172 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0071-Added-driver-for-HiFiBerry-Amp-amplifier-add-on-boar.patch @@ -0,0 +1,816 @@ +From f25572414689152e6208e5ca921185b1db536b24 Mon Sep 17 00:00:00 2001 +From: Daniel Matuschek +Date: Mon, 4 Aug 2014 11:09:58 +0200 +Subject: [PATCH 071/232] 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. + +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 +loaded, but won't work. This patch fixes this. I2C communication error will now +reported correctly by a non-zero return code. + +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. +--- + sound/soc/bcm/Kconfig | 7 + + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/hifiberry_amp.c | 127 +++++++++++++++ + sound/soc/codecs/Kconfig | 4 + + sound/soc/codecs/Makefile | 2 + + sound/soc/codecs/tas5713.c | 369 ++++++++++++++++++++++++++++++++++++++++++ + sound/soc/codecs/tas5713.h | 210 ++++++++++++++++++++++++ + 7 files changed, 721 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 + +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -29,6 +29,13 @@ config SND_BCM2708_SOC_HIFIBERRY_DIGI + help + Say Y or M if you want to add support for HifiBerry Digi S/PDIF output board. + ++config SND_BCM2708_SOC_HIFIBERRY_AMP ++ tristate "Support for the HifiBerry Amp" ++ depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S ++ select SND_SOC_TAS5713 ++ help ++ Say Y or M if you want to add support for the HifiBerry Amp amplifier board. ++ + config SND_BCM2708_SOC_RPI_DAC + tristate "Support for RPi-DAC" + depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -7,11 +7,13 @@ obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd + snd-soc-hifiberry-dac-objs := hifiberry_dac.o + 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-iqaudio-dac-objs := iqaudio-dac.o + + obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o + 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_IQAUDIO_DAC) += snd-soc-iqaudio-dac.o +--- /dev/null ++++ b/sound/soc/bcm/hifiberry_amp.c +@@ -0,0 +1,127 @@ ++/* ++ * ASoC Driver for HifiBerry AMP ++ * ++ * Author: Sebastian Eickhoff ++ * Copyright 2014 ++ * ++ * 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_amp_init(struct snd_soc_pcm_runtime *rtd) ++{ ++ // ToDo: init of the dsp-registers. ++ return 0; ++} ++ ++static int snd_rpi_hifiberry_amp_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, 64); ++} ++ ++static struct snd_soc_ops snd_rpi_hifiberry_amp_ops = { ++ .hw_params = snd_rpi_hifiberry_amp_hw_params, ++}; ++ ++static struct snd_soc_dai_link snd_rpi_hifiberry_amp_dai[] = { ++ { ++ .name = "HifiBerry AMP", ++ .stream_name = "HifiBerry AMP HiFi", ++ .cpu_dai_name = "bcm2708-i2s.0", ++ .codec_dai_name = "tas5713-hifi", ++ .platform_name = "bcm2708-i2s.0", ++ .codec_name = "tas5713.1-001b", ++ .dai_fmt = SND_SOC_DAIFMT_I2S | ++ SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_CBS_CFS, ++ .ops = &snd_rpi_hifiberry_amp_ops, ++ .init = snd_rpi_hifiberry_amp_init, ++ }, ++}; ++ ++ ++static struct snd_soc_card snd_rpi_hifiberry_amp = { ++ .name = "snd_rpi_hifiberry_amp", ++ .dai_link = snd_rpi_hifiberry_amp_dai, ++ .num_links = ARRAY_SIZE(snd_rpi_hifiberry_amp_dai), ++}; ++ ++static const struct of_device_id snd_rpi_hifiberry_amp_of_match[] = { ++ { .compatible = "hifiberry,hifiberry-amp", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, snd_rpi_hifiberry_amp_of_match); ++ ++ ++static int snd_rpi_hifiberry_amp_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ snd_rpi_hifiberry_amp.dev = &pdev->dev; ++ ++ if (pdev->dev.of_node) { ++ struct device_node *i2s_node; ++ struct snd_soc_dai_link *dai = &snd_rpi_hifiberry_amp_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_hifiberry_amp); ++ ++ if (ret != 0) { ++ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); ++ } ++ ++ return ret; ++} ++ ++ ++static int snd_rpi_hifiberry_amp_remove(struct platform_device *pdev) ++{ ++ return snd_soc_unregister_card(&snd_rpi_hifiberry_amp); ++} ++ ++ ++static struct platform_driver snd_rpi_hifiberry_amp_driver = { ++ .driver = { ++ .name = "snd-hifiberry-amp", ++ .owner = THIS_MODULE, ++ .of_match_table = snd_rpi_hifiberry_amp_of_match, ++ }, ++ .probe = snd_rpi_hifiberry_amp_probe, ++ .remove = snd_rpi_hifiberry_amp_remove, ++}; ++ ++ ++module_platform_driver(snd_rpi_hifiberry_amp_driver); ++ ++ ++MODULE_AUTHOR("Sebastian Eickhoff "); ++MODULE_DESCRIPTION("ASoC driver for HiFiBerry-AMP"); ++MODULE_LICENSE("GPL v2"); +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -117,6 +117,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 ++ select SND_SOC_TAS5713 if I2C + select SND_SOC_TLV320AIC26 if SPI_MASTER + select SND_SOC_TLV320AIC31XX if I2C + select SND_SOC_TLV320AIC32X4 if I2C +@@ -674,6 +675,9 @@ config SND_SOC_TFA9879 + tristate "NXP Semiconductors TFA9879 amplifier" + depends on I2C + ++config SND_SOC_TAS5713 ++ tristate ++ + config SND_SOC_TLV320AIC23 + tristate + +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -118,6 +118,7 @@ snd-soc-sti-sas-objs := sti-sas.o + snd-soc-tas5086-objs := tas5086.o + snd-soc-tas571x-objs := tas571x.o + snd-soc-tfa9879-objs := tfa9879.o ++snd-soc-tas5713-objs := tas5713.o + snd-soc-tlv320aic23-objs := tlv320aic23.o + snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o + snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o +@@ -312,6 +313,7 @@ obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc + obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o + obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o + obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o ++obj-$(CONFIG_SND_SOC_TAS5713) += snd-soc-tas5713.o + obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o + obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o + obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o +--- /dev/null ++++ b/sound/soc/codecs/tas5713.c +@@ -0,0 +1,369 @@ ++/* ++ * ASoC Driver for TAS5713 ++ * ++ * Author: Sebastian Eickhoff ++ * Copyright 2014 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "tas5713.h" ++ ++ ++static struct i2c_client *i2c; ++ ++struct tas5713_priv { ++ struct regmap *regmap; ++ int mclk_div; ++ struct snd_soc_codec *codec; ++}; ++ ++static struct tas5713_priv *priv_data; ++ ++ ++ ++ ++/* ++ * _ _ ___ _ ___ _ _ ++ * /_\ | | / __| /_\ / __|___ _ _| |_ _ _ ___| |___ ++ * / _ \| |__\__ \/ _ \ | (__/ _ \ ' \ _| '_/ _ \ (_-< ++ * /_/ \_\____|___/_/ \_\ \___\___/_||_\__|_| \___/_/__/ ++ * ++ */ ++ ++static const DECLARE_TLV_DB_SCALE(tas5713_vol_tlv, -10000, 50, 1); ++ ++ ++static const struct snd_kcontrol_new tas5713_snd_controls[] = { ++ SOC_SINGLE_TLV ("Master" , TAS5713_VOL_MASTER, 0, 248, 1, tas5713_vol_tlv), ++ SOC_DOUBLE_R_TLV("Channels" , TAS5713_VOL_CH1, TAS5713_VOL_CH2, 0, 248, 1, tas5713_vol_tlv) ++}; ++ ++ ++ ++ ++/* ++ * __ __ _ _ ___ _ ++ * | \/ |__ _ __| |_ (_)_ _ ___ | \ _ _(_)_ _____ _ _ ++ * | |\/| / _` / _| ' \| | ' \/ -_) | |) | '_| \ V / -_) '_| ++ * |_| |_\__,_\__|_||_|_|_||_\___| |___/|_| |_|\_/\___|_| ++ * ++ */ ++ ++static int tas5713_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ u16 blen = 0x00; ++ ++ struct snd_soc_codec *codec; ++ codec = dai->codec; ++ priv_data->codec = dai->codec; ++ ++ switch (params_format(params)) { ++ case SNDRV_PCM_FORMAT_S16_LE: ++ blen = 0x03; ++ break; ++ case SNDRV_PCM_FORMAT_S20_3LE: ++ blen = 0x1; ++ break; ++ case SNDRV_PCM_FORMAT_S24_LE: ++ blen = 0x04; ++ break; ++ case SNDRV_PCM_FORMAT_S32_LE: ++ blen = 0x05; ++ break; ++ default: ++ dev_err(dai->dev, "Unsupported word length: %u\n", ++ params_format(params)); ++ return -EINVAL; ++ } ++ ++ // set word length ++ snd_soc_update_bits(codec, TAS5713_SERIAL_DATA_INTERFACE, 0x7, blen); ++ ++ return 0; ++} ++ ++ ++static int tas5713_mute_stream(struct snd_soc_dai *dai, int mute, int stream) ++{ ++ unsigned int val = 0; ++ ++ struct tas5713_priv *tas5713; ++ struct snd_soc_codec *codec = dai->codec; ++ tas5713 = snd_soc_codec_get_drvdata(codec); ++ ++ if (mute) { ++ val = TAS5713_SOFT_MUTE_ALL; ++ } ++ ++ return regmap_write(tas5713->regmap, TAS5713_SOFT_MUTE, val); ++} ++ ++ ++static const struct snd_soc_dai_ops tas5713_dai_ops = { ++ .hw_params = tas5713_hw_params, ++ .mute_stream = tas5713_mute_stream, ++}; ++ ++ ++static struct snd_soc_dai_driver tas5713_dai = { ++ .name = "tas5713-hifi", ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_48000, ++ .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE ), ++ }, ++ .ops = &tas5713_dai_ops, ++}; ++ ++ ++ ++ ++/* ++ * ___ _ ___ _ ++ * / __|___ __| |___ __ | \ _ _(_)_ _____ _ _ ++ * | (__/ _ \/ _` / -_) _| | |) | '_| \ V / -_) '_| ++ * \___\___/\__,_\___\__| |___/|_| |_|\_/\___|_| ++ * ++ */ ++ ++static int tas5713_remove(struct snd_soc_codec *codec) ++{ ++ struct tas5713_priv *tas5713; ++ ++ tas5713 = snd_soc_codec_get_drvdata(codec); ++ ++ return 0; ++} ++ ++ ++static int tas5713_probe(struct snd_soc_codec *codec) ++{ ++ struct tas5713_priv *tas5713; ++ int i, ret; ++ ++ i2c = container_of(codec->dev, struct i2c_client, dev); ++ ++ tas5713 = snd_soc_codec_get_drvdata(codec); ++ ++ // Reset error ++ ret = snd_soc_write(codec, TAS5713_ERROR_STATUS, 0x00); ++ if (ret < 0) return ret; ++ ++ // Trim oscillator ++ ret = snd_soc_write(codec, TAS5713_OSC_TRIM, 0x00); ++ if (ret < 0) return ret; ++ msleep(1000); ++ ++ // Reset error ++ ret = snd_soc_write(codec, TAS5713_ERROR_STATUS, 0x00); ++ if (ret < 0) return ret; ++ ++ // Clock mode: 44/48kHz, MCLK=64xfs ++ ret = snd_soc_write(codec, TAS5713_CLOCK_CTRL, 0x60); ++ if (ret < 0) return ret; ++ ++ // I2S 24bit ++ ret = snd_soc_write(codec, TAS5713_SERIAL_DATA_INTERFACE, 0x05); ++ if (ret < 0) return ret; ++ ++ // Unmute ++ ret = snd_soc_write(codec, TAS5713_SYSTEM_CTRL2, 0x00); ++ if (ret < 0) return ret; ++ ret = snd_soc_write(codec, TAS5713_SOFT_MUTE, 0x00); ++ if (ret < 0) return ret; ++ ++ // Set volume to 0db ++ ret = snd_soc_write(codec, TAS5713_VOL_MASTER, 0x00); ++ if (ret < 0) return ret; ++ ++ // Now start programming the default initialization sequence ++ for (i = 0; i < ARRAY_SIZE(tas5713_init_sequence); ++i) { ++ ret = i2c_master_send(i2c, ++ tas5713_init_sequence[i].data, ++ tas5713_init_sequence[i].size); ++ if (ret < 0) { ++ printk(KERN_INFO "TAS5713 CODEC PROBE: InitSeq returns: %d\n", ret); ++ } ++ } ++ ++ // Unmute ++ ret = snd_soc_write(codec, TAS5713_SYSTEM_CTRL2, 0x00); ++ if (ret < 0) return ret; ++ ++ return 0; ++} ++ ++ ++static struct snd_soc_codec_driver soc_codec_dev_tas5713 = { ++ .probe = tas5713_probe, ++ .remove = tas5713_remove, ++ .controls = tas5713_snd_controls, ++ .num_controls = ARRAY_SIZE(tas5713_snd_controls), ++}; ++ ++ ++ ++ ++/* ++ * ___ ___ ___ ___ _ ++ * |_ _|_ ) __| | \ _ _(_)_ _____ _ _ ++ * | | / / (__ | |) | '_| \ V / -_) '_| ++ * |___/___\___| |___/|_| |_|\_/\___|_| ++ * ++ */ ++ ++static const struct reg_default tas5713_reg_defaults[] = { ++ { 0x07 ,0x80 }, // R7 - VOL_MASTER - -40dB ++ { 0x08 , 30 }, // R8 - VOL_CH1 - 0dB ++ { 0x09 , 30 }, // R9 - VOL_CH2 - 0dB ++ { 0x0A ,0x80 }, // R10 - VOL_HEADPHONE - -40dB ++}; ++ ++ ++static bool tas5713_reg_volatile(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case TAS5713_DEVICE_ID: ++ case TAS5713_ERROR_STATUS: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++ ++static const struct of_device_id tas5713_of_match[] = { ++ { .compatible = "ti,tas5713", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, tas5713_of_match); ++ ++ ++static struct regmap_config tas5713_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ ++ .max_register = TAS5713_MAX_REGISTER, ++ .volatile_reg = tas5713_reg_volatile, ++ ++ .cache_type = REGCACHE_RBTREE, ++ .reg_defaults = tas5713_reg_defaults, ++ .num_reg_defaults = ARRAY_SIZE(tas5713_reg_defaults), ++}; ++ ++ ++static int tas5713_i2c_probe(struct i2c_client *i2c, ++ const struct i2c_device_id *id) ++{ ++ int ret; ++ ++ priv_data = devm_kzalloc(&i2c->dev, sizeof *priv_data, GFP_KERNEL); ++ if (!priv_data) ++ return -ENOMEM; ++ ++ priv_data->regmap = devm_regmap_init_i2c(i2c, &tas5713_regmap_config); ++ if (IS_ERR(priv_data->regmap)) { ++ ret = PTR_ERR(priv_data->regmap); ++ return ret; ++ } ++ ++ i2c_set_clientdata(i2c, priv_data); ++ ++ ret = snd_soc_register_codec(&i2c->dev, ++ &soc_codec_dev_tas5713, &tas5713_dai, 1); ++ ++ return ret; ++} ++ ++ ++static int tas5713_i2c_remove(struct i2c_client *i2c) ++{ ++ snd_soc_unregister_codec(&i2c->dev); ++ i2c_set_clientdata(i2c, NULL); ++ ++ kfree(priv_data); ++ ++ return 0; ++} ++ ++ ++static const struct i2c_device_id tas5713_i2c_id[] = { ++ { "tas5713", 0 }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(i2c, tas5713_i2c_id); ++ ++ ++static struct i2c_driver tas5713_i2c_driver = { ++ .driver = { ++ .name = "tas5713", ++ .owner = THIS_MODULE, ++ .of_match_table = tas5713_of_match, ++ }, ++ .probe = tas5713_i2c_probe, ++ .remove = tas5713_i2c_remove, ++ .id_table = tas5713_i2c_id ++}; ++ ++ ++static int __init tas5713_modinit(void) ++{ ++ int ret = 0; ++ ++ ret = i2c_add_driver(&tas5713_i2c_driver); ++ if (ret) { ++ printk(KERN_ERR "Failed to register tas5713 I2C driver: %d\n", ++ ret); ++ } ++ ++ return ret; ++} ++module_init(tas5713_modinit); ++ ++ ++static void __exit tas5713_exit(void) ++{ ++ i2c_del_driver(&tas5713_i2c_driver); ++} ++module_exit(tas5713_exit); ++ ++ ++MODULE_AUTHOR("Sebastian Eickhoff "); ++MODULE_DESCRIPTION("ASoC driver for TAS5713"); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/sound/soc/codecs/tas5713.h +@@ -0,0 +1,210 @@ ++/* ++ * ASoC Driver for TAS5713 ++ * ++ * Author: Sebastian Eickhoff ++ * Copyright 2014 ++ * ++ * 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. ++ */ ++ ++#ifndef _TAS5713_H ++#define _TAS5713_H ++ ++ ++// TAS5713 I2C-bus register addresses ++ ++#define TAS5713_CLOCK_CTRL 0x00 ++#define TAS5713_DEVICE_ID 0x01 ++#define TAS5713_ERROR_STATUS 0x02 ++#define TAS5713_SYSTEM_CTRL1 0x03 ++#define TAS5713_SERIAL_DATA_INTERFACE 0x04 ++#define TAS5713_SYSTEM_CTRL2 0x05 ++#define TAS5713_SOFT_MUTE 0x06 ++#define TAS5713_VOL_MASTER 0x07 ++#define TAS5713_VOL_CH1 0x08 ++#define TAS5713_VOL_CH2 0x09 ++#define TAS5713_VOL_HEADPHONE 0x0A ++#define TAS5713_VOL_CONFIG 0x0E ++#define TAS5713_MODULATION_LIMIT 0x10 ++#define TAS5713_IC_DLY_CH1 0x11 ++#define TAS5713_IC_DLY_CH2 0x12 ++#define TAS5713_IC_DLY_CH3 0x13 ++#define TAS5713_IC_DLY_CH4 0x14 ++ ++#define TAS5713_START_STOP_PERIOD 0x1A ++#define TAS5713_OSC_TRIM 0x1B ++#define TAS5713_BKND_ERR 0x1C ++ ++#define TAS5713_INPUT_MUX 0x20 ++#define TAS5713_SRC_SELECT_CH4 0x21 ++#define TAS5713_PWM_MUX 0x25 ++ ++#define TAS5713_CH1_BQ0 0x29 ++#define TAS5713_CH1_BQ1 0x2A ++#define TAS5713_CH1_BQ2 0x2B ++#define TAS5713_CH1_BQ3 0x2C ++#define TAS5713_CH1_BQ4 0x2D ++#define TAS5713_CH1_BQ5 0x2E ++#define TAS5713_CH1_BQ6 0x2F ++#define TAS5713_CH1_BQ7 0x58 ++#define TAS5713_CH1_BQ8 0x59 ++ ++#define TAS5713_CH2_BQ0 0x30 ++#define TAS5713_CH2_BQ1 0x31 ++#define TAS5713_CH2_BQ2 0x32 ++#define TAS5713_CH2_BQ3 0x33 ++#define TAS5713_CH2_BQ4 0x34 ++#define TAS5713_CH2_BQ5 0x35 ++#define TAS5713_CH2_BQ6 0x36 ++#define TAS5713_CH2_BQ7 0x5C ++#define TAS5713_CH2_BQ8 0x5D ++ ++#define TAS5713_CH4_BQ0 0x5A ++#define TAS5713_CH4_BQ1 0x5B ++#define TAS5713_CH3_BQ0 0x5E ++#define TAS5713_CH3_BQ1 0x5F ++ ++#define TAS5713_DRC1_SOFTENING_FILTER_ALPHA_OMEGA 0x3B ++#define TAS5713_DRC1_ATTACK_RELEASE_RATE 0x3C ++#define TAS5713_DRC2_SOFTENING_FILTER_ALPHA_OMEGA 0x3E ++#define TAS5713_DRC2_ATTACK_RELEASE_RATE 0x3F ++#define TAS5713_DRC1_ATTACK_RELEASE_THRES 0x40 ++#define TAS5713_DRC2_ATTACK_RELEASE_THRES 0x43 ++#define TAS5713_DRC_CTRL 0x46 ++ ++#define TAS5713_BANK_SW_CTRL 0x50 ++#define TAS5713_CH1_OUTPUT_MIXER 0x51 ++#define TAS5713_CH2_OUTPUT_MIXER 0x52 ++#define TAS5713_CH1_INPUT_MIXER 0x53 ++#define TAS5713_CH2_INPUT_MIXER 0x54 ++#define TAS5713_OUTPUT_POST_SCALE 0x56 ++#define TAS5713_OUTPUT_PRESCALE 0x57 ++ ++#define TAS5713_IDF_POST_SCALE 0x62 ++ ++#define TAS5713_CH1_INLINE_MIXER 0x70 ++#define TAS5713_CH1_INLINE_DRC_EN_MIXER 0x71 ++#define TAS5713_CH1_R_CHANNEL_MIXER 0x72 ++#define TAS5713_CH1_L_CHANNEL_MIXER 0x73 ++#define TAS5713_CH2_INLINE_MIXER 0x74 ++#define TAS5713_CH2_INLINE_DRC_EN_MIXER 0x75 ++#define TAS5713_CH2_L_CHANNEL_MIXER 0x76 ++#define TAS5713_CH2_R_CHANNEL_MIXER 0x77 ++ ++#define TAS5713_UPDATE_DEV_ADDR_KEY 0xF8 ++#define TAS5713_UPDATE_DEV_ADDR_REG 0xF9 ++ ++#define TAS5713_REGISTER_COUNT 0x46 ++#define TAS5713_MAX_REGISTER 0xF9 ++ ++ ++// Bitmasks for registers ++#define TAS5713_SOFT_MUTE_ALL 0x07 ++ ++ ++ ++struct tas5713_init_command { ++ const int size; ++ const char *const data; ++}; ++ ++static const struct tas5713_init_command tas5713_init_sequence[] = { ++ ++ // Trim oscillator ++ { .size = 2, .data = "\x1B\x00" }, ++ // System control register 1 (0x03): block DC ++ { .size = 2, .data = "\x03\x80" }, ++ // Mute everything ++ { .size = 2, .data = "\x05\x40" }, ++ // Modulation limit register (0x10): 97.7% ++ { .size = 2, .data = "\x10\x02" }, ++ // Interchannel delay registers ++ // (0x11, 0x12, 0x13, and 0x14): BD mode ++ { .size = 2, .data = "\x11\xB8" }, ++ { .size = 2, .data = "\x12\x60" }, ++ { .size = 2, .data = "\x13\xA0" }, ++ { .size = 2, .data = "\x14\x48" }, ++ // PWM shutdown group register (0x19): no shutdown ++ { .size = 2, .data = "\x19\x00" }, ++ // Input multiplexer register (0x20): BD mode ++ { .size = 2, .data = "\x20\x00\x89\x77\x72" }, ++ // PWM output mux register (0x25) ++ // Channel 1 --> OUTA, channel 1 neg --> OUTB ++ // Channel 2 --> OUTC, channel 2 neg --> OUTD ++ { .size = 5, .data = "\x25\x01\x02\x13\x45" }, ++ // DRC control (0x46): DRC off ++ { .size = 5, .data = "\x46\x00\x00\x00\x00" }, ++ // BKND_ERR register (0x1C): 299ms reset period ++ { .size = 2, .data = "\x1C\x07" }, ++ // Mute channel 3 ++ { .size = 2, .data = "\x0A\xFF" }, ++ // Volume configuration register (0x0E): volume slew 512 steps ++ { .size = 2, .data = "\x0E\x90" }, ++ // Clock control register (0x00): 44/48kHz, MCLK=64xfs ++ { .size = 2, .data = "\x00\x60" }, ++ // Bank switch and eq control (0x50): no bank switching ++ { .size = 5, .data = "\x50\x00\x00\x00\x00" }, ++ // Volume registers (0x07, 0x08, 0x09, 0x0A) ++ { .size = 2, .data = "\x07\x20" }, ++ { .size = 2, .data = "\x08\x30" }, ++ { .size = 2, .data = "\x09\x30" }, ++ { .size = 2, .data = "\x0A\xFF" }, ++ // 0x72, 0x73, 0x76, 0x77 input mixer: ++ // no intermix between channels ++ { .size = 5, .data = "\x72\x00\x00\x00\x00" }, ++ { .size = 5, .data = "\x73\x00\x80\x00\x00" }, ++ { .size = 5, .data = "\x76\x00\x00\x00\x00" }, ++ { .size = 5, .data = "\x77\x00\x80\x00\x00" }, ++ // 0x70, 0x71, 0x74, 0x75 inline DRC mixer: ++ // no inline DRC inmix ++ { .size = 5, .data = "\x70\x00\x80\x00\x00" }, ++ { .size = 5, .data = "\x71\x00\x00\x00\x00" }, ++ { .size = 5, .data = "\x74\x00\x80\x00\x00" }, ++ { .size = 5, .data = "\x75\x00\x00\x00\x00" }, ++ // 0x56, 0x57 Output scale ++ { .size = 5, .data = "\x56\x00\x80\x00\x00" }, ++ { .size = 5, .data = "\x57\x00\x02\x00\x00" }, ++ // 0x3B, 0x3c ++ { .size = 9, .data = "\x3B\x00\x08\x00\x00\x00\x78\x00\x00" }, ++ { .size = 9, .data = "\x3C\x00\x00\x01\x00\xFF\xFF\xFF\x00" }, ++ { .size = 9, .data = "\x3E\x00\x08\x00\x00\x00\x78\x00\x00" }, ++ { .size = 9, .data = "\x3F\x00\x00\x01\x00\xFF\xFF\xFF\x00" }, ++ { .size = 9, .data = "\x40\x00\x00\x01\x00\xFF\xFF\xFF\x00" }, ++ { .size = 9, .data = "\x43\x00\x00\x01\x00\xFF\xFF\xFF\x00" }, ++ // 0x51, 0x52: output mixer ++ { .size = 9, .data = "\x51\x00\x80\x00\x00\x00\x00\x00\x00" }, ++ { .size = 9, .data = "\x52\x00\x80\x00\x00\x00\x00\x00\x00" }, ++ // PEQ defaults ++ { .size = 21, .data = "\x29\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x2A\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x2B\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x2C\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x2D\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x2E\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x2F\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x30\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x31\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x32\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x33\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x34\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x35\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x36\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x58\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x59\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x5C\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x5D\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x5E\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x5F\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x5A\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++ { .size = 21, .data = "\x5B\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, ++}; ++ ++ ++#endif /* _TAS5713_H */ diff --git a/target/linux/brcm2708/patches-4.4/0071-Added-support-for-HiFiBerry-DAC.patch b/target/linux/brcm2708/patches-4.4/0071-Added-support-for-HiFiBerry-DAC.patch deleted file mode 100644 index c38b623926..0000000000 --- a/target/linux/brcm2708/patches-4.4/0071-Added-support-for-HiFiBerry-DAC.patch +++ /dev/null @@ -1,190 +0,0 @@ -From debd3d30b1127328b5a1e444bd1550cb45c50362 Mon Sep 17 00:00:00 2001 -From: Daniel Matuschek -Date: Mon, 4 Aug 2014 10:06:56 +0200 -Subject: [PATCH 071/170] 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. ---- - sound/soc/bcm/Kconfig | 7 ++ - sound/soc/bcm/Makefile | 2 + - sound/soc/bcm/hifiberry_dacplus.c | 141 ++++++++++++++++++++++++++++++++++++++ - 3 files changed, 150 insertions(+) - create mode 100644 sound/soc/bcm/hifiberry_dacplus.c - ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -15,6 +15,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_DACPLUS -+ tristate "Support for HifiBerry DAC+" -+ depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S -+ select SND_SOC_PCM512x -+ 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 || SND_BCM2835_SOC_I2S ---- a/sound/soc/bcm/Makefile -+++ b/sound/soc/bcm/Makefile -@@ -5,11 +5,13 @@ obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd - - # BCM2708 Machine Support - snd-soc-hifiberry-dac-objs := hifiberry_dac.o -+snd-soc-hifiberry-dacplus-objs := hifiberry_dacplus.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_DACPLUS) += snd-soc-hifiberry-dacplus.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 ---- /dev/null -+++ b/sound/soc/bcm/hifiberry_dacplus.c -@@ -0,0 +1,141 @@ -+/* -+ * ASoC Driver for HiFiBerry DAC+ -+ * -+ * Author: Daniel Matuschek -+ * Copyright 2014 -+ * based on code by Florian Meier -+ * -+ * 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/pcm512x.h" -+ -+static int snd_rpi_hifiberry_dacplus_init(struct snd_soc_pcm_runtime *rtd) -+{ -+ struct snd_soc_codec *codec = rtd->codec; -+ snd_soc_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08); -+ snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0xf, 0x02); -+ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08,0x08); -+ return 0; -+} -+ -+static int snd_rpi_hifiberry_dacplus_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, 64); -+} -+ -+static int snd_rpi_hifiberry_dacplus_startup(struct snd_pcm_substream *substream) { -+ struct snd_soc_pcm_runtime *rtd = substream->private_data; -+ struct snd_soc_codec *codec = rtd->codec; -+ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08,0x08); -+ return 0; -+} -+ -+static void snd_rpi_hifiberry_dacplus_shutdown(struct snd_pcm_substream *substream) { -+ struct snd_soc_pcm_runtime *rtd = substream->private_data; -+ struct snd_soc_codec *codec = rtd->codec; -+ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08,0x00); -+} -+ -+/* machine stream operations */ -+static struct snd_soc_ops snd_rpi_hifiberry_dacplus_ops = { -+ .hw_params = snd_rpi_hifiberry_dacplus_hw_params, -+ .startup = snd_rpi_hifiberry_dacplus_startup, -+ .shutdown = snd_rpi_hifiberry_dacplus_shutdown, -+}; -+ -+static struct snd_soc_dai_link snd_rpi_hifiberry_dacplus_dai[] = { -+{ -+ .name = "HiFiBerry DAC+", -+ .stream_name = "HiFiBerry DAC+ HiFi", -+ .cpu_dai_name = "bcm2708-i2s.0", -+ .codec_dai_name = "pcm512x-hifi", -+ .platform_name = "bcm2708-i2s.0", -+ .codec_name = "pcm512x.1-004d", -+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | -+ SND_SOC_DAIFMT_CBS_CFS, -+ .ops = &snd_rpi_hifiberry_dacplus_ops, -+ .init = snd_rpi_hifiberry_dacplus_init, -+}, -+}; -+ -+/* audio machine driver */ -+static struct snd_soc_card snd_rpi_hifiberry_dacplus = { -+ .name = "snd_rpi_hifiberry_dacplus", -+ .dai_link = snd_rpi_hifiberry_dacplus_dai, -+ .num_links = ARRAY_SIZE(snd_rpi_hifiberry_dacplus_dai), -+}; -+ -+static int snd_rpi_hifiberry_dacplus_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ -+ snd_rpi_hifiberry_dacplus.dev = &pdev->dev; -+ -+ if (pdev->dev.of_node) { -+ struct device_node *i2s_node; -+ struct snd_soc_dai_link *dai = &snd_rpi_hifiberry_dacplus_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_hifiberry_dacplus); -+ if (ret) -+ dev_err(&pdev->dev, -+ "snd_soc_register_card() failed: %d\n", ret); -+ -+ return ret; -+} -+ -+static int snd_rpi_hifiberry_dacplus_remove(struct platform_device *pdev) -+{ -+ return snd_soc_unregister_card(&snd_rpi_hifiberry_dacplus); -+} -+ -+static const struct of_device_id snd_rpi_hifiberry_dacplus_of_match[] = { -+ { .compatible = "hifiberry,hifiberry-dacplus", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, snd_rpi_hifiberry_dacplus_of_match); -+ -+static struct platform_driver snd_rpi_hifiberry_dacplus_driver = { -+ .driver = { -+ .name = "snd-rpi-hifiberry-dacplus", -+ .owner = THIS_MODULE, -+ .of_match_table = snd_rpi_hifiberry_dacplus_of_match, -+ }, -+ .probe = snd_rpi_hifiberry_dacplus_probe, -+ .remove = snd_rpi_hifiberry_dacplus_remove, -+}; -+ -+module_platform_driver(snd_rpi_hifiberry_dacplus_driver); -+ -+MODULE_AUTHOR("Daniel Matuschek "); -+MODULE_DESCRIPTION("ASoC Driver for HiFiBerry DAC+"); -+MODULE_LICENSE("GPL v2"); diff --git a/target/linux/brcm2708/patches-4.4/0072-Added-driver-for-HiFiBerry-Amp-amplifier-add-on-boar.patch b/target/linux/brcm2708/patches-4.4/0072-Added-driver-for-HiFiBerry-Amp-amplifier-add-on-boar.patch deleted file mode 100644 index b6e6fe9f9b..0000000000 --- a/target/linux/brcm2708/patches-4.4/0072-Added-driver-for-HiFiBerry-Amp-amplifier-add-on-boar.patch +++ /dev/null @@ -1,816 +0,0 @@ -From 9c6f0b1812313d39afeefd700b7f9df9a82a47cd Mon Sep 17 00:00:00 2001 -From: Daniel Matuschek -Date: Mon, 4 Aug 2014 11:09:58 +0200 -Subject: [PATCH 072/170] 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. - -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 -loaded, but won't work. This patch fixes this. I2C communication error will now -reported correctly by a non-zero return code. - -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. ---- - sound/soc/bcm/Kconfig | 7 + - sound/soc/bcm/Makefile | 2 + - sound/soc/bcm/hifiberry_amp.c | 127 +++++++++++++++ - sound/soc/codecs/Kconfig | 4 + - sound/soc/codecs/Makefile | 2 + - sound/soc/codecs/tas5713.c | 369 ++++++++++++++++++++++++++++++++++++++++++ - sound/soc/codecs/tas5713.h | 210 ++++++++++++++++++++++++ - 7 files changed, 721 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 - ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -29,6 +29,13 @@ config SND_BCM2708_SOC_HIFIBERRY_DIGI - help - Say Y or M if you want to add support for HifiBerry Digi S/PDIF output board. - -+config SND_BCM2708_SOC_HIFIBERRY_AMP -+ tristate "Support for the HifiBerry Amp" -+ depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S -+ select SND_SOC_TAS5713 -+ help -+ Say Y or M if you want to add support for the HifiBerry Amp amplifier board. -+ - config SND_BCM2708_SOC_RPI_DAC - tristate "Support for RPi-DAC" - depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S ---- a/sound/soc/bcm/Makefile -+++ b/sound/soc/bcm/Makefile -@@ -7,11 +7,13 @@ obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd - snd-soc-hifiberry-dac-objs := hifiberry_dac.o - 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-iqaudio-dac-objs := iqaudio-dac.o - - obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o - 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_IQAUDIO_DAC) += snd-soc-iqaudio-dac.o ---- /dev/null -+++ b/sound/soc/bcm/hifiberry_amp.c -@@ -0,0 +1,127 @@ -+/* -+ * ASoC Driver for HifiBerry AMP -+ * -+ * Author: Sebastian Eickhoff -+ * Copyright 2014 -+ * -+ * 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_amp_init(struct snd_soc_pcm_runtime *rtd) -+{ -+ // ToDo: init of the dsp-registers. -+ return 0; -+} -+ -+static int snd_rpi_hifiberry_amp_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, 64); -+} -+ -+static struct snd_soc_ops snd_rpi_hifiberry_amp_ops = { -+ .hw_params = snd_rpi_hifiberry_amp_hw_params, -+}; -+ -+static struct snd_soc_dai_link snd_rpi_hifiberry_amp_dai[] = { -+ { -+ .name = "HifiBerry AMP", -+ .stream_name = "HifiBerry AMP HiFi", -+ .cpu_dai_name = "bcm2708-i2s.0", -+ .codec_dai_name = "tas5713-hifi", -+ .platform_name = "bcm2708-i2s.0", -+ .codec_name = "tas5713.1-001b", -+ .dai_fmt = SND_SOC_DAIFMT_I2S | -+ SND_SOC_DAIFMT_NB_NF | -+ SND_SOC_DAIFMT_CBS_CFS, -+ .ops = &snd_rpi_hifiberry_amp_ops, -+ .init = snd_rpi_hifiberry_amp_init, -+ }, -+}; -+ -+ -+static struct snd_soc_card snd_rpi_hifiberry_amp = { -+ .name = "snd_rpi_hifiberry_amp", -+ .dai_link = snd_rpi_hifiberry_amp_dai, -+ .num_links = ARRAY_SIZE(snd_rpi_hifiberry_amp_dai), -+}; -+ -+static const struct of_device_id snd_rpi_hifiberry_amp_of_match[] = { -+ { .compatible = "hifiberry,hifiberry-amp", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, snd_rpi_hifiberry_amp_of_match); -+ -+ -+static int snd_rpi_hifiberry_amp_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ -+ snd_rpi_hifiberry_amp.dev = &pdev->dev; -+ -+ if (pdev->dev.of_node) { -+ struct device_node *i2s_node; -+ struct snd_soc_dai_link *dai = &snd_rpi_hifiberry_amp_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_hifiberry_amp); -+ -+ if (ret != 0) { -+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); -+ } -+ -+ return ret; -+} -+ -+ -+static int snd_rpi_hifiberry_amp_remove(struct platform_device *pdev) -+{ -+ return snd_soc_unregister_card(&snd_rpi_hifiberry_amp); -+} -+ -+ -+static struct platform_driver snd_rpi_hifiberry_amp_driver = { -+ .driver = { -+ .name = "snd-hifiberry-amp", -+ .owner = THIS_MODULE, -+ .of_match_table = snd_rpi_hifiberry_amp_of_match, -+ }, -+ .probe = snd_rpi_hifiberry_amp_probe, -+ .remove = snd_rpi_hifiberry_amp_remove, -+}; -+ -+ -+module_platform_driver(snd_rpi_hifiberry_amp_driver); -+ -+ -+MODULE_AUTHOR("Sebastian Eickhoff "); -+MODULE_DESCRIPTION("ASoC driver for HiFiBerry-AMP"); -+MODULE_LICENSE("GPL v2"); ---- a/sound/soc/codecs/Kconfig -+++ b/sound/soc/codecs/Kconfig -@@ -117,6 +117,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 -+ select SND_SOC_TAS5713 if I2C - select SND_SOC_TLV320AIC26 if SPI_MASTER - select SND_SOC_TLV320AIC31XX if I2C - select SND_SOC_TLV320AIC32X4 if I2C -@@ -674,6 +675,9 @@ config SND_SOC_TFA9879 - tristate "NXP Semiconductors TFA9879 amplifier" - depends on I2C - -+config SND_SOC_TAS5713 -+ tristate -+ - config SND_SOC_TLV320AIC23 - tristate - ---- a/sound/soc/codecs/Makefile -+++ b/sound/soc/codecs/Makefile -@@ -118,6 +118,7 @@ snd-soc-sti-sas-objs := sti-sas.o - snd-soc-tas5086-objs := tas5086.o - snd-soc-tas571x-objs := tas571x.o - snd-soc-tfa9879-objs := tfa9879.o -+snd-soc-tas5713-objs := tas5713.o - snd-soc-tlv320aic23-objs := tlv320aic23.o - snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o - snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o -@@ -312,6 +313,7 @@ obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc - obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o - obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o - obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o -+obj-$(CONFIG_SND_SOC_TAS5713) += snd-soc-tas5713.o - obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o - obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o - obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o ---- /dev/null -+++ b/sound/soc/codecs/tas5713.c -@@ -0,0 +1,369 @@ -+/* -+ * ASoC Driver for TAS5713 -+ * -+ * Author: Sebastian Eickhoff -+ * Copyright 2014 -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include "tas5713.h" -+ -+ -+static struct i2c_client *i2c; -+ -+struct tas5713_priv { -+ struct regmap *regmap; -+ int mclk_div; -+ struct snd_soc_codec *codec; -+}; -+ -+static struct tas5713_priv *priv_data; -+ -+ -+ -+ -+/* -+ * _ _ ___ _ ___ _ _ -+ * /_\ | | / __| /_\ / __|___ _ _| |_ _ _ ___| |___ -+ * / _ \| |__\__ \/ _ \ | (__/ _ \ ' \ _| '_/ _ \ (_-< -+ * /_/ \_\____|___/_/ \_\ \___\___/_||_\__|_| \___/_/__/ -+ * -+ */ -+ -+static const DECLARE_TLV_DB_SCALE(tas5713_vol_tlv, -10000, 50, 1); -+ -+ -+static const struct snd_kcontrol_new tas5713_snd_controls[] = { -+ SOC_SINGLE_TLV ("Master" , TAS5713_VOL_MASTER, 0, 248, 1, tas5713_vol_tlv), -+ SOC_DOUBLE_R_TLV("Channels" , TAS5713_VOL_CH1, TAS5713_VOL_CH2, 0, 248, 1, tas5713_vol_tlv) -+}; -+ -+ -+ -+ -+/* -+ * __ __ _ _ ___ _ -+ * | \/ |__ _ __| |_ (_)_ _ ___ | \ _ _(_)_ _____ _ _ -+ * | |\/| / _` / _| ' \| | ' \/ -_) | |) | '_| \ V / -_) '_| -+ * |_| |_\__,_\__|_||_|_|_||_\___| |___/|_| |_|\_/\___|_| -+ * -+ */ -+ -+static int tas5713_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params, -+ struct snd_soc_dai *dai) -+{ -+ u16 blen = 0x00; -+ -+ struct snd_soc_codec *codec; -+ codec = dai->codec; -+ priv_data->codec = dai->codec; -+ -+ switch (params_format(params)) { -+ case SNDRV_PCM_FORMAT_S16_LE: -+ blen = 0x03; -+ break; -+ case SNDRV_PCM_FORMAT_S20_3LE: -+ blen = 0x1; -+ break; -+ case SNDRV_PCM_FORMAT_S24_LE: -+ blen = 0x04; -+ break; -+ case SNDRV_PCM_FORMAT_S32_LE: -+ blen = 0x05; -+ break; -+ default: -+ dev_err(dai->dev, "Unsupported word length: %u\n", -+ params_format(params)); -+ return -EINVAL; -+ } -+ -+ // set word length -+ snd_soc_update_bits(codec, TAS5713_SERIAL_DATA_INTERFACE, 0x7, blen); -+ -+ return 0; -+} -+ -+ -+static int tas5713_mute_stream(struct snd_soc_dai *dai, int mute, int stream) -+{ -+ unsigned int val = 0; -+ -+ struct tas5713_priv *tas5713; -+ struct snd_soc_codec *codec = dai->codec; -+ tas5713 = snd_soc_codec_get_drvdata(codec); -+ -+ if (mute) { -+ val = TAS5713_SOFT_MUTE_ALL; -+ } -+ -+ return regmap_write(tas5713->regmap, TAS5713_SOFT_MUTE, val); -+} -+ -+ -+static const struct snd_soc_dai_ops tas5713_dai_ops = { -+ .hw_params = tas5713_hw_params, -+ .mute_stream = tas5713_mute_stream, -+}; -+ -+ -+static struct snd_soc_dai_driver tas5713_dai = { -+ .name = "tas5713-hifi", -+ .playback = { -+ .stream_name = "Playback", -+ .channels_min = 2, -+ .channels_max = 2, -+ .rates = SNDRV_PCM_RATE_8000_48000, -+ .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE ), -+ }, -+ .ops = &tas5713_dai_ops, -+}; -+ -+ -+ -+ -+/* -+ * ___ _ ___ _ -+ * / __|___ __| |___ __ | \ _ _(_)_ _____ _ _ -+ * | (__/ _ \/ _` / -_) _| | |) | '_| \ V / -_) '_| -+ * \___\___/\__,_\___\__| |___/|_| |_|\_/\___|_| -+ * -+ */ -+ -+static int tas5713_remove(struct snd_soc_codec *codec) -+{ -+ struct tas5713_priv *tas5713; -+ -+ tas5713 = snd_soc_codec_get_drvdata(codec); -+ -+ return 0; -+} -+ -+ -+static int tas5713_probe(struct snd_soc_codec *codec) -+{ -+ struct tas5713_priv *tas5713; -+ int i, ret; -+ -+ i2c = container_of(codec->dev, struct i2c_client, dev); -+ -+ tas5713 = snd_soc_codec_get_drvdata(codec); -+ -+ // Reset error -+ ret = snd_soc_write(codec, TAS5713_ERROR_STATUS, 0x00); -+ if (ret < 0) return ret; -+ -+ // Trim oscillator -+ ret = snd_soc_write(codec, TAS5713_OSC_TRIM, 0x00); -+ if (ret < 0) return ret; -+ msleep(1000); -+ -+ // Reset error -+ ret = snd_soc_write(codec, TAS5713_ERROR_STATUS, 0x00); -+ if (ret < 0) return ret; -+ -+ // Clock mode: 44/48kHz, MCLK=64xfs -+ ret = snd_soc_write(codec, TAS5713_CLOCK_CTRL, 0x60); -+ if (ret < 0) return ret; -+ -+ // I2S 24bit -+ ret = snd_soc_write(codec, TAS5713_SERIAL_DATA_INTERFACE, 0x05); -+ if (ret < 0) return ret; -+ -+ // Unmute -+ ret = snd_soc_write(codec, TAS5713_SYSTEM_CTRL2, 0x00); -+ if (ret < 0) return ret; -+ ret = snd_soc_write(codec, TAS5713_SOFT_MUTE, 0x00); -+ if (ret < 0) return ret; -+ -+ // Set volume to 0db -+ ret = snd_soc_write(codec, TAS5713_VOL_MASTER, 0x00); -+ if (ret < 0) return ret; -+ -+ // Now start programming the default initialization sequence -+ for (i = 0; i < ARRAY_SIZE(tas5713_init_sequence); ++i) { -+ ret = i2c_master_send(i2c, -+ tas5713_init_sequence[i].data, -+ tas5713_init_sequence[i].size); -+ if (ret < 0) { -+ printk(KERN_INFO "TAS5713 CODEC PROBE: InitSeq returns: %d\n", ret); -+ } -+ } -+ -+ // Unmute -+ ret = snd_soc_write(codec, TAS5713_SYSTEM_CTRL2, 0x00); -+ if (ret < 0) return ret; -+ -+ return 0; -+} -+ -+ -+static struct snd_soc_codec_driver soc_codec_dev_tas5713 = { -+ .probe = tas5713_probe, -+ .remove = tas5713_remove, -+ .controls = tas5713_snd_controls, -+ .num_controls = ARRAY_SIZE(tas5713_snd_controls), -+}; -+ -+ -+ -+ -+/* -+ * ___ ___ ___ ___ _ -+ * |_ _|_ ) __| | \ _ _(_)_ _____ _ _ -+ * | | / / (__ | |) | '_| \ V / -_) '_| -+ * |___/___\___| |___/|_| |_|\_/\___|_| -+ * -+ */ -+ -+static const struct reg_default tas5713_reg_defaults[] = { -+ { 0x07 ,0x80 }, // R7 - VOL_MASTER - -40dB -+ { 0x08 , 30 }, // R8 - VOL_CH1 - 0dB -+ { 0x09 , 30 }, // R9 - VOL_CH2 - 0dB -+ { 0x0A ,0x80 }, // R10 - VOL_HEADPHONE - -40dB -+}; -+ -+ -+static bool tas5713_reg_volatile(struct device *dev, unsigned int reg) -+{ -+ switch (reg) { -+ case TAS5713_DEVICE_ID: -+ case TAS5713_ERROR_STATUS: -+ return true; -+ default: -+ return false; -+ } -+} -+ -+ -+static const struct of_device_id tas5713_of_match[] = { -+ { .compatible = "ti,tas5713", }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, tas5713_of_match); -+ -+ -+static struct regmap_config tas5713_regmap_config = { -+ .reg_bits = 8, -+ .val_bits = 8, -+ -+ .max_register = TAS5713_MAX_REGISTER, -+ .volatile_reg = tas5713_reg_volatile, -+ -+ .cache_type = REGCACHE_RBTREE, -+ .reg_defaults = tas5713_reg_defaults, -+ .num_reg_defaults = ARRAY_SIZE(tas5713_reg_defaults), -+}; -+ -+ -+static int tas5713_i2c_probe(struct i2c_client *i2c, -+ const struct i2c_device_id *id) -+{ -+ int ret; -+ -+ priv_data = devm_kzalloc(&i2c->dev, sizeof *priv_data, GFP_KERNEL); -+ if (!priv_data) -+ return -ENOMEM; -+ -+ priv_data->regmap = devm_regmap_init_i2c(i2c, &tas5713_regmap_config); -+ if (IS_ERR(priv_data->regmap)) { -+ ret = PTR_ERR(priv_data->regmap); -+ return ret; -+ } -+ -+ i2c_set_clientdata(i2c, priv_data); -+ -+ ret = snd_soc_register_codec(&i2c->dev, -+ &soc_codec_dev_tas5713, &tas5713_dai, 1); -+ -+ return ret; -+} -+ -+ -+static int tas5713_i2c_remove(struct i2c_client *i2c) -+{ -+ snd_soc_unregister_codec(&i2c->dev); -+ i2c_set_clientdata(i2c, NULL); -+ -+ kfree(priv_data); -+ -+ return 0; -+} -+ -+ -+static const struct i2c_device_id tas5713_i2c_id[] = { -+ { "tas5713", 0 }, -+ { } -+}; -+ -+MODULE_DEVICE_TABLE(i2c, tas5713_i2c_id); -+ -+ -+static struct i2c_driver tas5713_i2c_driver = { -+ .driver = { -+ .name = "tas5713", -+ .owner = THIS_MODULE, -+ .of_match_table = tas5713_of_match, -+ }, -+ .probe = tas5713_i2c_probe, -+ .remove = tas5713_i2c_remove, -+ .id_table = tas5713_i2c_id -+}; -+ -+ -+static int __init tas5713_modinit(void) -+{ -+ int ret = 0; -+ -+ ret = i2c_add_driver(&tas5713_i2c_driver); -+ if (ret) { -+ printk(KERN_ERR "Failed to register tas5713 I2C driver: %d\n", -+ ret); -+ } -+ -+ return ret; -+} -+module_init(tas5713_modinit); -+ -+ -+static void __exit tas5713_exit(void) -+{ -+ i2c_del_driver(&tas5713_i2c_driver); -+} -+module_exit(tas5713_exit); -+ -+ -+MODULE_AUTHOR("Sebastian Eickhoff "); -+MODULE_DESCRIPTION("ASoC driver for TAS5713"); -+MODULE_LICENSE("GPL v2"); ---- /dev/null -+++ b/sound/soc/codecs/tas5713.h -@@ -0,0 +1,210 @@ -+/* -+ * ASoC Driver for TAS5713 -+ * -+ * Author: Sebastian Eickhoff -+ * Copyright 2014 -+ * -+ * 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. -+ */ -+ -+#ifndef _TAS5713_H -+#define _TAS5713_H -+ -+ -+// TAS5713 I2C-bus register addresses -+ -+#define TAS5713_CLOCK_CTRL 0x00 -+#define TAS5713_DEVICE_ID 0x01 -+#define TAS5713_ERROR_STATUS 0x02 -+#define TAS5713_SYSTEM_CTRL1 0x03 -+#define TAS5713_SERIAL_DATA_INTERFACE 0x04 -+#define TAS5713_SYSTEM_CTRL2 0x05 -+#define TAS5713_SOFT_MUTE 0x06 -+#define TAS5713_VOL_MASTER 0x07 -+#define TAS5713_VOL_CH1 0x08 -+#define TAS5713_VOL_CH2 0x09 -+#define TAS5713_VOL_HEADPHONE 0x0A -+#define TAS5713_VOL_CONFIG 0x0E -+#define TAS5713_MODULATION_LIMIT 0x10 -+#define TAS5713_IC_DLY_CH1 0x11 -+#define TAS5713_IC_DLY_CH2 0x12 -+#define TAS5713_IC_DLY_CH3 0x13 -+#define TAS5713_IC_DLY_CH4 0x14 -+ -+#define TAS5713_START_STOP_PERIOD 0x1A -+#define TAS5713_OSC_TRIM 0x1B -+#define TAS5713_BKND_ERR 0x1C -+ -+#define TAS5713_INPUT_MUX 0x20 -+#define TAS5713_SRC_SELECT_CH4 0x21 -+#define TAS5713_PWM_MUX 0x25 -+ -+#define TAS5713_CH1_BQ0 0x29 -+#define TAS5713_CH1_BQ1 0x2A -+#define TAS5713_CH1_BQ2 0x2B -+#define TAS5713_CH1_BQ3 0x2C -+#define TAS5713_CH1_BQ4 0x2D -+#define TAS5713_CH1_BQ5 0x2E -+#define TAS5713_CH1_BQ6 0x2F -+#define TAS5713_CH1_BQ7 0x58 -+#define TAS5713_CH1_BQ8 0x59 -+ -+#define TAS5713_CH2_BQ0 0x30 -+#define TAS5713_CH2_BQ1 0x31 -+#define TAS5713_CH2_BQ2 0x32 -+#define TAS5713_CH2_BQ3 0x33 -+#define TAS5713_CH2_BQ4 0x34 -+#define TAS5713_CH2_BQ5 0x35 -+#define TAS5713_CH2_BQ6 0x36 -+#define TAS5713_CH2_BQ7 0x5C -+#define TAS5713_CH2_BQ8 0x5D -+ -+#define TAS5713_CH4_BQ0 0x5A -+#define TAS5713_CH4_BQ1 0x5B -+#define TAS5713_CH3_BQ0 0x5E -+#define TAS5713_CH3_BQ1 0x5F -+ -+#define TAS5713_DRC1_SOFTENING_FILTER_ALPHA_OMEGA 0x3B -+#define TAS5713_DRC1_ATTACK_RELEASE_RATE 0x3C -+#define TAS5713_DRC2_SOFTENING_FILTER_ALPHA_OMEGA 0x3E -+#define TAS5713_DRC2_ATTACK_RELEASE_RATE 0x3F -+#define TAS5713_DRC1_ATTACK_RELEASE_THRES 0x40 -+#define TAS5713_DRC2_ATTACK_RELEASE_THRES 0x43 -+#define TAS5713_DRC_CTRL 0x46 -+ -+#define TAS5713_BANK_SW_CTRL 0x50 -+#define TAS5713_CH1_OUTPUT_MIXER 0x51 -+#define TAS5713_CH2_OUTPUT_MIXER 0x52 -+#define TAS5713_CH1_INPUT_MIXER 0x53 -+#define TAS5713_CH2_INPUT_MIXER 0x54 -+#define TAS5713_OUTPUT_POST_SCALE 0x56 -+#define TAS5713_OUTPUT_PRESCALE 0x57 -+ -+#define TAS5713_IDF_POST_SCALE 0x62 -+ -+#define TAS5713_CH1_INLINE_MIXER 0x70 -+#define TAS5713_CH1_INLINE_DRC_EN_MIXER 0x71 -+#define TAS5713_CH1_R_CHANNEL_MIXER 0x72 -+#define TAS5713_CH1_L_CHANNEL_MIXER 0x73 -+#define TAS5713_CH2_INLINE_MIXER 0x74 -+#define TAS5713_CH2_INLINE_DRC_EN_MIXER 0x75 -+#define TAS5713_CH2_L_CHANNEL_MIXER 0x76 -+#define TAS5713_CH2_R_CHANNEL_MIXER 0x77 -+ -+#define TAS5713_UPDATE_DEV_ADDR_KEY 0xF8 -+#define TAS5713_UPDATE_DEV_ADDR_REG 0xF9 -+ -+#define TAS5713_REGISTER_COUNT 0x46 -+#define TAS5713_MAX_REGISTER 0xF9 -+ -+ -+// Bitmasks for registers -+#define TAS5713_SOFT_MUTE_ALL 0x07 -+ -+ -+ -+struct tas5713_init_command { -+ const int size; -+ const char *const data; -+}; -+ -+static const struct tas5713_init_command tas5713_init_sequence[] = { -+ -+ // Trim oscillator -+ { .size = 2, .data = "\x1B\x00" }, -+ // System control register 1 (0x03): block DC -+ { .size = 2, .data = "\x03\x80" }, -+ // Mute everything -+ { .size = 2, .data = "\x05\x40" }, -+ // Modulation limit register (0x10): 97.7% -+ { .size = 2, .data = "\x10\x02" }, -+ // Interchannel delay registers -+ // (0x11, 0x12, 0x13, and 0x14): BD mode -+ { .size = 2, .data = "\x11\xB8" }, -+ { .size = 2, .data = "\x12\x60" }, -+ { .size = 2, .data = "\x13\xA0" }, -+ { .size = 2, .data = "\x14\x48" }, -+ // PWM shutdown group register (0x19): no shutdown -+ { .size = 2, .data = "\x19\x00" }, -+ // Input multiplexer register (0x20): BD mode -+ { .size = 2, .data = "\x20\x00\x89\x77\x72" }, -+ // PWM output mux register (0x25) -+ // Channel 1 --> OUTA, channel 1 neg --> OUTB -+ // Channel 2 --> OUTC, channel 2 neg --> OUTD -+ { .size = 5, .data = "\x25\x01\x02\x13\x45" }, -+ // DRC control (0x46): DRC off -+ { .size = 5, .data = "\x46\x00\x00\x00\x00" }, -+ // BKND_ERR register (0x1C): 299ms reset period -+ { .size = 2, .data = "\x1C\x07" }, -+ // Mute channel 3 -+ { .size = 2, .data = "\x0A\xFF" }, -+ // Volume configuration register (0x0E): volume slew 512 steps -+ { .size = 2, .data = "\x0E\x90" }, -+ // Clock control register (0x00): 44/48kHz, MCLK=64xfs -+ { .size = 2, .data = "\x00\x60" }, -+ // Bank switch and eq control (0x50): no bank switching -+ { .size = 5, .data = "\x50\x00\x00\x00\x00" }, -+ // Volume registers (0x07, 0x08, 0x09, 0x0A) -+ { .size = 2, .data = "\x07\x20" }, -+ { .size = 2, .data = "\x08\x30" }, -+ { .size = 2, .data = "\x09\x30" }, -+ { .size = 2, .data = "\x0A\xFF" }, -+ // 0x72, 0x73, 0x76, 0x77 input mixer: -+ // no intermix between channels -+ { .size = 5, .data = "\x72\x00\x00\x00\x00" }, -+ { .size = 5, .data = "\x73\x00\x80\x00\x00" }, -+ { .size = 5, .data = "\x76\x00\x00\x00\x00" }, -+ { .size = 5, .data = "\x77\x00\x80\x00\x00" }, -+ // 0x70, 0x71, 0x74, 0x75 inline DRC mixer: -+ // no inline DRC inmix -+ { .size = 5, .data = "\x70\x00\x80\x00\x00" }, -+ { .size = 5, .data = "\x71\x00\x00\x00\x00" }, -+ { .size = 5, .data = "\x74\x00\x80\x00\x00" }, -+ { .size = 5, .data = "\x75\x00\x00\x00\x00" }, -+ // 0x56, 0x57 Output scale -+ { .size = 5, .data = "\x56\x00\x80\x00\x00" }, -+ { .size = 5, .data = "\x57\x00\x02\x00\x00" }, -+ // 0x3B, 0x3c -+ { .size = 9, .data = "\x3B\x00\x08\x00\x00\x00\x78\x00\x00" }, -+ { .size = 9, .data = "\x3C\x00\x00\x01\x00\xFF\xFF\xFF\x00" }, -+ { .size = 9, .data = "\x3E\x00\x08\x00\x00\x00\x78\x00\x00" }, -+ { .size = 9, .data = "\x3F\x00\x00\x01\x00\xFF\xFF\xFF\x00" }, -+ { .size = 9, .data = "\x40\x00\x00\x01\x00\xFF\xFF\xFF\x00" }, -+ { .size = 9, .data = "\x43\x00\x00\x01\x00\xFF\xFF\xFF\x00" }, -+ // 0x51, 0x52: output mixer -+ { .size = 9, .data = "\x51\x00\x80\x00\x00\x00\x00\x00\x00" }, -+ { .size = 9, .data = "\x52\x00\x80\x00\x00\x00\x00\x00\x00" }, -+ // PEQ defaults -+ { .size = 21, .data = "\x29\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x2A\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x2B\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x2C\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x2D\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x2E\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x2F\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x30\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x31\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x32\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x33\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x34\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x35\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x36\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x58\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x59\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x5C\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x5D\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x5E\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x5F\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x5A\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+ { .size = 21, .data = "\x5B\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }, -+}; -+ -+ -+#endif /* _TAS5713_H */ diff --git a/target/linux/brcm2708/patches-4.4/0072-Update-ds1307-driver-for-device-tree-support.patch b/target/linux/brcm2708/patches-4.4/0072-Update-ds1307-driver-for-device-tree-support.patch new file mode 100644 index 0000000000..9499635520 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0072-Update-ds1307-driver-for-device-tree-support.patch @@ -0,0 +1,27 @@ +From d0bfff366e12c9b401ac7eafabab7337e82549ea Mon Sep 17 00:00:00 2001 +From: Ryan Coe +Date: Sat, 31 Jan 2015 18:25:49 -0700 +Subject: [PATCH 072/232] Update ds1307 driver for device-tree support + +Signed-off-by: Ryan Coe +--- + drivers/rtc/rtc-ds1307.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/drivers/rtc/rtc-ds1307.c ++++ b/drivers/rtc/rtc-ds1307.c +@@ -1207,6 +1207,14 @@ static int ds1307_remove(struct i2c_clie + return 0; + } + ++#ifdef CONFIG_OF ++static const struct of_device_id ds1307_of_match[] = { ++ { .compatible = "maxim,ds1307" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, ds1307_of_match); ++#endif ++ + static struct i2c_driver ds1307_driver = { + .driver = { + .name = "rtc-ds1307", diff --git a/target/linux/brcm2708/patches-4.4/0073-BCM270x_DT-Add-pwr_led-and-the-required-input-trigge.patch b/target/linux/brcm2708/patches-4.4/0073-BCM270x_DT-Add-pwr_led-and-the-required-input-trigge.patch new file mode 100644 index 0000000000..3a26865941 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0073-BCM270x_DT-Add-pwr_led-and-the-required-input-trigge.patch @@ -0,0 +1,170 @@ +From 96aac93fdea2caa77d4746aa001c2d792b314112 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 6 Feb 2015 13:50:57 +0000 +Subject: [PATCH 073/232] 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. + +leds-gpio: Implement the brightness_get method + +The power LED uses some clever logic that means it is driven +by a voltage measuring circuit when configured as input, otherwise +it is driven by the GPIO output value. This patch wires up the +brightness_get method for leds-gpio so that user-space can monitor +the LED value via /sys/class/gpio/led1/brightness. Using the input +trigger this returns an indication of the system power health, +otherwise it is just whatever value the trigger has written most +recently. + +See: https://github.com/raspberrypi/linux/issues/1064 +--- + drivers/leds/leds-gpio.c | 18 +++++++++++- + drivers/leds/trigger/Kconfig | 7 +++++ + drivers/leds/trigger/Makefile | 1 + + drivers/leds/trigger/ledtrig-input.c | 54 ++++++++++++++++++++++++++++++++++++ + include/linux/leds.h | 3 ++ + 5 files changed, 82 insertions(+), 1 deletion(-) + create mode 100644 drivers/leds/trigger/ledtrig-input.c + +--- a/drivers/leds/leds-gpio.c ++++ b/drivers/leds/leds-gpio.c +@@ -42,6 +42,13 @@ static void gpio_led_work(struct work_st + led_dat->platform_gpio_blink_set(led_dat->gpiod, + led_dat->new_level, NULL, NULL); + led_dat->blinking = 0; ++ } else if (led_dat->cdev.flags & SET_GPIO_INPUT) { ++ gpiod_direction_input(led_dat->gpiod); ++ led_dat->cdev.flags &= ~SET_GPIO_INPUT; ++ } ++ else if (led_dat->cdev.flags & SET_GPIO_OUTPUT) { ++ gpiod_direction_output(led_dat->gpiod, led_dat->new_level); ++ led_dat->cdev.flags &= ~SET_GPIO_OUTPUT; + } else + gpiod_set_value_cansleep(led_dat->gpiod, led_dat->new_level); + } +@@ -62,7 +69,8 @@ static void gpio_led_set(struct led_clas + * seem to have a reliable way to know if we're already in one; so + * let's just assume the worst. + */ +- if (led_dat->can_sleep) { ++ if (led_dat->can_sleep || ++ (led_dat->cdev.flags & (SET_GPIO_INPUT | SET_GPIO_OUTPUT) )) { + led_dat->new_level = level; + schedule_work(&led_dat->work); + } else { +@@ -75,6 +83,13 @@ static void gpio_led_set(struct led_clas + } + } + ++static enum led_brightness gpio_led_get(struct led_classdev *led_cdev) ++{ ++ struct gpio_led_data *led_dat = ++ container_of(led_cdev, struct gpio_led_data, cdev); ++ return gpiod_get_value_cansleep(led_dat->gpiod) ? LED_FULL : LED_OFF; ++} ++ + static int gpio_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, unsigned long *delay_off) + { +@@ -131,6 +146,7 @@ static int create_gpio_led(const struct + led_dat->cdev.blink_set = gpio_blink_set; + } + led_dat->cdev.brightness_set = gpio_led_set; ++ led_dat->cdev.brightness_get = gpio_led_get; + if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) + state = !!gpiod_get_value_cansleep(led_dat->gpiod); + else +--- a/drivers/leds/trigger/Kconfig ++++ b/drivers/leds/trigger/Kconfig +@@ -126,4 +126,11 @@ config LEDS_TRIGGER_USBDEV + This allows LEDs to be controlled by the presence/activity of + an USB device. If unsure, say N. + ++config LEDS_TRIGGER_INPUT ++ tristate "LED Input Trigger" ++ depends on LEDS_TRIGGERS ++ help ++ This allows the GPIOs assigned to be LEDs to be initialised to inputs. ++ If unsure, say Y. ++ + endif # LEDS_TRIGGERS +--- a/drivers/leds/trigger/Makefile ++++ b/drivers/leds/trigger/Makefile +@@ -8,3 +8,4 @@ obj-$(CONFIG_LEDS_TRIGGER_CPU) += ledtr + obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o + obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o + obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o ++obj-$(CONFIG_LEDS_TRIGGER_INPUT) += ledtrig-input.o +--- /dev/null ++++ b/drivers/leds/trigger/ledtrig-input.c +@@ -0,0 +1,54 @@ ++/* ++ * Set LED GPIO to Input "Trigger" ++ * ++ * Copyright 2015 Phil Elwell ++ * ++ * Based on Nick Forbes's ledtrig-default-on.c. ++ * ++ * 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 "../leds.h" ++ ++static void input_trig_activate(struct led_classdev *led_cdev) ++{ ++ led_cdev->flags |= SET_GPIO_INPUT; ++ led_set_brightness_async(led_cdev, 0); ++} ++ ++static void input_trig_deactivate(struct led_classdev *led_cdev) ++{ ++ led_cdev->flags |= SET_GPIO_OUTPUT; ++ led_set_brightness_async(led_cdev, 0); ++} ++ ++static struct led_trigger input_led_trigger = { ++ .name = "input", ++ .activate = input_trig_activate, ++ .deactivate = input_trig_deactivate, ++}; ++ ++static int __init input_trig_init(void) ++{ ++ return led_trigger_register(&input_led_trigger); ++} ++ ++static void __exit input_trig_exit(void) ++{ ++ led_trigger_unregister(&input_led_trigger); ++} ++ ++module_init(input_trig_init); ++module_exit(input_trig_exit); ++ ++MODULE_AUTHOR("Phil Elwell "); ++MODULE_DESCRIPTION("Set LED GPIO to Input \"trigger\""); ++MODULE_LICENSE("GPL"); +--- a/include/linux/leds.h ++++ b/include/linux/leds.h +@@ -48,6 +48,9 @@ struct led_classdev { + #define SET_BRIGHTNESS_ASYNC (1 << 21) + #define SET_BRIGHTNESS_SYNC (1 << 22) + #define LED_DEV_CAP_FLASH (1 << 23) ++ /* Additions for Raspberry Pi PWR LED */ ++#define SET_GPIO_INPUT (1 << 30) ++#define SET_GPIO_OUTPUT (1 << 31) + + /* Set LED brightness level */ + /* Must not sleep, use a workqueue if needed */ diff --git a/target/linux/brcm2708/patches-4.4/0073-Update-ds1307-driver-for-device-tree-support.patch b/target/linux/brcm2708/patches-4.4/0073-Update-ds1307-driver-for-device-tree-support.patch deleted file mode 100644 index 73afc0975b..0000000000 --- a/target/linux/brcm2708/patches-4.4/0073-Update-ds1307-driver-for-device-tree-support.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 21d08f232ffc350b2930f761f3da26f9867b1d4d Mon Sep 17 00:00:00 2001 -From: Ryan Coe -Date: Sat, 31 Jan 2015 18:25:49 -0700 -Subject: [PATCH 073/170] Update ds1307 driver for device-tree support - -Signed-off-by: Ryan Coe ---- - drivers/rtc/rtc-ds1307.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - ---- a/drivers/rtc/rtc-ds1307.c -+++ b/drivers/rtc/rtc-ds1307.c -@@ -1207,6 +1207,14 @@ static int ds1307_remove(struct i2c_clie - return 0; - } - -+#ifdef CONFIG_OF -+static const struct of_device_id ds1307_of_match[] = { -+ { .compatible = "maxim,ds1307" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, ds1307_of_match); -+#endif -+ - static struct i2c_driver ds1307_driver = { - .driver = { - .name = "rtc-ds1307", diff --git a/target/linux/brcm2708/patches-4.4/0074-BCM270x_DT-Add-pwr_led-and-the-required-input-trigge.patch b/target/linux/brcm2708/patches-4.4/0074-BCM270x_DT-Add-pwr_led-and-the-required-input-trigge.patch deleted file mode 100644 index 2c5fe9907f..0000000000 --- a/target/linux/brcm2708/patches-4.4/0074-BCM270x_DT-Add-pwr_led-and-the-required-input-trigge.patch +++ /dev/null @@ -1,170 +0,0 @@ -From a2b52a3af1314e2b691d9518afb3051193758671 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Fri, 6 Feb 2015 13:50:57 +0000 -Subject: [PATCH 074/170] 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. - -leds-gpio: Implement the brightness_get method - -The power LED uses some clever logic that means it is driven -by a voltage measuring circuit when configured as input, otherwise -it is driven by the GPIO output value. This patch wires up the -brightness_get method for leds-gpio so that user-space can monitor -the LED value via /sys/class/gpio/led1/brightness. Using the input -trigger this returns an indication of the system power health, -otherwise it is just whatever value the trigger has written most -recently. - -See: https://github.com/raspberrypi/linux/issues/1064 ---- - drivers/leds/leds-gpio.c | 18 +++++++++++- - drivers/leds/trigger/Kconfig | 7 +++++ - drivers/leds/trigger/Makefile | 1 + - drivers/leds/trigger/ledtrig-input.c | 54 ++++++++++++++++++++++++++++++++++++ - include/linux/leds.h | 3 ++ - 5 files changed, 82 insertions(+), 1 deletion(-) - create mode 100644 drivers/leds/trigger/ledtrig-input.c - ---- a/drivers/leds/leds-gpio.c -+++ b/drivers/leds/leds-gpio.c -@@ -42,6 +42,13 @@ static void gpio_led_work(struct work_st - led_dat->platform_gpio_blink_set(led_dat->gpiod, - led_dat->new_level, NULL, NULL); - led_dat->blinking = 0; -+ } else if (led_dat->cdev.flags & SET_GPIO_INPUT) { -+ gpiod_direction_input(led_dat->gpiod); -+ led_dat->cdev.flags &= ~SET_GPIO_INPUT; -+ } -+ else if (led_dat->cdev.flags & SET_GPIO_OUTPUT) { -+ gpiod_direction_output(led_dat->gpiod, led_dat->new_level); -+ led_dat->cdev.flags &= ~SET_GPIO_OUTPUT; - } else - gpiod_set_value_cansleep(led_dat->gpiod, led_dat->new_level); - } -@@ -62,7 +69,8 @@ static void gpio_led_set(struct led_clas - * seem to have a reliable way to know if we're already in one; so - * let's just assume the worst. - */ -- if (led_dat->can_sleep) { -+ if (led_dat->can_sleep || -+ (led_dat->cdev.flags & (SET_GPIO_INPUT | SET_GPIO_OUTPUT) )) { - led_dat->new_level = level; - schedule_work(&led_dat->work); - } else { -@@ -75,6 +83,13 @@ static void gpio_led_set(struct led_clas - } - } - -+static enum led_brightness gpio_led_get(struct led_classdev *led_cdev) -+{ -+ struct gpio_led_data *led_dat = -+ container_of(led_cdev, struct gpio_led_data, cdev); -+ return gpiod_get_value_cansleep(led_dat->gpiod) ? LED_FULL : LED_OFF; -+} -+ - static int gpio_blink_set(struct led_classdev *led_cdev, - unsigned long *delay_on, unsigned long *delay_off) - { -@@ -131,6 +146,7 @@ static int create_gpio_led(const struct - led_dat->cdev.blink_set = gpio_blink_set; - } - led_dat->cdev.brightness_set = gpio_led_set; -+ led_dat->cdev.brightness_get = gpio_led_get; - if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) - state = !!gpiod_get_value_cansleep(led_dat->gpiod); - else ---- a/drivers/leds/trigger/Kconfig -+++ b/drivers/leds/trigger/Kconfig -@@ -126,4 +126,11 @@ config LEDS_TRIGGER_USBDEV - This allows LEDs to be controlled by the presence/activity of - an USB device. If unsure, say N. - -+config LEDS_TRIGGER_INPUT -+ tristate "LED Input Trigger" -+ depends on LEDS_TRIGGERS -+ help -+ This allows the GPIOs assigned to be LEDs to be initialised to inputs. -+ If unsure, say Y. -+ - endif # LEDS_TRIGGERS ---- a/drivers/leds/trigger/Makefile -+++ b/drivers/leds/trigger/Makefile -@@ -8,3 +8,4 @@ obj-$(CONFIG_LEDS_TRIGGER_CPU) += ledtr - obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o - obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o - obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o -+obj-$(CONFIG_LEDS_TRIGGER_INPUT) += ledtrig-input.o ---- /dev/null -+++ b/drivers/leds/trigger/ledtrig-input.c -@@ -0,0 +1,54 @@ -+/* -+ * Set LED GPIO to Input "Trigger" -+ * -+ * Copyright 2015 Phil Elwell -+ * -+ * Based on Nick Forbes's ledtrig-default-on.c. -+ * -+ * 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 "../leds.h" -+ -+static void input_trig_activate(struct led_classdev *led_cdev) -+{ -+ led_cdev->flags |= SET_GPIO_INPUT; -+ led_set_brightness_async(led_cdev, 0); -+} -+ -+static void input_trig_deactivate(struct led_classdev *led_cdev) -+{ -+ led_cdev->flags |= SET_GPIO_OUTPUT; -+ led_set_brightness_async(led_cdev, 0); -+} -+ -+static struct led_trigger input_led_trigger = { -+ .name = "input", -+ .activate = input_trig_activate, -+ .deactivate = input_trig_deactivate, -+}; -+ -+static int __init input_trig_init(void) -+{ -+ return led_trigger_register(&input_led_trigger); -+} -+ -+static void __exit input_trig_exit(void) -+{ -+ led_trigger_unregister(&input_led_trigger); -+} -+ -+module_init(input_trig_init); -+module_exit(input_trig_exit); -+ -+MODULE_AUTHOR("Phil Elwell "); -+MODULE_DESCRIPTION("Set LED GPIO to Input \"trigger\""); -+MODULE_LICENSE("GPL"); ---- a/include/linux/leds.h -+++ b/include/linux/leds.h -@@ -48,6 +48,9 @@ struct led_classdev { - #define SET_BRIGHTNESS_ASYNC (1 << 21) - #define SET_BRIGHTNESS_SYNC (1 << 22) - #define LED_DEV_CAP_FLASH (1 << 23) -+ /* Additions for Raspberry Pi PWR LED */ -+#define SET_GPIO_INPUT (1 << 30) -+#define SET_GPIO_OUTPUT (1 << 31) - - /* Set LED brightness level */ - /* Must not sleep, use a workqueue if needed */ diff --git a/target/linux/brcm2708/patches-4.4/0074-enc28j60-Add-device-tree-compatible-string-and-an-ov.patch b/target/linux/brcm2708/patches-4.4/0074-enc28j60-Add-device-tree-compatible-string-and-an-ov.patch new file mode 100644 index 0000000000..4b7ae74473 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0074-enc28j60-Add-device-tree-compatible-string-and-an-ov.patch @@ -0,0 +1,29 @@ +From d3a1dcc1ce28233707c395b11cd2403ba22aebda Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 27 Feb 2015 15:10:24 +0000 +Subject: [PATCH 074/232] enc28j60: Add device tree compatible string and an + overlay + +--- + drivers/net/ethernet/microchip/enc28j60.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/net/ethernet/microchip/enc28j60.c ++++ b/drivers/net/ethernet/microchip/enc28j60.c +@@ -1630,9 +1630,16 @@ static int enc28j60_remove(struct spi_de + return 0; + } + ++static const struct of_device_id enc28j60_of_match[] = { ++ { .compatible = "microchip,enc28j60", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, enc28j60_of_match); ++ + static struct spi_driver enc28j60_driver = { + .driver = { + .name = DRV_NAME, ++ .of_match_table = enc28j60_of_match, + }, + .probe = enc28j60_probe, + .remove = enc28j60_remove, diff --git a/target/linux/brcm2708/patches-4.4/0075-Add-driver-for-rpi-proto.patch b/target/linux/brcm2708/patches-4.4/0075-Add-driver-for-rpi-proto.patch new file mode 100644 index 0000000000..f99b5e9c7c --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0075-Add-driver-for-rpi-proto.patch @@ -0,0 +1,210 @@ +From a058f705de89cb54126d9bf8d099acb7ececb6e7 Mon Sep 17 00:00:00 2001 +From: Waldemar Brodkorb +Date: Wed, 25 Mar 2015 09:26:17 +0100 +Subject: [PATCH 075/232] 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 +--- + sound/soc/bcm/Kconfig | 7 +++ + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/rpi-proto.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 162 insertions(+) + create mode 100644 sound/soc/bcm/rpi-proto.c + +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -43,6 +43,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 || SND_BCM2835_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 || SND_BCM2835_SOC_I2S +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -9,6 +9,7 @@ snd-soc-hifiberry-dacplus-objs := hifibe + 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 +@@ -16,4 +17,5 @@ obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_D + 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 +--- /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"); diff --git a/target/linux/brcm2708/patches-4.4/0075-enc28j60-Add-device-tree-compatible-string-and-an-ov.patch b/target/linux/brcm2708/patches-4.4/0075-enc28j60-Add-device-tree-compatible-string-and-an-ov.patch deleted file mode 100644 index 10ef683cbb..0000000000 --- a/target/linux/brcm2708/patches-4.4/0075-enc28j60-Add-device-tree-compatible-string-and-an-ov.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 12ad9b7c670220b1b8b80ebf507651d106cb28c0 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Fri, 27 Feb 2015 15:10:24 +0000 -Subject: [PATCH 075/170] enc28j60: Add device tree compatible string and an - overlay - ---- - drivers/net/ethernet/microchip/enc28j60.c | 7 +++++++ - 1 file changed, 7 insertions(+) - ---- a/drivers/net/ethernet/microchip/enc28j60.c -+++ b/drivers/net/ethernet/microchip/enc28j60.c -@@ -1630,9 +1630,16 @@ static int enc28j60_remove(struct spi_de - return 0; - } - -+static const struct of_device_id enc28j60_of_match[] = { -+ { .compatible = "microchip,enc28j60", }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(of, enc28j60_of_match); -+ - static struct spi_driver enc28j60_driver = { - .driver = { - .name = DRV_NAME, -+ .of_match_table = enc28j60_of_match, - }, - .probe = enc28j60_probe, - .remove = enc28j60_remove, diff --git a/target/linux/brcm2708/patches-4.4/0076-Add-driver-for-rpi-proto.patch b/target/linux/brcm2708/patches-4.4/0076-Add-driver-for-rpi-proto.patch deleted file mode 100644 index deef6b6ba7..0000000000 --- a/target/linux/brcm2708/patches-4.4/0076-Add-driver-for-rpi-proto.patch +++ /dev/null @@ -1,210 +0,0 @@ -From 660d2fba0483aafc4a1ce678016620c0871bb759 Mon Sep 17 00:00:00 2001 -From: Waldemar Brodkorb -Date: Wed, 25 Mar 2015 09:26:17 +0100 -Subject: [PATCH 076/170] 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 ---- - sound/soc/bcm/Kconfig | 7 +++ - sound/soc/bcm/Makefile | 2 + - sound/soc/bcm/rpi-proto.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 162 insertions(+) - create mode 100644 sound/soc/bcm/rpi-proto.c - ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -43,6 +43,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 || SND_BCM2835_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 || SND_BCM2835_SOC_I2S ---- a/sound/soc/bcm/Makefile -+++ b/sound/soc/bcm/Makefile -@@ -9,6 +9,7 @@ snd-soc-hifiberry-dacplus-objs := hifibe - 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 -@@ -16,4 +17,5 @@ obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_D - 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 ---- /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"); diff --git a/target/linux/brcm2708/patches-4.4/0076-config-Add-default-configs.patch b/target/linux/brcm2708/patches-4.4/0076-config-Add-default-configs.patch new file mode 100644 index 0000000000..ab218d42b2 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0076-config-Add-default-configs.patch @@ -0,0 +1,2537 @@ +From 78cf26e64b4ce0c4567829c42990a89ab59a6553 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Mon, 13 Apr 2015 17:16:29 +0100 +Subject: [PATCH 076/232] config: Add default configs + +--- + arch/arm/configs/bcm2709_defconfig | 1254 +++++++++++++++++++++++++++++++++++ + arch/arm/configs/bcmrpi_defconfig | 1265 ++++++++++++++++++++++++++++++++++++ + 2 files changed, 2519 insertions(+) + create mode 100644 arch/arm/configs/bcm2709_defconfig + create mode 100644 arch/arm/configs/bcmrpi_defconfig + +--- /dev/null ++++ b/arch/arm/configs/bcm2709_defconfig +@@ -0,0 +1,1254 @@ ++# 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_CPUSETS=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_CACHE_L2X0 is not set ++CONFIG_SMP=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++CONFIG_VMSPLIT_2G=y ++CONFIG_PREEMPT_VOLUNTARY=y ++CONFIG_AEABI=y ++CONFIG_OABI_COMPAT=y ++# CONFIG_CPU_SW_DOMAIN_PAN is not set ++CONFIG_CLEANCACHE=y ++CONFIG_FRONTSWAP=y ++CONFIG_CMA=y ++CONFIG_ZSMALLOC=m ++CONFIG_PGTABLE_MAPPING=y ++CONFIG_UACCESS_WITH_MEMCPY=y ++CONFIG_SECCOMP=y ++# CONFIG_ATAGS is not set ++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_IEEE802154=m ++CONFIG_IEEE802154_6LOWPAN=m ++CONFIG_MAC802154=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_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_MTD=m ++CONFIG_MTD_BLOCK=m ++CONFIG_MTD_NAND=m ++CONFIG_MTD_UBI=m ++CONFIG_ZRAM=m ++CONFIG_ZRAM_LZ4_COMPRESS=y ++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_TI_ST=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_THIN_PROVISIONING=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_WL_MEDIATEK=y ++CONFIG_MT7601U=m ++CONFIG_RTL8192CU=m ++CONFIG_ZD1211RW=m ++CONFIG_MWIFIEX=m ++CONFIG_MWIFIEX_SDIO=m ++CONFIG_WIMAX_I2400M_USB=m ++CONFIG_IEEE802154_AT86RF230=m ++CONFIG_IEEE802154_MRF24J40=m ++CONFIG_IEEE802154_CC2520=m ++CONFIG_INPUT_POLLDEV=m ++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set ++CONFIG_INPUT_JOYDEV=m ++CONFIG_INPUT_EVDEV=m ++# CONFIG_KEYBOARD_ATKBD is not set ++CONFIG_KEYBOARD_GPIO=m ++# 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_JOYSTICK_RPISENSE=m ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_ADS7846=m ++CONFIG_TOUCHSCREEN_EGALAX=m ++CONFIG_TOUCHSCREEN_FT6236=m ++CONFIG_TOUCHSCREEN_RPI_FT5406=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_BRCM_CHAR_DRIVERS=y ++CONFIG_BCM_VC_CMA=y ++CONFIG_BCM_VCIO=y ++CONFIG_BCM_VC_SM=y ++CONFIG_DEVPTS_MULTIPLE_INSTANCES=y ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_DEVKMEM is not set ++CONFIG_SERIAL_8250=y ++# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set ++CONFIG_SERIAL_8250_CONSOLE=y ++# CONFIG_SERIAL_8250_DMA is not set ++CONFIG_SERIAL_8250_NR_UARTS=1 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=0 ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++CONFIG_SERIAL_OF_PLATFORM=y ++CONFIG_TTY_PRINTK=y ++CONFIG_HW_RANDOM=y ++CONFIG_HW_RANDOM_BCM2835=y ++CONFIG_RAW_DRIVER=y ++CONFIG_I2C=y ++CONFIG_I2C_CHARDEV=m ++CONFIG_I2C_BCM2708=m ++CONFIG_SPI=y ++CONFIG_SPI_BCM2835=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_POWER_RESET=y ++CONFIG_POWER_RESET_GPIO=y ++CONFIG_HWMON=m ++CONFIG_SENSORS_SHT21=m ++CONFIG_SENSORS_SHTC1=m ++CONFIG_THERMAL=y ++CONFIG_THERMAL_BCM2835=y ++CONFIG_WATCHDOG=y ++CONFIG_BCM2835_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_FB_UDL=m ++CONFIG_FB_SSD1307=m ++CONFIG_FB_RPISENSE=m ++# 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_BCM2835_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_BCM2708_SOC_RASPIDAC3=m ++CONFIG_SND_SOC_ADAU1701=m ++CONFIG_SND_SOC_WM8804_I2C=m ++CONFIG_SND_SIMPLE_CARD=m ++CONFIG_SOUND_PRIME=m ++CONFIG_HIDRAW=y ++CONFIG_UHID=m ++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_BCM2835_SDHOST=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_BCM2835=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_ILI9163=m ++CONFIG_FB_TFT_ILI9320=m ++CONFIG_FB_TFT_ILI9325=m ++CONFIG_FB_TFT_ILI9340=m ++CONFIG_FB_TFT_ILI9341=m ++CONFIG_FB_TFT_ILI9481=m ++CONFIG_FB_TFT_ILI9486=m ++CONFIG_FB_TFT_PCD8544=m ++CONFIG_FB_TFT_RA8875=m ++CONFIG_FB_TFT_S6D02A1=m ++CONFIG_FB_TFT_S6D1121=m ++CONFIG_FB_TFT_SSD1289=m ++CONFIG_FB_TFT_SSD1306=m ++CONFIG_FB_TFT_SSD1331=m ++CONFIG_FB_TFT_SSD1351=m ++CONFIG_FB_TFT_ST7735R=m ++CONFIG_FB_TFT_TINYLCD=m ++CONFIG_FB_TFT_TLS8204=m ++CONFIG_FB_TFT_UC1701=m ++CONFIG_FB_TFT_UPD161704=m ++CONFIG_FB_TFT_WATTEROTT=m ++CONFIG_FB_FLEX=m ++CONFIG_FB_TFT_FBTFT_DEVICE=m ++CONFIG_MAILBOX=y ++CONFIG_BCM2835_MBOX=y ++# CONFIG_IOMMU_SUPPORT is not set ++CONFIG_EXTCON=m ++CONFIG_EXTCON_ARIZONA=m ++CONFIG_IIO=m ++CONFIG_IIO_BUFFER=y ++CONFIG_IIO_BUFFER_CB=y ++CONFIG_IIO_KFIFO_BUF=m ++CONFIG_MCP320X=m ++CONFIG_DHT11=m ++CONFIG_PWM_BCM2835=m ++CONFIG_RASPBERRYPI_FIRMWARE=y ++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_F2FS_FS=y ++CONFIG_FANOTIFY=y ++CONFIG_QFMT_V1=m ++CONFIG_QFMT_V2=m ++CONFIG_AUTOFS4_FS=y ++CONFIG_FUSE_FS=m ++CONFIG_CUSE=m ++CONFIG_OVERLAY_FS=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_JFFS2_FS=m ++CONFIG_JFFS2_SUMMARY=y ++CONFIG_UBIFS_FS=m ++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_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_UPCALL=y ++CONFIG_CIFS_XATTR=y ++CONFIG_CIFS_POSIX=y ++CONFIG_CIFS_ACL=y ++CONFIG_CIFS_DFS_UPCALL=y ++CONFIG_CIFS_SMB2=y ++CONFIG_CIFS_FSCACHE=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_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_TGR192=m ++CONFIG_CRYPTO_WP512=m ++CONFIG_CRYPTO_CAST5=m ++CONFIG_CRYPTO_DES=y ++CONFIG_CRYPTO_USER_API_SKCIPHER=m ++# CONFIG_CRYPTO_HW is not set ++CONFIG_ARM_CRYPTO=y ++CONFIG_CRYPTO_SHA1_ARM_NEON=m ++CONFIG_CRYPTO_AES_ARM_BS=m ++CONFIG_CRC_ITU_T=y ++CONFIG_LIBCRC32C=y +--- /dev/null ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -0,0 +1,1265 @@ ++# 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_CPUSETS=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_PREEMPT_VOLUNTARY=y ++CONFIG_AEABI=y ++CONFIG_OABI_COMPAT=y ++# CONFIG_CPU_SW_DOMAIN_PAN is not set ++CONFIG_CLEANCACHE=y ++CONFIG_FRONTSWAP=y ++CONFIG_CMA=y ++CONFIG_ZSMALLOC=m ++CONFIG_PGTABLE_MAPPING=y ++CONFIG_UACCESS_WITH_MEMCPY=y ++CONFIG_SECCOMP=y ++# CONFIG_ATAGS is not set ++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_IEEE802154=m ++CONFIG_IEEE802154_6LOWPAN=m ++CONFIG_MAC802154=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_HCIUART=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_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_MTD=m ++CONFIG_MTD_BLOCK=m ++CONFIG_MTD_NAND=m ++CONFIG_MTD_UBI=m ++CONFIG_ZRAM=m ++CONFIG_ZRAM_LZ4_COMPRESS=y ++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_TI_ST=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_THIN_PROVISIONING=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_WL_MEDIATEK=y ++CONFIG_MT7601U=m ++CONFIG_RTL8192CU=m ++CONFIG_ZD1211RW=m ++CONFIG_MWIFIEX=m ++CONFIG_MWIFIEX_SDIO=m ++CONFIG_WIMAX_I2400M_USB=m ++CONFIG_IEEE802154_AT86RF230=m ++CONFIG_IEEE802154_MRF24J40=m ++CONFIG_IEEE802154_CC2520=m ++CONFIG_INPUT_POLLDEV=m ++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set ++CONFIG_INPUT_JOYDEV=m ++CONFIG_INPUT_EVDEV=m ++# CONFIG_KEYBOARD_ATKBD is not set ++CONFIG_KEYBOARD_GPIO=m ++# 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_JOYSTICK_RPISENSE=m ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_ADS7846=m ++CONFIG_TOUCHSCREEN_EGALAX=m ++CONFIG_TOUCHSCREEN_FT6236=m ++CONFIG_TOUCHSCREEN_RPI_FT5406=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_BRCM_CHAR_DRIVERS=y ++CONFIG_BCM_VC_CMA=y ++CONFIG_BCM_VCIO=y ++CONFIG_BCM_VC_SM=y ++CONFIG_DEVPTS_MULTIPLE_INSTANCES=y ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_DEVKMEM is not set ++CONFIG_SERIAL_8250=y ++# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set ++CONFIG_SERIAL_8250_CONSOLE=y ++# CONFIG_SERIAL_8250_DMA is not set ++CONFIG_SERIAL_8250_NR_UARTS=1 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=0 ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++CONFIG_SERIAL_OF_PLATFORM=y ++CONFIG_TTY_PRINTK=y ++CONFIG_HW_RANDOM=y ++CONFIG_RAW_DRIVER=y ++CONFIG_I2C=y ++CONFIG_I2C_CHARDEV=m ++CONFIG_I2C_BCM2708=m ++CONFIG_SPI=y ++CONFIG_SPI_BCM2835=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_POWER_RESET=y ++CONFIG_POWER_RESET_GPIO=y ++CONFIG_HWMON=m ++CONFIG_SENSORS_SHT21=m ++CONFIG_SENSORS_SHTC1=m ++CONFIG_THERMAL=y ++CONFIG_THERMAL_BCM2835=y ++CONFIG_WATCHDOG=y ++CONFIG_BCM2835_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_FB_UDL=m ++CONFIG_FB_SSD1307=m ++CONFIG_FB_RPISENSE=m ++# 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_BCM2835_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_BCM2708_SOC_RASPIDAC3=m ++CONFIG_SND_SOC_ADAU1701=m ++CONFIG_SND_SOC_WM8804_I2C=m ++CONFIG_SND_SIMPLE_CARD=m ++CONFIG_SOUND_PRIME=m ++CONFIG_HIDRAW=y ++CONFIG_UHID=m ++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_DWC2=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_USB_GADGET=m ++CONFIG_USB_ZERO=m ++CONFIG_USB_AUDIO=m ++CONFIG_USB_ETH=m ++CONFIG_USB_GADGETFS=m ++CONFIG_USB_MASS_STORAGE=m ++CONFIG_USB_G_SERIAL=m ++CONFIG_USB_MIDI_GADGET=m ++CONFIG_USB_G_PRINTER=m ++CONFIG_USB_CDC_COMPOSITE=m ++CONFIG_USB_G_ACM_MS=m ++CONFIG_USB_G_MULTI=m ++CONFIG_USB_G_HID=m ++CONFIG_USB_G_WEBCAM=m ++CONFIG_MMC=y ++CONFIG_MMC_BLOCK_MINORS=32 ++CONFIG_MMC_BCM2835=y ++CONFIG_MMC_BCM2835_DMA=y ++CONFIG_MMC_BCM2835_SDHOST=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_BCM2835=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_ILI9163=m ++CONFIG_FB_TFT_ILI9320=m ++CONFIG_FB_TFT_ILI9325=m ++CONFIG_FB_TFT_ILI9340=m ++CONFIG_FB_TFT_ILI9341=m ++CONFIG_FB_TFT_ILI9481=m ++CONFIG_FB_TFT_ILI9486=m ++CONFIG_FB_TFT_PCD8544=m ++CONFIG_FB_TFT_RA8875=m ++CONFIG_FB_TFT_S6D02A1=m ++CONFIG_FB_TFT_S6D1121=m ++CONFIG_FB_TFT_SSD1289=m ++CONFIG_FB_TFT_SSD1306=m ++CONFIG_FB_TFT_SSD1331=m ++CONFIG_FB_TFT_SSD1351=m ++CONFIG_FB_TFT_ST7735R=m ++CONFIG_FB_TFT_TINYLCD=m ++CONFIG_FB_TFT_TLS8204=m ++CONFIG_FB_TFT_UC1701=m ++CONFIG_FB_TFT_UPD161704=m ++CONFIG_FB_TFT_WATTEROTT=m ++CONFIG_FB_FLEX=m ++CONFIG_FB_TFT_FBTFT_DEVICE=m ++CONFIG_MAILBOX=y ++CONFIG_BCM2835_MBOX=y ++# CONFIG_IOMMU_SUPPORT is not set ++CONFIG_EXTCON=m ++CONFIG_EXTCON_ARIZONA=m ++CONFIG_IIO=m ++CONFIG_IIO_BUFFER=y ++CONFIG_IIO_BUFFER_CB=m ++CONFIG_IIO_KFIFO_BUF=m ++CONFIG_MCP320X=m ++CONFIG_DHT11=m ++CONFIG_PWM_BCM2835=m ++CONFIG_RASPBERRYPI_FIRMWARE=y ++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_F2FS_FS=y ++CONFIG_FANOTIFY=y ++CONFIG_QFMT_V1=m ++CONFIG_QFMT_V2=m ++CONFIG_AUTOFS4_FS=y ++CONFIG_FUSE_FS=m ++CONFIG_CUSE=m ++CONFIG_OVERLAY_FS=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_JFFS2_FS=m ++CONFIG_JFFS2_SUMMARY=y ++CONFIG_UBIFS_FS=m ++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_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_UPCALL=y ++CONFIG_CIFS_XATTR=y ++CONFIG_CIFS_POSIX=y ++CONFIG_CIFS_ACL=y ++CONFIG_CIFS_DFS_UPCALL=y ++CONFIG_CIFS_SMB2=y ++CONFIG_CIFS_FSCACHE=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_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_SHA512=m ++CONFIG_CRYPTO_TGR192=m ++CONFIG_CRYPTO_WP512=m ++CONFIG_CRYPTO_CAST5=m ++CONFIG_CRYPTO_DES=y ++CONFIG_CRYPTO_USER_API_SKCIPHER=m ++# CONFIG_CRYPTO_HW is not set ++CONFIG_ARM_CRYPTO=y ++CONFIG_CRYPTO_SHA1_ARM=m ++CONFIG_CRYPTO_AES_ARM=m ++CONFIG_CRC_ITU_T=y ++CONFIG_LIBCRC32C=y diff --git a/target/linux/brcm2708/patches-4.4/0077-bcm2835-bcm2835_defconfig.patch b/target/linux/brcm2708/patches-4.4/0077-bcm2835-bcm2835_defconfig.patch new file mode 100644 index 0000000000..be5e02334e --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0077-bcm2835-bcm2835_defconfig.patch @@ -0,0 +1,1426 @@ +From 0ee42e134a9a28fd825e1390634113108c0eebc0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= +Date: Wed, 29 Apr 2015 17:24:02 +0200 +Subject: [PATCH 077/232] bcm2835: bcm2835_defconfig +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Some options in bcm2835_defconfig are now the default and +some have changed. Update to keep functionality. + +No longer available: SCSI_MULTI_LUN and RESOURCE_COUNTERS. + +Signed-off-by: Noralf Trønnes + +bcm2835: bcm2835_defconfig enable MMC_BCM2835 + +Enable the downstream bcm2835-mmc driver and DMA support. + +Signed-off-by: Noralf Trønnes + +bcm2835: bcm2835_defconfig enable BCM2708_MBOX + +Enable the mailbox driver. + +Signed-off-by: Noralf Trønnes + +bcm2835: bcm2835_defconfig use FB_BCM2708 + +Enable the bcm2708 framebuffer driver. +Disable the simple framebuffer driver, which matches the +device handed over by u-boot. + +Signed-off-by: Noralf Trønnes + +bcm2835: Merge bcm2835_defconfig with bcmrpi_defconfig + +These commands where used to make this commit: + +./scripts/diffconfig -m arch/arm/configs/bcm2835_defconfig arch/arm/configs/bcmrpi_defconfig > merge.cfg + +cat << EOF > filter +CONFIG_ARCH_BCM2708 +CONFIG_BCM2708_DT +CONFIG_ARM_PATCH_PHYS_VIRT +CONFIG_PHYS_OFFSET +CONFIG_CMDLINE +CONFIG_BCM2708_WDT +CONFIG_HW_RANDOM_BCM2708 +CONFIG_I2C_BCM2708 +CONFIG_SPI_BCM2708 +CONFIG_SND_BCM2708_SOC_I2S +CONFIG_USB_DWCOTG +CONFIG_LIRC_RPI +EOF + +grep -F -v -f filter merge.cfg > filtered.cfg + +cat << EOF > added.cfg +CONFIG_WATCHDOG=y +CONFIG_BCM2835_WDT=y +CONFIG_MISC_FILESYSTEMS=y +CONFIG_SND_BCM2835_SOC_I2S=m +EOF + +ARCH=arm scripts/kconfig/merge_config.sh arch/arm/configs/bcm2835_defconfig filtered.cfg added.cfg +ARCH=arm make oldconfig + +ARCH=arm make savedefconfig +cp defconfig arch/arm/configs/bcm2835_defconfig + +rm merge.cfg filter filtered.cfg added.cfg defconfig + +ARCH=arm make bcm2835_defconfig +ARCH=arm make oldconfig + +Signed-off-by: Noralf Trønnes + +configs: Incorporate v4.1 dependency changes + +Commit 78e9b7de78bb53e8bc7f4c4a60ebacb250c0c190 added a +dependency on TI_ST instead of selecting it, disabling: +CONFIG_BT_WILINK=m +CONFIG_RADIO_WL128X=m + +Commit 652ccae5cc4e1305fb0a4619947f9ee89d8c7f5a added a +depency on ARM_CRYPTO, disabling: +CONFIG_CRYPTO_SHA*_ARM*=m +CONFIG_CRYPTO_AES_ARM*=m + +Signed-off-by: Noralf Trønnes + +Conflicts: + arch/arm/configs/bcm2709_defconfig + +bcm2835: Sync bcm2835_defconfig with bcmrpi_defconfig + +These commands where used to make this commit: + +: Get changed and new config values from a merge +./scripts/diffconfig -m arch/arm/configs/bcm2835_defconfig arch/arm/configs/bcmrpi_defconfig > merge.cfg + +: Remove these options +cat << EOF > filter +CONFIG_ARCH_BCM2708 +CONFIG_BCM2708_DT +CONFIG_ARM_PATCH_PHYS_VIRT +CONFIG_PHYS_OFFSET +CONFIG_CMDLINE +CONFIG_BCM2708_WDT +CONFIG_HW_RANDOM_BCM2708 +CONFIG_SPI_BCM2708 +EOF + +: Apply filter +grep -F -v -f filter merge.cfg > filtered.cfg + +: Add these options +: watchdog contains the restart/poweroff code. +cat << EOF > added.cfg +CONFIG_WATCHDOG=y +CONFIG_BCM2835_WDT=y +CONFIG_MISC_FILESYSTEMS=y +CONFIG_I2C_BCM2835=m +CONFIG_SND_BCM2835_SOC_I2S=m +EOF + +: Create new config +ARCH=arm scripts/kconfig/merge_config.sh arch/arm/configs/bcm2835_defconfig filtered.cfg added.cfg +: Verify +ARCH=arm make oldconfig + +: Update bcm2835_defconfig +ARCH=arm make savedefconfig +cp defconfig arch/arm/configs/bcm2835_defconfig + +: Clean up +rm merge.cfg filter filtered.cfg added.cfg defconfig + +Signed-off-by: Noralf Trønnes +--- + arch/arm/configs/bcm2835_defconfig | 1166 +++++++++++++++++++++++++++++++++++- + 1 file changed, 1140 insertions(+), 26 deletions(-) + +--- a/arch/arm/configs/bcm2835_defconfig ++++ b/arch/arm/configs/bcm2835_defconfig +@@ -1,105 +1,1103 @@ + # 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_LOG_BUF_SHIFT=18 + CONFIG_CGROUP_FREEZER=y + CONFIG_CGROUP_DEVICE=y + CONFIG_CPUSETS=y + CONFIG_CGROUP_CPUACCT=y +-CONFIG_RESOURCE_COUNTERS=y ++CONFIG_MEMCG=y + CONFIG_CGROUP_PERF=y + CONFIG_CFS_BANDWIDTH=y + CONFIG_RT_GROUP_SCHED=y ++CONFIG_BLK_CGROUP=y + CONFIG_NAMESPACES=y + CONFIG_SCHED_AUTOGROUP=y +-CONFIG_RELAY=y + CONFIG_BLK_DEV_INITRD=y +-CONFIG_RD_BZIP2=y +-CONFIG_RD_LZMA=y +-CONFIG_RD_XZ=y +-CONFIG_RD_LZO=y + CONFIG_CC_OPTIMIZE_FOR_SIZE=y +-CONFIG_KALLSYMS_ALL=y + CONFIG_EMBEDDED=y + # CONFIG_COMPAT_BRK is not set + CONFIG_PROFILING=y +-CONFIG_OPROFILE=y ++CONFIG_OPROFILE=m ++CONFIG_KPROBES=y + CONFIG_JUMP_LABEL=y ++CONFIG_CC_STACKPROTECTOR_REGULAR=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_MULTI_V6=y + # CONFIG_ARCH_MULTI_V7 is not set + CONFIG_ARCH_BCM=y + CONFIG_ARCH_BCM2835=y +-CONFIG_PREEMPT_VOLUNTARY=y ++CONFIG_PREEMPT=y + CONFIG_AEABI=y ++CONFIG_OABI_COMPAT=y + CONFIG_KSM=y + CONFIG_CLEANCACHE=y ++CONFIG_FRONTSWAP=y ++CONFIG_CMA=y ++CONFIG_ZSMALLOC=m ++CONFIG_PGTABLE_MAPPING=y ++CONFIG_UACCESS_WITH_MEMCPY=y + CONFIG_SECCOMP=y +-CONFIG_CC_STACKPROTECTOR=y ++CONFIG_ZBOOT_ROM_TEXT=0x0 ++CONFIG_ZBOOT_ROM_BSS=0x0 + CONFIG_KEXEC=y + CONFIG_CRASH_DUMP=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_VFP=y + # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++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_NETWORK_SECMARK=y + CONFIG_NETFILTER=y +-CONFIG_CFG80211=y +-CONFIG_MAC80211=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_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_STANDALONE is not set ++CONFIG_DMA_CMA=y ++CONFIG_CMA_SIZE_MBYTES=5 ++CONFIG_ZRAM=m ++CONFIG_ZRAM_LZ4_COMPRESS=y ++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_TI_ST=m + CONFIG_SCSI=y ++# CONFIG_SCSI_PROC_FS is not set + CONFIG_BLK_DEV_SD=y +-CONFIG_SCSI_MULTI_LUN=y ++CONFIG_CHR_DEV_ST=m ++CONFIG_CHR_DEV_OSST=m ++CONFIG_BLK_DEV_SR=m ++CONFIG_CHR_DEV_SG=m + CONFIG_SCSI_CONSTANTS=y + CONFIG_SCSI_SCAN_ASYNC=y ++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_ZD1211RW=y +-CONFIG_INPUT_EVDEV=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_WL_MEDIATEK=y ++CONFIG_MT7601U=m ++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_KEYBOARD_ATKBD is not set ++CONFIG_KEYBOARD_GPIO=m ++# 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_JOYSTICK_RPISENSE=m ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_ADS7846=m ++CONFIG_TOUCHSCREEN_EGALAX=m ++CONFIG_TOUCHSCREEN_RPI_FT5406=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_BRCM_CHAR_DRIVERS=y ++CONFIG_BCM_VC_CMA=y ++CONFIG_BCM_VCIO=y ++CONFIG_BCM_VC_SM=y ++CONFIG_DEVPTS_MULTIPLE_INSTANCES=y + # CONFIG_LEGACY_PTYS is not set + # CONFIG_DEVKMEM is not set ++CONFIG_SERIAL_8250=y ++# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set ++CONFIG_SERIAL_8250_CONSOLE=y ++# CONFIG_SERIAL_8250_DMA is not set ++CONFIG_SERIAL_8250_NR_UARTS=1 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=0 + CONFIG_SERIAL_AMBA_PL011=y + CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++CONFIG_SERIAL_OF_PLATFORM=y + CONFIG_TTY_PRINTK=y ++CONFIG_HW_RANDOM=y ++CONFIG_HW_RANDOM_BCM2835=m ++CONFIG_RAW_DRIVER=y + CONFIG_I2C=y +-CONFIG_I2C_CHARDEV=y +-CONFIG_I2C_BCM2835=y ++CONFIG_I2C_CHARDEV=m ++CONFIG_I2C_BCM2708=m ++CONFIG_I2C_BCM2835=m + CONFIG_SPI=y +-CONFIG_SPI_BCM2835=y ++CONFIG_SPI_BCM2835=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_POWER_RESET=y ++CONFIG_POWER_RESET_GPIO=y + # CONFIG_HWMON is not set ++CONFIG_THERMAL=y ++CONFIG_THERMAL_BCM2835=y ++CONFIG_WATCHDOG=y ++CONFIG_BCM2835_WDT=y ++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_SIMPLE=y ++CONFIG_FB_BCM2708=y ++CONFIG_FB_SSD1307=m ++CONFIG_FB_RPISENSE=m ++# CONFIG_BACKLIGHT_GENERIC is not set ++CONFIG_BACKLIGHT_GPIO=m + CONFIG_FRAMEBUFFER_CONSOLE=y + CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=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_BCM2835_SOC_I2S=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_SOC_WM8804_I2C=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_DWC2=y ++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_BCM2835_SDHOST=y + CONFIG_MMC_SDHCI=y + CONFIG_MMC_SDHCI_PLTFM=y + CONFIG_MMC_SDHCI_BCM2835=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=y +-CONFIG_LEDS_TRIGGER_CAMERA=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_BCM2835=y ++CONFIG_DMA_BCM2708=y ++CONFIG_UIO=m ++CONFIG_UIO_PDRV_GENIRQ=m + CONFIG_STAGING=y +-CONFIG_USB_DWC2=y +-CONFIG_USB_DWC2_HOST=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_MAILBOX=y ++CONFIG_BCM2835_MBOX=y + # CONFIG_IOMMU_SUPPORT is not set ++CONFIG_EXTCON=m ++CONFIG_EXTCON_ARIZONA=m ++CONFIG_IIO=m ++CONFIG_IIO_BUFFER=y ++CONFIG_IIO_BUFFER_CB=y ++CONFIG_IIO_KFIFO_BUF=m ++CONFIG_DHT11=m ++CONFIG_RASPBERRYPI_FIRMWARE=y + CONFIG_EXT2_FS=y + CONFIG_EXT2_FS_XATTR=y + CONFIG_EXT2_FS_POSIX_ACL=y +@@ -107,18 +1105,110 @@ CONFIG_EXT3_FS=y + CONFIG_EXT3_FS_POSIX_ACL=y + 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_F2FS_FS=y + 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_MISC_FILESYSTEMS is not set ++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_NFS_FS=y +-CONFIG_NFSD=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_UPCALL=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=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=y ++CONFIG_DLM=m + CONFIG_PRINTK_TIME=y + CONFIG_BOOT_PRINTK_DELAY=y + CONFIG_DYNAMIC_DEBUG=y +@@ -128,14 +1218,38 @@ CONFIG_DEBUG_INFO=y + CONFIG_UNUSED_SYMBOLS=y + CONFIG_DEBUG_MEMORY_INIT=y + CONFIG_LOCKUP_DETECTOR=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_TEST_KSTRTOX=y + CONFIG_KGDB=y + CONFIG_KGDB_KDB=y ++CONFIG_KDB_KEYBOARD=y + CONFIG_STRICT_DEVMEM=y + CONFIG_DEBUG_LL=y + CONFIG_EARLY_PRINTK=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_SHA512=m ++CONFIG_CRYPTO_TGR192=m ++CONFIG_CRYPTO_WP512=m ++CONFIG_CRYPTO_CAST5=m ++CONFIG_CRYPTO_DES=y ++# CONFIG_CRYPTO_HW is not set ++CONFIG_ARM_CRYPTO=y ++CONFIG_CRYPTO_SHA1_ARM=m ++CONFIG_CRYPTO_AES_ARM=m ++CONFIG_CRC_ITU_T=y ++CONFIG_LIBCRC32C=y + # CONFIG_XZ_DEC_ARM is not set + # CONFIG_XZ_DEC_ARMTHUMB is not set diff --git a/target/linux/brcm2708/patches-4.4/0077-config-Add-default-configs.patch b/target/linux/brcm2708/patches-4.4/0077-config-Add-default-configs.patch deleted file mode 100644 index ffd1a0bfe4..0000000000 --- a/target/linux/brcm2708/patches-4.4/0077-config-Add-default-configs.patch +++ /dev/null @@ -1,2537 +0,0 @@ -From e2af169e60b4ebd710e733996d57ad86264bae27 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Mon, 13 Apr 2015 17:16:29 +0100 -Subject: [PATCH 077/170] config: Add default configs - ---- - arch/arm/configs/bcm2709_defconfig | 1254 +++++++++++++++++++++++++++++++++++ - arch/arm/configs/bcmrpi_defconfig | 1265 ++++++++++++++++++++++++++++++++++++ - 2 files changed, 2519 insertions(+) - create mode 100644 arch/arm/configs/bcm2709_defconfig - create mode 100644 arch/arm/configs/bcmrpi_defconfig - ---- /dev/null -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -0,0 +1,1254 @@ -+# 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_CPUSETS=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_CACHE_L2X0 is not set -+CONFIG_SMP=y -+CONFIG_HAVE_ARM_ARCH_TIMER=y -+CONFIG_VMSPLIT_2G=y -+CONFIG_PREEMPT_VOLUNTARY=y -+CONFIG_AEABI=y -+CONFIG_OABI_COMPAT=y -+# CONFIG_CPU_SW_DOMAIN_PAN is not set -+CONFIG_CLEANCACHE=y -+CONFIG_FRONTSWAP=y -+CONFIG_CMA=y -+CONFIG_ZSMALLOC=m -+CONFIG_PGTABLE_MAPPING=y -+CONFIG_UACCESS_WITH_MEMCPY=y -+CONFIG_SECCOMP=y -+# CONFIG_ATAGS is not set -+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_IEEE802154=m -+CONFIG_IEEE802154_6LOWPAN=m -+CONFIG_MAC802154=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_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_MTD=m -+CONFIG_MTD_BLOCK=m -+CONFIG_MTD_NAND=m -+CONFIG_MTD_UBI=m -+CONFIG_ZRAM=m -+CONFIG_ZRAM_LZ4_COMPRESS=y -+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_TI_ST=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_THIN_PROVISIONING=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_WL_MEDIATEK=y -+CONFIG_MT7601U=m -+CONFIG_RTL8192CU=m -+CONFIG_ZD1211RW=m -+CONFIG_MWIFIEX=m -+CONFIG_MWIFIEX_SDIO=m -+CONFIG_WIMAX_I2400M_USB=m -+CONFIG_IEEE802154_AT86RF230=m -+CONFIG_IEEE802154_MRF24J40=m -+CONFIG_IEEE802154_CC2520=m -+CONFIG_INPUT_POLLDEV=m -+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set -+CONFIG_INPUT_JOYDEV=m -+CONFIG_INPUT_EVDEV=m -+# CONFIG_KEYBOARD_ATKBD is not set -+CONFIG_KEYBOARD_GPIO=m -+# 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_JOYSTICK_RPISENSE=m -+CONFIG_INPUT_TOUCHSCREEN=y -+CONFIG_TOUCHSCREEN_ADS7846=m -+CONFIG_TOUCHSCREEN_EGALAX=m -+CONFIG_TOUCHSCREEN_FT6236=m -+CONFIG_TOUCHSCREEN_RPI_FT5406=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_BRCM_CHAR_DRIVERS=y -+CONFIG_BCM_VC_CMA=y -+CONFIG_BCM_VCIO=y -+CONFIG_BCM_VC_SM=y -+CONFIG_DEVPTS_MULTIPLE_INSTANCES=y -+# CONFIG_LEGACY_PTYS is not set -+# CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_8250=y -+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set -+CONFIG_SERIAL_8250_CONSOLE=y -+# CONFIG_SERIAL_8250_DMA is not set -+CONFIG_SERIAL_8250_NR_UARTS=1 -+CONFIG_SERIAL_8250_RUNTIME_UARTS=0 -+CONFIG_SERIAL_AMBA_PL011=y -+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+CONFIG_SERIAL_OF_PLATFORM=y -+CONFIG_TTY_PRINTK=y -+CONFIG_HW_RANDOM=y -+CONFIG_HW_RANDOM_BCM2835=y -+CONFIG_RAW_DRIVER=y -+CONFIG_I2C=y -+CONFIG_I2C_CHARDEV=m -+CONFIG_I2C_BCM2708=m -+CONFIG_SPI=y -+CONFIG_SPI_BCM2835=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_POWER_RESET=y -+CONFIG_POWER_RESET_GPIO=y -+CONFIG_HWMON=m -+CONFIG_SENSORS_SHT21=m -+CONFIG_SENSORS_SHTC1=m -+CONFIG_THERMAL=y -+CONFIG_THERMAL_BCM2835=y -+CONFIG_WATCHDOG=y -+CONFIG_BCM2835_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_FB_UDL=m -+CONFIG_FB_SSD1307=m -+CONFIG_FB_RPISENSE=m -+# 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_BCM2835_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_BCM2708_SOC_RASPIDAC3=m -+CONFIG_SND_SOC_ADAU1701=m -+CONFIG_SND_SOC_WM8804_I2C=m -+CONFIG_SND_SIMPLE_CARD=m -+CONFIG_SOUND_PRIME=m -+CONFIG_HIDRAW=y -+CONFIG_UHID=m -+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_BCM2835_SDHOST=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_BCM2835=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_ILI9163=m -+CONFIG_FB_TFT_ILI9320=m -+CONFIG_FB_TFT_ILI9325=m -+CONFIG_FB_TFT_ILI9340=m -+CONFIG_FB_TFT_ILI9341=m -+CONFIG_FB_TFT_ILI9481=m -+CONFIG_FB_TFT_ILI9486=m -+CONFIG_FB_TFT_PCD8544=m -+CONFIG_FB_TFT_RA8875=m -+CONFIG_FB_TFT_S6D02A1=m -+CONFIG_FB_TFT_S6D1121=m -+CONFIG_FB_TFT_SSD1289=m -+CONFIG_FB_TFT_SSD1306=m -+CONFIG_FB_TFT_SSD1331=m -+CONFIG_FB_TFT_SSD1351=m -+CONFIG_FB_TFT_ST7735R=m -+CONFIG_FB_TFT_TINYLCD=m -+CONFIG_FB_TFT_TLS8204=m -+CONFIG_FB_TFT_UC1701=m -+CONFIG_FB_TFT_UPD161704=m -+CONFIG_FB_TFT_WATTEROTT=m -+CONFIG_FB_FLEX=m -+CONFIG_FB_TFT_FBTFT_DEVICE=m -+CONFIG_MAILBOX=y -+CONFIG_BCM2835_MBOX=y -+# CONFIG_IOMMU_SUPPORT is not set -+CONFIG_EXTCON=m -+CONFIG_EXTCON_ARIZONA=m -+CONFIG_IIO=m -+CONFIG_IIO_BUFFER=y -+CONFIG_IIO_BUFFER_CB=y -+CONFIG_IIO_KFIFO_BUF=m -+CONFIG_MCP320X=m -+CONFIG_DHT11=m -+CONFIG_PWM_BCM2835=m -+CONFIG_RASPBERRYPI_FIRMWARE=y -+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_F2FS_FS=y -+CONFIG_FANOTIFY=y -+CONFIG_QFMT_V1=m -+CONFIG_QFMT_V2=m -+CONFIG_AUTOFS4_FS=y -+CONFIG_FUSE_FS=m -+CONFIG_CUSE=m -+CONFIG_OVERLAY_FS=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_JFFS2_FS=m -+CONFIG_JFFS2_SUMMARY=y -+CONFIG_UBIFS_FS=m -+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_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_UPCALL=y -+CONFIG_CIFS_XATTR=y -+CONFIG_CIFS_POSIX=y -+CONFIG_CIFS_ACL=y -+CONFIG_CIFS_DFS_UPCALL=y -+CONFIG_CIFS_SMB2=y -+CONFIG_CIFS_FSCACHE=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_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_TGR192=m -+CONFIG_CRYPTO_WP512=m -+CONFIG_CRYPTO_CAST5=m -+CONFIG_CRYPTO_DES=y -+CONFIG_CRYPTO_USER_API_SKCIPHER=m -+# CONFIG_CRYPTO_HW is not set -+CONFIG_ARM_CRYPTO=y -+CONFIG_CRYPTO_SHA1_ARM_NEON=m -+CONFIG_CRYPTO_AES_ARM_BS=m -+CONFIG_CRC_ITU_T=y -+CONFIG_LIBCRC32C=y ---- /dev/null -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -0,0 +1,1265 @@ -+# 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_CPUSETS=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_PREEMPT_VOLUNTARY=y -+CONFIG_AEABI=y -+CONFIG_OABI_COMPAT=y -+# CONFIG_CPU_SW_DOMAIN_PAN is not set -+CONFIG_CLEANCACHE=y -+CONFIG_FRONTSWAP=y -+CONFIG_CMA=y -+CONFIG_ZSMALLOC=m -+CONFIG_PGTABLE_MAPPING=y -+CONFIG_UACCESS_WITH_MEMCPY=y -+CONFIG_SECCOMP=y -+# CONFIG_ATAGS is not set -+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_IEEE802154=m -+CONFIG_IEEE802154_6LOWPAN=m -+CONFIG_MAC802154=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_HCIUART=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_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_MTD=m -+CONFIG_MTD_BLOCK=m -+CONFIG_MTD_NAND=m -+CONFIG_MTD_UBI=m -+CONFIG_ZRAM=m -+CONFIG_ZRAM_LZ4_COMPRESS=y -+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_TI_ST=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_THIN_PROVISIONING=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_WL_MEDIATEK=y -+CONFIG_MT7601U=m -+CONFIG_RTL8192CU=m -+CONFIG_ZD1211RW=m -+CONFIG_MWIFIEX=m -+CONFIG_MWIFIEX_SDIO=m -+CONFIG_WIMAX_I2400M_USB=m -+CONFIG_IEEE802154_AT86RF230=m -+CONFIG_IEEE802154_MRF24J40=m -+CONFIG_IEEE802154_CC2520=m -+CONFIG_INPUT_POLLDEV=m -+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set -+CONFIG_INPUT_JOYDEV=m -+CONFIG_INPUT_EVDEV=m -+# CONFIG_KEYBOARD_ATKBD is not set -+CONFIG_KEYBOARD_GPIO=m -+# 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_JOYSTICK_RPISENSE=m -+CONFIG_INPUT_TOUCHSCREEN=y -+CONFIG_TOUCHSCREEN_ADS7846=m -+CONFIG_TOUCHSCREEN_EGALAX=m -+CONFIG_TOUCHSCREEN_FT6236=m -+CONFIG_TOUCHSCREEN_RPI_FT5406=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_BRCM_CHAR_DRIVERS=y -+CONFIG_BCM_VC_CMA=y -+CONFIG_BCM_VCIO=y -+CONFIG_BCM_VC_SM=y -+CONFIG_DEVPTS_MULTIPLE_INSTANCES=y -+# CONFIG_LEGACY_PTYS is not set -+# CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_8250=y -+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set -+CONFIG_SERIAL_8250_CONSOLE=y -+# CONFIG_SERIAL_8250_DMA is not set -+CONFIG_SERIAL_8250_NR_UARTS=1 -+CONFIG_SERIAL_8250_RUNTIME_UARTS=0 -+CONFIG_SERIAL_AMBA_PL011=y -+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+CONFIG_SERIAL_OF_PLATFORM=y -+CONFIG_TTY_PRINTK=y -+CONFIG_HW_RANDOM=y -+CONFIG_RAW_DRIVER=y -+CONFIG_I2C=y -+CONFIG_I2C_CHARDEV=m -+CONFIG_I2C_BCM2708=m -+CONFIG_SPI=y -+CONFIG_SPI_BCM2835=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_POWER_RESET=y -+CONFIG_POWER_RESET_GPIO=y -+CONFIG_HWMON=m -+CONFIG_SENSORS_SHT21=m -+CONFIG_SENSORS_SHTC1=m -+CONFIG_THERMAL=y -+CONFIG_THERMAL_BCM2835=y -+CONFIG_WATCHDOG=y -+CONFIG_BCM2835_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_FB_UDL=m -+CONFIG_FB_SSD1307=m -+CONFIG_FB_RPISENSE=m -+# 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_BCM2835_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_BCM2708_SOC_RASPIDAC3=m -+CONFIG_SND_SOC_ADAU1701=m -+CONFIG_SND_SOC_WM8804_I2C=m -+CONFIG_SND_SIMPLE_CARD=m -+CONFIG_SOUND_PRIME=m -+CONFIG_HIDRAW=y -+CONFIG_UHID=m -+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_DWC2=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_USB_GADGET=m -+CONFIG_USB_ZERO=m -+CONFIG_USB_AUDIO=m -+CONFIG_USB_ETH=m -+CONFIG_USB_GADGETFS=m -+CONFIG_USB_MASS_STORAGE=m -+CONFIG_USB_G_SERIAL=m -+CONFIG_USB_MIDI_GADGET=m -+CONFIG_USB_G_PRINTER=m -+CONFIG_USB_CDC_COMPOSITE=m -+CONFIG_USB_G_ACM_MS=m -+CONFIG_USB_G_MULTI=m -+CONFIG_USB_G_HID=m -+CONFIG_USB_G_WEBCAM=m -+CONFIG_MMC=y -+CONFIG_MMC_BLOCK_MINORS=32 -+CONFIG_MMC_BCM2835=y -+CONFIG_MMC_BCM2835_DMA=y -+CONFIG_MMC_BCM2835_SDHOST=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_BCM2835=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_ILI9163=m -+CONFIG_FB_TFT_ILI9320=m -+CONFIG_FB_TFT_ILI9325=m -+CONFIG_FB_TFT_ILI9340=m -+CONFIG_FB_TFT_ILI9341=m -+CONFIG_FB_TFT_ILI9481=m -+CONFIG_FB_TFT_ILI9486=m -+CONFIG_FB_TFT_PCD8544=m -+CONFIG_FB_TFT_RA8875=m -+CONFIG_FB_TFT_S6D02A1=m -+CONFIG_FB_TFT_S6D1121=m -+CONFIG_FB_TFT_SSD1289=m -+CONFIG_FB_TFT_SSD1306=m -+CONFIG_FB_TFT_SSD1331=m -+CONFIG_FB_TFT_SSD1351=m -+CONFIG_FB_TFT_ST7735R=m -+CONFIG_FB_TFT_TINYLCD=m -+CONFIG_FB_TFT_TLS8204=m -+CONFIG_FB_TFT_UC1701=m -+CONFIG_FB_TFT_UPD161704=m -+CONFIG_FB_TFT_WATTEROTT=m -+CONFIG_FB_FLEX=m -+CONFIG_FB_TFT_FBTFT_DEVICE=m -+CONFIG_MAILBOX=y -+CONFIG_BCM2835_MBOX=y -+# CONFIG_IOMMU_SUPPORT is not set -+CONFIG_EXTCON=m -+CONFIG_EXTCON_ARIZONA=m -+CONFIG_IIO=m -+CONFIG_IIO_BUFFER=y -+CONFIG_IIO_BUFFER_CB=m -+CONFIG_IIO_KFIFO_BUF=m -+CONFIG_MCP320X=m -+CONFIG_DHT11=m -+CONFIG_PWM_BCM2835=m -+CONFIG_RASPBERRYPI_FIRMWARE=y -+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_F2FS_FS=y -+CONFIG_FANOTIFY=y -+CONFIG_QFMT_V1=m -+CONFIG_QFMT_V2=m -+CONFIG_AUTOFS4_FS=y -+CONFIG_FUSE_FS=m -+CONFIG_CUSE=m -+CONFIG_OVERLAY_FS=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_JFFS2_FS=m -+CONFIG_JFFS2_SUMMARY=y -+CONFIG_UBIFS_FS=m -+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_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_UPCALL=y -+CONFIG_CIFS_XATTR=y -+CONFIG_CIFS_POSIX=y -+CONFIG_CIFS_ACL=y -+CONFIG_CIFS_DFS_UPCALL=y -+CONFIG_CIFS_SMB2=y -+CONFIG_CIFS_FSCACHE=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_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_SHA512=m -+CONFIG_CRYPTO_TGR192=m -+CONFIG_CRYPTO_WP512=m -+CONFIG_CRYPTO_CAST5=m -+CONFIG_CRYPTO_DES=y -+CONFIG_CRYPTO_USER_API_SKCIPHER=m -+# CONFIG_CRYPTO_HW is not set -+CONFIG_ARM_CRYPTO=y -+CONFIG_CRYPTO_SHA1_ARM=m -+CONFIG_CRYPTO_AES_ARM=m -+CONFIG_CRC_ITU_T=y -+CONFIG_LIBCRC32C=y diff --git a/target/linux/brcm2708/patches-4.4/0078-bcm2835-bcm2835_defconfig.patch b/target/linux/brcm2708/patches-4.4/0078-bcm2835-bcm2835_defconfig.patch deleted file mode 100644 index d4a60cd6ca..0000000000 --- a/target/linux/brcm2708/patches-4.4/0078-bcm2835-bcm2835_defconfig.patch +++ /dev/null @@ -1,1426 +0,0 @@ -From e5292198fc83805a9f4751fddeb802f69c0270f0 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= -Date: Wed, 29 Apr 2015 17:24:02 +0200 -Subject: [PATCH 078/170] bcm2835: bcm2835_defconfig -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Some options in bcm2835_defconfig are now the default and -some have changed. Update to keep functionality. - -No longer available: SCSI_MULTI_LUN and RESOURCE_COUNTERS. - -Signed-off-by: Noralf Trønnes - -bcm2835: bcm2835_defconfig enable MMC_BCM2835 - -Enable the downstream bcm2835-mmc driver and DMA support. - -Signed-off-by: Noralf Trønnes - -bcm2835: bcm2835_defconfig enable BCM2708_MBOX - -Enable the mailbox driver. - -Signed-off-by: Noralf Trønnes - -bcm2835: bcm2835_defconfig use FB_BCM2708 - -Enable the bcm2708 framebuffer driver. -Disable the simple framebuffer driver, which matches the -device handed over by u-boot. - -Signed-off-by: Noralf Trønnes - -bcm2835: Merge bcm2835_defconfig with bcmrpi_defconfig - -These commands where used to make this commit: - -./scripts/diffconfig -m arch/arm/configs/bcm2835_defconfig arch/arm/configs/bcmrpi_defconfig > merge.cfg - -cat << EOF > filter -CONFIG_ARCH_BCM2708 -CONFIG_BCM2708_DT -CONFIG_ARM_PATCH_PHYS_VIRT -CONFIG_PHYS_OFFSET -CONFIG_CMDLINE -CONFIG_BCM2708_WDT -CONFIG_HW_RANDOM_BCM2708 -CONFIG_I2C_BCM2708 -CONFIG_SPI_BCM2708 -CONFIG_SND_BCM2708_SOC_I2S -CONFIG_USB_DWCOTG -CONFIG_LIRC_RPI -EOF - -grep -F -v -f filter merge.cfg > filtered.cfg - -cat << EOF > added.cfg -CONFIG_WATCHDOG=y -CONFIG_BCM2835_WDT=y -CONFIG_MISC_FILESYSTEMS=y -CONFIG_SND_BCM2835_SOC_I2S=m -EOF - -ARCH=arm scripts/kconfig/merge_config.sh arch/arm/configs/bcm2835_defconfig filtered.cfg added.cfg -ARCH=arm make oldconfig - -ARCH=arm make savedefconfig -cp defconfig arch/arm/configs/bcm2835_defconfig - -rm merge.cfg filter filtered.cfg added.cfg defconfig - -ARCH=arm make bcm2835_defconfig -ARCH=arm make oldconfig - -Signed-off-by: Noralf Trønnes - -configs: Incorporate v4.1 dependency changes - -Commit 78e9b7de78bb53e8bc7f4c4a60ebacb250c0c190 added a -dependency on TI_ST instead of selecting it, disabling: -CONFIG_BT_WILINK=m -CONFIG_RADIO_WL128X=m - -Commit 652ccae5cc4e1305fb0a4619947f9ee89d8c7f5a added a -depency on ARM_CRYPTO, disabling: -CONFIG_CRYPTO_SHA*_ARM*=m -CONFIG_CRYPTO_AES_ARM*=m - -Signed-off-by: Noralf Trønnes - -Conflicts: - arch/arm/configs/bcm2709_defconfig - -bcm2835: Sync bcm2835_defconfig with bcmrpi_defconfig - -These commands where used to make this commit: - -: Get changed and new config values from a merge -./scripts/diffconfig -m arch/arm/configs/bcm2835_defconfig arch/arm/configs/bcmrpi_defconfig > merge.cfg - -: Remove these options -cat << EOF > filter -CONFIG_ARCH_BCM2708 -CONFIG_BCM2708_DT -CONFIG_ARM_PATCH_PHYS_VIRT -CONFIG_PHYS_OFFSET -CONFIG_CMDLINE -CONFIG_BCM2708_WDT -CONFIG_HW_RANDOM_BCM2708 -CONFIG_SPI_BCM2708 -EOF - -: Apply filter -grep -F -v -f filter merge.cfg > filtered.cfg - -: Add these options -: watchdog contains the restart/poweroff code. -cat << EOF > added.cfg -CONFIG_WATCHDOG=y -CONFIG_BCM2835_WDT=y -CONFIG_MISC_FILESYSTEMS=y -CONFIG_I2C_BCM2835=m -CONFIG_SND_BCM2835_SOC_I2S=m -EOF - -: Create new config -ARCH=arm scripts/kconfig/merge_config.sh arch/arm/configs/bcm2835_defconfig filtered.cfg added.cfg -: Verify -ARCH=arm make oldconfig - -: Update bcm2835_defconfig -ARCH=arm make savedefconfig -cp defconfig arch/arm/configs/bcm2835_defconfig - -: Clean up -rm merge.cfg filter filtered.cfg added.cfg defconfig - -Signed-off-by: Noralf Trønnes ---- - arch/arm/configs/bcm2835_defconfig | 1166 +++++++++++++++++++++++++++++++++++- - 1 file changed, 1140 insertions(+), 26 deletions(-) - ---- a/arch/arm/configs/bcm2835_defconfig -+++ b/arch/arm/configs/bcm2835_defconfig -@@ -1,105 +1,1103 @@ - # 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_LOG_BUF_SHIFT=18 - CONFIG_CGROUP_FREEZER=y - CONFIG_CGROUP_DEVICE=y - CONFIG_CPUSETS=y - CONFIG_CGROUP_CPUACCT=y --CONFIG_RESOURCE_COUNTERS=y -+CONFIG_MEMCG=y - CONFIG_CGROUP_PERF=y - CONFIG_CFS_BANDWIDTH=y - CONFIG_RT_GROUP_SCHED=y -+CONFIG_BLK_CGROUP=y - CONFIG_NAMESPACES=y - CONFIG_SCHED_AUTOGROUP=y --CONFIG_RELAY=y - CONFIG_BLK_DEV_INITRD=y --CONFIG_RD_BZIP2=y --CONFIG_RD_LZMA=y --CONFIG_RD_XZ=y --CONFIG_RD_LZO=y - CONFIG_CC_OPTIMIZE_FOR_SIZE=y --CONFIG_KALLSYMS_ALL=y - CONFIG_EMBEDDED=y - # CONFIG_COMPAT_BRK is not set - CONFIG_PROFILING=y --CONFIG_OPROFILE=y -+CONFIG_OPROFILE=m -+CONFIG_KPROBES=y - CONFIG_JUMP_LABEL=y -+CONFIG_CC_STACKPROTECTOR_REGULAR=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_MULTI_V6=y - # CONFIG_ARCH_MULTI_V7 is not set - CONFIG_ARCH_BCM=y - CONFIG_ARCH_BCM2835=y --CONFIG_PREEMPT_VOLUNTARY=y -+CONFIG_PREEMPT=y - CONFIG_AEABI=y -+CONFIG_OABI_COMPAT=y - CONFIG_KSM=y - CONFIG_CLEANCACHE=y -+CONFIG_FRONTSWAP=y -+CONFIG_CMA=y -+CONFIG_ZSMALLOC=m -+CONFIG_PGTABLE_MAPPING=y -+CONFIG_UACCESS_WITH_MEMCPY=y - CONFIG_SECCOMP=y --CONFIG_CC_STACKPROTECTOR=y -+CONFIG_ZBOOT_ROM_TEXT=0x0 -+CONFIG_ZBOOT_ROM_BSS=0x0 - CONFIG_KEXEC=y - CONFIG_CRASH_DUMP=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_VFP=y - # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set -+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_NETWORK_SECMARK=y - CONFIG_NETFILTER=y --CONFIG_CFG80211=y --CONFIG_MAC80211=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_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_STANDALONE is not set -+CONFIG_DMA_CMA=y -+CONFIG_CMA_SIZE_MBYTES=5 -+CONFIG_ZRAM=m -+CONFIG_ZRAM_LZ4_COMPRESS=y -+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_TI_ST=m - CONFIG_SCSI=y -+# CONFIG_SCSI_PROC_FS is not set - CONFIG_BLK_DEV_SD=y --CONFIG_SCSI_MULTI_LUN=y -+CONFIG_CHR_DEV_ST=m -+CONFIG_CHR_DEV_OSST=m -+CONFIG_BLK_DEV_SR=m -+CONFIG_CHR_DEV_SG=m - CONFIG_SCSI_CONSTANTS=y - CONFIG_SCSI_SCAN_ASYNC=y -+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_ZD1211RW=y --CONFIG_INPUT_EVDEV=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_WL_MEDIATEK=y -+CONFIG_MT7601U=m -+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_KEYBOARD_ATKBD is not set -+CONFIG_KEYBOARD_GPIO=m -+# 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_JOYSTICK_RPISENSE=m -+CONFIG_INPUT_TOUCHSCREEN=y -+CONFIG_TOUCHSCREEN_ADS7846=m -+CONFIG_TOUCHSCREEN_EGALAX=m -+CONFIG_TOUCHSCREEN_RPI_FT5406=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_BRCM_CHAR_DRIVERS=y -+CONFIG_BCM_VC_CMA=y -+CONFIG_BCM_VCIO=y -+CONFIG_BCM_VC_SM=y -+CONFIG_DEVPTS_MULTIPLE_INSTANCES=y - # CONFIG_LEGACY_PTYS is not set - # CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_8250=y -+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set -+CONFIG_SERIAL_8250_CONSOLE=y -+# CONFIG_SERIAL_8250_DMA is not set -+CONFIG_SERIAL_8250_NR_UARTS=1 -+CONFIG_SERIAL_8250_RUNTIME_UARTS=0 - CONFIG_SERIAL_AMBA_PL011=y - CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+CONFIG_SERIAL_OF_PLATFORM=y - CONFIG_TTY_PRINTK=y -+CONFIG_HW_RANDOM=y -+CONFIG_HW_RANDOM_BCM2835=m -+CONFIG_RAW_DRIVER=y - CONFIG_I2C=y --CONFIG_I2C_CHARDEV=y --CONFIG_I2C_BCM2835=y -+CONFIG_I2C_CHARDEV=m -+CONFIG_I2C_BCM2708=m -+CONFIG_I2C_BCM2835=m - CONFIG_SPI=y --CONFIG_SPI_BCM2835=y -+CONFIG_SPI_BCM2835=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_POWER_RESET=y -+CONFIG_POWER_RESET_GPIO=y - # CONFIG_HWMON is not set -+CONFIG_THERMAL=y -+CONFIG_THERMAL_BCM2835=y -+CONFIG_WATCHDOG=y -+CONFIG_BCM2835_WDT=y -+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_SIMPLE=y -+CONFIG_FB_BCM2708=y -+CONFIG_FB_SSD1307=m -+CONFIG_FB_RPISENSE=m -+# CONFIG_BACKLIGHT_GENERIC is not set -+CONFIG_BACKLIGHT_GPIO=m - CONFIG_FRAMEBUFFER_CONSOLE=y - CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=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_BCM2835_SOC_I2S=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_SOC_WM8804_I2C=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_DWC2=y -+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_BCM2835_SDHOST=y - CONFIG_MMC_SDHCI=y - CONFIG_MMC_SDHCI_PLTFM=y - CONFIG_MMC_SDHCI_BCM2835=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=y --CONFIG_LEDS_TRIGGER_CAMERA=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_BCM2835=y -+CONFIG_DMA_BCM2708=y -+CONFIG_UIO=m -+CONFIG_UIO_PDRV_GENIRQ=m - CONFIG_STAGING=y --CONFIG_USB_DWC2=y --CONFIG_USB_DWC2_HOST=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_MAILBOX=y -+CONFIG_BCM2835_MBOX=y - # CONFIG_IOMMU_SUPPORT is not set -+CONFIG_EXTCON=m -+CONFIG_EXTCON_ARIZONA=m -+CONFIG_IIO=m -+CONFIG_IIO_BUFFER=y -+CONFIG_IIO_BUFFER_CB=y -+CONFIG_IIO_KFIFO_BUF=m -+CONFIG_DHT11=m -+CONFIG_RASPBERRYPI_FIRMWARE=y - CONFIG_EXT2_FS=y - CONFIG_EXT2_FS_XATTR=y - CONFIG_EXT2_FS_POSIX_ACL=y -@@ -107,18 +1105,110 @@ CONFIG_EXT3_FS=y - CONFIG_EXT3_FS_POSIX_ACL=y - 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_F2FS_FS=y - 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_MISC_FILESYSTEMS is not set -+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_NFS_FS=y --CONFIG_NFSD=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_UPCALL=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=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=y -+CONFIG_DLM=m - CONFIG_PRINTK_TIME=y - CONFIG_BOOT_PRINTK_DELAY=y - CONFIG_DYNAMIC_DEBUG=y -@@ -128,14 +1218,38 @@ CONFIG_DEBUG_INFO=y - CONFIG_UNUSED_SYMBOLS=y - CONFIG_DEBUG_MEMORY_INIT=y - CONFIG_LOCKUP_DETECTOR=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_TEST_KSTRTOX=y - CONFIG_KGDB=y - CONFIG_KGDB_KDB=y -+CONFIG_KDB_KEYBOARD=y - CONFIG_STRICT_DEVMEM=y - CONFIG_DEBUG_LL=y - CONFIG_EARLY_PRINTK=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_SHA512=m -+CONFIG_CRYPTO_TGR192=m -+CONFIG_CRYPTO_WP512=m -+CONFIG_CRYPTO_CAST5=m -+CONFIG_CRYPTO_DES=y -+# CONFIG_CRYPTO_HW is not set -+CONFIG_ARM_CRYPTO=y -+CONFIG_CRYPTO_SHA1_ARM=m -+CONFIG_CRYPTO_AES_ARM=m -+CONFIG_CRC_ITU_T=y -+CONFIG_LIBCRC32C=y - # CONFIG_XZ_DEC_ARM is not set - # CONFIG_XZ_DEC_ARMTHUMB is not set diff --git a/target/linux/brcm2708/patches-4.4/0078-rpi-ft5406-Add-touchscreen-driver-for-pi-LCD-display.patch b/target/linux/brcm2708/patches-4.4/0078-rpi-ft5406-Add-touchscreen-driver-for-pi-LCD-display.patch new file mode 100644 index 0000000000..9a1445da28 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0078-rpi-ft5406-Add-touchscreen-driver-for-pi-LCD-display.patch @@ -0,0 +1,290 @@ +From d46fbf49354f8c82cc216d56d9addad113703c50 Mon Sep 17 00:00:00 2001 +From: Gordon Hollingworth +Date: Tue, 12 May 2015 14:47:56 +0100 +Subject: [PATCH 078/232] rpi-ft5406: Add touchscreen driver for pi LCD display + +Fix driver detection failure Check that the buffer response is non-zero meaning the touchscreen was detected + +rpi-ft5406: Use firmware API +--- + drivers/input/touchscreen/Kconfig | 7 + + drivers/input/touchscreen/Makefile | 1 + + drivers/input/touchscreen/rpi-ft5406.c | 246 +++++++++++++++++++++++++++++++++ + 3 files changed, 254 insertions(+) + create mode 100644 drivers/input/touchscreen/rpi-ft5406.c + +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -608,6 +608,13 @@ config TOUCHSCREEN_EDT_FT5X06 + To compile this driver as a module, choose M here: the + module will be called edt-ft5x06. + ++config TOUCHSCREEN_RPI_FT5406 ++ tristate "Raspberry Pi FT5406 driver" ++ depends on RASPBERRYPI_FIRMWARE ++ help ++ Say Y here to enable the Raspberry Pi memory based FT5406 device ++ ++ + config TOUCHSCREEN_MIGOR + tristate "Renesas MIGO-R touchscreen" + depends on SH_MIGOR && I2C +--- a/drivers/input/touchscreen/Makefile ++++ b/drivers/input/touchscreen/Makefile +@@ -29,6 +29,7 @@ obj-$(CONFIG_TOUCHSCREEN_DA9034) += da90 + obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o + obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o + obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06) += edt-ft5x06.o ++obj-$(CONFIG_TOUCHSCREEN_RPI_FT5406) += rpi-ft5406.o + obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o + obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o + obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o +--- /dev/null ++++ b/drivers/input/touchscreen/rpi-ft5406.c +@@ -0,0 +1,246 @@ ++/* ++ * Driver for memory based ft5406 touchscreen ++ * ++ * Copyright (C) 2015 Raspberry Pi ++ * ++ * ++ * 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 ++ ++#define MAXIMUM_SUPPORTED_POINTS 10 ++struct ft5406_regs { ++ uint8_t device_mode; ++ uint8_t gesture_id; ++ uint8_t num_points; ++ struct ft5406_touch { ++ uint8_t xh; ++ uint8_t xl; ++ uint8_t yh; ++ uint8_t yl; ++ uint8_t res1; ++ uint8_t res2; ++ } point[MAXIMUM_SUPPORTED_POINTS]; ++}; ++ ++#define SCREEN_WIDTH 800 ++#define SCREEN_HEIGHT 480 ++ ++struct ft5406 { ++ struct platform_device * pdev; ++ struct input_dev * input_dev; ++ void __iomem * ts_base; ++ struct ft5406_regs * regs; ++ struct task_struct * thread; ++}; ++ ++/* Thread to poll for touchscreen events ++ * ++ * This thread polls the memory based register copy of the ft5406 registers ++ * using the number of points register to know whether the copy has been ++ * updated (we write 99 to the memory copy, the GPU will write between ++ * 0 - 10 points) ++ */ ++static int ft5406_thread(void *arg) ++{ ++ struct ft5406 *ts = (struct ft5406 *) arg; ++ struct ft5406_regs regs; ++ int known_ids = 0; ++ ++ while(!kthread_should_stop()) ++ { ++ // 60fps polling ++ msleep_interruptible(17); ++ memcpy_fromio(®s, ts->regs, sizeof(*ts->regs)); ++ writel(99, &ts->regs->num_points); ++ // Do not output if theres no new information (num_points is 99) ++ // or we have no touch points and don't need to release any ++ if(!(regs.num_points == 99 || (regs.num_points == 0 && known_ids == 0))) ++ { ++ int i; ++ int modified_ids = 0, released_ids; ++ for(i = 0; i < regs.num_points; i++) ++ { ++ int x = (((int) regs.point[i].xh & 0xf) << 8) + regs.point[i].xl; ++ int y = (((int) regs.point[i].yh & 0xf) << 8) + regs.point[i].yl; ++ int touchid = (regs.point[i].yh >> 4) & 0xf; ++ ++ modified_ids |= 1 << touchid; ++ ++ if(!((1 << touchid) & known_ids)) ++ dev_dbg(&ts->pdev->dev, "x = %d, y = %d, touchid = %d\n", x, y, touchid); ++ ++ input_mt_slot(ts->input_dev, touchid); ++ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); ++ ++ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x); ++ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y); ++ ++ } ++ ++ released_ids = known_ids & ~modified_ids; ++ for(i = 0; released_ids && i < MAXIMUM_SUPPORTED_POINTS; i++) ++ { ++ if(released_ids & (1<pdev->dev, "Released %d, known = %x modified = %x\n", i, known_ids, modified_ids); ++ input_mt_slot(ts->input_dev, i); ++ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); ++ modified_ids &= ~(1 << i); ++ } ++ } ++ known_ids = modified_ids; ++ ++ input_mt_report_pointer_emulation(ts->input_dev, true); ++ input_sync(ts->input_dev); ++ } ++ ++ } ++ ++ return 0; ++} ++ ++static int ft5406_probe(struct platform_device *pdev) ++{ ++ int ret; ++ struct input_dev * input_dev = input_allocate_device(); ++ struct ft5406 * ts; ++ struct device_node *fw_node; ++ struct rpi_firmware *fw; ++ u32 touchbuf; ++ ++ dev_info(&pdev->dev, "Probing device\n"); ++ ++ fw_node = of_parse_phandle(pdev->dev.of_node, "firmware", 0); ++ if (!fw_node) { ++ dev_err(&pdev->dev, "Missing firmware node\n"); ++ return -ENOENT; ++ } ++ ++ fw = rpi_firmware_get(fw_node); ++ if (!fw) ++ return -EPROBE_DEFER; ++ ++ ret = rpi_firmware_property(fw, RPI_FIRMWARE_FRAMEBUFFER_GET_TOUCHBUF, ++ &touchbuf, sizeof(touchbuf)); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to get touch buffer\n"); ++ return ret; ++ } ++ ++ if (!touchbuf) { ++ dev_err(&pdev->dev, "Touchscreen not detected\n"); ++ return -ENODEV; ++ } ++ ++ dev_dbg(&pdev->dev, "Got TS buffer 0x%x\n", touchbuf); ++ ++ ts = kzalloc(sizeof(struct ft5406), GFP_KERNEL); ++ ++ if (!ts || !input_dev) { ++ ret = -ENOMEM; ++ dev_err(&pdev->dev, "Failed to allocate memory\n"); ++ return ret; ++ } ++ ts->input_dev = input_dev; ++ platform_set_drvdata(pdev, ts); ++ ts->pdev = pdev; ++ ++ input_dev->name = "FT5406 memory based driver"; ++ ++ __set_bit(EV_KEY, input_dev->evbit); ++ __set_bit(EV_SYN, input_dev->evbit); ++ __set_bit(EV_ABS, input_dev->evbit); ++ ++ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, ++ SCREEN_WIDTH, 0, 0); ++ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, ++ SCREEN_HEIGHT, 0, 0); ++ ++ input_mt_init_slots(input_dev, MAXIMUM_SUPPORTED_POINTS, INPUT_MT_DIRECT); ++ ++ input_set_drvdata(input_dev, ts); ++ ++ ret = input_register_device(input_dev); ++ if (ret) { ++ dev_err(&pdev->dev, "could not register input device, %d\n", ++ ret); ++ return ret; ++ } ++ ++ // mmap the physical memory ++ touchbuf &= ~0xc0000000; ++ ts->ts_base = ioremap(touchbuf, sizeof(*ts->regs)); ++ if(ts->ts_base == NULL) ++ { ++ dev_err(&pdev->dev, "Failed to map physical address\n"); ++ input_unregister_device(input_dev); ++ kzfree(ts); ++ return -ENOMEM; ++ } ++ ++ ts->regs = (struct ft5406_regs *) ts->ts_base; ++ ++ // create thread to poll the touch events ++ ts->thread = kthread_run(ft5406_thread, ts, "ft5406"); ++ if(ts->thread == NULL) ++ { ++ dev_err(&pdev->dev, "Failed to create kernel thread"); ++ iounmap(ts->ts_base); ++ input_unregister_device(input_dev); ++ kzfree(ts); ++ } ++ ++ return 0; ++} ++ ++static int ft5406_remove(struct platform_device *pdev) ++{ ++ struct ft5406 *ts = (struct ft5406 *) platform_get_drvdata(pdev); ++ ++ dev_info(&pdev->dev, "Removing rpi-ft5406\n"); ++ ++ kthread_stop(ts->thread); ++ iounmap(ts->ts_base); ++ input_unregister_device(ts->input_dev); ++ kzfree(ts); ++ ++ return 0; ++} ++ ++static const struct of_device_id ft5406_match[] = { ++ { .compatible = "rpi,rpi-ft5406", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ft5406_match); ++ ++static struct platform_driver ft5406_driver = { ++ .driver = { ++ .name = "rpi-ft5406", ++ .owner = THIS_MODULE, ++ .of_match_table = ft5406_match, ++ }, ++ .probe = ft5406_probe, ++ .remove = ft5406_remove, ++}; ++ ++module_platform_driver(ft5406_driver); ++ ++MODULE_AUTHOR("Gordon Hollingworth"); ++MODULE_DESCRIPTION("Touchscreen driver for memory based FT5406"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/brcm2708/patches-4.4/0079-Improve-__copy_to_user-and-__copy_from_user-performa.patch b/target/linux/brcm2708/patches-4.4/0079-Improve-__copy_to_user-and-__copy_from_user-performa.patch new file mode 100644 index 0000000000..ea6aac7fad --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0079-Improve-__copy_to_user-and-__copy_from_user-performa.patch @@ -0,0 +1,1510 @@ +From 4514b02d95de8be6d6298ee20a5204815cf669d0 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 13 Oct 2014 11:47:53 +0100 +Subject: [PATCH 079/232] 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: Add mmiocpy/set aliases for memcpy/set + +See: https://github.com/raspberrypi/linux/issues/1082 +--- + arch/arm/include/asm/string.h | 5 + + arch/arm/include/asm/uaccess.h | 3 + + 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 | 61 +++++ + arch/arm/lib/memcpymove.h | 506 +++++++++++++++++++++++++++++++++++++ + arch/arm/lib/memmove_rpi.S | 61 +++++ + arch/arm/lib/memset_rpi.S | 123 +++++++++ + arch/arm/lib/uaccess_with_memcpy.c | 112 +++++++- + 12 files changed, 1365 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 + +--- a/arch/arm/include/asm/string.h ++++ b/arch/arm/include/asm/string.h +@@ -24,6 +24,11 @@ extern void * memchr(const void *, int, + #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) \ +--- a/arch/arm/include/asm/uaccess.h ++++ b/arch/arm/include/asm/uaccess.h +@@ -493,6 +493,9 @@ do { \ + extern unsigned long __must_check + arm_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); ++ + static inline unsigned long __must_check + __copy_from_user(void *to, const void __user *from, unsigned long n) + { +--- a/arch/arm/lib/Makefile ++++ b/arch/arm/lib/Makefile +@@ -6,9 +6,8 @@ + + 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 \ +@@ -18,6 +17,16 @@ lib-y := backtrace.o changebit.o csumip + mmu-y := clear_user.o copy_page.o getuser.o putuser.o \ + copy_from_user.o copy_to_user.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 ++ + # using lib_ here won't override already available weak symbols + obj-$(CONFIG_UACCESS_WITH_MEMCPY) += uaccess_with_memcpy.o + +--- /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 +--- a/arch/arm/lib/copy_from_user.S ++++ b/arch/arm/lib/copy_from_user.S +@@ -89,11 +89,13 @@ + + .text + +-ENTRY(arm_copy_from_user) ++ENTRY(__copy_from_user_std) ++WEAK(arm_copy_from_user) + + #include "copy_template.S" + + ENDPROC(arm_copy_from_user) ++ENDPROC(__copy_from_user_std) + + .pushsection .fixup,"ax" + .align 0 +--- /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); +--- /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) +--- /dev/null ++++ b/arch/arm/lib/memcpy_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 *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(mmiocpy) ++ENTRY(memcpy) ++ memcpy 0 ++ENDPROC(memcpy) ++ENDPROC(mmiocpy) +--- /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 +--- /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) +--- /dev/null ++++ b/arch/arm/lib/memset_rpi.S +@@ -0,0 +1,123 @@ ++/* ++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(mmioset) ++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) ++ENDPROC(mmioset) +--- 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 *_a + 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) + { + unsigned long ua_flags; +@@ -138,6 +183,54 @@ out: + 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 + arm_copy_to_user(void __user *to, const void *from, unsigned long n) + { +@@ -148,7 +241,7 @@ arm_copy_to_user(void __user *to, const + * 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) { + unsigned long ua_flags = uaccess_save_and_enable(); + n = __copy_to_user_std(to, from, n); + uaccess_restore(ua_flags); +@@ -157,6 +250,21 @@ arm_copy_to_user(void __user *to, const + } + return n; + } ++ ++unsigned long __must_check ++arm_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) diff --git a/target/linux/brcm2708/patches-4.4/0079-rpi-ft5406-Add-touchscreen-driver-for-pi-LCD-display.patch b/target/linux/brcm2708/patches-4.4/0079-rpi-ft5406-Add-touchscreen-driver-for-pi-LCD-display.patch deleted file mode 100644 index 0b3f67848d..0000000000 --- a/target/linux/brcm2708/patches-4.4/0079-rpi-ft5406-Add-touchscreen-driver-for-pi-LCD-display.patch +++ /dev/null @@ -1,290 +0,0 @@ -From 6fb12ee15eb9b0f5b6fea66c861a9db8ec6671ed Mon Sep 17 00:00:00 2001 -From: Gordon Hollingworth -Date: Tue, 12 May 2015 14:47:56 +0100 -Subject: [PATCH 079/170] rpi-ft5406: Add touchscreen driver for pi LCD display - -Fix driver detection failure Check that the buffer response is non-zero meaning the touchscreen was detected - -rpi-ft5406: Use firmware API ---- - drivers/input/touchscreen/Kconfig | 7 + - drivers/input/touchscreen/Makefile | 1 + - drivers/input/touchscreen/rpi-ft5406.c | 246 +++++++++++++++++++++++++++++++++ - 3 files changed, 254 insertions(+) - create mode 100644 drivers/input/touchscreen/rpi-ft5406.c - ---- a/drivers/input/touchscreen/Kconfig -+++ b/drivers/input/touchscreen/Kconfig -@@ -608,6 +608,13 @@ config TOUCHSCREEN_EDT_FT5X06 - To compile this driver as a module, choose M here: the - module will be called edt-ft5x06. - -+config TOUCHSCREEN_RPI_FT5406 -+ tristate "Raspberry Pi FT5406 driver" -+ depends on RASPBERRYPI_FIRMWARE -+ help -+ Say Y here to enable the Raspberry Pi memory based FT5406 device -+ -+ - config TOUCHSCREEN_MIGOR - tristate "Renesas MIGO-R touchscreen" - depends on SH_MIGOR && I2C ---- a/drivers/input/touchscreen/Makefile -+++ b/drivers/input/touchscreen/Makefile -@@ -29,6 +29,7 @@ obj-$(CONFIG_TOUCHSCREEN_DA9034) += da90 - obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o - obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o - obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06) += edt-ft5x06.o -+obj-$(CONFIG_TOUCHSCREEN_RPI_FT5406) += rpi-ft5406.o - obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o - obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o - obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o ---- /dev/null -+++ b/drivers/input/touchscreen/rpi-ft5406.c -@@ -0,0 +1,246 @@ -+/* -+ * Driver for memory based ft5406 touchscreen -+ * -+ * Copyright (C) 2015 Raspberry Pi -+ * -+ * -+ * 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 -+ -+#define MAXIMUM_SUPPORTED_POINTS 10 -+struct ft5406_regs { -+ uint8_t device_mode; -+ uint8_t gesture_id; -+ uint8_t num_points; -+ struct ft5406_touch { -+ uint8_t xh; -+ uint8_t xl; -+ uint8_t yh; -+ uint8_t yl; -+ uint8_t res1; -+ uint8_t res2; -+ } point[MAXIMUM_SUPPORTED_POINTS]; -+}; -+ -+#define SCREEN_WIDTH 800 -+#define SCREEN_HEIGHT 480 -+ -+struct ft5406 { -+ struct platform_device * pdev; -+ struct input_dev * input_dev; -+ void __iomem * ts_base; -+ struct ft5406_regs * regs; -+ struct task_struct * thread; -+}; -+ -+/* Thread to poll for touchscreen events -+ * -+ * This thread polls the memory based register copy of the ft5406 registers -+ * using the number of points register to know whether the copy has been -+ * updated (we write 99 to the memory copy, the GPU will write between -+ * 0 - 10 points) -+ */ -+static int ft5406_thread(void *arg) -+{ -+ struct ft5406 *ts = (struct ft5406 *) arg; -+ struct ft5406_regs regs; -+ int known_ids = 0; -+ -+ while(!kthread_should_stop()) -+ { -+ // 60fps polling -+ msleep_interruptible(17); -+ memcpy_fromio(®s, ts->regs, sizeof(*ts->regs)); -+ writel(99, &ts->regs->num_points); -+ // Do not output if theres no new information (num_points is 99) -+ // or we have no touch points and don't need to release any -+ if(!(regs.num_points == 99 || (regs.num_points == 0 && known_ids == 0))) -+ { -+ int i; -+ int modified_ids = 0, released_ids; -+ for(i = 0; i < regs.num_points; i++) -+ { -+ int x = (((int) regs.point[i].xh & 0xf) << 8) + regs.point[i].xl; -+ int y = (((int) regs.point[i].yh & 0xf) << 8) + regs.point[i].yl; -+ int touchid = (regs.point[i].yh >> 4) & 0xf; -+ -+ modified_ids |= 1 << touchid; -+ -+ if(!((1 << touchid) & known_ids)) -+ dev_dbg(&ts->pdev->dev, "x = %d, y = %d, touchid = %d\n", x, y, touchid); -+ -+ input_mt_slot(ts->input_dev, touchid); -+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); -+ -+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x); -+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y); -+ -+ } -+ -+ released_ids = known_ids & ~modified_ids; -+ for(i = 0; released_ids && i < MAXIMUM_SUPPORTED_POINTS; i++) -+ { -+ if(released_ids & (1<pdev->dev, "Released %d, known = %x modified = %x\n", i, known_ids, modified_ids); -+ input_mt_slot(ts->input_dev, i); -+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); -+ modified_ids &= ~(1 << i); -+ } -+ } -+ known_ids = modified_ids; -+ -+ input_mt_report_pointer_emulation(ts->input_dev, true); -+ input_sync(ts->input_dev); -+ } -+ -+ } -+ -+ return 0; -+} -+ -+static int ft5406_probe(struct platform_device *pdev) -+{ -+ int ret; -+ struct input_dev * input_dev = input_allocate_device(); -+ struct ft5406 * ts; -+ struct device_node *fw_node; -+ struct rpi_firmware *fw; -+ u32 touchbuf; -+ -+ dev_info(&pdev->dev, "Probing device\n"); -+ -+ fw_node = of_parse_phandle(pdev->dev.of_node, "firmware", 0); -+ if (!fw_node) { -+ dev_err(&pdev->dev, "Missing firmware node\n"); -+ return -ENOENT; -+ } -+ -+ fw = rpi_firmware_get(fw_node); -+ if (!fw) -+ return -EPROBE_DEFER; -+ -+ ret = rpi_firmware_property(fw, RPI_FIRMWARE_FRAMEBUFFER_GET_TOUCHBUF, -+ &touchbuf, sizeof(touchbuf)); -+ if (ret) { -+ dev_err(&pdev->dev, "Failed to get touch buffer\n"); -+ return ret; -+ } -+ -+ if (!touchbuf) { -+ dev_err(&pdev->dev, "Touchscreen not detected\n"); -+ return -ENODEV; -+ } -+ -+ dev_dbg(&pdev->dev, "Got TS buffer 0x%x\n", touchbuf); -+ -+ ts = kzalloc(sizeof(struct ft5406), GFP_KERNEL); -+ -+ if (!ts || !input_dev) { -+ ret = -ENOMEM; -+ dev_err(&pdev->dev, "Failed to allocate memory\n"); -+ return ret; -+ } -+ ts->input_dev = input_dev; -+ platform_set_drvdata(pdev, ts); -+ ts->pdev = pdev; -+ -+ input_dev->name = "FT5406 memory based driver"; -+ -+ __set_bit(EV_KEY, input_dev->evbit); -+ __set_bit(EV_SYN, input_dev->evbit); -+ __set_bit(EV_ABS, input_dev->evbit); -+ -+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, -+ SCREEN_WIDTH, 0, 0); -+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, -+ SCREEN_HEIGHT, 0, 0); -+ -+ input_mt_init_slots(input_dev, MAXIMUM_SUPPORTED_POINTS, INPUT_MT_DIRECT); -+ -+ input_set_drvdata(input_dev, ts); -+ -+ ret = input_register_device(input_dev); -+ if (ret) { -+ dev_err(&pdev->dev, "could not register input device, %d\n", -+ ret); -+ return ret; -+ } -+ -+ // mmap the physical memory -+ touchbuf &= ~0xc0000000; -+ ts->ts_base = ioremap(touchbuf, sizeof(*ts->regs)); -+ if(ts->ts_base == NULL) -+ { -+ dev_err(&pdev->dev, "Failed to map physical address\n"); -+ input_unregister_device(input_dev); -+ kzfree(ts); -+ return -ENOMEM; -+ } -+ -+ ts->regs = (struct ft5406_regs *) ts->ts_base; -+ -+ // create thread to poll the touch events -+ ts->thread = kthread_run(ft5406_thread, ts, "ft5406"); -+ if(ts->thread == NULL) -+ { -+ dev_err(&pdev->dev, "Failed to create kernel thread"); -+ iounmap(ts->ts_base); -+ input_unregister_device(input_dev); -+ kzfree(ts); -+ } -+ -+ return 0; -+} -+ -+static int ft5406_remove(struct platform_device *pdev) -+{ -+ struct ft5406 *ts = (struct ft5406 *) platform_get_drvdata(pdev); -+ -+ dev_info(&pdev->dev, "Removing rpi-ft5406\n"); -+ -+ kthread_stop(ts->thread); -+ iounmap(ts->ts_base); -+ input_unregister_device(ts->input_dev); -+ kzfree(ts); -+ -+ return 0; -+} -+ -+static const struct of_device_id ft5406_match[] = { -+ { .compatible = "rpi,rpi-ft5406", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, ft5406_match); -+ -+static struct platform_driver ft5406_driver = { -+ .driver = { -+ .name = "rpi-ft5406", -+ .owner = THIS_MODULE, -+ .of_match_table = ft5406_match, -+ }, -+ .probe = ft5406_probe, -+ .remove = ft5406_remove, -+}; -+ -+module_platform_driver(ft5406_driver); -+ -+MODULE_AUTHOR("Gordon Hollingworth"); -+MODULE_DESCRIPTION("Touchscreen driver for memory based FT5406"); -+MODULE_LICENSE("GPL"); diff --git a/target/linux/brcm2708/patches-4.4/0080-Improve-__copy_to_user-and-__copy_from_user-performa.patch b/target/linux/brcm2708/patches-4.4/0080-Improve-__copy_to_user-and-__copy_from_user-performa.patch deleted file mode 100644 index d09b5ae86f..0000000000 --- a/target/linux/brcm2708/patches-4.4/0080-Improve-__copy_to_user-and-__copy_from_user-performa.patch +++ /dev/null @@ -1,1510 +0,0 @@ -From b57a6269e03fe055f78ab50add6e2c68d43254e2 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 13 Oct 2014 11:47:53 +0100 -Subject: [PATCH 080/170] 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: Add mmiocpy/set aliases for memcpy/set - -See: https://github.com/raspberrypi/linux/issues/1082 ---- - arch/arm/include/asm/string.h | 5 + - arch/arm/include/asm/uaccess.h | 3 + - 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 | 61 +++++ - arch/arm/lib/memcpymove.h | 506 +++++++++++++++++++++++++++++++++++++ - arch/arm/lib/memmove_rpi.S | 61 +++++ - arch/arm/lib/memset_rpi.S | 123 +++++++++ - arch/arm/lib/uaccess_with_memcpy.c | 112 +++++++- - 12 files changed, 1365 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 - ---- a/arch/arm/include/asm/string.h -+++ b/arch/arm/include/asm/string.h -@@ -24,6 +24,11 @@ extern void * memchr(const void *, int, - #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) \ ---- a/arch/arm/include/asm/uaccess.h -+++ b/arch/arm/include/asm/uaccess.h -@@ -493,6 +493,9 @@ do { \ - extern unsigned long __must_check - arm_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); -+ - static inline unsigned long __must_check - __copy_from_user(void *to, const void __user *from, unsigned long n) - { ---- a/arch/arm/lib/Makefile -+++ b/arch/arm/lib/Makefile -@@ -6,9 +6,8 @@ - - 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 \ -@@ -18,6 +17,16 @@ lib-y := backtrace.o changebit.o csumip - mmu-y := clear_user.o copy_page.o getuser.o putuser.o \ - copy_from_user.o copy_to_user.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 -+ - # using lib_ here won't override already available weak symbols - obj-$(CONFIG_UACCESS_WITH_MEMCPY) += uaccess_with_memcpy.o - ---- /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 ---- a/arch/arm/lib/copy_from_user.S -+++ b/arch/arm/lib/copy_from_user.S -@@ -89,11 +89,13 @@ - - .text - --ENTRY(arm_copy_from_user) -+ENTRY(__copy_from_user_std) -+WEAK(arm_copy_from_user) - - #include "copy_template.S" - - ENDPROC(arm_copy_from_user) -+ENDPROC(__copy_from_user_std) - - .pushsection .fixup,"ax" - .align 0 ---- /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); ---- /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) ---- /dev/null -+++ b/arch/arm/lib/memcpy_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 *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(mmiocpy) -+ENTRY(memcpy) -+ memcpy 0 -+ENDPROC(memcpy) -+ENDPROC(mmiocpy) ---- /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 ---- /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) ---- /dev/null -+++ b/arch/arm/lib/memset_rpi.S -@@ -0,0 +1,123 @@ -+/* -+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(mmioset) -+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) -+ENDPROC(mmioset) ---- 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 *_a - 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) - { - unsigned long ua_flags; -@@ -138,6 +183,54 @@ out: - 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 - arm_copy_to_user(void __user *to, const void *from, unsigned long n) - { -@@ -148,7 +241,7 @@ arm_copy_to_user(void __user *to, const - * 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) { - unsigned long ua_flags = uaccess_save_and_enable(); - n = __copy_to_user_std(to, from, n); - uaccess_restore(ua_flags); -@@ -157,6 +250,21 @@ arm_copy_to_user(void __user *to, const - } - return n; - } -+ -+unsigned long __must_check -+arm_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) diff --git a/target/linux/brcm2708/patches-4.4/0080-gpio-poweroff-Allow-it-to-work-on-Raspberry-Pi.patch b/target/linux/brcm2708/patches-4.4/0080-gpio-poweroff-Allow-it-to-work-on-Raspberry-Pi.patch new file mode 100644 index 0000000000..b8edcc8596 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0080-gpio-poweroff-Allow-it-to-work-on-Raspberry-Pi.patch @@ -0,0 +1,35 @@ +From 11e9f3f8268e79dd28450eacffe1d7484f2a7361 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 25 Jun 2015 12:16:11 +0100 +Subject: [PATCH 080/232] gpio-poweroff: Allow it to work on Raspberry Pi + +The Raspberry Pi firmware manages the power-down and reboot +process. To do this it installs a pm_power_off handler, causing +the gpio-poweroff module to abort the probe function. + +This patch introduces a "force" DT property that overrides that +behaviour, and also adds a DT overlay to enable and control it. + +Note that running in an active-low configuration (DT parameter +"active_low") requires a custom dt-blob.bin and probably won't +allow a reboot without switching off, so an external inversion +of the trigger signal may be preferable. +--- + drivers/power/reset/gpio-poweroff.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/power/reset/gpio-poweroff.c ++++ b/drivers/power/reset/gpio-poweroff.c +@@ -49,9 +49,11 @@ static int gpio_poweroff_probe(struct pl + { + bool input = false; + enum gpiod_flags flags; ++ bool force = false; + + /* If a pm_power_off function has already been added, leave it alone */ +- if (pm_power_off != NULL) { ++ force = of_property_read_bool(pdev->dev.of_node, "force"); ++ if (!force && (pm_power_off != NULL)) { + dev_err(&pdev->dev, + "%s: pm_power_off function already registered", + __func__); diff --git a/target/linux/brcm2708/patches-4.4/0081-gpio-poweroff-Allow-it-to-work-on-Raspberry-Pi.patch b/target/linux/brcm2708/patches-4.4/0081-gpio-poweroff-Allow-it-to-work-on-Raspberry-Pi.patch deleted file mode 100644 index 804813246d..0000000000 --- a/target/linux/brcm2708/patches-4.4/0081-gpio-poweroff-Allow-it-to-work-on-Raspberry-Pi.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 44d43099a94c8b2db818c80c67789ef5f9857b81 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Thu, 25 Jun 2015 12:16:11 +0100 -Subject: [PATCH 081/170] gpio-poweroff: Allow it to work on Raspberry Pi - -The Raspberry Pi firmware manages the power-down and reboot -process. To do this it installs a pm_power_off handler, causing -the gpio-poweroff module to abort the probe function. - -This patch introduces a "force" DT property that overrides that -behaviour, and also adds a DT overlay to enable and control it. - -Note that running in an active-low configuration (DT parameter -"active_low") requires a custom dt-blob.bin and probably won't -allow a reboot without switching off, so an external inversion -of the trigger signal may be preferable. ---- - drivers/power/reset/gpio-poweroff.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - ---- a/drivers/power/reset/gpio-poweroff.c -+++ b/drivers/power/reset/gpio-poweroff.c -@@ -49,9 +49,11 @@ static int gpio_poweroff_probe(struct pl - { - bool input = false; - enum gpiod_flags flags; -+ bool force = false; - - /* If a pm_power_off function has already been added, leave it alone */ -- if (pm_power_off != NULL) { -+ force = of_property_read_bool(pdev->dev.of_node, "force"); -+ if (!force && (pm_power_off != NULL)) { - dev_err(&pdev->dev, - "%s: pm_power_off function already registered", - __func__); diff --git a/target/linux/brcm2708/patches-4.4/0081-spidev-Add-spidev-compatible-string-to-silence-warni.patch b/target/linux/brcm2708/patches-4.4/0081-spidev-Add-spidev-compatible-string-to-silence-warni.patch new file mode 100644 index 0000000000..d62a0d2461 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0081-spidev-Add-spidev-compatible-string-to-silence-warni.patch @@ -0,0 +1,21 @@ +From 958d09ad7f70ff5dd38a6c60b9e3f103e125168a Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 14 Jul 2015 10:26:09 +0100 +Subject: [PATCH 081/232] spidev: Add "spidev" compatible string to silence + warning + +See: https://github.com/raspberrypi/linux/issues/1054 +--- + drivers/spi/spidev.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/spi/spidev.c ++++ b/drivers/spi/spidev.c +@@ -695,6 +695,7 @@ static struct class *spidev_class; + static const struct of_device_id spidev_dt_ids[] = { + { .compatible = "rohm,dh2228fv" }, + { .compatible = "lineartechnology,ltc2488" }, ++ { .compatible = "spidev" }, + {}, + }; + MODULE_DEVICE_TABLE(of, spidev_dt_ids); diff --git a/target/linux/brcm2708/patches-4.4/0082-scripts-dtc-Add-overlay-support.patch b/target/linux/brcm2708/patches-4.4/0082-scripts-dtc-Add-overlay-support.patch new file mode 100644 index 0000000000..4f882e20f6 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0082-scripts-dtc-Add-overlay-support.patch @@ -0,0 +1,4389 @@ +From 462447ecf49eb6dd6d77f3d21b272dd4fa68bee9 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 14 Jul 2015 17:00:18 +0100 +Subject: [PATCH 082/232] scripts/dtc: Add overlay support + +--- + scripts/dtc/checks.c | 119 ++- + scripts/dtc/dtc-lexer.l | 5 + + scripts/dtc/dtc-lexer.lex.c_shipped | 490 ++++----- + scripts/dtc/dtc-parser.tab.c_shipped | 1896 +++++++++++++++++++--------------- + scripts/dtc/dtc-parser.tab.h_shipped | 107 +- + scripts/dtc/dtc-parser.y | 23 +- + scripts/dtc/dtc.c | 9 +- + scripts/dtc/dtc.h | 38 + + scripts/dtc/flattree.c | 141 ++- + scripts/dtc/version_gen.h | 2 +- + 10 files changed, 1685 insertions(+), 1145 deletions(-) + +--- a/scripts/dtc/checks.c ++++ b/scripts/dtc/checks.c +@@ -458,21 +458,91 @@ static void fixup_phandle_references(str + struct node *node, struct property *prop) + { + struct marker *m = prop->val.markers; ++ struct fixup *f, **fp; ++ struct fixup_entry *fe, **fep; + struct node *refnode; + cell_t phandle; ++ int has_phandle_refs; ++ ++ has_phandle_refs = 0; ++ for_each_marker_of_type(m, REF_PHANDLE) { ++ has_phandle_refs = 1; ++ break; ++ } ++ ++ if (!has_phandle_refs) ++ return; + + for_each_marker_of_type(m, REF_PHANDLE) { + assert(m->offset + sizeof(cell_t) <= prop->val.len); + + refnode = get_node_by_ref(dt, m->ref); +- if (! refnode) { ++ if (!refnode && !symbol_fixup_support) { + FAIL(c, "Reference to non-existent node or label \"%s\"\n", +- m->ref); ++ m->ref); + continue; + } + +- phandle = get_node_phandle(dt, refnode); +- *((cell_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle); ++ if (!refnode) { ++ /* allocate fixup entry */ ++ fe = xmalloc(sizeof(*fe)); ++ ++ fe->node = node; ++ fe->prop = prop; ++ fe->offset = m->offset; ++ fe->next = NULL; ++ ++ /* search for an already existing fixup */ ++ for_each_fixup(dt, f) ++ if (strcmp(f->ref, m->ref) == 0) ++ break; ++ ++ /* no fixup found, add new */ ++ if (f == NULL) { ++ f = xmalloc(sizeof(*f)); ++ f->ref = m->ref; ++ f->entries = NULL; ++ f->next = NULL; ++ ++ /* add it to the tree */ ++ fp = &dt->fixups; ++ while (*fp) ++ fp = &(*fp)->next; ++ *fp = f; ++ } ++ ++ /* and now append fixup entry */ ++ fep = &f->entries; ++ while (*fep) ++ fep = &(*fep)->next; ++ *fep = fe; ++ ++ /* mark the entry as unresolved */ ++ phandle = 0xdeadbeef; ++ } else { ++ phandle = get_node_phandle(dt, refnode); ++ ++ /* if it's a plugin, we need to record it */ ++ if (symbol_fixup_support && dt->is_plugin) { ++ ++ /* allocate a new local fixup entry */ ++ fe = xmalloc(sizeof(*fe)); ++ ++ fe->node = node; ++ fe->prop = prop; ++ fe->offset = m->offset; ++ fe->next = NULL; ++ ++ /* append it to the local fixups */ ++ fep = &dt->local_fixups; ++ while (*fep) ++ fep = &(*fep)->next; ++ *fep = fe; ++ } ++ } ++ ++ *((cell_t *)(prop->val.val + m->offset)) = ++ cpu_to_fdt32(phandle); + } + } + ERROR(phandle_references, NULL, NULL, fixup_phandle_references, NULL, +@@ -652,6 +722,45 @@ static void check_obsolete_chosen_interr + } + TREE_WARNING(obsolete_chosen_interrupt_controller, NULL); + ++static void check_auto_label_phandles(struct check *c, struct node *dt, ++ struct node *node) ++{ ++ struct label *l; ++ struct symbol *s, **sp; ++ int has_label; ++ ++ if (!symbol_fixup_support) ++ return; ++ ++ has_label = 0; ++ for_each_label(node->labels, l) { ++ has_label = 1; ++ break; ++ } ++ ++ if (!has_label) ++ return; ++ ++ /* force allocation of a phandle for this node */ ++ (void)get_node_phandle(dt, node); ++ ++ /* add the symbol */ ++ for_each_label(node->labels, l) { ++ ++ s = xmalloc(sizeof(*s)); ++ s->label = l; ++ s->node = node; ++ s->next = NULL; ++ ++ /* add it to the symbols list */ ++ sp = &dt->symbols; ++ while (*sp) ++ sp = &((*sp)->next); ++ *sp = s; ++ } ++} ++NODE_WARNING(auto_label_phandles, NULL); ++ + static struct check *check_table[] = { + &duplicate_node_names, &duplicate_property_names, + &node_name_chars, &node_name_format, &property_name_chars, +@@ -670,6 +779,8 @@ static struct check *check_table[] = { + &avoid_default_addr_size, + &obsolete_chosen_interrupt_controller, + ++ &auto_label_phandles, ++ + &always_fail, + }; + +--- a/scripts/dtc/dtc-lexer.l ++++ b/scripts/dtc/dtc-lexer.l +@@ -113,6 +113,11 @@ static void lexical_error(const char *fm + return DT_V1; + } + ++<*>"/plugin/" { ++ DPRINT("Keyword: /plugin/\n"); ++ return DT_PLUGIN; ++ } ++ + <*>"/memreserve/" { + DPRINT("Keyword: /memreserve/\n"); + BEGIN_DEFAULT(); +--- a/scripts/dtc/dtc-lexer.lex.c_shipped ++++ b/scripts/dtc/dtc-lexer.lex.c_shipped +@@ -9,7 +9,7 @@ + #define FLEX_SCANNER + #define YY_FLEX_MAJOR_VERSION 2 + #define YY_FLEX_MINOR_VERSION 5 +-#define YY_FLEX_SUBMINOR_VERSION 39 ++#define YY_FLEX_SUBMINOR_VERSION 35 + #if YY_FLEX_SUBMINOR_VERSION > 0 + #define FLEX_BETA + #endif +@@ -162,12 +162,7 @@ typedef unsigned int flex_uint32_t; + typedef struct yy_buffer_state *YY_BUFFER_STATE; + #endif + +-#ifndef YY_TYPEDEF_YY_SIZE_T +-#define YY_TYPEDEF_YY_SIZE_T +-typedef size_t yy_size_t; +-#endif +- +-extern yy_size_t yyleng; ++extern int yyleng; + + extern FILE *yyin, *yyout; + +@@ -176,7 +171,6 @@ extern FILE *yyin, *yyout; + #define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) +- #define YY_LINENO_REWIND_TO(ptr) + + /* Return all but the first "n" matched characters back to the input stream. */ + #define yyless(n) \ +@@ -194,6 +188,11 @@ extern FILE *yyin, *yyout; + + #define unput(c) yyunput( c, (yytext_ptr) ) + ++#ifndef YY_TYPEDEF_YY_SIZE_T ++#define YY_TYPEDEF_YY_SIZE_T ++typedef size_t yy_size_t; ++#endif ++ + #ifndef YY_STRUCT_YY_BUFFER_STATE + #define YY_STRUCT_YY_BUFFER_STATE + struct yy_buffer_state +@@ -211,7 +210,7 @@ struct yy_buffer_state + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ +- yy_size_t yy_n_chars; ++ int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to +@@ -281,8 +280,8 @@ static YY_BUFFER_STATE * yy_buffer_stack + + /* yy_hold_char holds the character lost when yytext is formed. */ + static char yy_hold_char; +-static yy_size_t yy_n_chars; /* number of characters read into yy_ch_buf */ +-yy_size_t yyleng; ++static int yy_n_chars; /* number of characters read into yy_ch_buf */ ++int yyleng; + + /* Points to current character in buffer. */ + static char *yy_c_buf_p = (char *) 0; +@@ -310,7 +309,7 @@ static void yy_init_buffer (YY_BUFFER_ST + + YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); + YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); +-YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ); ++YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); + + void *yyalloc (yy_size_t ); + void *yyrealloc (void *,yy_size_t ); +@@ -342,7 +341,7 @@ void yyfree (void * ); + + /* Begin user sect3 */ + +-#define yywrap() 1 ++#define yywrap(n) 1 + #define YY_SKIP_YYWRAP + + typedef unsigned char YY_CHAR; +@@ -373,8 +372,8 @@ static void yy_fatal_error (yyconst char + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +-#define YY_NUM_RULES 30 +-#define YY_END_OF_BUFFER 31 ++#define YY_NUM_RULES 31 ++#define YY_END_OF_BUFFER 32 + /* This struct is not used in this scanner, + but its presence is necessary. */ + struct yy_trans_info +@@ -382,25 +381,26 @@ struct yy_trans_info + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +-static yyconst flex_int16_t yy_accept[159] = ++static yyconst flex_int16_t yy_accept[166] = + { 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 31, 29, +- 18, 18, 29, 29, 29, 29, 29, 29, 29, 29, +- 29, 29, 29, 29, 29, 29, 15, 16, 16, 29, +- 16, 10, 10, 18, 26, 0, 3, 0, 27, 12, +- 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, +- 21, 23, 25, 24, 22, 0, 9, 28, 0, 0, +- 0, 14, 14, 16, 16, 16, 10, 10, 10, 0, +- 12, 0, 11, 0, 0, 0, 20, 0, 0, 0, +- 0, 0, 0, 0, 0, 16, 10, 10, 10, 0, +- 13, 19, 0, 0, 0, 0, 0, 0, 0, 0, +- +- 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 16, 6, 0, 0, 0, 0, 0, 0, 2, +- 0, 0, 0, 0, 0, 0, 0, 0, 4, 17, +- 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, +- 5, 8, 0, 0, 0, 0, 7, 0 ++ 0, 0, 0, 0, 0, 0, 0, 0, 32, 30, ++ 19, 19, 30, 30, 30, 30, 30, 30, 30, 30, ++ 30, 30, 30, 30, 30, 30, 16, 17, 17, 30, ++ 17, 11, 11, 19, 27, 0, 3, 0, 28, 13, ++ 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, ++ 0, 22, 24, 26, 25, 23, 0, 10, 29, 0, ++ 0, 0, 15, 15, 17, 17, 17, 11, 11, 11, ++ 0, 13, 0, 12, 0, 0, 0, 21, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 17, 11, 11, ++ 11, 0, 14, 20, 0, 0, 0, 0, 0, 0, ++ ++ 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 17, 7, 0, 0, 0, ++ 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 4, 18, 0, 0, 5, 2, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 1, 0, 0, 0, 0, 6, 9, 0, ++ 0, 0, 0, 8, 0 + } ; + + static yyconst flex_int32_t yy_ec[256] = +@@ -416,9 +416,9 @@ static yyconst flex_int32_t yy_ec[256] = + 22, 22, 22, 22, 24, 22, 22, 25, 22, 22, + 1, 26, 27, 1, 22, 1, 21, 28, 29, 30, + +- 31, 21, 22, 22, 32, 22, 22, 33, 34, 35, +- 36, 37, 22, 38, 39, 40, 41, 42, 22, 25, +- 43, 22, 44, 45, 46, 1, 1, 1, 1, 1, ++ 31, 21, 32, 22, 33, 22, 22, 34, 35, 36, ++ 37, 38, 22, 39, 40, 41, 42, 43, 22, 25, ++ 44, 22, 45, 46, 47, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +@@ -435,163 +435,165 @@ static yyconst flex_int32_t yy_ec[256] = + 1, 1, 1, 1, 1 + } ; + +-static yyconst flex_int32_t yy_meta[47] = ++static yyconst flex_int32_t yy_meta[48] = + { 0, + 1, 1, 1, 1, 1, 1, 2, 3, 1, 2, + 2, 2, 4, 5, 5, 5, 6, 1, 1, 1, + 7, 8, 8, 8, 8, 1, 1, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, +- 8, 8, 8, 3, 1, 4 ++ 8, 8, 8, 8, 3, 1, 4 + } ; + +-static yyconst flex_int16_t yy_base[173] = ++static yyconst flex_int16_t yy_base[180] = + { 0, +- 0, 383, 34, 382, 65, 381, 37, 105, 387, 391, +- 54, 111, 367, 110, 109, 109, 112, 41, 366, 104, +- 367, 338, 124, 117, 0, 144, 391, 0, 121, 0, +- 135, 155, 140, 179, 391, 160, 391, 379, 391, 0, +- 368, 141, 391, 167, 370, 376, 346, 103, 342, 345, +- 391, 391, 391, 391, 391, 358, 391, 391, 175, 342, +- 338, 391, 355, 0, 185, 339, 184, 347, 346, 0, +- 0, 322, 175, 357, 175, 363, 352, 324, 330, 323, +- 332, 326, 201, 324, 329, 322, 391, 333, 181, 309, +- 391, 341, 340, 313, 320, 338, 178, 311, 146, 317, +- +- 314, 315, 335, 331, 303, 300, 309, 299, 308, 188, +- 336, 335, 391, 305, 320, 281, 283, 271, 203, 288, +- 281, 271, 266, 264, 245, 242, 208, 104, 391, 391, +- 244, 218, 204, 219, 206, 224, 201, 212, 204, 229, +- 215, 208, 207, 200, 219, 391, 233, 221, 200, 181, +- 391, 391, 149, 122, 86, 41, 391, 391, 245, 251, +- 259, 263, 267, 273, 280, 284, 292, 300, 304, 310, +- 318, 326 ++ 0, 393, 35, 392, 66, 391, 38, 107, 397, 401, ++ 55, 113, 377, 112, 111, 111, 114, 42, 376, 106, ++ 377, 347, 126, 120, 0, 147, 401, 0, 124, 0, ++ 137, 158, 170, 163, 401, 153, 401, 389, 401, 0, ++ 378, 120, 401, 131, 380, 386, 355, 139, 351, 355, ++ 351, 401, 401, 401, 401, 401, 367, 401, 401, 185, ++ 350, 346, 401, 364, 0, 185, 347, 189, 356, 355, ++ 0, 0, 330, 180, 366, 141, 372, 361, 332, 338, ++ 331, 341, 334, 326, 205, 331, 337, 329, 401, 341, ++ 167, 316, 401, 349, 348, 320, 328, 346, 180, 318, ++ ++ 324, 209, 324, 320, 322, 342, 338, 309, 306, 315, ++ 305, 315, 312, 192, 342, 341, 401, 293, 306, 282, ++ 268, 252, 255, 203, 285, 282, 272, 268, 252, 233, ++ 232, 239, 208, 107, 401, 401, 238, 211, 401, 211, ++ 212, 208, 228, 203, 215, 207, 233, 222, 212, 211, ++ 203, 227, 401, 237, 225, 204, 185, 401, 401, 149, ++ 128, 88, 42, 401, 401, 253, 259, 267, 271, 275, ++ 281, 288, 292, 300, 308, 312, 318, 326, 334 + } ; + +-static yyconst flex_int16_t yy_def[173] = ++static yyconst flex_int16_t yy_def[180] = + { 0, +- 158, 1, 1, 3, 158, 5, 1, 1, 158, 158, +- 158, 158, 158, 159, 160, 161, 158, 158, 158, 158, +- 162, 158, 158, 158, 163, 162, 158, 164, 165, 164, +- 164, 158, 158, 158, 158, 159, 158, 159, 158, 166, +- 158, 161, 158, 161, 167, 168, 158, 158, 158, 158, +- 158, 158, 158, 158, 158, 162, 158, 158, 158, 158, +- 158, 158, 162, 164, 165, 164, 158, 158, 158, 169, +- 166, 170, 161, 167, 167, 168, 158, 158, 158, 158, +- 158, 158, 158, 158, 158, 164, 158, 158, 169, 170, +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- +- 158, 164, 158, 158, 158, 158, 158, 158, 158, 171, +- 158, 164, 158, 158, 158, 158, 158, 158, 171, 158, +- 171, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- 172, 158, 158, 158, 172, 158, 172, 158, 158, 158, +- 158, 158, 158, 158, 158, 158, 158, 0, 158, 158, +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- 158, 158 ++ 165, 1, 1, 3, 165, 5, 1, 1, 165, 165, ++ 165, 165, 165, 166, 167, 168, 165, 165, 165, 165, ++ 169, 165, 165, 165, 170, 169, 165, 171, 172, 171, ++ 171, 165, 165, 165, 165, 166, 165, 166, 165, 173, ++ 165, 168, 165, 168, 174, 175, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 169, 165, 165, 165, ++ 165, 165, 165, 169, 171, 172, 171, 165, 165, 165, ++ 176, 173, 177, 168, 174, 174, 175, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 171, 165, 165, ++ 176, 177, 165, 165, 165, 165, 165, 165, 165, 165, ++ ++ 165, 165, 165, 165, 171, 165, 165, 165, 165, 165, ++ 165, 165, 165, 178, 165, 171, 165, 165, 165, 165, ++ 165, 165, 165, 178, 165, 178, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 179, 165, 165, ++ 165, 179, 165, 179, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 0, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165, 165 + } ; + +-static yyconst flex_int16_t yy_nxt[438] = ++static yyconst flex_int16_t yy_nxt[449] = + { 0, + 10, 11, 12, 11, 13, 14, 10, 15, 16, 10, + 10, 10, 17, 10, 10, 10, 10, 18, 19, 20, + 21, 21, 21, 21, 21, 10, 10, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, +- 21, 21, 21, 10, 22, 10, 24, 25, 25, 25, +- 32, 33, 33, 157, 26, 34, 34, 34, 51, 52, +- 27, 26, 26, 26, 26, 10, 11, 12, 11, 13, +- 14, 28, 15, 16, 28, 28, 28, 24, 28, 28, +- 28, 10, 18, 19, 20, 29, 29, 29, 29, 29, +- 30, 10, 29, 29, 29, 29, 29, 29, 29, 29, +- +- 29, 29, 29, 29, 29, 29, 29, 29, 10, 22, +- 10, 23, 34, 34, 34, 37, 39, 43, 32, 33, +- 33, 45, 54, 55, 46, 59, 45, 64, 156, 46, +- 64, 64, 64, 79, 44, 38, 59, 57, 134, 47, +- 135, 48, 80, 49, 47, 50, 48, 99, 61, 43, +- 50, 110, 41, 67, 67, 67, 60, 63, 63, 63, +- 57, 155, 68, 69, 63, 37, 44, 66, 67, 67, +- 67, 63, 63, 63, 63, 73, 59, 68, 69, 70, +- 34, 34, 34, 43, 75, 38, 154, 92, 83, 83, +- 83, 64, 44, 120, 64, 64, 64, 67, 67, 67, +- +- 44, 57, 99, 68, 69, 107, 68, 69, 120, 127, +- 108, 153, 152, 121, 83, 83, 83, 133, 133, 133, +- 146, 133, 133, 133, 146, 140, 140, 140, 121, 141, +- 140, 140, 140, 151, 141, 158, 150, 149, 148, 144, +- 147, 143, 142, 139, 147, 36, 36, 36, 36, 36, +- 36, 36, 36, 40, 138, 137, 136, 40, 40, 42, +- 42, 42, 42, 42, 42, 42, 42, 56, 56, 56, +- 56, 62, 132, 62, 64, 131, 130, 64, 129, 64, +- 64, 65, 128, 158, 65, 65, 65, 65, 71, 127, +- 71, 71, 74, 74, 74, 74, 74, 74, 74, 74, +- +- 76, 76, 76, 76, 76, 76, 76, 76, 89, 126, +- 89, 90, 125, 90, 90, 124, 90, 90, 119, 119, +- 119, 119, 119, 119, 119, 119, 145, 145, 145, 145, +- 145, 145, 145, 145, 123, 122, 59, 59, 118, 117, +- 116, 115, 114, 113, 45, 112, 108, 111, 109, 106, +- 105, 104, 46, 103, 91, 87, 102, 101, 100, 98, +- 97, 96, 95, 94, 93, 77, 75, 91, 88, 87, +- 86, 57, 85, 84, 57, 82, 81, 78, 77, 75, +- 72, 158, 58, 57, 53, 35, 158, 31, 23, 23, +- 9, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- 158, 158, 158, 158, 158, 158, 158 ++ 21, 21, 21, 21, 10, 22, 10, 24, 25, 25, ++ 25, 32, 33, 33, 164, 26, 34, 34, 34, 52, ++ 53, 27, 26, 26, 26, 26, 10, 11, 12, 11, ++ 13, 14, 28, 15, 16, 28, 28, 28, 24, 28, ++ 28, 28, 10, 18, 19, 20, 29, 29, 29, 29, ++ 29, 30, 10, 29, 29, 29, 29, 29, 29, 29, ++ ++ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, ++ 10, 22, 10, 23, 34, 34, 34, 37, 39, 43, ++ 32, 33, 33, 45, 55, 56, 46, 60, 43, 45, ++ 65, 163, 46, 65, 65, 65, 44, 38, 60, 74, ++ 58, 47, 141, 48, 142, 44, 49, 47, 50, 48, ++ 76, 51, 62, 94, 50, 41, 44, 51, 37, 61, ++ 64, 64, 64, 58, 34, 34, 34, 64, 162, 80, ++ 67, 68, 68, 68, 64, 64, 64, 64, 38, 81, ++ 69, 70, 71, 68, 68, 68, 60, 161, 43, 69, ++ 70, 65, 69, 70, 65, 65, 65, 125, 85, 85, ++ ++ 85, 58, 68, 68, 68, 44, 102, 110, 125, 133, ++ 102, 69, 70, 111, 114, 160, 159, 126, 85, 85, ++ 85, 140, 140, 140, 140, 140, 140, 153, 126, 147, ++ 147, 147, 153, 148, 147, 147, 147, 158, 148, 165, ++ 157, 156, 155, 151, 150, 149, 146, 154, 145, 144, ++ 143, 139, 154, 36, 36, 36, 36, 36, 36, 36, ++ 36, 40, 138, 137, 136, 40, 40, 42, 42, 42, ++ 42, 42, 42, 42, 42, 57, 57, 57, 57, 63, ++ 135, 63, 65, 134, 165, 65, 133, 65, 65, 66, ++ 132, 131, 66, 66, 66, 66, 72, 130, 72, 72, ++ ++ 75, 75, 75, 75, 75, 75, 75, 75, 77, 77, ++ 77, 77, 77, 77, 77, 77, 91, 129, 91, 92, ++ 128, 92, 92, 127, 92, 92, 124, 124, 124, 124, ++ 124, 124, 124, 124, 152, 152, 152, 152, 152, 152, ++ 152, 152, 60, 60, 123, 122, 121, 120, 119, 118, ++ 117, 45, 116, 111, 115, 113, 112, 109, 108, 107, ++ 46, 106, 93, 89, 105, 104, 103, 101, 100, 99, ++ 98, 97, 96, 95, 78, 76, 93, 90, 89, 88, ++ 58, 87, 86, 58, 84, 83, 82, 79, 78, 76, ++ 73, 165, 59, 58, 54, 35, 165, 31, 23, 23, ++ ++ 9, 165, 165, 165, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165 + } ; + +-static yyconst flex_int16_t yy_chk[438] = ++static yyconst flex_int16_t yy_chk[449] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +- 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, +- 7, 7, 7, 156, 3, 11, 11, 11, 18, 18, +- 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, ++ 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, ++ 3, 7, 7, 7, 163, 3, 11, 11, 11, 18, ++ 18, 3, 3, 3, 3, 3, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, +- 5, 8, 12, 12, 12, 14, 15, 16, 8, 8, +- 8, 17, 20, 20, 17, 23, 24, 29, 155, 24, +- 29, 29, 29, 48, 16, 14, 31, 29, 128, 17, +- 128, 17, 48, 17, 24, 17, 24, 99, 24, 42, +- 24, 99, 15, 33, 33, 33, 23, 26, 26, 26, +- 26, 154, 33, 33, 26, 36, 42, 31, 32, 32, +- 32, 26, 26, 26, 26, 44, 59, 32, 32, 32, +- 34, 34, 34, 73, 75, 36, 153, 75, 59, 59, +- 59, 65, 44, 110, 65, 65, 65, 67, 67, 67, +- +- 73, 65, 83, 89, 89, 97, 67, 67, 119, 127, +- 97, 150, 149, 110, 83, 83, 83, 133, 133, 133, +- 141, 127, 127, 127, 145, 136, 136, 136, 119, 136, +- 140, 140, 140, 148, 140, 147, 144, 143, 142, 139, +- 141, 138, 137, 135, 145, 159, 159, 159, 159, 159, +- 159, 159, 159, 160, 134, 132, 131, 160, 160, 161, +- 161, 161, 161, 161, 161, 161, 161, 162, 162, 162, +- 162, 163, 126, 163, 164, 125, 124, 164, 123, 164, +- 164, 165, 122, 121, 165, 165, 165, 165, 166, 120, +- 166, 166, 167, 167, 167, 167, 167, 167, 167, 167, +- +- 168, 168, 168, 168, 168, 168, 168, 168, 169, 118, +- 169, 170, 117, 170, 170, 116, 170, 170, 171, 171, +- 171, 171, 171, 171, 171, 171, 172, 172, 172, 172, +- 172, 172, 172, 172, 115, 114, 112, 111, 109, 108, +- 107, 106, 105, 104, 103, 102, 101, 100, 98, 96, +- 95, 94, 93, 92, 90, 88, 86, 85, 84, 82, +- 81, 80, 79, 78, 77, 76, 74, 72, 69, 68, +- 66, 63, 61, 60, 56, 50, 49, 47, 46, 45, ++ 5, 5, 5, 8, 12, 12, 12, 14, 15, 16, ++ 8, 8, 8, 17, 20, 20, 17, 23, 42, 24, ++ 29, 162, 24, 29, 29, 29, 16, 14, 31, 44, ++ 29, 17, 134, 17, 134, 42, 17, 24, 17, 24, ++ 76, 17, 24, 76, 24, 15, 44, 24, 36, 23, ++ 26, 26, 26, 26, 34, 34, 34, 26, 161, 48, ++ 31, 32, 32, 32, 26, 26, 26, 26, 36, 48, ++ 32, 32, 32, 33, 33, 33, 60, 160, 74, 91, ++ 91, 66, 33, 33, 66, 66, 66, 114, 60, 60, ++ ++ 60, 66, 68, 68, 68, 74, 85, 99, 124, 133, ++ 102, 68, 68, 99, 102, 157, 156, 114, 85, 85, ++ 85, 133, 133, 133, 140, 140, 140, 148, 124, 143, ++ 143, 143, 152, 143, 147, 147, 147, 155, 147, 154, ++ 151, 150, 149, 146, 145, 144, 142, 148, 141, 138, ++ 137, 132, 152, 166, 166, 166, 166, 166, 166, 166, ++ 166, 167, 131, 130, 129, 167, 167, 168, 168, 168, ++ 168, 168, 168, 168, 168, 169, 169, 169, 169, 170, ++ 128, 170, 171, 127, 126, 171, 125, 171, 171, 172, ++ 123, 122, 172, 172, 172, 172, 173, 121, 173, 173, ++ ++ 174, 174, 174, 174, 174, 174, 174, 174, 175, 175, ++ 175, 175, 175, 175, 175, 175, 176, 120, 176, 177, ++ 119, 177, 177, 118, 177, 177, 178, 178, 178, 178, ++ 178, 178, 178, 178, 179, 179, 179, 179, 179, 179, ++ 179, 179, 116, 115, 113, 112, 111, 110, 109, 108, ++ 107, 106, 105, 104, 103, 101, 100, 98, 97, 96, ++ 95, 94, 92, 90, 88, 87, 86, 84, 83, 82, ++ 81, 80, 79, 78, 77, 75, 73, 70, 69, 67, ++ 64, 62, 61, 57, 51, 50, 49, 47, 46, 45, + 41, 38, 22, 21, 19, 13, 9, 6, 4, 2, +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, + +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- 158, 158, 158, 158, 158, 158, 158 ++ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165 + } ; + + static yy_state_type yy_last_accepting_state; +@@ -662,7 +664,7 @@ static int dts_version = 1; + static void push_input_file(const char *filename); + static bool pop_input_file(void); + static void lexical_error(const char *fmt, ...); +-#line 666 "dtc-lexer.lex.c" ++#line 668 "dtc-lexer.lex.c" + + #define INITIAL 0 + #define BYTESTRING 1 +@@ -704,7 +706,7 @@ FILE *yyget_out (void ); + + void yyset_out (FILE * out_str ); + +-yy_size_t yyget_leng (void ); ++int yyget_leng (void ); + + char *yyget_text (void ); + +@@ -853,6 +855,10 @@ YY_DECL + register char *yy_cp, *yy_bp; + register int yy_act; + ++#line 68 "dtc-lexer.l" ++ ++#line 861 "dtc-lexer.lex.c" ++ + if ( !(yy_init) ) + { + (yy_init) = 1; +@@ -879,11 +885,6 @@ YY_DECL + yy_load_buffer_state( ); + } + +- { +-#line 68 "dtc-lexer.l" +- +-#line 886 "dtc-lexer.lex.c" +- + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); +@@ -901,7 +902,7 @@ YY_DECL + yy_match: + do + { +- register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; ++ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; +@@ -910,13 +911,13 @@ yy_match: + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; +- if ( yy_current_state >= 159 ) ++ if ( yy_current_state >= 166 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } +- while ( yy_current_state != 158 ); ++ while ( yy_current_state != 165 ); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + +@@ -1007,23 +1008,31 @@ case 5: + YY_RULE_SETUP + #line 116 "dtc-lexer.l" + { ++ DPRINT("Keyword: /plugin/\n"); ++ return DT_PLUGIN; ++ } ++ YY_BREAK ++case 6: ++YY_RULE_SETUP ++#line 121 "dtc-lexer.l" ++{ + DPRINT("Keyword: /memreserve/\n"); + BEGIN_DEFAULT(); + return DT_MEMRESERVE; + } + YY_BREAK +-case 6: ++case 7: + YY_RULE_SETUP +-#line 122 "dtc-lexer.l" ++#line 127 "dtc-lexer.l" + { + DPRINT("Keyword: /bits/\n"); + BEGIN_DEFAULT(); + return DT_BITS; + } + YY_BREAK +-case 7: ++case 8: + YY_RULE_SETUP +-#line 128 "dtc-lexer.l" ++#line 133 "dtc-lexer.l" + { + DPRINT("Keyword: /delete-property/\n"); + DPRINT("\n"); +@@ -1031,9 +1040,9 @@ YY_RULE_SETUP + return DT_DEL_PROP; + } + YY_BREAK +-case 8: ++case 9: + YY_RULE_SETUP +-#line 135 "dtc-lexer.l" ++#line 140 "dtc-lexer.l" + { + DPRINT("Keyword: /delete-node/\n"); + DPRINT("\n"); +@@ -1041,9 +1050,9 @@ YY_RULE_SETUP + return DT_DEL_NODE; + } + YY_BREAK +-case 9: ++case 10: + YY_RULE_SETUP +-#line 142 "dtc-lexer.l" ++#line 147 "dtc-lexer.l" + { + DPRINT("Label: %s\n", yytext); + yylval.labelref = xstrdup(yytext); +@@ -1051,9 +1060,9 @@ YY_RULE_SETUP + return DT_LABEL; + } + YY_BREAK +-case 10: ++case 11: + YY_RULE_SETUP +-#line 149 "dtc-lexer.l" ++#line 154 "dtc-lexer.l" + { + char *e; + DPRINT("Integer Literal: '%s'\n", yytext); +@@ -1073,10 +1082,10 @@ YY_RULE_SETUP + return DT_LITERAL; + } + YY_BREAK +-case 11: +-/* rule 11 can match eol */ ++case 12: ++/* rule 12 can match eol */ + YY_RULE_SETUP +-#line 168 "dtc-lexer.l" ++#line 173 "dtc-lexer.l" + { + struct data d; + DPRINT("Character literal: %s\n", yytext); +@@ -1098,18 +1107,18 @@ YY_RULE_SETUP + return DT_CHAR_LITERAL; + } + YY_BREAK +-case 12: ++case 13: + YY_RULE_SETUP +-#line 189 "dtc-lexer.l" ++#line 194 "dtc-lexer.l" + { /* label reference */ + DPRINT("Ref: %s\n", yytext+1); + yylval.labelref = xstrdup(yytext+1); + return DT_REF; + } + YY_BREAK +-case 13: ++case 14: + YY_RULE_SETUP +-#line 195 "dtc-lexer.l" ++#line 200 "dtc-lexer.l" + { /* new-style path reference */ + yytext[yyleng-1] = '\0'; + DPRINT("Ref: %s\n", yytext+2); +@@ -1117,27 +1126,27 @@ YY_RULE_SETUP + return DT_REF; + } + YY_BREAK +-case 14: ++case 15: + YY_RULE_SETUP +-#line 202 "dtc-lexer.l" ++#line 207 "dtc-lexer.l" + { + yylval.byte = strtol(yytext, NULL, 16); + DPRINT("Byte: %02x\n", (int)yylval.byte); + return DT_BYTE; + } + YY_BREAK +-case 15: ++case 16: + YY_RULE_SETUP +-#line 208 "dtc-lexer.l" ++#line 213 "dtc-lexer.l" + { + DPRINT("/BYTESTRING\n"); + BEGIN_DEFAULT(); + return ']'; + } + YY_BREAK +-case 16: ++case 17: + YY_RULE_SETUP +-#line 214 "dtc-lexer.l" ++#line 219 "dtc-lexer.l" + { + DPRINT("PropNodeName: %s\n", yytext); + yylval.propnodename = xstrdup((yytext[0] == '\\') ? +@@ -1146,75 +1155,75 @@ YY_RULE_SETUP + return DT_PROPNODENAME; + } + YY_BREAK +-case 17: ++case 18: + YY_RULE_SETUP +-#line 222 "dtc-lexer.l" ++#line 227 "dtc-lexer.l" + { + DPRINT("Binary Include\n"); + return DT_INCBIN; + } + YY_BREAK +-case 18: +-/* rule 18 can match eol */ +-YY_RULE_SETUP +-#line 227 "dtc-lexer.l" +-/* eat whitespace */ +- YY_BREAK + case 19: + /* rule 19 can match eol */ + YY_RULE_SETUP +-#line 228 "dtc-lexer.l" +-/* eat C-style comments */ ++#line 232 "dtc-lexer.l" ++/* eat whitespace */ + YY_BREAK + case 20: + /* rule 20 can match eol */ + YY_RULE_SETUP +-#line 229 "dtc-lexer.l" +-/* eat C++-style comments */ ++#line 233 "dtc-lexer.l" ++/* eat C-style comments */ + YY_BREAK + case 21: ++/* rule 21 can match eol */ + YY_RULE_SETUP +-#line 231 "dtc-lexer.l" +-{ return DT_LSHIFT; }; ++#line 234 "dtc-lexer.l" ++/* eat C++-style comments */ + YY_BREAK + case 22: + YY_RULE_SETUP +-#line 232 "dtc-lexer.l" +-{ return DT_RSHIFT; }; ++#line 236 "dtc-lexer.l" ++{ return DT_LSHIFT; }; + YY_BREAK + case 23: + YY_RULE_SETUP +-#line 233 "dtc-lexer.l" +-{ return DT_LE; }; ++#line 237 "dtc-lexer.l" ++{ return DT_RSHIFT; }; + YY_BREAK + case 24: + YY_RULE_SETUP +-#line 234 "dtc-lexer.l" +-{ return DT_GE; }; ++#line 238 "dtc-lexer.l" ++{ return DT_LE; }; + YY_BREAK + case 25: + YY_RULE_SETUP +-#line 235 "dtc-lexer.l" +-{ return DT_EQ; }; ++#line 239 "dtc-lexer.l" ++{ return DT_GE; }; + YY_BREAK + case 26: + YY_RULE_SETUP +-#line 236 "dtc-lexer.l" +-{ return DT_NE; }; ++#line 240 "dtc-lexer.l" ++{ return DT_EQ; }; + YY_BREAK + case 27: + YY_RULE_SETUP +-#line 237 "dtc-lexer.l" +-{ return DT_AND; }; ++#line 241 "dtc-lexer.l" ++{ return DT_NE; }; + YY_BREAK + case 28: + YY_RULE_SETUP +-#line 238 "dtc-lexer.l" +-{ return DT_OR; }; ++#line 242 "dtc-lexer.l" ++{ return DT_AND; }; + YY_BREAK + case 29: + YY_RULE_SETUP +-#line 240 "dtc-lexer.l" ++#line 243 "dtc-lexer.l" ++{ return DT_OR; }; ++ YY_BREAK ++case 30: ++YY_RULE_SETUP ++#line 245 "dtc-lexer.l" + { + DPRINT("Char: %c (\\x%02x)\n", yytext[0], + (unsigned)yytext[0]); +@@ -1230,12 +1239,12 @@ YY_RULE_SETUP + return yytext[0]; + } + YY_BREAK +-case 30: ++case 31: + YY_RULE_SETUP +-#line 255 "dtc-lexer.l" ++#line 260 "dtc-lexer.l" + ECHO; + YY_BREAK +-#line 1239 "dtc-lexer.lex.c" ++#line 1248 "dtc-lexer.lex.c" + + case YY_END_OF_BUFFER: + { +@@ -1365,7 +1374,6 @@ ECHO; + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +- } /* end of user's declarations */ + } /* end of yylex */ + + /* yy_get_next_buffer - try to read in a new buffer +@@ -1421,21 +1429,21 @@ static int yy_get_next_buffer (void) + + else + { +- yy_size_t num_to_read = ++ int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ +- YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; ++ YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { +- yy_size_t new_size = b->yy_buf_size * 2; ++ int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; +@@ -1466,7 +1474,7 @@ static int yy_get_next_buffer (void) + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), +- (yy_n_chars), num_to_read ); ++ (yy_n_chars), (size_t) num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } +@@ -1528,7 +1536,7 @@ static int yy_get_next_buffer (void) + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; +- if ( yy_current_state >= 159 ) ++ if ( yy_current_state >= 166 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; +@@ -1556,13 +1564,13 @@ static int yy_get_next_buffer (void) + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; +- if ( yy_current_state >= 159 ) ++ if ( yy_current_state >= 166 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; +- yy_is_jam = (yy_current_state == 158); ++ yy_is_jam = (yy_current_state == 165); + +- return yy_is_jam ? 0 : yy_current_state; ++ return yy_is_jam ? 0 : yy_current_state; + } + + #ifndef YY_NO_INPUT +@@ -1589,7 +1597,7 @@ static int yy_get_next_buffer (void) + + else + { /* need more input */ +- yy_size_t offset = (yy_c_buf_p) - (yytext_ptr); ++ int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) +@@ -1863,7 +1871,7 @@ void yypop_buffer_state (void) + */ + static void yyensure_buffer_stack (void) + { +- yy_size_t num_to_alloc; ++ int num_to_alloc; + + if (!(yy_buffer_stack)) { + +@@ -1960,12 +1968,12 @@ YY_BUFFER_STATE yy_scan_string (yyconst + * + * @return the newly allocated buffer state object. + */ +-YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len ) ++YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) + { + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; +- yy_size_t i; ++ int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; +@@ -2047,7 +2055,7 @@ FILE *yyget_out (void) + /** Get the length of the current token. + * + */ +-yy_size_t yyget_leng (void) ++int yyget_leng (void) + { + return yyleng; + } +@@ -2195,7 +2203,7 @@ void yyfree (void * ptr ) + + #define YYTABLES_NAME "yytables" + +-#line 254 "dtc-lexer.l" ++#line 260 "dtc-lexer.l" + + + +--- a/scripts/dtc/dtc-parser.tab.c_shipped ++++ b/scripts/dtc/dtc-parser.tab.c_shipped +@@ -1,19 +1,19 @@ +-/* A Bison parser, made by GNU Bison 3.0.2. */ ++/* A Bison parser, made by GNU Bison 2.5. */ + + /* Bison implementation for Yacc-like parsers in C +- +- Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. +- ++ ++ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. ++ + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +- ++ + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU 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 . */ + +@@ -26,7 +26,7 @@ + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. +- ++ + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +@@ -44,7 +44,7 @@ + #define YYBISON 1 + + /* Bison version. */ +-#define YYBISON_VERSION "3.0.2" ++#define YYBISON_VERSION "2.5" + + /* Skeleton name. */ + #define YYSKELETON_NAME "yacc.c" +@@ -58,13 +58,18 @@ + /* Pull parsers. */ + #define YYPULL 1 + ++/* Using locations. */ ++#define YYLSP_NEEDED 1 + + + + /* Copy the first part of user declarations. */ +-#line 20 "dtc-parser.y" /* yacc.c:339 */ ++ ++/* Line 268 of yacc.c */ ++#line 20 "dtc-parser.y" + + #include ++#include + + #include "dtc.h" + #include "srcpos.h" +@@ -80,15 +85,14 @@ extern void yyerror(char const *s); + extern struct boot_info *the_boot_info; + extern bool treesource_error; + +-#line 84 "dtc-parser.tab.c" /* yacc.c:339 */ + +-# ifndef YY_NULLPTR +-# if defined __cplusplus && 201103L <= __cplusplus +-# define YY_NULLPTR nullptr +-# else +-# define YY_NULLPTR 0 +-# endif +-# endif ++/* Line 268 of yacc.c */ ++#line 91 "dtc-parser.tab.c" ++ ++/* Enabling traces. */ ++#ifndef YYDEBUG ++# define YYDEBUG 0 ++#endif + + /* Enabling verbose error messages. */ + #ifdef YYERROR_VERBOSE +@@ -98,53 +102,51 @@ extern bool treesource_error; + # define YYERROR_VERBOSE 0 + #endif + +-/* In a future release of Bison, this section will be replaced +- by #include "dtc-parser.tab.h". */ +-#ifndef YY_YY_DTC_PARSER_TAB_H_INCLUDED +-# define YY_YY_DTC_PARSER_TAB_H_INCLUDED +-/* Debug traces. */ +-#ifndef YYDEBUG +-# define YYDEBUG 0 +-#endif +-#if YYDEBUG +-extern int yydebug; ++/* Enabling the token table. */ ++#ifndef YYTOKEN_TABLE ++# define YYTOKEN_TABLE 0 + #endif + +-/* Token type. */ ++ ++/* Tokens. */ + #ifndef YYTOKENTYPE + # define YYTOKENTYPE +- enum yytokentype +- { +- DT_V1 = 258, +- DT_MEMRESERVE = 259, +- DT_LSHIFT = 260, +- DT_RSHIFT = 261, +- DT_LE = 262, +- DT_GE = 263, +- DT_EQ = 264, +- DT_NE = 265, +- DT_AND = 266, +- DT_OR = 267, +- DT_BITS = 268, +- DT_DEL_PROP = 269, +- DT_DEL_NODE = 270, +- DT_PROPNODENAME = 271, +- DT_LITERAL = 272, +- DT_CHAR_LITERAL = 273, +- DT_BYTE = 274, +- DT_STRING = 275, +- DT_LABEL = 276, +- DT_REF = 277, +- DT_INCBIN = 278 +- }; ++ /* Put the tokens into the symbol table, so that GDB and other debuggers ++ know about them. */ ++ enum yytokentype { ++ DT_V1 = 258, ++ DT_PLUGIN = 259, ++ DT_MEMRESERVE = 260, ++ DT_LSHIFT = 261, ++ DT_RSHIFT = 262, ++ DT_LE = 263, ++ DT_GE = 264, ++ DT_EQ = 265, ++ DT_NE = 266, ++ DT_AND = 267, ++ DT_OR = 268, ++ DT_BITS = 269, ++ DT_DEL_PROP = 270, ++ DT_DEL_NODE = 271, ++ DT_PROPNODENAME = 272, ++ DT_LITERAL = 273, ++ DT_CHAR_LITERAL = 274, ++ DT_BYTE = 275, ++ DT_STRING = 276, ++ DT_LABEL = 277, ++ DT_REF = 278, ++ DT_INCBIN = 279 ++ }; + #endif + +-/* Value type. */ ++ ++ + #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +-typedef union YYSTYPE YYSTYPE; +-union YYSTYPE ++typedef union YYSTYPE + { +-#line 38 "dtc-parser.y" /* yacc.c:355 */ ++ ++/* Line 293 of yacc.c */ ++#line 39 "dtc-parser.y" + + char *propnodename; + char *labelref; +@@ -162,37 +164,37 @@ union YYSTYPE + struct node *nodelist; + struct reserve_info *re; + uint64_t integer; ++ int is_plugin; + +-#line 167 "dtc-parser.tab.c" /* yacc.c:355 */ +-}; ++ ++ ++/* Line 293 of yacc.c */ ++#line 173 "dtc-parser.tab.c" ++} YYSTYPE; + # define YYSTYPE_IS_TRIVIAL 1 ++# define yystype YYSTYPE /* obsolescent; will be withdrawn */ + # define YYSTYPE_IS_DECLARED 1 + #endif + +-/* Location type. */ + #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +-typedef struct YYLTYPE YYLTYPE; +-struct YYLTYPE ++typedef struct YYLTYPE + { + int first_line; + int first_column; + int last_line; + int last_column; +-}; ++} YYLTYPE; ++# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ + # define YYLTYPE_IS_DECLARED 1 + # define YYLTYPE_IS_TRIVIAL 1 + #endif + + +-extern YYSTYPE yylval; +-extern YYLTYPE yylloc; +-int yyparse (void); +- +-#endif /* !YY_YY_DTC_PARSER_TAB_H_INCLUDED */ +- + /* Copy the second part of user declarations. */ + +-#line 196 "dtc-parser.tab.c" /* yacc.c:358 */ ++ ++/* Line 343 of yacc.c */ ++#line 198 "dtc-parser.tab.c" + + #ifdef short + # undef short +@@ -206,8 +208,11 @@ typedef unsigned char yytype_uint8; + + #ifdef YYTYPE_INT8 + typedef YYTYPE_INT8 yytype_int8; +-#else ++#elif (defined __STDC__ || defined __C99__FUNC__ \ ++ || defined __cplusplus || defined _MSC_VER) + typedef signed char yytype_int8; ++#else ++typedef short int yytype_int8; + #endif + + #ifdef YYTYPE_UINT16 +@@ -227,7 +232,8 @@ typedef short int yytype_int16; + # define YYSIZE_T __SIZE_TYPE__ + # elif defined size_t + # define YYSIZE_T size_t +-# elif ! defined YYSIZE_T ++# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ ++ || defined __cplusplus || defined _MSC_VER) + # include /* INFRINGES ON USER NAME SPACE */ + # define YYSIZE_T size_t + # else +@@ -241,68 +247,39 @@ typedef short int yytype_int16; + # if defined YYENABLE_NLS && YYENABLE_NLS + # if ENABLE_NLS + # include /* INFRINGES ON USER NAME SPACE */ +-# define YY_(Msgid) dgettext ("bison-runtime", Msgid) ++# define YY_(msgid) dgettext ("bison-runtime", msgid) + # endif + # endif + # ifndef YY_ +-# define YY_(Msgid) Msgid +-# endif +-#endif +- +-#ifndef YY_ATTRIBUTE +-# if (defined __GNUC__ \ +- && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ +- || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C +-# define YY_ATTRIBUTE(Spec) __attribute__(Spec) +-# else +-# define YY_ATTRIBUTE(Spec) /* empty */ +-# endif +-#endif +- +-#ifndef YY_ATTRIBUTE_PURE +-# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) +-#endif +- +-#ifndef YY_ATTRIBUTE_UNUSED +-# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) +-#endif +- +-#if !defined _Noreturn \ +- && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112) +-# if defined _MSC_VER && 1200 <= _MSC_VER +-# define _Noreturn __declspec (noreturn) +-# else +-# define _Noreturn YY_ATTRIBUTE ((__noreturn__)) ++# define YY_(msgid) msgid + # endif + #endif + + /* Suppress unused-variable warnings by "using" E. */ + #if ! defined lint || defined __GNUC__ +-# define YYUSE(E) ((void) (E)) ++# define YYUSE(e) ((void) (e)) + #else +-# define YYUSE(E) /* empty */ ++# define YYUSE(e) /* empty */ + #endif + +-#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +-/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +-# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ +- _Pragma ("GCC diagnostic push") \ +- _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ +- _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +-# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ +- _Pragma ("GCC diagnostic pop") ++/* Identity function, used to suppress warnings about constant conditions. */ ++#ifndef lint ++# define YYID(n) (n) + #else +-# define YY_INITIAL_VALUE(Value) Value +-#endif +-#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +-# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +-# define YY_IGNORE_MAYBE_UNINITIALIZED_END ++#if (defined __STDC__ || defined __C99__FUNC__ \ ++ || defined __cplusplus || defined _MSC_VER) ++static int ++YYID (int yyi) ++#else ++static int ++YYID (yyi) ++ int yyi; + #endif +-#ifndef YY_INITIAL_VALUE +-# define YY_INITIAL_VALUE(Value) /* Nothing. */ ++{ ++ return yyi; ++} + #endif + +- + #if ! defined yyoverflow || YYERROR_VERBOSE + + /* The parser invokes alloca or malloc; define the necessary symbols. */ +@@ -320,9 +297,9 @@ typedef short int yytype_int16; + # define alloca _alloca + # else + # define YYSTACK_ALLOC alloca +-# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS ++# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ ++ || defined __cplusplus || defined _MSC_VER) + # include /* INFRINGES ON USER NAME SPACE */ +- /* Use EXIT_SUCCESS as a witness for stdlib.h. */ + # ifndef EXIT_SUCCESS + # define EXIT_SUCCESS 0 + # endif +@@ -332,8 +309,8 @@ typedef short int yytype_int16; + # endif + + # ifdef YYSTACK_ALLOC +- /* Pacify GCC's 'empty if-body' warning. */ +-# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) ++ /* Pacify GCC's `empty if-body' warning. */ ++# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) + # ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely +@@ -349,7 +326,7 @@ typedef short int yytype_int16; + # endif + # if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ +- && (defined YYFREE || defined free))) ++ && (defined YYFREE || defined free))) + # include /* INFRINGES ON USER NAME SPACE */ + # ifndef EXIT_SUCCESS + # define EXIT_SUCCESS 0 +@@ -357,13 +334,15 @@ typedef short int yytype_int16; + # endif + # ifndef YYMALLOC + # define YYMALLOC malloc +-# if ! defined malloc && ! defined EXIT_SUCCESS ++# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ ++ || defined __cplusplus || defined _MSC_VER) + void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ + # endif + # endif + # ifndef YYFREE + # define YYFREE free +-# if ! defined free && ! defined EXIT_SUCCESS ++# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ ++ || defined __cplusplus || defined _MSC_VER) + void free (void *); /* INFRINGES ON USER NAME SPACE */ + # endif + # endif +@@ -373,8 +352,8 @@ void free (void *); /* INFRINGES ON USER + + #if (! defined yyoverflow \ + && (! defined __cplusplus \ +- || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ +- && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) ++ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ ++ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + + /* A type that is properly aligned for any stack member. */ + union yyalloc +@@ -400,35 +379,35 @@ union yyalloc + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +-# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ +- do \ +- { \ +- YYSIZE_T yynewbytes; \ +- YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ +- Stack = &yyptr->Stack_alloc; \ +- yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ +- yyptr += yynewbytes / sizeof (*yyptr); \ +- } \ +- while (0) ++# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ ++ do \ ++ { \ ++ YYSIZE_T yynewbytes; \ ++ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ ++ Stack = &yyptr->Stack_alloc; \ ++ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ ++ yyptr += yynewbytes / sizeof (*yyptr); \ ++ } \ ++ while (YYID (0)) + + #endif + + #if defined YYCOPY_NEEDED && YYCOPY_NEEDED +-/* Copy COUNT objects from SRC to DST. The source and destination do ++/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ + # ifndef YYCOPY + # if defined __GNUC__ && 1 < __GNUC__ +-# define YYCOPY(Dst, Src, Count) \ +- __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) ++# define YYCOPY(To, From, Count) \ ++ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) + # else +-# define YYCOPY(Dst, Src, Count) \ +- do \ +- { \ +- YYSIZE_T yyi; \ +- for (yyi = 0; yyi < (Count); yyi++) \ +- (Dst)[yyi] = (Src)[yyi]; \ +- } \ +- while (0) ++# define YYCOPY(To, From, Count) \ ++ do \ ++ { \ ++ YYSIZE_T yyi; \ ++ for (yyi = 0; yyi < (Count); yyi++) \ ++ (To)[yyi] = (From)[yyi]; \ ++ } \ ++ while (YYID (0)) + # endif + # endif + #endif /* !YYCOPY_NEEDED */ +@@ -439,39 +418,37 @@ union yyalloc + #define YYLAST 136 + + /* YYNTOKENS -- Number of terminals. */ +-#define YYNTOKENS 47 ++#define YYNTOKENS 48 + /* YYNNTS -- Number of nonterminals. */ +-#define YYNNTS 28 ++#define YYNNTS 29 + /* YYNRULES -- Number of rules. */ +-#define YYNRULES 80 +-/* YYNSTATES -- Number of states. */ +-#define YYNSTATES 144 ++#define YYNRULES 82 ++/* YYNRULES -- Number of states. */ ++#define YYNSTATES 147 + +-/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned +- by yylex, with out-of-bounds checking. */ ++/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ + #define YYUNDEFTOK 2 +-#define YYMAXUTOK 278 ++#define YYMAXUTOK 279 + +-#define YYTRANSLATE(YYX) \ ++#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +-/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM +- as returned by yylex, without out-of-bounds checking. */ ++/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ + static const yytype_uint8 yytranslate[] = + { + 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, 46, 2, 2, 2, 44, 40, 2, +- 32, 34, 43, 41, 33, 42, 2, 25, 2, 2, +- 2, 2, 2, 2, 2, 2, 2, 2, 37, 24, +- 35, 28, 29, 36, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 47, 2, 2, 2, 45, 41, 2, ++ 33, 35, 44, 42, 34, 43, 2, 26, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 38, 25, ++ 36, 29, 30, 37, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +- 2, 30, 2, 31, 39, 2, 2, 2, 2, 2, ++ 2, 31, 2, 32, 40, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +- 2, 2, 2, 26, 38, 27, 45, 2, 2, 2, ++ 2, 2, 2, 27, 39, 28, 46, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +@@ -486,292 +463,335 @@ static const yytype_uint8 yytranslate[] + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, +- 15, 16, 17, 18, 19, 20, 21, 22, 23 ++ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 + }; + + #if YYDEBUG +- /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ ++/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in ++ YYRHS. */ ++static const yytype_uint16 yyprhs[] = ++{ ++ 0, 0, 3, 9, 10, 13, 14, 17, 22, 25, ++ 28, 32, 37, 41, 46, 52, 53, 56, 61, 64, ++ 68, 71, 74, 78, 83, 86, 96, 102, 105, 106, ++ 109, 112, 116, 118, 121, 124, 127, 129, 131, 135, ++ 137, 139, 145, 147, 151, 153, 157, 159, 163, 165, ++ 169, 171, 175, 177, 181, 185, 187, 191, 195, 199, ++ 203, 207, 211, 213, 217, 221, 223, 227, 231, 235, ++ 237, 239, 242, 245, 248, 249, 252, 255, 256, 259, ++ 262, 265, 269 ++}; ++ ++/* YYRHS -- A `-1'-separated list of the rules' RHS. */ ++static const yytype_int8 yyrhs[] = ++{ ++ 49, 0, -1, 3, 25, 50, 51, 53, -1, -1, ++ 4, 25, -1, -1, 52, 51, -1, 5, 60, 60, ++ 25, -1, 22, 52, -1, 26, 54, -1, 53, 26, ++ 54, -1, 53, 22, 23, 54, -1, 53, 23, 54, ++ -1, 53, 16, 23, 25, -1, 27, 55, 75, 28, ++ 25, -1, -1, 55, 56, -1, 17, 29, 57, 25, ++ -1, 17, 25, -1, 15, 17, 25, -1, 22, 56, ++ -1, 58, 21, -1, 58, 59, 30, -1, 58, 31, ++ 74, 32, -1, 58, 23, -1, 58, 24, 33, 21, ++ 34, 60, 34, 60, 35, -1, 58, 24, 33, 21, ++ 35, -1, 57, 22, -1, -1, 57, 34, -1, 58, ++ 22, -1, 14, 18, 36, -1, 36, -1, 59, 60, ++ -1, 59, 23, -1, 59, 22, -1, 18, -1, 19, ++ -1, 33, 61, 35, -1, 62, -1, 63, -1, 63, ++ 37, 61, 38, 62, -1, 64, -1, 63, 13, 64, ++ -1, 65, -1, 64, 12, 65, -1, 66, -1, 65, ++ 39, 66, -1, 67, -1, 66, 40, 67, -1, 68, ++ -1, 67, 41, 68, -1, 69, -1, 68, 10, 69, ++ -1, 68, 11, 69, -1, 70, -1, 69, 36, 70, ++ -1, 69, 30, 70, -1, 69, 8, 70, -1, 69, ++ 9, 70, -1, 70, 6, 71, -1, 70, 7, 71, ++ -1, 71, -1, 71, 42, 72, -1, 71, 43, 72, ++ -1, 72, -1, 72, 44, 73, -1, 72, 26, 73, ++ -1, 72, 45, 73, -1, 73, -1, 60, -1, 43, ++ 73, -1, 46, 73, -1, 47, 73, -1, -1, 74, ++ 20, -1, 74, 22, -1, -1, 76, 75, -1, 76, ++ 56, -1, 17, 54, -1, 16, 17, 25, -1, 22, ++ 76, -1 ++}; ++ ++/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ + static const yytype_uint16 yyrline[] = + { +- 0, 104, 104, 113, 116, 123, 127, 135, 139, 144, +- 155, 165, 180, 188, 191, 198, 202, 206, 210, 218, +- 222, 226, 230, 234, 250, 260, 268, 271, 275, 282, +- 298, 303, 322, 336, 343, 344, 345, 352, 356, 357, +- 361, 362, 366, 367, 371, 372, 376, 377, 381, 382, +- 386, 387, 388, 392, 393, 394, 395, 396, 400, 401, +- 402, 406, 407, 408, 412, 413, 414, 415, 419, 420, +- 421, 422, 427, 430, 434, 442, 445, 449, 457, 461, +- 465 ++ 0, 108, 108, 119, 122, 130, 133, 140, 144, 152, ++ 156, 161, 172, 182, 197, 205, 208, 215, 219, 223, ++ 227, 235, 239, 243, 247, 251, 267, 277, 285, 288, ++ 292, 299, 315, 320, 339, 353, 360, 361, 362, 369, ++ 373, 374, 378, 379, 383, 384, 388, 389, 393, 394, ++ 398, 399, 403, 404, 405, 409, 410, 411, 412, 413, ++ 417, 418, 419, 423, 424, 425, 429, 430, 431, 432, ++ 436, 437, 438, 439, 444, 447, 451, 459, 462, 466, ++ 474, 478, 482 + }; + #endif + +-#if YYDEBUG || YYERROR_VERBOSE || 0 ++#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE + /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ + static const char *const yytname[] = + { +- "$end", "error", "$undefined", "DT_V1", "DT_MEMRESERVE", "DT_LSHIFT", +- "DT_RSHIFT", "DT_LE", "DT_GE", "DT_EQ", "DT_NE", "DT_AND", "DT_OR", +- "DT_BITS", "DT_DEL_PROP", "DT_DEL_NODE", "DT_PROPNODENAME", "DT_LITERAL", +- "DT_CHAR_LITERAL", "DT_BYTE", "DT_STRING", "DT_LABEL", "DT_REF", +- "DT_INCBIN", "';'", "'/'", "'{'", "'}'", "'='", "'>'", "'['", "']'", +- "'('", "','", "')'", "'<'", "'?'", "':'", "'|'", "'^'", "'&'", "'+'", +- "'-'", "'*'", "'%'", "'~'", "'!'", "$accept", "sourcefile", +- "memreserves", "memreserve", "devicetree", "nodedef", "proplist", +- "propdef", "propdata", "propdataprefix", "arrayprefix", "integer_prim", +- "integer_expr", "integer_trinary", "integer_or", "integer_and", +- "integer_bitor", "integer_bitxor", "integer_bitand", "integer_eq", +- "integer_rela", "integer_shift", "integer_add", "integer_mul", +- "integer_unary", "bytestring", "subnodes", "subnode", YY_NULLPTR ++ "$end", "error", "$undefined", "DT_V1", "DT_PLUGIN", "DT_MEMRESERVE", ++ "DT_LSHIFT", "DT_RSHIFT", "DT_LE", "DT_GE", "DT_EQ", "DT_NE", "DT_AND", ++ "DT_OR", "DT_BITS", "DT_DEL_PROP", "DT_DEL_NODE", "DT_PROPNODENAME", ++ "DT_LITERAL", "DT_CHAR_LITERAL", "DT_BYTE", "DT_STRING", "DT_LABEL", ++ "DT_REF", "DT_INCBIN", "';'", "'/'", "'{'", "'}'", "'='", "'>'", "'['", ++ "']'", "'('", "','", "')'", "'<'", "'?'", "':'", "'|'", "'^'", "'&'", ++ "'+'", "'-'", "'*'", "'%'", "'~'", "'!'", "$accept", "sourcefile", ++ "plugindecl", "memreserves", "memreserve", "devicetree", "nodedef", ++ "proplist", "propdef", "propdata", "propdataprefix", "arrayprefix", ++ "integer_prim", "integer_expr", "integer_trinary", "integer_or", ++ "integer_and", "integer_bitor", "integer_bitxor", "integer_bitand", ++ "integer_eq", "integer_rela", "integer_shift", "integer_add", ++ "integer_mul", "integer_unary", "bytestring", "subnodes", "subnode", 0 + }; + #endif + + # ifdef YYPRINT +-/* YYTOKNUM[NUM] -- (External) token number corresponding to the +- (internal) symbol number NUM (which must be that of a token). */ ++/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to ++ token YYLEX-NUM. */ + static const yytype_uint16 yytoknum[] = + { + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, +- 275, 276, 277, 278, 59, 47, 123, 125, 61, 62, +- 91, 93, 40, 44, 41, 60, 63, 58, 124, 94, +- 38, 43, 45, 42, 37, 126, 33 ++ 275, 276, 277, 278, 279, 59, 47, 123, 125, 61, ++ 62, 91, 93, 40, 44, 41, 60, 63, 58, 124, ++ 94, 38, 43, 45, 42, 37, 126, 33 + }; + # endif + +-#define YYPACT_NINF -81 +- +-#define yypact_value_is_default(Yystate) \ +- (!!((Yystate) == (-81))) +- +-#define YYTABLE_NINF -1 +- +-#define yytable_value_is_error(Yytable_value) \ +- 0 +- +- /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing +- STATE-NUM. */ +-static const yytype_int8 yypact[] = ++/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ ++static const yytype_uint8 yyr1[] = + { +- 16, -11, 21, 10, -81, 25, 10, 19, 10, -81, +- -81, -9, 25, -81, 2, 51, -81, -9, -9, -9, +- -81, 1, -81, -6, 50, 14, 28, 29, 36, 3, +- 58, 44, -3, -81, 47, -81, -81, 65, 68, 2, +- 2, -81, -81, -81, -81, -9, -9, -9, -9, -9, +- -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, +- -9, -9, -9, -9, -81, 63, 69, 2, -81, -81, +- 50, 57, 14, 28, 29, 36, 3, 3, 58, 58, +- 58, 58, 44, 44, -3, -3, -81, -81, -81, 79, +- 80, -8, 63, -81, 72, 63, -81, -81, -9, 76, +- 77, -81, -81, -81, -81, -81, 78, -81, -81, -81, +- -81, -81, 35, 4, -81, -81, -81, -81, 86, -81, +- -81, -81, 73, -81, -81, 33, 71, 84, 39, -81, +- -81, -81, -81, -81, 41, -81, -81, -81, 25, -81, +- 74, 25, 75, -81 ++ 0, 48, 49, 50, 50, 51, 51, 52, 52, 53, ++ 53, 53, 53, 53, 54, 55, 55, 56, 56, 56, ++ 56, 57, 57, 57, 57, 57, 57, 57, 58, 58, ++ 58, 59, 59, 59, 59, 59, 60, 60, 60, 61, ++ 62, 62, 63, 63, 64, 64, 65, 65, 66, 66, ++ 67, 67, 68, 68, 68, 69, 69, 69, 69, 69, ++ 70, 70, 70, 71, 71, 71, 72, 72, 72, 72, ++ 73, 73, 73, 73, 74, 74, 74, 75, 75, 75, ++ 76, 76, 76 + }; + +- /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. +- Performed when YYTABLE does not specify something else to do. Zero +- means the default is an error. */ +-static const yytype_uint8 yydefact[] = ++/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ ++static const yytype_uint8 yyr2[] = + { +- 0, 0, 0, 3, 1, 0, 0, 0, 3, 34, +- 35, 0, 0, 6, 0, 2, 4, 0, 0, 0, +- 68, 0, 37, 38, 40, 42, 44, 46, 48, 50, +- 53, 60, 63, 67, 0, 13, 7, 0, 0, 0, +- 0, 69, 70, 71, 36, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 5, 75, 0, 0, 10, 8, +- 41, 0, 43, 45, 47, 49, 51, 52, 56, 57, +- 55, 54, 58, 59, 61, 62, 65, 64, 66, 0, +- 0, 0, 0, 14, 0, 75, 11, 9, 0, 0, +- 0, 16, 26, 78, 18, 80, 0, 77, 76, 39, +- 17, 79, 0, 0, 12, 25, 15, 27, 0, 19, +- 28, 22, 0, 72, 30, 0, 0, 0, 0, 33, +- 32, 20, 31, 29, 0, 73, 74, 21, 0, 24, +- 0, 0, 0, 23 ++ 0, 2, 5, 0, 2, 0, 2, 4, 2, 2, ++ 3, 4, 3, 4, 5, 0, 2, 4, 2, 3, ++ 2, 2, 3, 4, 2, 9, 5, 2, 0, 2, ++ 2, 3, 1, 2, 2, 2, 1, 1, 3, 1, ++ 1, 5, 1, 3, 1, 3, 1, 3, 1, 3, ++ 1, 3, 1, 3, 3, 1, 3, 3, 3, 3, ++ 3, 3, 1, 3, 3, 1, 3, 3, 3, 1, ++ 1, 2, 2, 2, 0, 2, 2, 0, 2, 2, ++ 2, 3, 2 + }; + +- /* YYPGOTO[NTERM-NUM]. */ +-static const yytype_int8 yypgoto[] = ++/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. ++ Performed when YYTABLE doesn't specify something else to do. Zero ++ means the default is an error. */ ++static const yytype_uint8 yydefact[] = + { +- -81, -81, 100, 104, -81, -38, -81, -80, -81, -81, +- -81, -5, 66, 13, -81, 70, 67, 81, 64, 82, +- 37, 27, 34, 38, -14, -81, 22, 24 ++ 0, 0, 0, 3, 1, 0, 5, 4, 0, 0, ++ 0, 5, 36, 37, 0, 0, 8, 0, 2, 6, ++ 0, 0, 0, 70, 0, 39, 40, 42, 44, 46, ++ 48, 50, 52, 55, 62, 65, 69, 0, 15, 9, ++ 0, 0, 0, 0, 71, 72, 73, 38, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 7, 77, 0, ++ 0, 12, 10, 43, 0, 45, 47, 49, 51, 53, ++ 54, 58, 59, 57, 56, 60, 61, 63, 64, 67, ++ 66, 68, 0, 0, 0, 0, 16, 0, 77, 13, ++ 11, 0, 0, 0, 18, 28, 80, 20, 82, 0, ++ 79, 78, 41, 19, 81, 0, 0, 14, 27, 17, ++ 29, 0, 21, 30, 24, 0, 74, 32, 0, 0, ++ 0, 0, 35, 34, 22, 33, 31, 0, 75, 76, ++ 23, 0, 26, 0, 0, 0, 25 + }; + +- /* YYDEFGOTO[NTERM-NUM]. */ ++/* YYDEFGOTO[NTERM-NUM]. */ + static const yytype_int16 yydefgoto[] = + { +- -1, 2, 7, 8, 15, 36, 65, 93, 112, 113, +- 125, 20, 21, 22, 23, 24, 25, 26, 27, 28, +- 29, 30, 31, 32, 33, 128, 94, 95 ++ -1, 2, 6, 10, 11, 18, 39, 68, 96, 115, ++ 116, 128, 23, 24, 25, 26, 27, 28, 29, 30, ++ 31, 32, 33, 34, 35, 36, 131, 97, 98 + }; + +- /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If +- positive, shift that token. If negative, reduce the rule whose +- number is the opposite. If YYTABLE_NINF, syntax error. */ +-static const yytype_uint8 yytable[] = ++/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing ++ STATE-NUM. */ ++#define YYPACT_NINF -84 ++static const yytype_int8 yypact[] = + { +- 12, 68, 69, 41, 42, 43, 45, 34, 9, 10, +- 53, 54, 104, 3, 5, 107, 101, 118, 35, 1, +- 102, 4, 61, 11, 119, 120, 121, 122, 35, 97, +- 46, 6, 55, 17, 123, 44, 18, 19, 56, 124, +- 62, 63, 9, 10, 14, 51, 52, 86, 87, 88, +- 9, 10, 48, 103, 129, 130, 115, 11, 135, 116, +- 136, 47, 131, 57, 58, 11, 37, 49, 117, 50, +- 137, 64, 38, 39, 138, 139, 40, 89, 90, 91, +- 78, 79, 80, 81, 92, 59, 60, 66, 76, 77, +- 67, 82, 83, 96, 98, 99, 100, 84, 85, 106, +- 110, 111, 114, 126, 134, 127, 133, 141, 16, 143, +- 13, 109, 71, 74, 72, 70, 105, 108, 0, 0, +- 132, 0, 0, 0, 0, 0, 0, 0, 0, 73, +- 0, 0, 75, 140, 0, 0, 142 ++ 15, -12, 35, 42, -84, 27, 9, -84, 24, 9, ++ 43, 9, -84, -84, -10, 24, -84, 60, 44, -84, ++ -10, -10, -10, -84, 55, -84, -7, 52, 53, 51, ++ 54, 10, 2, 38, 37, -4, -84, 68, -84, -84, ++ 71, 73, 60, 60, -84, -84, -84, -84, -10, -10, ++ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, ++ -10, -10, -10, -10, -10, -10, -10, -84, 56, 72, ++ 60, -84, -84, 52, 61, 53, 51, 54, 10, 2, ++ 2, 38, 38, 38, 38, 37, 37, -4, -4, -84, ++ -84, -84, 81, 83, 34, 56, -84, 74, 56, -84, ++ -84, -10, 76, 78, -84, -84, -84, -84, -84, 79, ++ -84, -84, -84, -84, -84, -6, 3, -84, -84, -84, ++ -84, 87, -84, -84, -84, 75, -84, -84, 32, 70, ++ 86, 36, -84, -84, -84, -84, -84, 47, -84, -84, ++ -84, 24, -84, 77, 24, 80, -84 + }; + +-static const yytype_int16 yycheck[] = ++/* YYPGOTO[NTERM-NUM]. */ ++static const yytype_int8 yypgoto[] = + { +- 5, 39, 40, 17, 18, 19, 12, 12, 17, 18, +- 7, 8, 92, 24, 4, 95, 24, 13, 26, 3, +- 28, 0, 25, 32, 20, 21, 22, 23, 26, 67, +- 36, 21, 29, 42, 30, 34, 45, 46, 35, 35, +- 43, 44, 17, 18, 25, 9, 10, 61, 62, 63, +- 17, 18, 38, 91, 21, 22, 21, 32, 19, 24, +- 21, 11, 29, 5, 6, 32, 15, 39, 33, 40, +- 31, 24, 21, 22, 33, 34, 25, 14, 15, 16, +- 53, 54, 55, 56, 21, 41, 42, 22, 51, 52, +- 22, 57, 58, 24, 37, 16, 16, 59, 60, 27, +- 24, 24, 24, 17, 20, 32, 35, 33, 8, 34, +- 6, 98, 46, 49, 47, 45, 92, 95, -1, -1, +- 125, -1, -1, -1, -1, -1, -1, -1, -1, 48, +- -1, -1, 50, 138, -1, -1, 141 ++ -84, -84, -84, 98, 101, -84, -41, -84, -83, -84, ++ -84, -84, -8, 63, 12, -84, 66, 67, 65, 69, ++ 82, 29, 18, 25, 26, -17, -84, 20, 28 + }; + +- /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing +- symbol of state STATE-NUM. */ +-static const yytype_uint8 yystos[] = ++/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If ++ positive, shift that token. If negative, reduce the rule which ++ number is the opposite. If YYTABLE_NINF, syntax error. */ ++#define YYTABLE_NINF -1 ++static const yytype_uint8 yytable[] = + { +- 0, 3, 48, 24, 0, 4, 21, 49, 50, 17, +- 18, 32, 58, 50, 25, 51, 49, 42, 45, 46, +- 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, +- 68, 69, 70, 71, 58, 26, 52, 15, 21, 22, +- 25, 71, 71, 71, 34, 12, 36, 11, 38, 39, +- 40, 9, 10, 7, 8, 29, 35, 5, 6, 41, +- 42, 25, 43, 44, 24, 53, 22, 22, 52, 52, +- 62, 59, 63, 64, 65, 66, 67, 67, 68, 68, +- 68, 68, 69, 69, 70, 70, 71, 71, 71, 14, +- 15, 16, 21, 54, 73, 74, 24, 52, 37, 16, +- 16, 24, 28, 52, 54, 74, 27, 54, 73, 60, +- 24, 24, 55, 56, 24, 21, 24, 33, 13, 20, +- 21, 22, 23, 30, 35, 57, 17, 32, 72, 21, +- 22, 29, 58, 35, 20, 19, 21, 31, 33, 34, +- 58, 33, 58, 34 ++ 15, 71, 72, 44, 45, 46, 48, 37, 12, 13, ++ 56, 57, 107, 3, 8, 110, 118, 121, 1, 119, ++ 54, 55, 64, 14, 122, 123, 124, 125, 120, 100, ++ 49, 9, 58, 20, 126, 4, 21, 22, 59, 127, ++ 65, 66, 12, 13, 60, 61, 5, 89, 90, 91, ++ 12, 13, 7, 106, 132, 133, 138, 14, 139, 104, ++ 40, 38, 134, 105, 50, 14, 41, 42, 140, 17, ++ 43, 92, 93, 94, 81, 82, 83, 84, 95, 62, ++ 63, 141, 142, 79, 80, 85, 86, 38, 87, 88, ++ 47, 52, 51, 67, 69, 53, 70, 99, 102, 101, ++ 103, 113, 109, 114, 117, 129, 136, 137, 130, 19, ++ 16, 144, 74, 112, 73, 146, 76, 75, 111, 0, ++ 135, 77, 0, 108, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 143, 0, 78, 145 + }; + +- /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +-static const yytype_uint8 yyr1[] = ++#define yypact_value_is_default(yystate) \ ++ ((yystate) == (-84)) ++ ++#define yytable_value_is_error(yytable_value) \ ++ YYID (0) ++ ++static const yytype_int16 yycheck[] = + { +- 0, 47, 48, 49, 49, 50, 50, 51, 51, 51, +- 51, 51, 52, 53, 53, 54, 54, 54, 54, 55, +- 55, 55, 55, 55, 55, 55, 56, 56, 56, 57, +- 57, 57, 57, 57, 58, 58, 58, 59, 60, 60, +- 61, 61, 62, 62, 63, 63, 64, 64, 65, 65, +- 66, 66, 66, 67, 67, 67, 67, 67, 68, 68, +- 68, 69, 69, 69, 70, 70, 70, 70, 71, 71, +- 71, 71, 72, 72, 72, 73, 73, 73, 74, 74, +- 74 ++ 8, 42, 43, 20, 21, 22, 13, 15, 18, 19, ++ 8, 9, 95, 25, 5, 98, 22, 14, 3, 25, ++ 10, 11, 26, 33, 21, 22, 23, 24, 34, 70, ++ 37, 22, 30, 43, 31, 0, 46, 47, 36, 36, ++ 44, 45, 18, 19, 6, 7, 4, 64, 65, 66, ++ 18, 19, 25, 94, 22, 23, 20, 33, 22, 25, ++ 16, 27, 30, 29, 12, 33, 22, 23, 32, 26, ++ 26, 15, 16, 17, 56, 57, 58, 59, 22, 42, ++ 43, 34, 35, 54, 55, 60, 61, 27, 62, 63, ++ 35, 40, 39, 25, 23, 41, 23, 25, 17, 38, ++ 17, 25, 28, 25, 25, 18, 36, 21, 33, 11, ++ 9, 34, 49, 101, 48, 35, 51, 50, 98, -1, ++ 128, 52, -1, 95, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, 141, -1, 53, 144 + }; + +- /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +-static const yytype_uint8 yyr2[] = ++/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing ++ symbol of state STATE-NUM. */ ++static const yytype_uint8 yystos[] = + { +- 0, 2, 4, 0, 2, 4, 2, 2, 3, 4, +- 3, 4, 5, 0, 2, 4, 2, 3, 2, 2, +- 3, 4, 2, 9, 5, 2, 0, 2, 2, 3, +- 1, 2, 2, 2, 1, 1, 3, 1, 1, 5, +- 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, +- 1, 3, 3, 1, 3, 3, 3, 3, 3, 3, +- 1, 3, 3, 1, 3, 3, 3, 1, 1, 2, +- 2, 2, 0, 2, 2, 0, 2, 2, 2, 3, +- 2 ++ 0, 3, 49, 25, 0, 4, 50, 25, 5, 22, ++ 51, 52, 18, 19, 33, 60, 52, 26, 53, 51, ++ 43, 46, 47, 60, 61, 62, 63, 64, 65, 66, ++ 67, 68, 69, 70, 71, 72, 73, 60, 27, 54, ++ 16, 22, 23, 26, 73, 73, 73, 35, 13, 37, ++ 12, 39, 40, 41, 10, 11, 8, 9, 30, 36, ++ 6, 7, 42, 43, 26, 44, 45, 25, 55, 23, ++ 23, 54, 54, 64, 61, 65, 66, 67, 68, 69, ++ 69, 70, 70, 70, 70, 71, 71, 72, 72, 73, ++ 73, 73, 15, 16, 17, 22, 56, 75, 76, 25, ++ 54, 38, 17, 17, 25, 29, 54, 56, 76, 28, ++ 56, 75, 62, 25, 25, 57, 58, 25, 22, 25, ++ 34, 14, 21, 22, 23, 24, 31, 36, 59, 18, ++ 33, 74, 22, 23, 30, 60, 36, 21, 20, 22, ++ 32, 34, 35, 60, 34, 60, 35 + }; + +- +-#define yyerrok (yyerrstatus = 0) +-#define yyclearin (yychar = YYEMPTY) +-#define YYEMPTY (-2) +-#define YYEOF 0 +- +-#define YYACCEPT goto yyacceptlab +-#define YYABORT goto yyabortlab +-#define YYERROR goto yyerrorlab +- ++#define yyerrok (yyerrstatus = 0) ++#define yyclearin (yychar = YYEMPTY) ++#define YYEMPTY (-2) ++#define YYEOF 0 ++ ++#define YYACCEPT goto yyacceptlab ++#define YYABORT goto yyabortlab ++#define YYERROR goto yyerrorlab ++ ++ ++/* Like YYERROR except do call yyerror. This remains here temporarily ++ to ease the transition to the new meaning of YYERROR, for GCC. ++ Once GCC version 2 has supplanted version 1, this can go. However, ++ YYFAIL appears to be in use. Nevertheless, it is formally deprecated ++ in Bison 2.4.2's NEWS entry, where a plan to phase it out is ++ discussed. */ ++ ++#define YYFAIL goto yyerrlab ++#if defined YYFAIL ++ /* This is here to suppress warnings from the GCC cpp's ++ -Wunused-macros. Normally we don't worry about that warning, but ++ some users do, and we want to make it easy for users to remove ++ YYFAIL uses, which will produce warnings from Bison 2.5. */ ++#endif + + #define YYRECOVERING() (!!yyerrstatus) + +-#define YYBACKUP(Token, Value) \ +-do \ +- if (yychar == YYEMPTY) \ +- { \ +- yychar = (Token); \ +- yylval = (Value); \ +- YYPOPSTACK (yylen); \ +- yystate = *yyssp; \ +- goto yybackup; \ +- } \ +- else \ +- { \ ++#define YYBACKUP(Token, Value) \ ++do \ ++ if (yychar == YYEMPTY && yylen == 1) \ ++ { \ ++ yychar = (Token); \ ++ yylval = (Value); \ ++ YYPOPSTACK (1); \ ++ goto yybackup; \ ++ } \ ++ else \ ++ { \ + yyerror (YY_("syntax error: cannot back up")); \ +- YYERROR; \ +- } \ +-while (0) +- +-/* Error token number */ +-#define YYTERROR 1 +-#define YYERRCODE 256 ++ YYERROR; \ ++ } \ ++while (YYID (0)) ++ ++ ++#define YYTERROR 1 ++#define YYERRCODE 256 + + + /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + ++#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + #ifndef YYLLOC_DEFAULT +-# define YYLLOC_DEFAULT(Current, Rhs, N) \ +- do \ +- if (N) \ +- { \ +- (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ +- (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ +- (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ +- (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ +- } \ +- else \ +- { \ +- (Current).first_line = (Current).last_line = \ +- YYRHSLOC (Rhs, 0).last_line; \ +- (Current).first_column = (Current).last_column = \ +- YYRHSLOC (Rhs, 0).last_column; \ +- } \ +- while (0) ++# define YYLLOC_DEFAULT(Current, Rhs, N) \ ++ do \ ++ if (YYID (N)) \ ++ { \ ++ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ ++ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ ++ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ ++ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ ++ } \ ++ else \ ++ { \ ++ (Current).first_line = (Current).last_line = \ ++ YYRHSLOC (Rhs, 0).last_line; \ ++ (Current).first_column = (Current).last_column = \ ++ YYRHSLOC (Rhs, 0).last_column; \ ++ } \ ++ while (YYID (0)) + #endif + +-#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +- +- +-/* Enable debugging if requested. */ +-#if YYDEBUG +- +-# ifndef YYFPRINTF +-# include /* INFRINGES ON USER NAME SPACE */ +-# define YYFPRINTF fprintf +-# endif +- +-# define YYDPRINTF(Args) \ +-do { \ +- if (yydebug) \ +- YYFPRINTF Args; \ +-} while (0) +- + + /* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know +@@ -779,73 +799,82 @@ do { + + #ifndef YY_LOCATION_PRINT + # if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +- +-/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ +- +-YY_ATTRIBUTE_UNUSED +-static unsigned +-yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +-{ +- unsigned res = 0; +- int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; +- if (0 <= yylocp->first_line) +- { +- res += YYFPRINTF (yyo, "%d", yylocp->first_line); +- if (0 <= yylocp->first_column) +- res += YYFPRINTF (yyo, ".%d", yylocp->first_column); +- } +- if (0 <= yylocp->last_line) +- { +- if (yylocp->first_line < yylocp->last_line) +- { +- res += YYFPRINTF (yyo, "-%d", yylocp->last_line); +- if (0 <= end_col) +- res += YYFPRINTF (yyo, ".%d", end_col); +- } +- else if (0 <= end_col && yylocp->first_column < end_col) +- res += YYFPRINTF (yyo, "-%d", end_col); +- } +- return res; +- } +- +-# define YY_LOCATION_PRINT(File, Loc) \ +- yy_location_print_ (File, &(Loc)) +- ++# define YY_LOCATION_PRINT(File, Loc) \ ++ fprintf (File, "%d.%d-%d.%d", \ ++ (Loc).first_line, (Loc).first_column, \ ++ (Loc).last_line, (Loc).last_column) + # else + # define YY_LOCATION_PRINT(File, Loc) ((void) 0) + # endif + #endif + + +-# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +-do { \ +- if (yydebug) \ +- { \ +- YYFPRINTF (stderr, "%s ", Title); \ +- yy_symbol_print (stderr, \ +- Type, Value, Location); \ +- YYFPRINTF (stderr, "\n"); \ +- } \ +-} while (0) ++/* YYLEX -- calling `yylex' with the right arguments. */ + ++#ifdef YYLEX_PARAM ++# define YYLEX yylex (YYLEX_PARAM) ++#else ++# define YYLEX yylex () ++#endif + +-/*----------------------------------------. +-| Print this symbol's value on YYOUTPUT. | +-`----------------------------------------*/ ++/* Enable debugging if requested. */ ++#if YYDEBUG + ++# ifndef YYFPRINTF ++# include /* INFRINGES ON USER NAME SPACE */ ++# define YYFPRINTF fprintf ++# endif ++ ++# define YYDPRINTF(Args) \ ++do { \ ++ if (yydebug) \ ++ YYFPRINTF Args; \ ++} while (YYID (0)) ++ ++# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ ++do { \ ++ if (yydebug) \ ++ { \ ++ YYFPRINTF (stderr, "%s ", Title); \ ++ yy_symbol_print (stderr, \ ++ Type, Value, Location); \ ++ YYFPRINTF (stderr, "\n"); \ ++ } \ ++} while (YYID (0)) ++ ++ ++/*--------------------------------. ++| Print this symbol on YYOUTPUT. | ++`--------------------------------*/ ++ ++/*ARGSUSED*/ ++#if (defined __STDC__ || defined __C99__FUNC__ \ ++ || defined __cplusplus || defined _MSC_VER) + static void + yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp) ++#else ++static void ++yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp) ++ FILE *yyoutput; ++ int yytype; ++ YYSTYPE const * const yyvaluep; ++ YYLTYPE const * const yylocationp; ++#endif + { +- FILE *yyo = yyoutput; +- YYUSE (yyo); +- YYUSE (yylocationp); + if (!yyvaluep) + return; ++ YYUSE (yylocationp); + # ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); ++# else ++ YYUSE (yyoutput); + # endif +- YYUSE (yytype); ++ switch (yytype) ++ { ++ default: ++ break; ++ } + } + + +@@ -853,11 +882,23 @@ yy_symbol_value_print (FILE *yyoutput, i + | Print this symbol on YYOUTPUT. | + `--------------------------------*/ + ++#if (defined __STDC__ || defined __C99__FUNC__ \ ++ || defined __cplusplus || defined _MSC_VER) + static void + yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp) ++#else ++static void ++yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp) ++ FILE *yyoutput; ++ int yytype; ++ YYSTYPE const * const yyvaluep; ++ YYLTYPE const * const yylocationp; ++#endif + { +- YYFPRINTF (yyoutput, "%s %s (", +- yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); ++ if (yytype < YYNTOKENS) ++ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); ++ else ++ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + YY_LOCATION_PRINT (yyoutput, *yylocationp); + YYFPRINTF (yyoutput, ": "); +@@ -870,8 +911,16 @@ yy_symbol_print (FILE *yyoutput, int yyt + | TOP (included). | + `------------------------------------------------------------------*/ + ++#if (defined __STDC__ || defined __C99__FUNC__ \ ++ || defined __cplusplus || defined _MSC_VER) + static void + yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) ++#else ++static void ++yy_stack_print (yybottom, yytop) ++ yytype_int16 *yybottom; ++ yytype_int16 *yytop; ++#endif + { + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) +@@ -882,42 +931,50 @@ yy_stack_print (yytype_int16 *yybottom, + YYFPRINTF (stderr, "\n"); + } + +-# define YY_STACK_PRINT(Bottom, Top) \ +-do { \ +- if (yydebug) \ +- yy_stack_print ((Bottom), (Top)); \ +-} while (0) ++# define YY_STACK_PRINT(Bottom, Top) \ ++do { \ ++ if (yydebug) \ ++ yy_stack_print ((Bottom), (Top)); \ ++} while (YYID (0)) + + + /*------------------------------------------------. + | Report that the YYRULE is going to be reduced. | + `------------------------------------------------*/ + ++#if (defined __STDC__ || defined __C99__FUNC__ \ ++ || defined __cplusplus || defined _MSC_VER) ++static void ++yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule) ++#else + static void +-yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule) ++yy_reduce_print (yyvsp, yylsp, yyrule) ++ YYSTYPE *yyvsp; ++ YYLTYPE *yylsp; ++ int yyrule; ++#endif + { +- unsigned long int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; ++ unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", +- yyrule - 1, yylno); ++ yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); +- yy_symbol_print (stderr, +- yystos[yyssp[yyi + 1 - yynrhs]], +- &(yyvsp[(yyi + 1) - (yynrhs)]) +- , &(yylsp[(yyi + 1) - (yynrhs)]) ); ++ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], ++ &(yyvsp[(yyi + 1) - (yynrhs)]) ++ , &(yylsp[(yyi + 1) - (yynrhs)]) ); + YYFPRINTF (stderr, "\n"); + } + } + +-# define YY_REDUCE_PRINT(Rule) \ +-do { \ +- if (yydebug) \ +- yy_reduce_print (yyssp, yyvsp, yylsp, Rule); \ +-} while (0) ++# define YY_REDUCE_PRINT(Rule) \ ++do { \ ++ if (yydebug) \ ++ yy_reduce_print (yyvsp, yylsp, Rule); \ ++} while (YYID (0)) + + /* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +@@ -931,7 +988,7 @@ int yydebug; + + + /* YYINITDEPTH -- initial size of the parser's stacks. */ +-#ifndef YYINITDEPTH ++#ifndef YYINITDEPTH + # define YYINITDEPTH 200 + #endif + +@@ -954,8 +1011,15 @@ int yydebug; + # define yystrlen strlen + # else + /* Return the length of YYSTR. */ ++#if (defined __STDC__ || defined __C99__FUNC__ \ ++ || defined __cplusplus || defined _MSC_VER) + static YYSIZE_T + yystrlen (const char *yystr) ++#else ++static YYSIZE_T ++yystrlen (yystr) ++ const char *yystr; ++#endif + { + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) +@@ -971,8 +1035,16 @@ yystrlen (const char *yystr) + # else + /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ ++#if (defined __STDC__ || defined __C99__FUNC__ \ ++ || defined __cplusplus || defined _MSC_VER) + static char * + yystpcpy (char *yydest, const char *yysrc) ++#else ++static char * ++yystpcpy (yydest, yysrc) ++ char *yydest; ++ const char *yysrc; ++#endif + { + char *yyd = yydest; + const char *yys = yysrc; +@@ -1002,27 +1074,27 @@ yytnamerr (char *yyres, const char *yyst + char const *yyp = yystr; + + for (;;) +- switch (*++yyp) +- { +- case '\'': +- case ',': +- goto do_not_strip_quotes; +- +- case '\\': +- if (*++yyp != '\\') +- goto do_not_strip_quotes; +- /* Fall through. */ +- default: +- if (yyres) +- yyres[yyn] = *yyp; +- yyn++; +- break; +- +- case '"': +- if (yyres) +- yyres[yyn] = '\0'; +- return yyn; +- } ++ switch (*++yyp) ++ { ++ case '\'': ++ case ',': ++ goto do_not_strip_quotes; ++ ++ case '\\': ++ if (*++yyp != '\\') ++ goto do_not_strip_quotes; ++ /* Fall through. */ ++ default: ++ if (yyres) ++ yyres[yyn] = *yyp; ++ yyn++; ++ break; ++ ++ case '"': ++ if (yyres) ++ yyres[yyn] = '\0'; ++ return yyn; ++ } + do_not_strip_quotes: ; + } + +@@ -1045,11 +1117,12 @@ static int + yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) + { +- YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); ++ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]); + YYSIZE_T yysize = yysize0; ++ YYSIZE_T yysize1; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ +- const char *yyformat = YY_NULLPTR; ++ const char *yyformat = 0; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per +@@ -1057,6 +1130,10 @@ yysyntax_error (YYSIZE_T *yymsg_alloc, c + int yycount = 0; + + /* There are many possibilities here to consider: ++ - Assume YYFAIL is not used. It's too flawed to consider. See ++ ++ for details. YYERROR is fine as it does not invoke this ++ function. + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected +@@ -1105,13 +1182,11 @@ yysyntax_error (YYSIZE_T *yymsg_alloc, c + break; + } + yyarg[yycount++] = yytname[yyx]; +- { +- YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); +- if (! (yysize <= yysize1 +- && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) +- return 2; +- yysize = yysize1; +- } ++ yysize1 = yysize + yytnamerr (0, yytname[yyx]); ++ if (! (yysize <= yysize1 ++ && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) ++ return 2; ++ yysize = yysize1; + } + } + } +@@ -1131,12 +1206,10 @@ yysyntax_error (YYSIZE_T *yymsg_alloc, c + # undef YYCASE_ + } + +- { +- YYSIZE_T yysize1 = yysize + yystrlen (yyformat); +- if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) +- return 2; +- yysize = yysize1; +- } ++ yysize1 = yysize + yystrlen (yyformat); ++ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) ++ return 2; ++ yysize = yysize1; + + if (*yymsg_alloc < yysize) + { +@@ -1173,21 +1246,50 @@ yysyntax_error (YYSIZE_T *yymsg_alloc, c + | Release the memory associated to this symbol. | + `-----------------------------------------------*/ + ++/*ARGSUSED*/ ++#if (defined __STDC__ || defined __C99__FUNC__ \ ++ || defined __cplusplus || defined _MSC_VER) + static void + yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp) ++#else ++static void ++yydestruct (yymsg, yytype, yyvaluep, yylocationp) ++ const char *yymsg; ++ int yytype; ++ YYSTYPE *yyvaluep; ++ YYLTYPE *yylocationp; ++#endif + { + YYUSE (yyvaluep); + YYUSE (yylocationp); ++ + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + +- YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +- YYUSE (yytype); +- YY_IGNORE_MAYBE_UNINITIALIZED_END ++ switch (yytype) ++ { ++ ++ default: ++ break; ++ } + } + + ++/* Prevent warnings from -Wmissing-prototypes. */ ++#ifdef YYPARSE_PARAM ++#if defined __STDC__ || defined __cplusplus ++int yyparse (void *YYPARSE_PARAM); ++#else ++int yyparse (); ++#endif ++#else /* ! YYPARSE_PARAM */ ++#if defined __STDC__ || defined __cplusplus ++int yyparse (void); ++#else ++int yyparse (); ++#endif ++#endif /* ! YYPARSE_PARAM */ + + + /* The lookahead symbol. */ +@@ -1195,12 +1297,10 @@ int yychar; + + /* The semantic value of the lookahead symbol. */ + YYSTYPE yylval; ++ + /* Location data for the lookahead symbol. */ +-YYLTYPE yylloc +-# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +- = { 1, 1, 1, 1 } +-# endif +-; ++YYLTYPE yylloc; ++ + /* Number of syntax errors so far. */ + int yynerrs; + +@@ -1209,19 +1309,38 @@ int yynerrs; + | yyparse. | + `----------*/ + ++#ifdef YYPARSE_PARAM ++#if (defined __STDC__ || defined __C99__FUNC__ \ ++ || defined __cplusplus || defined _MSC_VER) ++int ++yyparse (void *YYPARSE_PARAM) ++#else ++int ++yyparse (YYPARSE_PARAM) ++ void *YYPARSE_PARAM; ++#endif ++#else /* ! YYPARSE_PARAM */ ++#if (defined __STDC__ || defined __C99__FUNC__ \ ++ || defined __cplusplus || defined _MSC_VER) + int + yyparse (void) ++#else ++int ++yyparse () ++ ++#endif ++#endif + { + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: +- 'yyss': related to states. +- 'yyvs': related to semantic values. +- 'yyls': related to locations. ++ `yyss': related to states. ++ `yyvs': related to semantic values. ++ `yyls': related to locations. + +- Refer to the stacks through separate pointers, to allow yyoverflow ++ Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ +@@ -1247,7 +1366,7 @@ yyparse (void) + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ +- int yytoken = 0; ++ int yytoken; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; +@@ -1266,9 +1385,10 @@ yyparse (void) + Keep to zero when no symbol should be popped. */ + int yylen = 0; + +- yyssp = yyss = yyssa; +- yyvsp = yyvs = yyvsa; +- yylsp = yyls = yylsa; ++ yytoken = 0; ++ yyss = yyssa; ++ yyvs = yyvsa; ++ yyls = yylsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); +@@ -1277,7 +1397,21 @@ yyparse (void) + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ +- yylsp[0] = yylloc; ++ ++ /* Initialize stack pointers. ++ Waste one element of value and location stack ++ so that they stay on the same level as the state stack. ++ The wasted elements are never initialized. */ ++ yyssp = yyss; ++ yyvsp = yyvs; ++ yylsp = yyls; ++ ++#if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL ++ /* Initialize the default location before parsing starts. */ ++ yylloc.first_line = yylloc.last_line = 1; ++ yylloc.first_column = yylloc.last_column = 1; ++#endif ++ + goto yysetstate; + + /*------------------------------------------------------------. +@@ -1298,26 +1432,26 @@ yyparse (void) + + #ifdef yyoverflow + { +- /* Give user a chance to reallocate the stack. Use copies of +- these so that the &'s don't force the real ones into +- memory. */ +- YYSTYPE *yyvs1 = yyvs; +- yytype_int16 *yyss1 = yyss; +- YYLTYPE *yyls1 = yyls; +- +- /* Each stack pointer address is followed by the size of the +- data in use in that stack, in bytes. This used to be a +- conditional around just the two extra args, but that might +- be undefined if yyoverflow is a macro. */ +- yyoverflow (YY_("memory exhausted"), +- &yyss1, yysize * sizeof (*yyssp), +- &yyvs1, yysize * sizeof (*yyvsp), +- &yyls1, yysize * sizeof (*yylsp), +- &yystacksize); +- +- yyls = yyls1; +- yyss = yyss1; +- yyvs = yyvs1; ++ /* Give user a chance to reallocate the stack. Use copies of ++ these so that the &'s don't force the real ones into ++ memory. */ ++ YYSTYPE *yyvs1 = yyvs; ++ yytype_int16 *yyss1 = yyss; ++ YYLTYPE *yyls1 = yyls; ++ ++ /* Each stack pointer address is followed by the size of the ++ data in use in that stack, in bytes. This used to be a ++ conditional around just the two extra args, but that might ++ be undefined if yyoverflow is a macro. */ ++ yyoverflow (YY_("memory exhausted"), ++ &yyss1, yysize * sizeof (*yyssp), ++ &yyvs1, yysize * sizeof (*yyvsp), ++ &yyls1, yysize * sizeof (*yylsp), ++ &yystacksize); ++ ++ yyls = yyls1; ++ yyss = yyss1; ++ yyvs = yyvs1; + } + #else /* no yyoverflow */ + # ifndef YYSTACK_RELOCATE +@@ -1325,23 +1459,23 @@ yyparse (void) + # else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) +- goto yyexhaustedlab; ++ goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) +- yystacksize = YYMAXDEPTH; ++ yystacksize = YYMAXDEPTH; + + { +- yytype_int16 *yyss1 = yyss; +- union yyalloc *yyptr = +- (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); +- if (! yyptr) +- goto yyexhaustedlab; +- YYSTACK_RELOCATE (yyss_alloc, yyss); +- YYSTACK_RELOCATE (yyvs_alloc, yyvs); +- YYSTACK_RELOCATE (yyls_alloc, yyls); ++ yytype_int16 *yyss1 = yyss; ++ union yyalloc *yyptr = ++ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); ++ if (! yyptr) ++ goto yyexhaustedlab; ++ YYSTACK_RELOCATE (yyss_alloc, yyss); ++ YYSTACK_RELOCATE (yyvs_alloc, yyvs); ++ YYSTACK_RELOCATE (yyls_alloc, yyls); + # undef YYSTACK_RELOCATE +- if (yyss1 != yyssa) +- YYSTACK_FREE (yyss1); ++ if (yyss1 != yyssa) ++ YYSTACK_FREE (yyss1); + } + # endif + #endif /* no yyoverflow */ +@@ -1351,10 +1485,10 @@ yyparse (void) + yylsp = yyls + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", +- (unsigned long int) yystacksize)); ++ (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) +- YYABORT; ++ YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); +@@ -1383,7 +1517,7 @@ yybackup: + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); +- yychar = yylex (); ++ yychar = YYLEX; + } + + if (yychar <= YYEOF) +@@ -1423,9 +1557,7 @@ yybackup: + yychar = YYEMPTY; + + yystate = yyn; +- YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; +- YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + goto yynewstate; + +@@ -1448,7 +1580,7 @@ yyreduce: + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: +- '$$ = $1'. ++ `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison +@@ -1463,273 +1595,322 @@ yyreduce: + switch (yyn) + { + case 2: +-#line 105 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 109 "dtc-parser.y" + { +- the_boot_info = build_boot_info((yyvsp[-1].re), (yyvsp[0].node), +- guess_boot_cpuid((yyvsp[0].node))); ++ (yyvsp[(5) - (5)].node)->is_plugin = (yyvsp[(3) - (5)].is_plugin); ++ (yyvsp[(5) - (5)].node)->is_root = 1; ++ the_boot_info = build_boot_info((yyvsp[(4) - (5)].re), (yyvsp[(5) - (5)].node), ++ guess_boot_cpuid((yyvsp[(5) - (5)].node))); + } +-#line 1472 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 3: +-#line 113 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 119 "dtc-parser.y" + { +- (yyval.re) = NULL; ++ (yyval.is_plugin) = 0; + } +-#line 1480 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 4: +-#line 117 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 123 "dtc-parser.y" + { +- (yyval.re) = chain_reserve_entry((yyvsp[-1].re), (yyvsp[0].re)); ++ (yyval.is_plugin) = 1; + } +-#line 1488 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 5: +-#line 124 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 130 "dtc-parser.y" + { +- (yyval.re) = build_reserve_entry((yyvsp[-2].integer), (yyvsp[-1].integer)); ++ (yyval.re) = NULL; + } +-#line 1496 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 6: +-#line 128 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 134 "dtc-parser.y" + { +- add_label(&(yyvsp[0].re)->labels, (yyvsp[-1].labelref)); +- (yyval.re) = (yyvsp[0].re); ++ (yyval.re) = chain_reserve_entry((yyvsp[(1) - (2)].re), (yyvsp[(2) - (2)].re)); + } +-#line 1505 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 7: +-#line 136 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 141 "dtc-parser.y" + { +- (yyval.node) = name_node((yyvsp[0].node), ""); ++ (yyval.re) = build_reserve_entry((yyvsp[(2) - (4)].integer), (yyvsp[(3) - (4)].integer)); + } +-#line 1513 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 8: +-#line 140 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 145 "dtc-parser.y" + { +- (yyval.node) = merge_nodes((yyvsp[-2].node), (yyvsp[0].node)); ++ add_label(&(yyvsp[(2) - (2)].re)->labels, (yyvsp[(1) - (2)].labelref)); ++ (yyval.re) = (yyvsp[(2) - (2)].re); + } +-#line 1521 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 9: +-#line 145 "dtc-parser.y" /* yacc.c:1646 */ +- { +- struct node *target = get_node_by_ref((yyvsp[-3].node), (yyvsp[-1].labelref)); + +- add_label(&target->labels, (yyvsp[-2].labelref)); +- if (target) +- merge_nodes(target, (yyvsp[0].node)); +- else +- ERROR(&(yylsp[-1]), "Label or path %s not found", (yyvsp[-1].labelref)); +- (yyval.node) = (yyvsp[-3].node); ++/* Line 1806 of yacc.c */ ++#line 153 "dtc-parser.y" ++ { ++ (yyval.node) = name_node((yyvsp[(2) - (2)].node), ""); + } +-#line 1536 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 10: +-#line 156 "dtc-parser.y" /* yacc.c:1646 */ +- { +- struct node *target = get_node_by_ref((yyvsp[-2].node), (yyvsp[-1].labelref)); + +- if (target) +- merge_nodes(target, (yyvsp[0].node)); +- else +- ERROR(&(yylsp[-1]), "Label or path %s not found", (yyvsp[-1].labelref)); +- (yyval.node) = (yyvsp[-2].node); ++/* Line 1806 of yacc.c */ ++#line 157 "dtc-parser.y" ++ { ++ (yyval.node) = merge_nodes((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + } +-#line 1550 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 11: +-#line 166 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 162 "dtc-parser.y" + { +- struct node *target = get_node_by_ref((yyvsp[-3].node), (yyvsp[-1].labelref)); ++ struct node *target = get_node_by_ref((yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].labelref)); + ++ add_label(&target->labels, (yyvsp[(2) - (4)].labelref)); + if (target) +- delete_node(target); ++ merge_nodes(target, (yyvsp[(4) - (4)].node)); + else +- ERROR(&(yylsp[-1]), "Label or path %s not found", (yyvsp[-1].labelref)); +- +- +- (yyval.node) = (yyvsp[-3].node); ++ ERROR(&(yylsp[(3) - (4)]), "Label or path %s not found", (yyvsp[(3) - (4)].labelref)); ++ (yyval.node) = (yyvsp[(1) - (4)].node); + } +-#line 1566 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 12: +-#line 181 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 173 "dtc-parser.y" + { +- (yyval.node) = build_node((yyvsp[-3].proplist), (yyvsp[-2].nodelist)); ++ struct node *target = get_node_by_ref((yyvsp[(1) - (3)].node), (yyvsp[(2) - (3)].labelref)); ++ ++ if (target) ++ merge_nodes(target, (yyvsp[(3) - (3)].node)); ++ else ++ ERROR(&(yylsp[(2) - (3)]), "Label or path %s not found", (yyvsp[(2) - (3)].labelref)); ++ (yyval.node) = (yyvsp[(1) - (3)].node); + } +-#line 1574 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 13: +-#line 188 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 183 "dtc-parser.y" + { +- (yyval.proplist) = NULL; ++ struct node *target = get_node_by_ref((yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].labelref)); ++ ++ if (target) ++ delete_node(target); ++ else ++ ERROR(&(yylsp[(3) - (4)]), "Label or path %s not found", (yyvsp[(3) - (4)].labelref)); ++ ++ ++ (yyval.node) = (yyvsp[(1) - (4)].node); + } +-#line 1582 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 14: +-#line 192 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 198 "dtc-parser.y" + { +- (yyval.proplist) = chain_property((yyvsp[0].prop), (yyvsp[-1].proplist)); ++ (yyval.node) = build_node((yyvsp[(2) - (5)].proplist), (yyvsp[(3) - (5)].nodelist)); + } +-#line 1590 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 15: +-#line 199 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 205 "dtc-parser.y" + { +- (yyval.prop) = build_property((yyvsp[-3].propnodename), (yyvsp[-1].data)); ++ (yyval.proplist) = NULL; + } +-#line 1598 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 16: +-#line 203 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 209 "dtc-parser.y" + { +- (yyval.prop) = build_property((yyvsp[-1].propnodename), empty_data); ++ (yyval.proplist) = chain_property((yyvsp[(2) - (2)].prop), (yyvsp[(1) - (2)].proplist)); + } +-#line 1606 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 17: +-#line 207 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 216 "dtc-parser.y" + { +- (yyval.prop) = build_property_delete((yyvsp[-1].propnodename)); ++ (yyval.prop) = build_property((yyvsp[(1) - (4)].propnodename), (yyvsp[(3) - (4)].data)); + } +-#line 1614 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 18: +-#line 211 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 220 "dtc-parser.y" + { +- add_label(&(yyvsp[0].prop)->labels, (yyvsp[-1].labelref)); +- (yyval.prop) = (yyvsp[0].prop); ++ (yyval.prop) = build_property((yyvsp[(1) - (2)].propnodename), empty_data); + } +-#line 1623 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 19: +-#line 219 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 224 "dtc-parser.y" + { +- (yyval.data) = data_merge((yyvsp[-1].data), (yyvsp[0].data)); ++ (yyval.prop) = build_property_delete((yyvsp[(2) - (3)].propnodename)); + } +-#line 1631 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 20: +-#line 223 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 228 "dtc-parser.y" + { +- (yyval.data) = data_merge((yyvsp[-2].data), (yyvsp[-1].array).data); ++ add_label(&(yyvsp[(2) - (2)].prop)->labels, (yyvsp[(1) - (2)].labelref)); ++ (yyval.prop) = (yyvsp[(2) - (2)].prop); + } +-#line 1639 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 21: +-#line 227 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 236 "dtc-parser.y" + { +- (yyval.data) = data_merge((yyvsp[-3].data), (yyvsp[-1].data)); ++ (yyval.data) = data_merge((yyvsp[(1) - (2)].data), (yyvsp[(2) - (2)].data)); + } +-#line 1647 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 22: +-#line 231 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 240 "dtc-parser.y" + { +- (yyval.data) = data_add_marker((yyvsp[-1].data), REF_PATH, (yyvsp[0].labelref)); ++ (yyval.data) = data_merge((yyvsp[(1) - (3)].data), (yyvsp[(2) - (3)].array).data); + } +-#line 1655 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 23: +-#line 235 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 244 "dtc-parser.y" ++ { ++ (yyval.data) = data_merge((yyvsp[(1) - (4)].data), (yyvsp[(3) - (4)].data)); ++ } ++ break; ++ ++ case 24: ++ ++/* Line 1806 of yacc.c */ ++#line 248 "dtc-parser.y" ++ { ++ (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), REF_PATH, (yyvsp[(2) - (2)].labelref)); ++ } ++ break; ++ ++ case 25: ++ ++/* Line 1806 of yacc.c */ ++#line 252 "dtc-parser.y" + { +- FILE *f = srcfile_relative_open((yyvsp[-5].data).val, NULL); ++ FILE *f = srcfile_relative_open((yyvsp[(4) - (9)].data).val, NULL); + struct data d; + +- if ((yyvsp[-3].integer) != 0) +- if (fseek(f, (yyvsp[-3].integer), SEEK_SET) != 0) ++ if ((yyvsp[(6) - (9)].integer) != 0) ++ if (fseek(f, (yyvsp[(6) - (9)].integer), SEEK_SET) != 0) + die("Couldn't seek to offset %llu in \"%s\": %s", +- (unsigned long long)(yyvsp[-3].integer), (yyvsp[-5].data).val, ++ (unsigned long long)(yyvsp[(6) - (9)].integer), (yyvsp[(4) - (9)].data).val, + strerror(errno)); + +- d = data_copy_file(f, (yyvsp[-1].integer)); ++ d = data_copy_file(f, (yyvsp[(8) - (9)].integer)); + +- (yyval.data) = data_merge((yyvsp[-8].data), d); ++ (yyval.data) = data_merge((yyvsp[(1) - (9)].data), d); + fclose(f); + } +-#line 1675 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 24: +-#line 251 "dtc-parser.y" /* yacc.c:1646 */ ++ case 26: ++ ++/* Line 1806 of yacc.c */ ++#line 268 "dtc-parser.y" + { +- FILE *f = srcfile_relative_open((yyvsp[-1].data).val, NULL); ++ FILE *f = srcfile_relative_open((yyvsp[(4) - (5)].data).val, NULL); + struct data d = empty_data; + + d = data_copy_file(f, -1); + +- (yyval.data) = data_merge((yyvsp[-4].data), d); ++ (yyval.data) = data_merge((yyvsp[(1) - (5)].data), d); + fclose(f); + } +-#line 1689 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 25: +-#line 261 "dtc-parser.y" /* yacc.c:1646 */ ++ case 27: ++ ++/* Line 1806 of yacc.c */ ++#line 278 "dtc-parser.y" + { +- (yyval.data) = data_add_marker((yyvsp[-1].data), LABEL, (yyvsp[0].labelref)); ++ (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref)); + } +-#line 1697 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 26: +-#line 268 "dtc-parser.y" /* yacc.c:1646 */ ++ case 28: ++ ++/* Line 1806 of yacc.c */ ++#line 285 "dtc-parser.y" + { + (yyval.data) = empty_data; + } +-#line 1705 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 27: +-#line 272 "dtc-parser.y" /* yacc.c:1646 */ ++ case 29: ++ ++/* Line 1806 of yacc.c */ ++#line 289 "dtc-parser.y" + { +- (yyval.data) = (yyvsp[-1].data); ++ (yyval.data) = (yyvsp[(1) - (2)].data); + } +-#line 1713 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 28: +-#line 276 "dtc-parser.y" /* yacc.c:1646 */ ++ case 30: ++ ++/* Line 1806 of yacc.c */ ++#line 293 "dtc-parser.y" + { +- (yyval.data) = data_add_marker((yyvsp[-1].data), LABEL, (yyvsp[0].labelref)); ++ (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref)); + } +-#line 1721 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 29: +-#line 283 "dtc-parser.y" /* yacc.c:1646 */ ++ case 31: ++ ++/* Line 1806 of yacc.c */ ++#line 300 "dtc-parser.y" + { + unsigned long long bits; + +- bits = (yyvsp[-1].integer); ++ bits = (yyvsp[(2) - (3)].integer); + + if ((bits != 8) && (bits != 16) && + (bits != 32) && (bits != 64)) { +- ERROR(&(yylsp[-1]), "Array elements must be" ++ ERROR(&(yylsp[(2) - (3)]), "Array elements must be" + " 8, 16, 32 or 64-bits"); + bits = 32; + } +@@ -1737,23 +1918,25 @@ yyreduce: + (yyval.array).data = empty_data; + (yyval.array).bits = bits; + } +-#line 1741 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 30: +-#line 299 "dtc-parser.y" /* yacc.c:1646 */ ++ case 32: ++ ++/* Line 1806 of yacc.c */ ++#line 316 "dtc-parser.y" + { + (yyval.array).data = empty_data; + (yyval.array).bits = 32; + } +-#line 1750 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 31: +-#line 304 "dtc-parser.y" /* yacc.c:1646 */ ++ case 33: ++ ++/* Line 1806 of yacc.c */ ++#line 321 "dtc-parser.y" + { +- if ((yyvsp[-1].array).bits < 64) { +- uint64_t mask = (1ULL << (yyvsp[-1].array).bits) - 1; ++ if ((yyvsp[(1) - (2)].array).bits < 64) { ++ uint64_t mask = (1ULL << (yyvsp[(1) - (2)].array).bits) - 1; + /* + * Bits above mask must either be all zero + * (positive within range of mask) or all one +@@ -1762,258 +1945,293 @@ yyreduce: + * within the mask to one (i.e. | in the + * mask), all bits are one. + */ +- if (((yyvsp[0].integer) > mask) && (((yyvsp[0].integer) | mask) != -1ULL)) +- ERROR(&(yylsp[0]), "Value out of range for" +- " %d-bit array element", (yyvsp[-1].array).bits); ++ if (((yyvsp[(2) - (2)].integer) > mask) && (((yyvsp[(2) - (2)].integer) | mask) != -1ULL)) ++ ERROR(&(yylsp[(2) - (2)]), "Value out of range for" ++ " %d-bit array element", (yyvsp[(1) - (2)].array).bits); + } + +- (yyval.array).data = data_append_integer((yyvsp[-1].array).data, (yyvsp[0].integer), (yyvsp[-1].array).bits); ++ (yyval.array).data = data_append_integer((yyvsp[(1) - (2)].array).data, (yyvsp[(2) - (2)].integer), (yyvsp[(1) - (2)].array).bits); + } +-#line 1773 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 32: +-#line 323 "dtc-parser.y" /* yacc.c:1646 */ ++ case 34: ++ ++/* Line 1806 of yacc.c */ ++#line 340 "dtc-parser.y" + { +- uint64_t val = ~0ULL >> (64 - (yyvsp[-1].array).bits); ++ uint64_t val = ~0ULL >> (64 - (yyvsp[(1) - (2)].array).bits); + +- if ((yyvsp[-1].array).bits == 32) +- (yyvsp[-1].array).data = data_add_marker((yyvsp[-1].array).data, ++ if ((yyvsp[(1) - (2)].array).bits == 32) ++ (yyvsp[(1) - (2)].array).data = data_add_marker((yyvsp[(1) - (2)].array).data, + REF_PHANDLE, +- (yyvsp[0].labelref)); ++ (yyvsp[(2) - (2)].labelref)); + else +- ERROR(&(yylsp[0]), "References are only allowed in " ++ ERROR(&(yylsp[(2) - (2)]), "References are only allowed in " + "arrays with 32-bit elements."); + +- (yyval.array).data = data_append_integer((yyvsp[-1].array).data, val, (yyvsp[-1].array).bits); ++ (yyval.array).data = data_append_integer((yyvsp[(1) - (2)].array).data, val, (yyvsp[(1) - (2)].array).bits); + } +-#line 1791 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 33: +-#line 337 "dtc-parser.y" /* yacc.c:1646 */ ++ case 35: ++ ++/* Line 1806 of yacc.c */ ++#line 354 "dtc-parser.y" + { +- (yyval.array).data = data_add_marker((yyvsp[-1].array).data, LABEL, (yyvsp[0].labelref)); ++ (yyval.array).data = data_add_marker((yyvsp[(1) - (2)].array).data, LABEL, (yyvsp[(2) - (2)].labelref)); + } +-#line 1799 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 36: +-#line 346 "dtc-parser.y" /* yacc.c:1646 */ ++ case 38: ++ ++/* Line 1806 of yacc.c */ ++#line 363 "dtc-parser.y" + { +- (yyval.integer) = (yyvsp[-1].integer); ++ (yyval.integer) = (yyvsp[(2) - (3)].integer); + } +-#line 1807 "dtc-parser.tab.c" /* yacc.c:1646 */ +- break; +- +- case 39: +-#line 357 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-4].integer) ? (yyvsp[-2].integer) : (yyvsp[0].integer); } +-#line 1813 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 41: +-#line 362 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-2].integer) || (yyvsp[0].integer); } +-#line 1819 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 374 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (5)].integer) ? (yyvsp[(3) - (5)].integer) : (yyvsp[(5) - (5)].integer); } + break; + + case 43: +-#line 367 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-2].integer) && (yyvsp[0].integer); } +-#line 1825 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 379 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (3)].integer) || (yyvsp[(3) - (3)].integer); } + break; + + case 45: +-#line 372 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-2].integer) | (yyvsp[0].integer); } +-#line 1831 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 384 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (3)].integer) && (yyvsp[(3) - (3)].integer); } + break; + + case 47: +-#line 377 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-2].integer) ^ (yyvsp[0].integer); } +-#line 1837 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 389 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (3)].integer) | (yyvsp[(3) - (3)].integer); } + break; + + case 49: +-#line 382 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-2].integer) & (yyvsp[0].integer); } +-#line 1843 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 394 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (3)].integer) ^ (yyvsp[(3) - (3)].integer); } + break; + + case 51: +-#line 387 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-2].integer) == (yyvsp[0].integer); } +-#line 1849 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 399 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (3)].integer) & (yyvsp[(3) - (3)].integer); } + break; + +- case 52: +-#line 388 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-2].integer) != (yyvsp[0].integer); } +-#line 1855 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ case 53: ++ ++/* Line 1806 of yacc.c */ ++#line 404 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (3)].integer) == (yyvsp[(3) - (3)].integer); } + break; + + case 54: +-#line 393 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-2].integer) < (yyvsp[0].integer); } +-#line 1861 "dtc-parser.tab.c" /* yacc.c:1646 */ +- break; + +- case 55: +-#line 394 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-2].integer) > (yyvsp[0].integer); } +-#line 1867 "dtc-parser.tab.c" /* yacc.c:1646 */ ++/* Line 1806 of yacc.c */ ++#line 405 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (3)].integer) != (yyvsp[(3) - (3)].integer); } + break; + + case 56: +-#line 395 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-2].integer) <= (yyvsp[0].integer); } +-#line 1873 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 410 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (3)].integer) < (yyvsp[(3) - (3)].integer); } + break; + + case 57: +-#line 396 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-2].integer) >= (yyvsp[0].integer); } +-#line 1879 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 411 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (3)].integer) > (yyvsp[(3) - (3)].integer); } + break; + + case 58: +-#line 400 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-2].integer) << (yyvsp[0].integer); } +-#line 1885 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 412 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (3)].integer) <= (yyvsp[(3) - (3)].integer); } + break; + + case 59: +-#line 401 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-2].integer) >> (yyvsp[0].integer); } +-#line 1891 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 413 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (3)].integer) >= (yyvsp[(3) - (3)].integer); } ++ break; ++ ++ case 60: ++ ++/* Line 1806 of yacc.c */ ++#line 417 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (3)].integer) << (yyvsp[(3) - (3)].integer); } + break; + + case 61: +-#line 406 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-2].integer) + (yyvsp[0].integer); } +-#line 1897 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 418 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (3)].integer) >> (yyvsp[(3) - (3)].integer); } + break; + +- case 62: +-#line 407 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-2].integer) - (yyvsp[0].integer); } +-#line 1903 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ case 63: ++ ++/* Line 1806 of yacc.c */ ++#line 423 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (3)].integer) + (yyvsp[(3) - (3)].integer); } + break; + + case 64: +-#line 412 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-2].integer) * (yyvsp[0].integer); } +-#line 1909 "dtc-parser.tab.c" /* yacc.c:1646 */ +- break; + +- case 65: +-#line 413 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-2].integer) / (yyvsp[0].integer); } +-#line 1915 "dtc-parser.tab.c" /* yacc.c:1646 */ ++/* Line 1806 of yacc.c */ ++#line 424 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (3)].integer) - (yyvsp[(3) - (3)].integer); } + break; + + case 66: +-#line 414 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = (yyvsp[-2].integer) % (yyvsp[0].integer); } +-#line 1921 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 429 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (3)].integer) * (yyvsp[(3) - (3)].integer); } + break; + +- case 69: +-#line 420 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = -(yyvsp[0].integer); } +-#line 1927 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ case 67: ++ ++/* Line 1806 of yacc.c */ ++#line 430 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (3)].integer) / (yyvsp[(3) - (3)].integer); } + break; + +- case 70: +-#line 421 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = ~(yyvsp[0].integer); } +-#line 1933 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ case 68: ++ ++/* Line 1806 of yacc.c */ ++#line 431 "dtc-parser.y" ++ { (yyval.integer) = (yyvsp[(1) - (3)].integer) % (yyvsp[(3) - (3)].integer); } + break; + + case 71: +-#line 422 "dtc-parser.y" /* yacc.c:1646 */ +- { (yyval.integer) = !(yyvsp[0].integer); } +-#line 1939 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 437 "dtc-parser.y" ++ { (yyval.integer) = -(yyvsp[(2) - (2)].integer); } + break; + + case 72: +-#line 427 "dtc-parser.y" /* yacc.c:1646 */ +- { +- (yyval.data) = empty_data; +- } +-#line 1947 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 438 "dtc-parser.y" ++ { (yyval.integer) = ~(yyvsp[(2) - (2)].integer); } + break; + + case 73: +-#line 431 "dtc-parser.y" /* yacc.c:1646 */ +- { +- (yyval.data) = data_append_byte((yyvsp[-1].data), (yyvsp[0].byte)); +- } +-#line 1955 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 439 "dtc-parser.y" ++ { (yyval.integer) = !(yyvsp[(2) - (2)].integer); } + break; + + case 74: +-#line 435 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 444 "dtc-parser.y" + { +- (yyval.data) = data_add_marker((yyvsp[-1].data), LABEL, (yyvsp[0].labelref)); ++ (yyval.data) = empty_data; + } +-#line 1963 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 75: +-#line 442 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 448 "dtc-parser.y" + { +- (yyval.nodelist) = NULL; ++ (yyval.data) = data_append_byte((yyvsp[(1) - (2)].data), (yyvsp[(2) - (2)].byte)); + } +-#line 1971 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 76: +-#line 446 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 452 "dtc-parser.y" + { +- (yyval.nodelist) = chain_node((yyvsp[-1].node), (yyvsp[0].nodelist)); ++ (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref)); + } +-#line 1979 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 77: +-#line 450 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 459 "dtc-parser.y" + { +- ERROR(&(yylsp[0]), "Properties must precede subnodes"); +- YYERROR; ++ (yyval.nodelist) = NULL; + } +-#line 1988 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 78: +-#line 458 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 463 "dtc-parser.y" + { +- (yyval.node) = name_node((yyvsp[0].node), (yyvsp[-1].propnodename)); ++ (yyval.nodelist) = chain_node((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].nodelist)); + } +-#line 1996 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 79: +-#line 462 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 467 "dtc-parser.y" + { +- (yyval.node) = name_node(build_node_delete(), (yyvsp[-1].propnodename)); ++ ERROR(&(yylsp[(2) - (2)]), "Properties must precede subnodes"); ++ YYERROR; + } +-#line 2004 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 80: +-#line 466 "dtc-parser.y" /* yacc.c:1646 */ ++ ++/* Line 1806 of yacc.c */ ++#line 475 "dtc-parser.y" + { +- add_label(&(yyvsp[0].node)->labels, (yyvsp[-1].labelref)); +- (yyval.node) = (yyvsp[0].node); ++ (yyval.node) = name_node((yyvsp[(2) - (2)].node), (yyvsp[(1) - (2)].propnodename)); + } +-#line 2013 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + ++ case 81: + +-#line 2017 "dtc-parser.tab.c" /* yacc.c:1646 */ ++/* Line 1806 of yacc.c */ ++#line 479 "dtc-parser.y" ++ { ++ (yyval.node) = name_node(build_node_delete(), (yyvsp[(2) - (3)].propnodename)); ++ } ++ break; ++ ++ case 82: ++ ++/* Line 1806 of yacc.c */ ++#line 483 "dtc-parser.y" ++ { ++ add_label(&(yyvsp[(2) - (2)].node)->labels, (yyvsp[(1) - (2)].labelref)); ++ (yyval.node) = (yyvsp[(2) - (2)].node); ++ } ++ break; ++ ++ ++ ++/* Line 1806 of yacc.c */ ++#line 2235 "dtc-parser.tab.c" + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires +@@ -2036,7 +2254,7 @@ yyreduce: + *++yyvsp = yyval; + *++yylsp = yyloc; + +- /* Now 'shift' the result of the reduction. Determine what state ++ /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + +@@ -2051,9 +2269,9 @@ yyreduce: + goto yynewstate; + + +-/*--------------------------------------. +-| yyerrlab -- here on detecting error. | +-`--------------------------------------*/ ++/*------------------------------------. ++| yyerrlab -- here on detecting error | ++`------------------------------------*/ + yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ +@@ -2104,20 +2322,20 @@ yyerrlab: + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an +- error, discard it. */ ++ error, discard it. */ + + if (yychar <= YYEOF) +- { +- /* Return failure if at end of input. */ +- if (yychar == YYEOF) +- YYABORT; +- } ++ { ++ /* Return failure if at end of input. */ ++ if (yychar == YYEOF) ++ YYABORT; ++ } + else +- { +- yydestruct ("Error: discarding", +- yytoken, &yylval, &yylloc); +- yychar = YYEMPTY; +- } ++ { ++ yydestruct ("Error: discarding", ++ yytoken, &yylval, &yylloc); ++ yychar = YYEMPTY; ++ } + } + + /* Else will try to reuse lookahead token after shifting the error +@@ -2137,7 +2355,7 @@ yyerrorlab: + goto yyerrorlab; + + yyerror_range[1] = yylsp[1-yylen]; +- /* Do not reclaim the symbols of the rule whose action triggered ++ /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; +@@ -2150,37 +2368,35 @@ yyerrorlab: + | yyerrlab1 -- common code for both syntax error and YYERROR. | + `-------------------------------------------------------------*/ + yyerrlab1: +- yyerrstatus = 3; /* Each real token shifted decrements this. */ ++ yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) +- { +- yyn += YYTERROR; +- if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) +- { +- yyn = yytable[yyn]; +- if (0 < yyn) +- break; +- } +- } ++ { ++ yyn += YYTERROR; ++ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) ++ { ++ yyn = yytable[yyn]; ++ if (0 < yyn) ++ break; ++ } ++ } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) +- YYABORT; ++ YYABORT; + + yyerror_range[1] = *yylsp; + yydestruct ("Error: popping", +- yystos[yystate], yyvsp, yylsp); ++ yystos[yystate], yyvsp, yylsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + +- YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; +- YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + /* Using YYLLOC is tempting, but would change the location of +@@ -2209,7 +2425,7 @@ yyabortlab: + yyresult = 1; + goto yyreturn; + +-#if !defined yyoverflow || YYERROR_VERBOSE ++#if !defined(yyoverflow) || YYERROR_VERBOSE + /*-------------------------------------------------. + | yyexhaustedlab -- memory exhaustion comes here. | + `-------------------------------------------------*/ +@@ -2228,14 +2444,14 @@ yyreturn: + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc); + } +- /* Do not reclaim the symbols of the rule whose action triggered ++ /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", +- yystos[*yyssp], yyvsp, yylsp); ++ yystos[*yyssp], yyvsp, yylsp); + YYPOPSTACK (1); + } + #ifndef yyoverflow +@@ -2246,12 +2462,18 @@ yyreturn: + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + #endif +- return yyresult; ++ /* Make sure YYID is used. */ ++ return YYID (yyresult); + } +-#line 472 "dtc-parser.y" /* yacc.c:1906 */ ++ ++ ++ ++/* Line 2067 of yacc.c */ ++#line 489 "dtc-parser.y" + + + void yyerror(char const *s) + { + ERROR(&yylloc, "%s", s); + } ++ +--- a/scripts/dtc/dtc-parser.tab.h_shipped ++++ b/scripts/dtc/dtc-parser.tab.h_shipped +@@ -1,19 +1,19 @@ +-/* A Bison parser, made by GNU Bison 3.0.2. */ ++/* A Bison parser, made by GNU Bison 2.5. */ + + /* Bison interface for Yacc-like parsers in C +- +- Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. +- ++ ++ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. ++ + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +- ++ + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU 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 . */ + +@@ -26,55 +26,50 @@ + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. +- ++ + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +-#ifndef YY_YY_DTC_PARSER_TAB_H_INCLUDED +-# define YY_YY_DTC_PARSER_TAB_H_INCLUDED +-/* Debug traces. */ +-#ifndef YYDEBUG +-# define YYDEBUG 0 +-#endif +-#if YYDEBUG +-extern int yydebug; +-#endif + +-/* Token type. */ ++/* Tokens. */ + #ifndef YYTOKENTYPE + # define YYTOKENTYPE +- enum yytokentype +- { +- DT_V1 = 258, +- DT_MEMRESERVE = 259, +- DT_LSHIFT = 260, +- DT_RSHIFT = 261, +- DT_LE = 262, +- DT_GE = 263, +- DT_EQ = 264, +- DT_NE = 265, +- DT_AND = 266, +- DT_OR = 267, +- DT_BITS = 268, +- DT_DEL_PROP = 269, +- DT_DEL_NODE = 270, +- DT_PROPNODENAME = 271, +- DT_LITERAL = 272, +- DT_CHAR_LITERAL = 273, +- DT_BYTE = 274, +- DT_STRING = 275, +- DT_LABEL = 276, +- DT_REF = 277, +- DT_INCBIN = 278 +- }; ++ /* Put the tokens into the symbol table, so that GDB and other debuggers ++ know about them. */ ++ enum yytokentype { ++ DT_V1 = 258, ++ DT_PLUGIN = 259, ++ DT_MEMRESERVE = 260, ++ DT_LSHIFT = 261, ++ DT_RSHIFT = 262, ++ DT_LE = 263, ++ DT_GE = 264, ++ DT_EQ = 265, ++ DT_NE = 266, ++ DT_AND = 267, ++ DT_OR = 268, ++ DT_BITS = 269, ++ DT_DEL_PROP = 270, ++ DT_DEL_NODE = 271, ++ DT_PROPNODENAME = 272, ++ DT_LITERAL = 273, ++ DT_CHAR_LITERAL = 274, ++ DT_BYTE = 275, ++ DT_STRING = 276, ++ DT_LABEL = 277, ++ DT_REF = 278, ++ DT_INCBIN = 279 ++ }; + #endif + +-/* Value type. */ ++ ++ + #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +-typedef union YYSTYPE YYSTYPE; +-union YYSTYPE ++typedef union YYSTYPE + { +-#line 38 "dtc-parser.y" /* yacc.c:1909 */ ++ ++/* Line 2068 of yacc.c */ ++#line 39 "dtc-parser.y" + + char *propnodename; + char *labelref; +@@ -92,30 +87,32 @@ union YYSTYPE + struct node *nodelist; + struct reserve_info *re; + uint64_t integer; ++ int is_plugin; ++ ++ + +-#line 97 "dtc-parser.tab.h" /* yacc.c:1909 */ +-}; ++/* Line 2068 of yacc.c */ ++#line 96 "dtc-parser.tab.h" ++} YYSTYPE; + # define YYSTYPE_IS_TRIVIAL 1 ++# define yystype YYSTYPE /* obsolescent; will be withdrawn */ + # define YYSTYPE_IS_DECLARED 1 + #endif + +-/* Location type. */ ++extern YYSTYPE yylval; ++ + #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +-typedef struct YYLTYPE YYLTYPE; +-struct YYLTYPE ++typedef struct YYLTYPE + { + int first_line; + int first_column; + int last_line; + int last_column; +-}; ++} YYLTYPE; ++# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ + # define YYLTYPE_IS_DECLARED 1 + # define YYLTYPE_IS_TRIVIAL 1 + #endif + +- +-extern YYSTYPE yylval; + extern YYLTYPE yylloc; +-int yyparse (void); + +-#endif /* !YY_YY_DTC_PARSER_TAB_H_INCLUDED */ +--- a/scripts/dtc/dtc-parser.y ++++ b/scripts/dtc/dtc-parser.y +@@ -19,6 +19,7 @@ + */ + %{ + #include ++#include + + #include "dtc.h" + #include "srcpos.h" +@@ -52,9 +53,11 @@ extern bool treesource_error; + struct node *nodelist; + struct reserve_info *re; + uint64_t integer; ++ int is_plugin; + } + + %token DT_V1 ++%token DT_PLUGIN + %token DT_MEMRESERVE + %token DT_LSHIFT DT_RSHIFT DT_LE DT_GE DT_EQ DT_NE DT_AND DT_OR + %token DT_BITS +@@ -71,6 +74,7 @@ extern bool treesource_error; + + %type propdata + %type propdataprefix ++%type plugindecl + %type memreserve + %type memreserves + %type arrayprefix +@@ -101,10 +105,23 @@ extern bool treesource_error; + %% + + sourcefile: +- DT_V1 ';' memreserves devicetree ++ DT_V1 ';' plugindecl memreserves devicetree + { +- the_boot_info = build_boot_info($3, $4, +- guess_boot_cpuid($4)); ++ $5->is_plugin = $3; ++ $5->is_root = 1; ++ the_boot_info = build_boot_info($4, $5, ++ guess_boot_cpuid($5)); ++ } ++ ; ++ ++plugindecl: ++ /* empty */ ++ { ++ $$ = 0; ++ } ++ | DT_PLUGIN ';' ++ { ++ $$ = 1; + } + ; + +--- a/scripts/dtc/dtc.c ++++ b/scripts/dtc/dtc.c +@@ -29,6 +29,7 @@ int reservenum; /* Number of memory res + int minsize; /* Minimum blob size */ + int padsize; /* Additional padding to blob */ + int phandle_format = PHANDLE_BOTH; /* Use linux,phandle or phandle properties */ ++int symbol_fixup_support = 0; + + static void fill_fullpaths(struct node *tree, const char *prefix) + { +@@ -51,7 +52,7 @@ static void fill_fullpaths(struct node * + #define FDT_VERSION(version) _FDT_VERSION(version) + #define _FDT_VERSION(version) #version + static const char usage_synopsis[] = "dtc [options] "; +-static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:hv"; ++static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:hv@"; + static struct option const usage_long_opts[] = { + {"quiet", no_argument, NULL, 'q'}, + {"in-format", a_argument, NULL, 'I'}, +@@ -69,6 +70,7 @@ static struct option const usage_long_op + {"phandle", a_argument, NULL, 'H'}, + {"warning", a_argument, NULL, 'W'}, + {"error", a_argument, NULL, 'E'}, ++ {"symbols", a_argument, NULL, '@'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, + {NULL, no_argument, NULL, 0x0}, +@@ -99,6 +101,7 @@ static const char * const usage_opts_hel + "\t\tboth - Both \"linux,phandle\" and \"phandle\" properties", + "\n\tEnable/disable warnings (prefix with \"no-\")", + "\n\tEnable/disable errors (prefix with \"no-\")", ++ "\n\tSymbols and Fixups support", + "\n\tPrint this help and exit", + "\n\tPrint version and exit", + NULL, +@@ -186,7 +189,9 @@ int main(int argc, char *argv[]) + case 'E': + parse_checks_option(false, true, optarg); + break; +- ++ case '@': ++ symbol_fixup_support = 1; ++ break; + case 'h': + usage(NULL); + default: +--- a/scripts/dtc/dtc.h ++++ b/scripts/dtc/dtc.h +@@ -54,6 +54,7 @@ extern int reservenum; /* Number of mem + extern int minsize; /* Minimum blob size */ + extern int padsize; /* Additional padding to blob */ + extern int phandle_format; /* Use linux,phandle or phandle properties */ ++extern int symbol_fixup_support;/* enable symbols & fixup support */ + + #define PHANDLE_LEGACY 0x1 + #define PHANDLE_EPAPR 0x2 +@@ -132,6 +133,25 @@ struct label { + struct label *next; + }; + ++struct fixup_entry { ++ int offset; ++ struct node *node; ++ struct property *prop; ++ struct fixup_entry *next; ++}; ++ ++struct fixup { ++ char *ref; ++ struct fixup_entry *entries; ++ struct fixup *next; ++}; ++ ++struct symbol { ++ struct label *label; ++ struct node *node; ++ struct symbol *next; ++}; ++ + struct property { + bool deleted; + char *name; +@@ -158,6 +178,12 @@ struct node { + int addr_cells, size_cells; + + struct label *labels; ++ ++ int is_root; ++ int is_plugin; ++ struct fixup *fixups; ++ struct symbol *symbols; ++ struct fixup_entry *local_fixups; + }; + + #define for_each_label_withdel(l0, l) \ +@@ -181,6 +207,18 @@ struct node { + for_each_child_withdel(n, c) \ + if (!(c)->deleted) + ++#define for_each_fixup(n, f) \ ++ for ((f) = (n)->fixups; (f); (f) = (f)->next) ++ ++#define for_each_fixup_entry(f, fe) \ ++ for ((fe) = (f)->entries; (fe); (fe) = (fe)->next) ++ ++#define for_each_symbol(n, s) \ ++ for ((s) = (n)->symbols; (s); (s) = (s)->next) ++ ++#define for_each_local_fixup_entry(n, fe) \ ++ for ((fe) = (n)->local_fixups; (fe); (fe) = (fe)->next) ++ + void add_label(struct label **labels, char *label); + void delete_labels(struct label **labels); + +--- a/scripts/dtc/flattree.c ++++ b/scripts/dtc/flattree.c +@@ -262,6 +262,12 @@ static void flatten_tree(struct node *tr + struct property *prop; + struct node *child; + bool seen_name_prop = false; ++ struct symbol *sym; ++ struct fixup *f; ++ struct fixup_entry *fe; ++ char *name, *s; ++ const char *fullpath; ++ int namesz, nameoff, vallen; + + if (tree->deleted) + return; +@@ -276,8 +282,6 @@ static void flatten_tree(struct node *tr + emit->align(etarget, sizeof(cell_t)); + + for_each_property(tree, prop) { +- int nameoff; +- + if (streq(prop->name, "name")) + seen_name_prop = true; + +@@ -310,6 +314,139 @@ static void flatten_tree(struct node *tr + flatten_tree(child, emit, etarget, strbuf, vi); + } + ++ if (!symbol_fixup_support) ++ goto no_symbols; ++ ++ /* add the symbol nodes (if any) */ ++ if (tree->symbols) { ++ ++ emit->beginnode(etarget, NULL); ++ emit->string(etarget, "__symbols__", 0); ++ emit->align(etarget, sizeof(cell_t)); ++ ++ for_each_symbol(tree, sym) { ++ ++ vallen = strlen(sym->node->fullpath); ++ ++ nameoff = stringtable_insert(strbuf, sym->label->label); ++ ++ emit->property(etarget, NULL); ++ emit->cell(etarget, vallen + 1); ++ emit->cell(etarget, nameoff); ++ ++ if ((vi->flags & FTF_VARALIGN) && vallen >= 8) ++ emit->align(etarget, 8); ++ ++ emit->string(etarget, sym->node->fullpath, ++ strlen(sym->node->fullpath)); ++ emit->align(etarget, sizeof(cell_t)); ++ } ++ ++ emit->endnode(etarget, NULL); ++ } ++ ++ /* add the fixup nodes */ ++ if (tree->fixups) { ++ ++ /* emit the external fixups */ ++ emit->beginnode(etarget, NULL); ++ emit->string(etarget, "__fixups__", 0); ++ emit->align(etarget, sizeof(cell_t)); ++ ++ for_each_fixup(tree, f) { ++ ++ namesz = 0; ++ for_each_fixup_entry(f, fe) { ++ fullpath = fe->node->fullpath; ++ if (fullpath[0] == '\0') ++ fullpath = "/"; ++ namesz += strlen(fullpath) + 1; ++ namesz += strlen(fe->prop->name) + 1; ++ namesz += 32; /* space for : + '\0' */ ++ } ++ ++ name = xmalloc(namesz); ++ ++ s = name; ++ for_each_fixup_entry(f, fe) { ++ fullpath = fe->node->fullpath; ++ if (fullpath[0] == '\0') ++ fullpath = "/"; ++ snprintf(s, name + namesz - s, "%s:%s:%d", ++ fullpath, ++ fe->prop->name, fe->offset); ++ s += strlen(s) + 1; ++ } ++ ++ nameoff = stringtable_insert(strbuf, f->ref); ++ vallen = s - name - 1; ++ ++ emit->property(etarget, NULL); ++ emit->cell(etarget, vallen + 1); ++ emit->cell(etarget, nameoff); ++ ++ if ((vi->flags & FTF_VARALIGN) && vallen >= 8) ++ emit->align(etarget, 8); ++ ++ emit->string(etarget, name, vallen); ++ emit->align(etarget, sizeof(cell_t)); ++ ++ free(name); ++ } ++ ++ emit->endnode(etarget, tree->labels); ++ } ++ ++ /* add the local fixup property */ ++ if (tree->local_fixups) { ++ ++ /* emit the external fixups */ ++ emit->beginnode(etarget, NULL); ++ emit->string(etarget, "__local_fixups__", 0); ++ emit->align(etarget, sizeof(cell_t)); ++ ++ namesz = 0; ++ for_each_local_fixup_entry(tree, fe) { ++ fullpath = fe->node->fullpath; ++ if (fullpath[0] == '\0') ++ fullpath = "/"; ++ namesz += strlen(fullpath) + 1; ++ namesz += strlen(fe->prop->name) + 1; ++ namesz += 32; /* space for : + '\0' */ ++ } ++ ++ name = xmalloc(namesz); ++ ++ s = name; ++ for_each_local_fixup_entry(tree, fe) { ++ fullpath = fe->node->fullpath; ++ if (fullpath[0] == '\0') ++ fullpath = "/"; ++ snprintf(s, name + namesz - s, "%s:%s:%d", ++ fullpath, fe->prop->name, ++ fe->offset); ++ s += strlen(s) + 1; ++ } ++ ++ nameoff = stringtable_insert(strbuf, "fixup"); ++ vallen = s - name - 1; ++ ++ emit->property(etarget, NULL); ++ emit->cell(etarget, vallen + 1); ++ emit->cell(etarget, nameoff); ++ ++ if ((vi->flags & FTF_VARALIGN) && vallen >= 8) ++ emit->align(etarget, 8); ++ ++ emit->string(etarget, name, vallen); ++ emit->align(etarget, sizeof(cell_t)); ++ ++ free(name); ++ ++ emit->endnode(etarget, tree->labels); ++ } ++ ++no_symbols: + emit->endnode(etarget, tree->labels); + } + +--- a/scripts/dtc/version_gen.h ++++ b/scripts/dtc/version_gen.h +@@ -1 +1 @@ +-#define DTC_VERSION "DTC 1.4.1-g9d3649bd" ++#define DTC_VERSION "DTC 1.4.1-g9d3649bd-dirty" diff --git a/target/linux/brcm2708/patches-4.4/0082-spidev-Add-spidev-compatible-string-to-silence-warni.patch b/target/linux/brcm2708/patches-4.4/0082-spidev-Add-spidev-compatible-string-to-silence-warni.patch deleted file mode 100644 index e27e143c97..0000000000 --- a/target/linux/brcm2708/patches-4.4/0082-spidev-Add-spidev-compatible-string-to-silence-warni.patch +++ /dev/null @@ -1,21 +0,0 @@ -From 5b2d8bca046bd6f1a426e9205f194ece5b95e300 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Tue, 14 Jul 2015 10:26:09 +0100 -Subject: [PATCH 082/170] spidev: Add "spidev" compatible string to silence - warning - -See: https://github.com/raspberrypi/linux/issues/1054 ---- - drivers/spi/spidev.c | 1 + - 1 file changed, 1 insertion(+) - ---- a/drivers/spi/spidev.c -+++ b/drivers/spi/spidev.c -@@ -695,6 +695,7 @@ static struct class *spidev_class; - static const struct of_device_id spidev_dt_ids[] = { - { .compatible = "rohm,dh2228fv" }, - { .compatible = "lineartechnology,ltc2488" }, -+ { .compatible = "spidev" }, - {}, - }; - MODULE_DEVICE_TABLE(of, spidev_dt_ids); diff --git a/target/linux/brcm2708/patches-4.4/0083-mfd-Add-Raspberry-Pi-Sense-HAT-core-driver.patch b/target/linux/brcm2708/patches-4.4/0083-mfd-Add-Raspberry-Pi-Sense-HAT-core-driver.patch new file mode 100644 index 0000000000..d36d569f9a --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0083-mfd-Add-Raspberry-Pi-Sense-HAT-core-driver.patch @@ -0,0 +1,838 @@ +From 98ec469c223432d4f40317023a04cfa71f661c10 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 14 Jul 2015 14:32:47 +0100 +Subject: [PATCH 083/232] mfd: Add Raspberry Pi Sense HAT core driver + +--- + drivers/input/joystick/Kconfig | 8 + + drivers/input/joystick/Makefile | 1 + + drivers/input/joystick/rpisense-js.c | 153 ++++++++++++++++ + drivers/mfd/Kconfig | 8 + + drivers/mfd/Makefile | 2 + + drivers/mfd/rpisense-core.c | 157 +++++++++++++++++ + drivers/video/fbdev/Kconfig | 13 ++ + drivers/video/fbdev/Makefile | 1 + + drivers/video/fbdev/rpisense-fb.c | 293 +++++++++++++++++++++++++++++++ + include/linux/mfd/rpisense/core.h | 47 +++++ + include/linux/mfd/rpisense/framebuffer.h | 32 ++++ + include/linux/mfd/rpisense/joystick.h | 35 ++++ + 12 files changed, 750 insertions(+) + create mode 100644 drivers/input/joystick/rpisense-js.c + create mode 100644 drivers/mfd/rpisense-core.c + create mode 100644 drivers/video/fbdev/rpisense-fb.c + create mode 100644 include/linux/mfd/rpisense/core.h + create mode 100644 include/linux/mfd/rpisense/framebuffer.h + create mode 100644 include/linux/mfd/rpisense/joystick.h + +--- a/drivers/input/joystick/Kconfig ++++ b/drivers/input/joystick/Kconfig +@@ -330,4 +330,12 @@ config JOYSTICK_MAPLE + To compile this as a module choose M here: the module will be called + maplecontrol. + ++config JOYSTICK_RPISENSE ++ tristate "Raspberry Pi Sense HAT joystick" ++ depends on GPIOLIB && INPUT ++ select MFD_RPISENSE_CORE ++ ++ help ++ This is the joystick driver for the Raspberry Pi Sense HAT ++ + endif +--- a/drivers/input/joystick/Makefile ++++ b/drivers/input/joystick/Makefile +@@ -32,4 +32,5 @@ obj-$(CONFIG_JOYSTICK_WARRIOR) += warri + obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o + obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o + obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o ++obj-$(CONFIG_JOYSTICK_RPISENSE) += rpisense-js.o + +--- /dev/null ++++ b/drivers/input/joystick/rpisense-js.c +@@ -0,0 +1,153 @@ ++/* ++ * Raspberry Pi Sense HAT joystick driver ++ * http://raspberrypi.org ++ * ++ * Copyright (C) 2015 Raspberry Pi ++ * ++ * Author: Serge Schneider ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ */ ++ ++#include ++ ++#include ++#include ++ ++static struct rpisense *rpisense; ++static unsigned char keymap[5] = {KEY_DOWN, KEY_RIGHT, KEY_UP, KEY_ENTER, KEY_LEFT,}; ++ ++static void keys_work_fn(struct work_struct *work) ++{ ++ int i; ++ static s32 prev_keys; ++ struct rpisense_js *rpisense_js = &rpisense->joystick; ++ s32 keys = rpisense_reg_read(rpisense, RPISENSE_KEYS); ++ s32 changes = keys ^ prev_keys; ++ ++ prev_keys = keys; ++ for (i = 0; i < 5; i++) { ++ if (changes & 1) { ++ input_report_key(rpisense_js->keys_dev, ++ keymap[i], keys & 1); ++ } ++ changes >>= 1; ++ keys >>= 1; ++ } ++ input_sync(rpisense_js->keys_dev); ++} ++ ++static irqreturn_t keys_irq_handler(int irq, void *pdev) ++{ ++ struct rpisense_js *rpisense_js = &rpisense->joystick; ++ ++ schedule_work(&rpisense_js->keys_work_s); ++ return IRQ_HANDLED; ++} ++ ++static int rpisense_js_probe(struct platform_device *pdev) ++{ ++ int ret; ++ int i; ++ struct rpisense_js *rpisense_js; ++ ++ rpisense = rpisense_get_dev(); ++ rpisense_js = &rpisense->joystick; ++ ++ INIT_WORK(&rpisense_js->keys_work_s, keys_work_fn); ++ ++ rpisense_js->keys_dev = input_allocate_device(); ++ if (!rpisense_js->keys_dev) { ++ dev_err(&pdev->dev, "Could not allocate input device.\n"); ++ return -ENOMEM; ++ } ++ ++ rpisense_js->keys_dev->evbit[0] = BIT_MASK(EV_KEY); ++ for (i = 0; i < ARRAY_SIZE(keymap); i++) { ++ set_bit(keymap[i], ++ rpisense_js->keys_dev->keybit); ++ } ++ ++ rpisense_js->keys_dev->name = "Raspberry Pi Sense HAT Joystick"; ++ rpisense_js->keys_dev->phys = "rpi-sense-joy/input0"; ++ rpisense_js->keys_dev->id.bustype = BUS_I2C; ++ rpisense_js->keys_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); ++ rpisense_js->keys_dev->keycode = keymap; ++ rpisense_js->keys_dev->keycodesize = sizeof(unsigned char); ++ rpisense_js->keys_dev->keycodemax = ARRAY_SIZE(keymap); ++ ++ ret = input_register_device(rpisense_js->keys_dev); ++ if (ret) { ++ dev_err(&pdev->dev, "Could not register input device.\n"); ++ goto err_keys_alloc; ++ } ++ ++ ret = gpiod_direction_input(rpisense_js->keys_desc); ++ if (ret) { ++ dev_err(&pdev->dev, "Could not set keys-int direction.\n"); ++ goto err_keys_reg; ++ } ++ ++ rpisense_js->keys_irq = gpiod_to_irq(rpisense_js->keys_desc); ++ if (rpisense_js->keys_irq < 0) { ++ dev_err(&pdev->dev, "Could not determine keys-int IRQ.\n"); ++ ret = rpisense_js->keys_irq; ++ goto err_keys_reg; ++ } ++ ++ ret = devm_request_irq(&pdev->dev, rpisense_js->keys_irq, ++ keys_irq_handler, IRQF_TRIGGER_RISING, ++ "keys", &pdev->dev); ++ if (ret) { ++ dev_err(&pdev->dev, "IRQ request failed.\n"); ++ goto err_keys_reg; ++ } ++ return 0; ++err_keys_reg: ++ input_unregister_device(rpisense_js->keys_dev); ++err_keys_alloc: ++ input_free_device(rpisense_js->keys_dev); ++ return ret; ++} ++ ++static int rpisense_js_remove(struct platform_device *pdev) ++{ ++ struct rpisense_js *rpisense_js = &rpisense->joystick; ++ ++ input_unregister_device(rpisense_js->keys_dev); ++ input_free_device(rpisense_js->keys_dev); ++ return 0; ++} ++ ++#ifdef CONFIG_OF ++static const struct of_device_id rpisense_js_id[] = { ++ { .compatible = "rpi,rpi-sense-js" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, rpisense_js_id); ++#endif ++ ++static struct platform_device_id rpisense_js_device_id[] = { ++ { .name = "rpi-sense-js" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(platform, rpisense_js_device_id); ++ ++static struct platform_driver rpisense_js_driver = { ++ .probe = rpisense_js_probe, ++ .remove = rpisense_js_remove, ++ .driver = { ++ .name = "rpi-sense-js", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++module_platform_driver(rpisense_js_driver); ++ ++MODULE_DESCRIPTION("Raspberry Pi Sense HAT joystick driver"); ++MODULE_AUTHOR("Serge Schneider "); ++MODULE_LICENSE("GPL"); +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -10,6 +10,14 @@ config MFD_CORE + select IRQ_DOMAIN + default n + ++config MFD_RPISENSE_CORE ++ tristate "Raspberry Pi Sense HAT core functions" ++ depends on I2C ++ select MFD_CORE ++ help ++ This is the core driver for the Raspberry Pi Sense HAT. This provides ++ the necessary functions to communicate with the hardware. ++ + config MFD_CS5535 + tristate "AMD CS5535 and CS5536 southbridge core functions" + select MFD_CORE +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -194,3 +194,5 @@ intel-soc-pmic-objs := intel_soc_pmic_c + intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC) += intel_soc_pmic_bxtwc.o + obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o + obj-$(CONFIG_MFD_MT6397) += mt6397-core.o ++ ++obj-$(CONFIG_MFD_RPISENSE_CORE) += rpisense-core.o +--- /dev/null ++++ b/drivers/mfd/rpisense-core.c +@@ -0,0 +1,157 @@ ++/* ++ * Raspberry Pi Sense HAT core driver ++ * http://raspberrypi.org ++ * ++ * Copyright (C) 2015 Raspberry Pi ++ * ++ * Author: Serge Schneider ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This driver is based on wm8350 implementation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct rpisense *rpisense; ++ ++static void rpisense_client_dev_register(struct rpisense *rpisense, ++ const char *name, ++ struct platform_device **pdev) ++{ ++ int ret; ++ ++ *pdev = platform_device_alloc(name, -1); ++ if (*pdev == NULL) { ++ dev_err(rpisense->dev, "Failed to allocate %s\n", name); ++ return; ++ } ++ ++ (*pdev)->dev.parent = rpisense->dev; ++ platform_set_drvdata(*pdev, rpisense); ++ ret = platform_device_add(*pdev); ++ if (ret != 0) { ++ dev_err(rpisense->dev, "Failed to register %s: %d\n", ++ name, ret); ++ platform_device_put(*pdev); ++ *pdev = NULL; ++ } ++} ++ ++static int rpisense_probe(struct i2c_client *i2c, ++ const struct i2c_device_id *id) ++{ ++ int ret; ++ struct rpisense_js *rpisense_js; ++ ++ rpisense = devm_kzalloc(&i2c->dev, sizeof(struct rpisense), GFP_KERNEL); ++ if (rpisense == NULL) ++ return -ENOMEM; ++ ++ i2c_set_clientdata(i2c, rpisense); ++ rpisense->dev = &i2c->dev; ++ rpisense->i2c_client = i2c; ++ ++ ret = rpisense_reg_read(rpisense, RPISENSE_WAI); ++ if (ret > 0) { ++ if (ret != 's') ++ return -EINVAL; ++ } else { ++ return ret; ++ } ++ ret = rpisense_reg_read(rpisense, RPISENSE_VER); ++ if (ret < 0) ++ return ret; ++ ++ dev_info(rpisense->dev, ++ "Raspberry Pi Sense HAT firmware version %i\n", ret); ++ ++ rpisense_js = &rpisense->joystick; ++ rpisense_js->keys_desc = devm_gpiod_get(&i2c->dev, ++ "keys-int", GPIOD_IN); ++ if (IS_ERR(rpisense_js->keys_desc)) { ++ dev_warn(&i2c->dev, "Failed to get keys-int descriptor.\n"); ++ rpisense_js->keys_desc = gpio_to_desc(23); ++ if (rpisense_js->keys_desc == NULL) { ++ dev_err(&i2c->dev, "GPIO23 fallback failed.\n"); ++ return PTR_ERR(rpisense_js->keys_desc); ++ } ++ } ++ rpisense_client_dev_register(rpisense, "rpi-sense-js", ++ &(rpisense->joystick.pdev)); ++ rpisense_client_dev_register(rpisense, "rpi-sense-fb", ++ &(rpisense->framebuffer.pdev)); ++ ++ return 0; ++} ++ ++static int rpisense_remove(struct i2c_client *i2c) ++{ ++ struct rpisense *rpisense = i2c_get_clientdata(i2c); ++ ++ platform_device_unregister(rpisense->joystick.pdev); ++ return 0; ++} ++ ++struct rpisense *rpisense_get_dev(void) ++{ ++ return rpisense; ++} ++EXPORT_SYMBOL_GPL(rpisense_get_dev); ++ ++s32 rpisense_reg_read(struct rpisense *rpisense, int reg) ++{ ++ int ret = i2c_smbus_read_byte_data(rpisense->i2c_client, reg); ++ ++ if (ret < 0) ++ dev_err(rpisense->dev, "Read from reg %d failed\n", reg); ++ /* Due to the BCM270x I2C clock stretching bug, some values ++ * may have MSB set. Clear it to avoid incorrect values. ++ * */ ++ return ret & 0x7F; ++} ++EXPORT_SYMBOL_GPL(rpisense_reg_read); ++ ++int rpisense_block_write(struct rpisense *rpisense, const char *buf, int count) ++{ ++ int ret = i2c_master_send(rpisense->i2c_client, buf, count); ++ ++ if (ret < 0) ++ dev_err(rpisense->dev, "Block write failed\n"); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(rpisense_block_write); ++ ++static const struct i2c_device_id rpisense_i2c_id[] = { ++ { "rpi-sense", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, rpisense_i2c_id); ++ ++ ++static struct i2c_driver rpisense_driver = { ++ .driver = { ++ .name = "rpi-sense", ++ .owner = THIS_MODULE, ++ }, ++ .probe = rpisense_probe, ++ .remove = rpisense_remove, ++ .id_table = rpisense_i2c_id, ++}; ++ ++module_i2c_driver(rpisense_driver); ++ ++MODULE_DESCRIPTION("Raspberry Pi Sense HAT core driver"); ++MODULE_AUTHOR("Serge Schneider "); ++MODULE_LICENSE("GPL"); ++ +--- a/drivers/video/fbdev/Kconfig ++++ b/drivers/video/fbdev/Kconfig +@@ -2506,3 +2506,16 @@ config FB_SM712 + This driver is also available as a module. The module will be + called sm712fb. If you want to compile it as a module, say M + here and read . ++ ++config FB_RPISENSE ++ tristate "Raspberry Pi Sense HAT framebuffer" ++ depends on FB ++ select MFD_RPISENSE_CORE ++ select FB_SYS_FOPS ++ select FB_SYS_FILLRECT ++ select FB_SYS_COPYAREA ++ select FB_SYS_IMAGEBLIT ++ select FB_DEFERRED_IO ++ ++ help ++ This is the framebuffer driver for the Raspberry Pi Sense HAT +--- a/drivers/video/fbdev/Makefile ++++ b/drivers/video/fbdev/Makefile +@@ -150,6 +150,7 @@ obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o + obj-$(CONFIG_FB_MXS) += mxsfb.o + obj-$(CONFIG_FB_SSD1307) += ssd1307fb.o + obj-$(CONFIG_FB_SIMPLE) += simplefb.o ++obj-$(CONFIG_FB_RPISENSE) += rpisense-fb.o + + # the test framebuffer is last + obj-$(CONFIG_FB_VIRTUAL) += vfb.o +--- /dev/null ++++ b/drivers/video/fbdev/rpisense-fb.c +@@ -0,0 +1,293 @@ ++/* ++ * Raspberry Pi Sense HAT framebuffer driver ++ * http://raspberrypi.org ++ * ++ * Copyright (C) 2015 Raspberry Pi ++ * ++ * Author: Serge Schneider ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++static bool lowlight; ++module_param(lowlight, bool, 0); ++MODULE_PARM_DESC(lowlight, "Reduce LED matrix brightness to one third"); ++ ++static struct rpisense *rpisense; ++ ++struct rpisense_fb_param { ++ char __iomem *vmem; ++ u8 *vmem_work; ++ u32 vmemsize; ++ u8 *gamma; ++}; ++ ++static u8 gamma_default[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, ++ 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, ++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0E, 0x0F, 0x11, ++ 0x12, 0x14, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F,}; ++ ++static u8 gamma_low[32] = {0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, ++ 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, ++ 0x03, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, ++ 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x0A, 0x0A,}; ++ ++static u8 gamma_user[32]; ++ ++static struct rpisense_fb_param rpisense_fb_param = { ++ .vmem = NULL, ++ .vmemsize = 128, ++ .gamma = gamma_default, ++}; ++ ++static struct fb_deferred_io rpisense_fb_defio; ++ ++static struct fb_fix_screeninfo rpisense_fb_fix = { ++ .id = "RPi-Sense FB", ++ .type = FB_TYPE_PACKED_PIXELS, ++ .visual = FB_VISUAL_TRUECOLOR, ++ .xpanstep = 0, ++ .ypanstep = 0, ++ .ywrapstep = 0, ++ .accel = FB_ACCEL_NONE, ++ .line_length = 16, ++}; ++ ++static struct fb_var_screeninfo rpisense_fb_var = { ++ .xres = 8, ++ .yres = 8, ++ .xres_virtual = 8, ++ .yres_virtual = 8, ++ .bits_per_pixel = 16, ++ .red = {11, 5, 0}, ++ .green = {5, 6, 0}, ++ .blue = {0, 5, 0}, ++}; ++ ++static ssize_t rpisense_fb_write(struct fb_info *info, ++ const char __user *buf, size_t count, ++ loff_t *ppos) ++{ ++ ssize_t res = fb_sys_write(info, buf, count, ppos); ++ ++ schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay); ++ return res; ++} ++ ++static void rpisense_fb_fillrect(struct fb_info *info, ++ const struct fb_fillrect *rect) ++{ ++ sys_fillrect(info, rect); ++ schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay); ++} ++ ++static void rpisense_fb_copyarea(struct fb_info *info, ++ const struct fb_copyarea *area) ++{ ++ sys_copyarea(info, area); ++ schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay); ++} ++ ++static void rpisense_fb_imageblit(struct fb_info *info, ++ const struct fb_image *image) ++{ ++ sys_imageblit(info, image); ++ schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay); ++} ++ ++static void rpisense_fb_deferred_io(struct fb_info *info, ++ struct list_head *pagelist) ++{ ++ int i; ++ int j; ++ u8 *vmem_work = rpisense_fb_param.vmem_work; ++ u16 *mem = (u16 *)rpisense_fb_param.vmem; ++ u8 *gamma = rpisense_fb_param.gamma; ++ ++ vmem_work[0] = 0; ++ for (j = 0; j < 8; j++) { ++ for (i = 0; i < 8; i++) { ++ vmem_work[(j * 24) + i + 1] = ++ gamma[(mem[(j * 8) + i] >> 11) & 0x1F]; ++ vmem_work[(j * 24) + (i + 8) + 1] = ++ gamma[(mem[(j * 8) + i] >> 6) & 0x1F]; ++ vmem_work[(j * 24) + (i + 16) + 1] = ++ gamma[(mem[(j * 8) + i]) & 0x1F]; ++ } ++ } ++ rpisense_block_write(rpisense, vmem_work, 193); ++} ++ ++static struct fb_deferred_io rpisense_fb_defio = { ++ .delay = HZ/100, ++ .deferred_io = rpisense_fb_deferred_io, ++}; ++ ++static int rpisense_fb_ioctl(struct fb_info *info, unsigned int cmd, ++ unsigned long arg) ++{ ++ switch (cmd) { ++ case SENSEFB_FBIOGET_GAMMA: ++ if (copy_to_user((void __user *) arg, rpisense_fb_param.gamma, ++ sizeof(u8[32]))) ++ return -EFAULT; ++ return 0; ++ case SENSEFB_FBIOSET_GAMMA: ++ if (copy_from_user(gamma_user, (void __user *)arg, ++ sizeof(u8[32]))) ++ return -EFAULT; ++ rpisense_fb_param.gamma = gamma_user; ++ schedule_delayed_work(&info->deferred_work, ++ rpisense_fb_defio.delay); ++ return 0; ++ case SENSEFB_FBIORESET_GAMMA: ++ switch (arg) { ++ case 0: ++ rpisense_fb_param.gamma = gamma_default; ++ break; ++ case 1: ++ rpisense_fb_param.gamma = gamma_low; ++ break; ++ case 2: ++ rpisense_fb_param.gamma = gamma_user; ++ break; ++ default: ++ return -EINVAL; ++ } ++ schedule_delayed_work(&info->deferred_work, ++ rpisense_fb_defio.delay); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static struct fb_ops rpisense_fb_ops = { ++ .owner = THIS_MODULE, ++ .fb_read = fb_sys_read, ++ .fb_write = rpisense_fb_write, ++ .fb_fillrect = rpisense_fb_fillrect, ++ .fb_copyarea = rpisense_fb_copyarea, ++ .fb_imageblit = rpisense_fb_imageblit, ++ .fb_ioctl = rpisense_fb_ioctl, ++}; ++ ++static int rpisense_fb_probe(struct platform_device *pdev) ++{ ++ struct fb_info *info; ++ int ret = -ENOMEM; ++ struct rpisense_fb *rpisense_fb; ++ ++ rpisense = rpisense_get_dev(); ++ rpisense_fb = &rpisense->framebuffer; ++ ++ rpisense_fb_param.vmem = vzalloc(rpisense_fb_param.vmemsize); ++ if (!rpisense_fb_param.vmem) ++ return ret; ++ ++ rpisense_fb_param.vmem_work = devm_kmalloc(&pdev->dev, 193, GFP_KERNEL); ++ if (!rpisense_fb_param.vmem_work) ++ goto err_malloc; ++ ++ info = framebuffer_alloc(0, &pdev->dev); ++ if (!info) { ++ dev_err(&pdev->dev, "Could not allocate framebuffer.\n"); ++ goto err_malloc; ++ } ++ rpisense_fb->info = info; ++ ++ rpisense_fb_fix.smem_start = (unsigned long)rpisense_fb_param.vmem; ++ rpisense_fb_fix.smem_len = rpisense_fb_param.vmemsize; ++ ++ info->fbops = &rpisense_fb_ops; ++ info->fix = rpisense_fb_fix; ++ info->var = rpisense_fb_var; ++ info->fbdefio = &rpisense_fb_defio; ++ info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; ++ info->screen_base = rpisense_fb_param.vmem; ++ info->screen_size = rpisense_fb_param.vmemsize; ++ ++ if (lowlight) ++ rpisense_fb_param.gamma = gamma_low; ++ ++ fb_deferred_io_init(info); ++ ++ ret = register_framebuffer(info); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Could not register framebuffer.\n"); ++ goto err_fballoc; ++ } ++ ++ fb_info(info, "%s frame buffer device\n", info->fix.id); ++ schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay); ++ return 0; ++err_fballoc: ++ framebuffer_release(info); ++err_malloc: ++ vfree(rpisense_fb_param.vmem); ++ return ret; ++} ++ ++static int rpisense_fb_remove(struct platform_device *pdev) ++{ ++ struct rpisense_fb *rpisense_fb = &rpisense->framebuffer; ++ struct fb_info *info = rpisense_fb->info; ++ ++ if (info) { ++ unregister_framebuffer(info); ++ fb_deferred_io_cleanup(info); ++ framebuffer_release(info); ++ vfree(rpisense_fb_param.vmem); ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_OF ++static const struct of_device_id rpisense_fb_id[] = { ++ { .compatible = "rpi,rpi-sense-fb" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, rpisense_fb_id); ++#endif ++ ++static struct platform_device_id rpisense_fb_device_id[] = { ++ { .name = "rpi-sense-fb" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(platform, rpisense_fb_device_id); ++ ++static struct platform_driver rpisense_fb_driver = { ++ .probe = rpisense_fb_probe, ++ .remove = rpisense_fb_remove, ++ .driver = { ++ .name = "rpi-sense-fb", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++module_platform_driver(rpisense_fb_driver); ++ ++MODULE_DESCRIPTION("Raspberry Pi Sense HAT framebuffer driver"); ++MODULE_AUTHOR("Serge Schneider "); ++MODULE_LICENSE("GPL"); ++ +--- /dev/null ++++ b/include/linux/mfd/rpisense/core.h +@@ -0,0 +1,47 @@ ++/* ++ * Raspberry Pi Sense HAT core driver ++ * http://raspberrypi.org ++ * ++ * Copyright (C) 2015 Raspberry Pi ++ * ++ * Author: Serge Schneider ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ */ ++ ++#ifndef __LINUX_MFD_RPISENSE_CORE_H_ ++#define __LINUX_MFD_RPISENSE_CORE_H_ ++ ++#include ++#include ++ ++/* ++ * Register values. ++ */ ++#define RPISENSE_FB 0x00 ++#define RPISENSE_WAI 0xF0 ++#define RPISENSE_VER 0xF1 ++#define RPISENSE_KEYS 0xF2 ++#define RPISENSE_EE_WP 0xF3 ++ ++#define RPISENSE_ID 's' ++ ++struct rpisense { ++ struct device *dev; ++ struct i2c_client *i2c_client; ++ ++ /* Client devices */ ++ struct rpisense_js joystick; ++ struct rpisense_fb framebuffer; ++}; ++ ++struct rpisense *rpisense_get_dev(void); ++s32 rpisense_reg_read(struct rpisense *rpisense, int reg); ++int rpisense_reg_write(struct rpisense *rpisense, int reg, u16 val); ++int rpisense_block_write(struct rpisense *rpisense, const char *buf, int count); ++ ++#endif +--- /dev/null ++++ b/include/linux/mfd/rpisense/framebuffer.h +@@ -0,0 +1,32 @@ ++/* ++ * Raspberry Pi Sense HAT framebuffer driver ++ * http://raspberrypi.org ++ * ++ * Copyright (C) 2015 Raspberry Pi ++ * ++ * Author: Serge Schneider ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ */ ++ ++#ifndef __LINUX_RPISENSE_FB_H_ ++#define __LINUX_RPISENSE_FB_H_ ++ ++#define SENSEFB_FBIO_IOC_MAGIC 0xF1 ++ ++#define SENSEFB_FBIOGET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 0) ++#define SENSEFB_FBIOSET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 1) ++#define SENSEFB_FBIORESET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 2) ++ ++struct rpisense; ++ ++struct rpisense_fb { ++ struct platform_device *pdev; ++ struct fb_info *info; ++}; ++ ++#endif +--- /dev/null ++++ b/include/linux/mfd/rpisense/joystick.h +@@ -0,0 +1,35 @@ ++/* ++ * Raspberry Pi Sense HAT joystick driver ++ * http://raspberrypi.org ++ * ++ * Copyright (C) 2015 Raspberry Pi ++ * ++ * Author: Serge Schneider ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ */ ++ ++#ifndef __LINUX_RPISENSE_JOYSTICK_H_ ++#define __LINUX_RPISENSE_JOYSTICK_H_ ++ ++#include ++#include ++#include ++#include ++ ++struct rpisense; ++ ++struct rpisense_js { ++ struct platform_device *pdev; ++ struct input_dev *keys_dev; ++ struct gpio_desc *keys_desc; ++ struct work_struct keys_work_s; ++ int keys_irq; ++}; ++ ++ ++#endif diff --git a/target/linux/brcm2708/patches-4.4/0083-scripts-dtc-Add-overlay-support.patch b/target/linux/brcm2708/patches-4.4/0083-scripts-dtc-Add-overlay-support.patch deleted file mode 100644 index 404f10f4cf..0000000000 --- a/target/linux/brcm2708/patches-4.4/0083-scripts-dtc-Add-overlay-support.patch +++ /dev/null @@ -1,4389 +0,0 @@ -From 534c337ea28bee34f886a1d1e00d281365c94efa Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Tue, 14 Jul 2015 17:00:18 +0100 -Subject: [PATCH 083/170] scripts/dtc: Add overlay support - ---- - scripts/dtc/checks.c | 119 ++- - scripts/dtc/dtc-lexer.l | 5 + - scripts/dtc/dtc-lexer.lex.c_shipped | 490 ++++----- - scripts/dtc/dtc-parser.tab.c_shipped | 1896 +++++++++++++++++++--------------- - scripts/dtc/dtc-parser.tab.h_shipped | 107 +- - scripts/dtc/dtc-parser.y | 23 +- - scripts/dtc/dtc.c | 9 +- - scripts/dtc/dtc.h | 38 + - scripts/dtc/flattree.c | 141 ++- - scripts/dtc/version_gen.h | 2 +- - 10 files changed, 1685 insertions(+), 1145 deletions(-) - ---- a/scripts/dtc/checks.c -+++ b/scripts/dtc/checks.c -@@ -458,21 +458,91 @@ static void fixup_phandle_references(str - struct node *node, struct property *prop) - { - struct marker *m = prop->val.markers; -+ struct fixup *f, **fp; -+ struct fixup_entry *fe, **fep; - struct node *refnode; - cell_t phandle; -+ int has_phandle_refs; -+ -+ has_phandle_refs = 0; -+ for_each_marker_of_type(m, REF_PHANDLE) { -+ has_phandle_refs = 1; -+ break; -+ } -+ -+ if (!has_phandle_refs) -+ return; - - for_each_marker_of_type(m, REF_PHANDLE) { - assert(m->offset + sizeof(cell_t) <= prop->val.len); - - refnode = get_node_by_ref(dt, m->ref); -- if (! refnode) { -+ if (!refnode && !symbol_fixup_support) { - FAIL(c, "Reference to non-existent node or label \"%s\"\n", -- m->ref); -+ m->ref); - continue; - } - -- phandle = get_node_phandle(dt, refnode); -- *((cell_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle); -+ if (!refnode) { -+ /* allocate fixup entry */ -+ fe = xmalloc(sizeof(*fe)); -+ -+ fe->node = node; -+ fe->prop = prop; -+ fe->offset = m->offset; -+ fe->next = NULL; -+ -+ /* search for an already existing fixup */ -+ for_each_fixup(dt, f) -+ if (strcmp(f->ref, m->ref) == 0) -+ break; -+ -+ /* no fixup found, add new */ -+ if (f == NULL) { -+ f = xmalloc(sizeof(*f)); -+ f->ref = m->ref; -+ f->entries = NULL; -+ f->next = NULL; -+ -+ /* add it to the tree */ -+ fp = &dt->fixups; -+ while (*fp) -+ fp = &(*fp)->next; -+ *fp = f; -+ } -+ -+ /* and now append fixup entry */ -+ fep = &f->entries; -+ while (*fep) -+ fep = &(*fep)->next; -+ *fep = fe; -+ -+ /* mark the entry as unresolved */ -+ phandle = 0xdeadbeef; -+ } else { -+ phandle = get_node_phandle(dt, refnode); -+ -+ /* if it's a plugin, we need to record it */ -+ if (symbol_fixup_support && dt->is_plugin) { -+ -+ /* allocate a new local fixup entry */ -+ fe = xmalloc(sizeof(*fe)); -+ -+ fe->node = node; -+ fe->prop = prop; -+ fe->offset = m->offset; -+ fe->next = NULL; -+ -+ /* append it to the local fixups */ -+ fep = &dt->local_fixups; -+ while (*fep) -+ fep = &(*fep)->next; -+ *fep = fe; -+ } -+ } -+ -+ *((cell_t *)(prop->val.val + m->offset)) = -+ cpu_to_fdt32(phandle); - } - } - ERROR(phandle_references, NULL, NULL, fixup_phandle_references, NULL, -@@ -652,6 +722,45 @@ static void check_obsolete_chosen_interr - } - TREE_WARNING(obsolete_chosen_interrupt_controller, NULL); - -+static void check_auto_label_phandles(struct check *c, struct node *dt, -+ struct node *node) -+{ -+ struct label *l; -+ struct symbol *s, **sp; -+ int has_label; -+ -+ if (!symbol_fixup_support) -+ return; -+ -+ has_label = 0; -+ for_each_label(node->labels, l) { -+ has_label = 1; -+ break; -+ } -+ -+ if (!has_label) -+ return; -+ -+ /* force allocation of a phandle for this node */ -+ (void)get_node_phandle(dt, node); -+ -+ /* add the symbol */ -+ for_each_label(node->labels, l) { -+ -+ s = xmalloc(sizeof(*s)); -+ s->label = l; -+ s->node = node; -+ s->next = NULL; -+ -+ /* add it to the symbols list */ -+ sp = &dt->symbols; -+ while (*sp) -+ sp = &((*sp)->next); -+ *sp = s; -+ } -+} -+NODE_WARNING(auto_label_phandles, NULL); -+ - static struct check *check_table[] = { - &duplicate_node_names, &duplicate_property_names, - &node_name_chars, &node_name_format, &property_name_chars, -@@ -670,6 +779,8 @@ static struct check *check_table[] = { - &avoid_default_addr_size, - &obsolete_chosen_interrupt_controller, - -+ &auto_label_phandles, -+ - &always_fail, - }; - ---- a/scripts/dtc/dtc-lexer.l -+++ b/scripts/dtc/dtc-lexer.l -@@ -113,6 +113,11 @@ static void lexical_error(const char *fm - return DT_V1; - } - -+<*>"/plugin/" { -+ DPRINT("Keyword: /plugin/\n"); -+ return DT_PLUGIN; -+ } -+ - <*>"/memreserve/" { - DPRINT("Keyword: /memreserve/\n"); - BEGIN_DEFAULT(); ---- a/scripts/dtc/dtc-lexer.lex.c_shipped -+++ b/scripts/dtc/dtc-lexer.lex.c_shipped -@@ -9,7 +9,7 @@ - #define FLEX_SCANNER - #define YY_FLEX_MAJOR_VERSION 2 - #define YY_FLEX_MINOR_VERSION 5 --#define YY_FLEX_SUBMINOR_VERSION 39 -+#define YY_FLEX_SUBMINOR_VERSION 35 - #if YY_FLEX_SUBMINOR_VERSION > 0 - #define FLEX_BETA - #endif -@@ -162,12 +162,7 @@ typedef unsigned int flex_uint32_t; - typedef struct yy_buffer_state *YY_BUFFER_STATE; - #endif - --#ifndef YY_TYPEDEF_YY_SIZE_T --#define YY_TYPEDEF_YY_SIZE_T --typedef size_t yy_size_t; --#endif -- --extern yy_size_t yyleng; -+extern int yyleng; - - extern FILE *yyin, *yyout; - -@@ -176,7 +171,6 @@ extern FILE *yyin, *yyout; - #define EOB_ACT_LAST_MATCH 2 - - #define YY_LESS_LINENO(n) -- #define YY_LINENO_REWIND_TO(ptr) - - /* Return all but the first "n" matched characters back to the input stream. */ - #define yyless(n) \ -@@ -194,6 +188,11 @@ extern FILE *yyin, *yyout; - - #define unput(c) yyunput( c, (yytext_ptr) ) - -+#ifndef YY_TYPEDEF_YY_SIZE_T -+#define YY_TYPEDEF_YY_SIZE_T -+typedef size_t yy_size_t; -+#endif -+ - #ifndef YY_STRUCT_YY_BUFFER_STATE - #define YY_STRUCT_YY_BUFFER_STATE - struct yy_buffer_state -@@ -211,7 +210,7 @@ struct yy_buffer_state - /* Number of characters read into yy_ch_buf, not including EOB - * characters. - */ -- yy_size_t yy_n_chars; -+ int yy_n_chars; - - /* Whether we "own" the buffer - i.e., we know we created it, - * and can realloc() it to grow it, and should free() it to -@@ -281,8 +280,8 @@ static YY_BUFFER_STATE * yy_buffer_stack - - /* yy_hold_char holds the character lost when yytext is formed. */ - static char yy_hold_char; --static yy_size_t yy_n_chars; /* number of characters read into yy_ch_buf */ --yy_size_t yyleng; -+static int yy_n_chars; /* number of characters read into yy_ch_buf */ -+int yyleng; - - /* Points to current character in buffer. */ - static char *yy_c_buf_p = (char *) 0; -@@ -310,7 +309,7 @@ static void yy_init_buffer (YY_BUFFER_ST - - YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); - YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); --YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ); -+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); - - void *yyalloc (yy_size_t ); - void *yyrealloc (void *,yy_size_t ); -@@ -342,7 +341,7 @@ void yyfree (void * ); - - /* Begin user sect3 */ - --#define yywrap() 1 -+#define yywrap(n) 1 - #define YY_SKIP_YYWRAP - - typedef unsigned char YY_CHAR; -@@ -373,8 +372,8 @@ static void yy_fatal_error (yyconst char - *yy_cp = '\0'; \ - (yy_c_buf_p) = yy_cp; - --#define YY_NUM_RULES 30 --#define YY_END_OF_BUFFER 31 -+#define YY_NUM_RULES 31 -+#define YY_END_OF_BUFFER 32 - /* This struct is not used in this scanner, - but its presence is necessary. */ - struct yy_trans_info -@@ -382,25 +381,26 @@ struct yy_trans_info - flex_int32_t yy_verify; - flex_int32_t yy_nxt; - }; --static yyconst flex_int16_t yy_accept[159] = -+static yyconst flex_int16_t yy_accept[166] = - { 0, -- 0, 0, 0, 0, 0, 0, 0, 0, 31, 29, -- 18, 18, 29, 29, 29, 29, 29, 29, 29, 29, -- 29, 29, 29, 29, 29, 29, 15, 16, 16, 29, -- 16, 10, 10, 18, 26, 0, 3, 0, 27, 12, -- 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, -- 21, 23, 25, 24, 22, 0, 9, 28, 0, 0, -- 0, 14, 14, 16, 16, 16, 10, 10, 10, 0, -- 12, 0, 11, 0, 0, 0, 20, 0, 0, 0, -- 0, 0, 0, 0, 0, 16, 10, 10, 10, 0, -- 13, 19, 0, 0, 0, 0, 0, 0, 0, 0, -- -- 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 16, 6, 0, 0, 0, 0, 0, 0, 2, -- 0, 0, 0, 0, 0, 0, 0, 0, 4, 17, -- 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, -- 5, 8, 0, 0, 0, 0, 7, 0 -+ 0, 0, 0, 0, 0, 0, 0, 0, 32, 30, -+ 19, 19, 30, 30, 30, 30, 30, 30, 30, 30, -+ 30, 30, 30, 30, 30, 30, 16, 17, 17, 30, -+ 17, 11, 11, 19, 27, 0, 3, 0, 28, 13, -+ 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, -+ 0, 22, 24, 26, 25, 23, 0, 10, 29, 0, -+ 0, 0, 15, 15, 17, 17, 17, 11, 11, 11, -+ 0, 13, 0, 12, 0, 0, 0, 21, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 17, 11, 11, -+ 11, 0, 14, 20, 0, 0, 0, 0, 0, 0, -+ -+ 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 17, 7, 0, 0, 0, -+ 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 4, 18, 0, 0, 5, 2, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 1, 0, 0, 0, 0, 6, 9, 0, -+ 0, 0, 0, 8, 0 - } ; - - static yyconst flex_int32_t yy_ec[256] = -@@ -416,9 +416,9 @@ static yyconst flex_int32_t yy_ec[256] = - 22, 22, 22, 22, 24, 22, 22, 25, 22, 22, - 1, 26, 27, 1, 22, 1, 21, 28, 29, 30, - -- 31, 21, 22, 22, 32, 22, 22, 33, 34, 35, -- 36, 37, 22, 38, 39, 40, 41, 42, 22, 25, -- 43, 22, 44, 45, 46, 1, 1, 1, 1, 1, -+ 31, 21, 32, 22, 33, 22, 22, 34, 35, 36, -+ 37, 38, 22, 39, 40, 41, 42, 43, 22, 25, -+ 44, 22, 45, 46, 47, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -@@ -435,163 +435,165 @@ static yyconst flex_int32_t yy_ec[256] = - 1, 1, 1, 1, 1 - } ; - --static yyconst flex_int32_t yy_meta[47] = -+static yyconst flex_int32_t yy_meta[48] = - { 0, - 1, 1, 1, 1, 1, 1, 2, 3, 1, 2, - 2, 2, 4, 5, 5, 5, 6, 1, 1, 1, - 7, 8, 8, 8, 8, 1, 1, 7, 7, 7, - 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, -- 8, 8, 8, 3, 1, 4 -+ 8, 8, 8, 8, 3, 1, 4 - } ; - --static yyconst flex_int16_t yy_base[173] = -+static yyconst flex_int16_t yy_base[180] = - { 0, -- 0, 383, 34, 382, 65, 381, 37, 105, 387, 391, -- 54, 111, 367, 110, 109, 109, 112, 41, 366, 104, -- 367, 338, 124, 117, 0, 144, 391, 0, 121, 0, -- 135, 155, 140, 179, 391, 160, 391, 379, 391, 0, -- 368, 141, 391, 167, 370, 376, 346, 103, 342, 345, -- 391, 391, 391, 391, 391, 358, 391, 391, 175, 342, -- 338, 391, 355, 0, 185, 339, 184, 347, 346, 0, -- 0, 322, 175, 357, 175, 363, 352, 324, 330, 323, -- 332, 326, 201, 324, 329, 322, 391, 333, 181, 309, -- 391, 341, 340, 313, 320, 338, 178, 311, 146, 317, -- -- 314, 315, 335, 331, 303, 300, 309, 299, 308, 188, -- 336, 335, 391, 305, 320, 281, 283, 271, 203, 288, -- 281, 271, 266, 264, 245, 242, 208, 104, 391, 391, -- 244, 218, 204, 219, 206, 224, 201, 212, 204, 229, -- 215, 208, 207, 200, 219, 391, 233, 221, 200, 181, -- 391, 391, 149, 122, 86, 41, 391, 391, 245, 251, -- 259, 263, 267, 273, 280, 284, 292, 300, 304, 310, -- 318, 326 -+ 0, 393, 35, 392, 66, 391, 38, 107, 397, 401, -+ 55, 113, 377, 112, 111, 111, 114, 42, 376, 106, -+ 377, 347, 126, 120, 0, 147, 401, 0, 124, 0, -+ 137, 158, 170, 163, 401, 153, 401, 389, 401, 0, -+ 378, 120, 401, 131, 380, 386, 355, 139, 351, 355, -+ 351, 401, 401, 401, 401, 401, 367, 401, 401, 185, -+ 350, 346, 401, 364, 0, 185, 347, 189, 356, 355, -+ 0, 0, 330, 180, 366, 141, 372, 361, 332, 338, -+ 331, 341, 334, 326, 205, 331, 337, 329, 401, 341, -+ 167, 316, 401, 349, 348, 320, 328, 346, 180, 318, -+ -+ 324, 209, 324, 320, 322, 342, 338, 309, 306, 315, -+ 305, 315, 312, 192, 342, 341, 401, 293, 306, 282, -+ 268, 252, 255, 203, 285, 282, 272, 268, 252, 233, -+ 232, 239, 208, 107, 401, 401, 238, 211, 401, 211, -+ 212, 208, 228, 203, 215, 207, 233, 222, 212, 211, -+ 203, 227, 401, 237, 225, 204, 185, 401, 401, 149, -+ 128, 88, 42, 401, 401, 253, 259, 267, 271, 275, -+ 281, 288, 292, 300, 308, 312, 318, 326, 334 - } ; - --static yyconst flex_int16_t yy_def[173] = -+static yyconst flex_int16_t yy_def[180] = - { 0, -- 158, 1, 1, 3, 158, 5, 1, 1, 158, 158, -- 158, 158, 158, 159, 160, 161, 158, 158, 158, 158, -- 162, 158, 158, 158, 163, 162, 158, 164, 165, 164, -- 164, 158, 158, 158, 158, 159, 158, 159, 158, 166, -- 158, 161, 158, 161, 167, 168, 158, 158, 158, 158, -- 158, 158, 158, 158, 158, 162, 158, 158, 158, 158, -- 158, 158, 162, 164, 165, 164, 158, 158, 158, 169, -- 166, 170, 161, 167, 167, 168, 158, 158, 158, 158, -- 158, 158, 158, 158, 158, 164, 158, 158, 169, 170, -- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, -- -- 158, 164, 158, 158, 158, 158, 158, 158, 158, 171, -- 158, 164, 158, 158, 158, 158, 158, 158, 171, 158, -- 171, 158, 158, 158, 158, 158, 158, 158, 158, 158, -- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, -- 172, 158, 158, 158, 172, 158, 172, 158, 158, 158, -- 158, 158, 158, 158, 158, 158, 158, 0, 158, 158, -- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, -- 158, 158 -+ 165, 1, 1, 3, 165, 5, 1, 1, 165, 165, -+ 165, 165, 165, 166, 167, 168, 165, 165, 165, 165, -+ 169, 165, 165, 165, 170, 169, 165, 171, 172, 171, -+ 171, 165, 165, 165, 165, 166, 165, 166, 165, 173, -+ 165, 168, 165, 168, 174, 175, 165, 165, 165, 165, -+ 165, 165, 165, 165, 165, 165, 169, 165, 165, 165, -+ 165, 165, 165, 169, 171, 172, 171, 165, 165, 165, -+ 176, 173, 177, 168, 174, 174, 175, 165, 165, 165, -+ 165, 165, 165, 165, 165, 165, 165, 171, 165, 165, -+ 176, 177, 165, 165, 165, 165, 165, 165, 165, 165, -+ -+ 165, 165, 165, 165, 171, 165, 165, 165, 165, 165, -+ 165, 165, 165, 178, 165, 171, 165, 165, 165, 165, -+ 165, 165, 165, 178, 165, 178, 165, 165, 165, 165, -+ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, -+ 165, 165, 165, 165, 165, 165, 165, 179, 165, 165, -+ 165, 179, 165, 179, 165, 165, 165, 165, 165, 165, -+ 165, 165, 165, 165, 0, 165, 165, 165, 165, 165, -+ 165, 165, 165, 165, 165, 165, 165, 165, 165 - } ; - --static yyconst flex_int16_t yy_nxt[438] = -+static yyconst flex_int16_t yy_nxt[449] = - { 0, - 10, 11, 12, 11, 13, 14, 10, 15, 16, 10, - 10, 10, 17, 10, 10, 10, 10, 18, 19, 20, - 21, 21, 21, 21, 21, 10, 10, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, -- 21, 21, 21, 10, 22, 10, 24, 25, 25, 25, -- 32, 33, 33, 157, 26, 34, 34, 34, 51, 52, -- 27, 26, 26, 26, 26, 10, 11, 12, 11, 13, -- 14, 28, 15, 16, 28, 28, 28, 24, 28, 28, -- 28, 10, 18, 19, 20, 29, 29, 29, 29, 29, -- 30, 10, 29, 29, 29, 29, 29, 29, 29, 29, -- -- 29, 29, 29, 29, 29, 29, 29, 29, 10, 22, -- 10, 23, 34, 34, 34, 37, 39, 43, 32, 33, -- 33, 45, 54, 55, 46, 59, 45, 64, 156, 46, -- 64, 64, 64, 79, 44, 38, 59, 57, 134, 47, -- 135, 48, 80, 49, 47, 50, 48, 99, 61, 43, -- 50, 110, 41, 67, 67, 67, 60, 63, 63, 63, -- 57, 155, 68, 69, 63, 37, 44, 66, 67, 67, -- 67, 63, 63, 63, 63, 73, 59, 68, 69, 70, -- 34, 34, 34, 43, 75, 38, 154, 92, 83, 83, -- 83, 64, 44, 120, 64, 64, 64, 67, 67, 67, -- -- 44, 57, 99, 68, 69, 107, 68, 69, 120, 127, -- 108, 153, 152, 121, 83, 83, 83, 133, 133, 133, -- 146, 133, 133, 133, 146, 140, 140, 140, 121, 141, -- 140, 140, 140, 151, 141, 158, 150, 149, 148, 144, -- 147, 143, 142, 139, 147, 36, 36, 36, 36, 36, -- 36, 36, 36, 40, 138, 137, 136, 40, 40, 42, -- 42, 42, 42, 42, 42, 42, 42, 56, 56, 56, -- 56, 62, 132, 62, 64, 131, 130, 64, 129, 64, -- 64, 65, 128, 158, 65, 65, 65, 65, 71, 127, -- 71, 71, 74, 74, 74, 74, 74, 74, 74, 74, -- -- 76, 76, 76, 76, 76, 76, 76, 76, 89, 126, -- 89, 90, 125, 90, 90, 124, 90, 90, 119, 119, -- 119, 119, 119, 119, 119, 119, 145, 145, 145, 145, -- 145, 145, 145, 145, 123, 122, 59, 59, 118, 117, -- 116, 115, 114, 113, 45, 112, 108, 111, 109, 106, -- 105, 104, 46, 103, 91, 87, 102, 101, 100, 98, -- 97, 96, 95, 94, 93, 77, 75, 91, 88, 87, -- 86, 57, 85, 84, 57, 82, 81, 78, 77, 75, -- 72, 158, 58, 57, 53, 35, 158, 31, 23, 23, -- 9, 158, 158, 158, 158, 158, 158, 158, 158, 158, -- -- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, -- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, -- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, -- 158, 158, 158, 158, 158, 158, 158 -+ 21, 21, 21, 21, 10, 22, 10, 24, 25, 25, -+ 25, 32, 33, 33, 164, 26, 34, 34, 34, 52, -+ 53, 27, 26, 26, 26, 26, 10, 11, 12, 11, -+ 13, 14, 28, 15, 16, 28, 28, 28, 24, 28, -+ 28, 28, 10, 18, 19, 20, 29, 29, 29, 29, -+ 29, 30, 10, 29, 29, 29, 29, 29, 29, 29, -+ -+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, -+ 10, 22, 10, 23, 34, 34, 34, 37, 39, 43, -+ 32, 33, 33, 45, 55, 56, 46, 60, 43, 45, -+ 65, 163, 46, 65, 65, 65, 44, 38, 60, 74, -+ 58, 47, 141, 48, 142, 44, 49, 47, 50, 48, -+ 76, 51, 62, 94, 50, 41, 44, 51, 37, 61, -+ 64, 64, 64, 58, 34, 34, 34, 64, 162, 80, -+ 67, 68, 68, 68, 64, 64, 64, 64, 38, 81, -+ 69, 70, 71, 68, 68, 68, 60, 161, 43, 69, -+ 70, 65, 69, 70, 65, 65, 65, 125, 85, 85, -+ -+ 85, 58, 68, 68, 68, 44, 102, 110, 125, 133, -+ 102, 69, 70, 111, 114, 160, 159, 126, 85, 85, -+ 85, 140, 140, 140, 140, 140, 140, 153, 126, 147, -+ 147, 147, 153, 148, 147, 147, 147, 158, 148, 165, -+ 157, 156, 155, 151, 150, 149, 146, 154, 145, 144, -+ 143, 139, 154, 36, 36, 36, 36, 36, 36, 36, -+ 36, 40, 138, 137, 136, 40, 40, 42, 42, 42, -+ 42, 42, 42, 42, 42, 57, 57, 57, 57, 63, -+ 135, 63, 65, 134, 165, 65, 133, 65, 65, 66, -+ 132, 131, 66, 66, 66, 66, 72, 130, 72, 72, -+ -+ 75, 75, 75, 75, 75, 75, 75, 75, 77, 77, -+ 77, 77, 77, 77, 77, 77, 91, 129, 91, 92, -+ 128, 92, 92, 127, 92, 92, 124, 124, 124, 124, -+ 124, 124, 124, 124, 152, 152, 152, 152, 152, 152, -+ 152, 152, 60, 60, 123, 122, 121, 120, 119, 118, -+ 117, 45, 116, 111, 115, 113, 112, 109, 108, 107, -+ 46, 106, 93, 89, 105, 104, 103, 101, 100, 99, -+ 98, 97, 96, 95, 78, 76, 93, 90, 89, 88, -+ 58, 87, 86, 58, 84, 83, 82, 79, 78, 76, -+ 73, 165, 59, 58, 54, 35, 165, 31, 23, 23, -+ -+ 9, 165, 165, 165, 165, 165, 165, 165, 165, 165, -+ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, -+ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, -+ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, -+ 165, 165, 165, 165, 165, 165, 165, 165 - } ; - --static yyconst flex_int16_t yy_chk[438] = -+static yyconst flex_int16_t yy_chk[449] = - { 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, -- 7, 7, 7, 156, 3, 11, 11, 11, 18, 18, -- 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, -+ 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, -+ 3, 7, 7, 7, 163, 3, 11, 11, 11, 18, -+ 18, 3, 3, 3, 3, 3, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, -- 5, 8, 12, 12, 12, 14, 15, 16, 8, 8, -- 8, 17, 20, 20, 17, 23, 24, 29, 155, 24, -- 29, 29, 29, 48, 16, 14, 31, 29, 128, 17, -- 128, 17, 48, 17, 24, 17, 24, 99, 24, 42, -- 24, 99, 15, 33, 33, 33, 23, 26, 26, 26, -- 26, 154, 33, 33, 26, 36, 42, 31, 32, 32, -- 32, 26, 26, 26, 26, 44, 59, 32, 32, 32, -- 34, 34, 34, 73, 75, 36, 153, 75, 59, 59, -- 59, 65, 44, 110, 65, 65, 65, 67, 67, 67, -- -- 73, 65, 83, 89, 89, 97, 67, 67, 119, 127, -- 97, 150, 149, 110, 83, 83, 83, 133, 133, 133, -- 141, 127, 127, 127, 145, 136, 136, 136, 119, 136, -- 140, 140, 140, 148, 140, 147, 144, 143, 142, 139, -- 141, 138, 137, 135, 145, 159, 159, 159, 159, 159, -- 159, 159, 159, 160, 134, 132, 131, 160, 160, 161, -- 161, 161, 161, 161, 161, 161, 161, 162, 162, 162, -- 162, 163, 126, 163, 164, 125, 124, 164, 123, 164, -- 164, 165, 122, 121, 165, 165, 165, 165, 166, 120, -- 166, 166, 167, 167, 167, 167, 167, 167, 167, 167, -- -- 168, 168, 168, 168, 168, 168, 168, 168, 169, 118, -- 169, 170, 117, 170, 170, 116, 170, 170, 171, 171, -- 171, 171, 171, 171, 171, 171, 172, 172, 172, 172, -- 172, 172, 172, 172, 115, 114, 112, 111, 109, 108, -- 107, 106, 105, 104, 103, 102, 101, 100, 98, 96, -- 95, 94, 93, 92, 90, 88, 86, 85, 84, 82, -- 81, 80, 79, 78, 77, 76, 74, 72, 69, 68, -- 66, 63, 61, 60, 56, 50, 49, 47, 46, 45, -+ 5, 5, 5, 8, 12, 12, 12, 14, 15, 16, -+ 8, 8, 8, 17, 20, 20, 17, 23, 42, 24, -+ 29, 162, 24, 29, 29, 29, 16, 14, 31, 44, -+ 29, 17, 134, 17, 134, 42, 17, 24, 17, 24, -+ 76, 17, 24, 76, 24, 15, 44, 24, 36, 23, -+ 26, 26, 26, 26, 34, 34, 34, 26, 161, 48, -+ 31, 32, 32, 32, 26, 26, 26, 26, 36, 48, -+ 32, 32, 32, 33, 33, 33, 60, 160, 74, 91, -+ 91, 66, 33, 33, 66, 66, 66, 114, 60, 60, -+ -+ 60, 66, 68, 68, 68, 74, 85, 99, 124, 133, -+ 102, 68, 68, 99, 102, 157, 156, 114, 85, 85, -+ 85, 133, 133, 133, 140, 140, 140, 148, 124, 143, -+ 143, 143, 152, 143, 147, 147, 147, 155, 147, 154, -+ 151, 150, 149, 146, 145, 144, 142, 148, 141, 138, -+ 137, 132, 152, 166, 166, 166, 166, 166, 166, 166, -+ 166, 167, 131, 130, 129, 167, 167, 168, 168, 168, -+ 168, 168, 168, 168, 168, 169, 169, 169, 169, 170, -+ 128, 170, 171, 127, 126, 171, 125, 171, 171, 172, -+ 123, 122, 172, 172, 172, 172, 173, 121, 173, 173, -+ -+ 174, 174, 174, 174, 174, 174, 174, 174, 175, 175, -+ 175, 175, 175, 175, 175, 175, 176, 120, 176, 177, -+ 119, 177, 177, 118, 177, 177, 178, 178, 178, 178, -+ 178, 178, 178, 178, 179, 179, 179, 179, 179, 179, -+ 179, 179, 116, 115, 113, 112, 111, 110, 109, 108, -+ 107, 106, 105, 104, 103, 101, 100, 98, 97, 96, -+ 95, 94, 92, 90, 88, 87, 86, 84, 83, 82, -+ 81, 80, 79, 78, 77, 75, 73, 70, 69, 67, -+ 64, 62, 61, 57, 51, 50, 49, 47, 46, 45, - 41, 38, 22, 21, 19, 13, 9, 6, 4, 2, -- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, - -- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, -- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, -- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, -- 158, 158, 158, 158, 158, 158, 158 -+ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, -+ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, -+ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, -+ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, -+ 165, 165, 165, 165, 165, 165, 165, 165 - } ; - - static yy_state_type yy_last_accepting_state; -@@ -662,7 +664,7 @@ static int dts_version = 1; - static void push_input_file(const char *filename); - static bool pop_input_file(void); - static void lexical_error(const char *fmt, ...); --#line 666 "dtc-lexer.lex.c" -+#line 668 "dtc-lexer.lex.c" - - #define INITIAL 0 - #define BYTESTRING 1 -@@ -704,7 +706,7 @@ FILE *yyget_out (void ); - - void yyset_out (FILE * out_str ); - --yy_size_t yyget_leng (void ); -+int yyget_leng (void ); - - char *yyget_text (void ); - -@@ -853,6 +855,10 @@ YY_DECL - register char *yy_cp, *yy_bp; - register int yy_act; - -+#line 68 "dtc-lexer.l" -+ -+#line 861 "dtc-lexer.lex.c" -+ - if ( !(yy_init) ) - { - (yy_init) = 1; -@@ -879,11 +885,6 @@ YY_DECL - yy_load_buffer_state( ); - } - -- { --#line 68 "dtc-lexer.l" -- --#line 886 "dtc-lexer.lex.c" -- - while ( 1 ) /* loops until end-of-file is reached */ - { - yy_cp = (yy_c_buf_p); -@@ -901,7 +902,7 @@ YY_DECL - yy_match: - do - { -- register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; -+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; - if ( yy_accept[yy_current_state] ) - { - (yy_last_accepting_state) = yy_current_state; -@@ -910,13 +911,13 @@ yy_match: - while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) - { - yy_current_state = (int) yy_def[yy_current_state]; -- if ( yy_current_state >= 159 ) -+ if ( yy_current_state >= 166 ) - yy_c = yy_meta[(unsigned int) yy_c]; - } - yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; - ++yy_cp; - } -- while ( yy_current_state != 158 ); -+ while ( yy_current_state != 165 ); - yy_cp = (yy_last_accepting_cpos); - yy_current_state = (yy_last_accepting_state); - -@@ -1007,23 +1008,31 @@ case 5: - YY_RULE_SETUP - #line 116 "dtc-lexer.l" - { -+ DPRINT("Keyword: /plugin/\n"); -+ return DT_PLUGIN; -+ } -+ YY_BREAK -+case 6: -+YY_RULE_SETUP -+#line 121 "dtc-lexer.l" -+{ - DPRINT("Keyword: /memreserve/\n"); - BEGIN_DEFAULT(); - return DT_MEMRESERVE; - } - YY_BREAK --case 6: -+case 7: - YY_RULE_SETUP --#line 122 "dtc-lexer.l" -+#line 127 "dtc-lexer.l" - { - DPRINT("Keyword: /bits/\n"); - BEGIN_DEFAULT(); - return DT_BITS; - } - YY_BREAK --case 7: -+case 8: - YY_RULE_SETUP --#line 128 "dtc-lexer.l" -+#line 133 "dtc-lexer.l" - { - DPRINT("Keyword: /delete-property/\n"); - DPRINT("\n"); -@@ -1031,9 +1040,9 @@ YY_RULE_SETUP - return DT_DEL_PROP; - } - YY_BREAK --case 8: -+case 9: - YY_RULE_SETUP --#line 135 "dtc-lexer.l" -+#line 140 "dtc-lexer.l" - { - DPRINT("Keyword: /delete-node/\n"); - DPRINT("\n"); -@@ -1041,9 +1050,9 @@ YY_RULE_SETUP - return DT_DEL_NODE; - } - YY_BREAK --case 9: -+case 10: - YY_RULE_SETUP --#line 142 "dtc-lexer.l" -+#line 147 "dtc-lexer.l" - { - DPRINT("Label: %s\n", yytext); - yylval.labelref = xstrdup(yytext); -@@ -1051,9 +1060,9 @@ YY_RULE_SETUP - return DT_LABEL; - } - YY_BREAK --case 10: -+case 11: - YY_RULE_SETUP --#line 149 "dtc-lexer.l" -+#line 154 "dtc-lexer.l" - { - char *e; - DPRINT("Integer Literal: '%s'\n", yytext); -@@ -1073,10 +1082,10 @@ YY_RULE_SETUP - return DT_LITERAL; - } - YY_BREAK --case 11: --/* rule 11 can match eol */ -+case 12: -+/* rule 12 can match eol */ - YY_RULE_SETUP --#line 168 "dtc-lexer.l" -+#line 173 "dtc-lexer.l" - { - struct data d; - DPRINT("Character literal: %s\n", yytext); -@@ -1098,18 +1107,18 @@ YY_RULE_SETUP - return DT_CHAR_LITERAL; - } - YY_BREAK --case 12: -+case 13: - YY_RULE_SETUP --#line 189 "dtc-lexer.l" -+#line 194 "dtc-lexer.l" - { /* label reference */ - DPRINT("Ref: %s\n", yytext+1); - yylval.labelref = xstrdup(yytext+1); - return DT_REF; - } - YY_BREAK --case 13: -+case 14: - YY_RULE_SETUP --#line 195 "dtc-lexer.l" -+#line 200 "dtc-lexer.l" - { /* new-style path reference */ - yytext[yyleng-1] = '\0'; - DPRINT("Ref: %s\n", yytext+2); -@@ -1117,27 +1126,27 @@ YY_RULE_SETUP - return DT_REF; - } - YY_BREAK --case 14: -+case 15: - YY_RULE_SETUP --#line 202 "dtc-lexer.l" -+#line 207 "dtc-lexer.l" - { - yylval.byte = strtol(yytext, NULL, 16); - DPRINT("Byte: %02x\n", (int)yylval.byte); - return DT_BYTE; - } - YY_BREAK --case 15: -+case 16: - YY_RULE_SETUP --#line 208 "dtc-lexer.l" -+#line 213 "dtc-lexer.l" - { - DPRINT("/BYTESTRING\n"); - BEGIN_DEFAULT(); - return ']'; - } - YY_BREAK --case 16: -+case 17: - YY_RULE_SETUP --#line 214 "dtc-lexer.l" -+#line 219 "dtc-lexer.l" - { - DPRINT("PropNodeName: %s\n", yytext); - yylval.propnodename = xstrdup((yytext[0] == '\\') ? -@@ -1146,75 +1155,75 @@ YY_RULE_SETUP - return DT_PROPNODENAME; - } - YY_BREAK --case 17: -+case 18: - YY_RULE_SETUP --#line 222 "dtc-lexer.l" -+#line 227 "dtc-lexer.l" - { - DPRINT("Binary Include\n"); - return DT_INCBIN; - } - YY_BREAK --case 18: --/* rule 18 can match eol */ --YY_RULE_SETUP --#line 227 "dtc-lexer.l" --/* eat whitespace */ -- YY_BREAK - case 19: - /* rule 19 can match eol */ - YY_RULE_SETUP --#line 228 "dtc-lexer.l" --/* eat C-style comments */ -+#line 232 "dtc-lexer.l" -+/* eat whitespace */ - YY_BREAK - case 20: - /* rule 20 can match eol */ - YY_RULE_SETUP --#line 229 "dtc-lexer.l" --/* eat C++-style comments */ -+#line 233 "dtc-lexer.l" -+/* eat C-style comments */ - YY_BREAK - case 21: -+/* rule 21 can match eol */ - YY_RULE_SETUP --#line 231 "dtc-lexer.l" --{ return DT_LSHIFT; }; -+#line 234 "dtc-lexer.l" -+/* eat C++-style comments */ - YY_BREAK - case 22: - YY_RULE_SETUP --#line 232 "dtc-lexer.l" --{ return DT_RSHIFT; }; -+#line 236 "dtc-lexer.l" -+{ return DT_LSHIFT; }; - YY_BREAK - case 23: - YY_RULE_SETUP --#line 233 "dtc-lexer.l" --{ return DT_LE; }; -+#line 237 "dtc-lexer.l" -+{ return DT_RSHIFT; }; - YY_BREAK - case 24: - YY_RULE_SETUP --#line 234 "dtc-lexer.l" --{ return DT_GE; }; -+#line 238 "dtc-lexer.l" -+{ return DT_LE; }; - YY_BREAK - case 25: - YY_RULE_SETUP --#line 235 "dtc-lexer.l" --{ return DT_EQ; }; -+#line 239 "dtc-lexer.l" -+{ return DT_GE; }; - YY_BREAK - case 26: - YY_RULE_SETUP --#line 236 "dtc-lexer.l" --{ return DT_NE; }; -+#line 240 "dtc-lexer.l" -+{ return DT_EQ; }; - YY_BREAK - case 27: - YY_RULE_SETUP --#line 237 "dtc-lexer.l" --{ return DT_AND; }; -+#line 241 "dtc-lexer.l" -+{ return DT_NE; }; - YY_BREAK - case 28: - YY_RULE_SETUP --#line 238 "dtc-lexer.l" --{ return DT_OR; }; -+#line 242 "dtc-lexer.l" -+{ return DT_AND; }; - YY_BREAK - case 29: - YY_RULE_SETUP --#line 240 "dtc-lexer.l" -+#line 243 "dtc-lexer.l" -+{ return DT_OR; }; -+ YY_BREAK -+case 30: -+YY_RULE_SETUP -+#line 245 "dtc-lexer.l" - { - DPRINT("Char: %c (\\x%02x)\n", yytext[0], - (unsigned)yytext[0]); -@@ -1230,12 +1239,12 @@ YY_RULE_SETUP - return yytext[0]; - } - YY_BREAK --case 30: -+case 31: - YY_RULE_SETUP --#line 255 "dtc-lexer.l" -+#line 260 "dtc-lexer.l" - ECHO; - YY_BREAK --#line 1239 "dtc-lexer.lex.c" -+#line 1248 "dtc-lexer.lex.c" - - case YY_END_OF_BUFFER: - { -@@ -1365,7 +1374,6 @@ ECHO; - "fatal flex scanner internal error--no action found" ); - } /* end of action switch */ - } /* end of scanning one token */ -- } /* end of user's declarations */ - } /* end of yylex */ - - /* yy_get_next_buffer - try to read in a new buffer -@@ -1421,21 +1429,21 @@ static int yy_get_next_buffer (void) - - else - { -- yy_size_t num_to_read = -+ int num_to_read = - YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; - - while ( num_to_read <= 0 ) - { /* Not enough room in the buffer - grow it. */ - - /* just a shorter name for the current buffer */ -- YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; -+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER; - - int yy_c_buf_p_offset = - (int) ((yy_c_buf_p) - b->yy_ch_buf); - - if ( b->yy_is_our_buffer ) - { -- yy_size_t new_size = b->yy_buf_size * 2; -+ int new_size = b->yy_buf_size * 2; - - if ( new_size <= 0 ) - b->yy_buf_size += b->yy_buf_size / 8; -@@ -1466,7 +1474,7 @@ static int yy_get_next_buffer (void) - - /* Read in more data. */ - YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), -- (yy_n_chars), num_to_read ); -+ (yy_n_chars), (size_t) num_to_read ); - - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); - } -@@ -1528,7 +1536,7 @@ static int yy_get_next_buffer (void) - while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) - { - yy_current_state = (int) yy_def[yy_current_state]; -- if ( yy_current_state >= 159 ) -+ if ( yy_current_state >= 166 ) - yy_c = yy_meta[(unsigned int) yy_c]; - } - yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; -@@ -1556,13 +1564,13 @@ static int yy_get_next_buffer (void) - while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) - { - yy_current_state = (int) yy_def[yy_current_state]; -- if ( yy_current_state >= 159 ) -+ if ( yy_current_state >= 166 ) - yy_c = yy_meta[(unsigned int) yy_c]; - } - yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; -- yy_is_jam = (yy_current_state == 158); -+ yy_is_jam = (yy_current_state == 165); - -- return yy_is_jam ? 0 : yy_current_state; -+ return yy_is_jam ? 0 : yy_current_state; - } - - #ifndef YY_NO_INPUT -@@ -1589,7 +1597,7 @@ static int yy_get_next_buffer (void) - - else - { /* need more input */ -- yy_size_t offset = (yy_c_buf_p) - (yytext_ptr); -+ int offset = (yy_c_buf_p) - (yytext_ptr); - ++(yy_c_buf_p); - - switch ( yy_get_next_buffer( ) ) -@@ -1863,7 +1871,7 @@ void yypop_buffer_state (void) - */ - static void yyensure_buffer_stack (void) - { -- yy_size_t num_to_alloc; -+ int num_to_alloc; - - if (!(yy_buffer_stack)) { - -@@ -1960,12 +1968,12 @@ YY_BUFFER_STATE yy_scan_string (yyconst - * - * @return the newly allocated buffer state object. - */ --YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len ) -+YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) - { - YY_BUFFER_STATE b; - char *buf; - yy_size_t n; -- yy_size_t i; -+ int i; - - /* Get memory for full buffer, including space for trailing EOB's. */ - n = _yybytes_len + 2; -@@ -2047,7 +2055,7 @@ FILE *yyget_out (void) - /** Get the length of the current token. - * - */ --yy_size_t yyget_leng (void) -+int yyget_leng (void) - { - return yyleng; - } -@@ -2195,7 +2203,7 @@ void yyfree (void * ptr ) - - #define YYTABLES_NAME "yytables" - --#line 254 "dtc-lexer.l" -+#line 260 "dtc-lexer.l" - - - ---- a/scripts/dtc/dtc-parser.tab.c_shipped -+++ b/scripts/dtc/dtc-parser.tab.c_shipped -@@ -1,19 +1,19 @@ --/* A Bison parser, made by GNU Bison 3.0.2. */ -+/* A Bison parser, made by GNU Bison 2.5. */ - - /* Bison implementation for Yacc-like parsers in C -- -- Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. -- -+ -+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. -+ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. -- -+ - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU 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 . */ - -@@ -26,7 +26,7 @@ - special exception, which will cause the skeleton and the resulting - Bison output files to be licensed under the GNU General Public - License without this special exception. -- -+ - This special exception was added by the Free Software Foundation in - version 2.2 of Bison. */ - -@@ -44,7 +44,7 @@ - #define YYBISON 1 - - /* Bison version. */ --#define YYBISON_VERSION "3.0.2" -+#define YYBISON_VERSION "2.5" - - /* Skeleton name. */ - #define YYSKELETON_NAME "yacc.c" -@@ -58,13 +58,18 @@ - /* Pull parsers. */ - #define YYPULL 1 - -+/* Using locations. */ -+#define YYLSP_NEEDED 1 - - - - /* Copy the first part of user declarations. */ --#line 20 "dtc-parser.y" /* yacc.c:339 */ -+ -+/* Line 268 of yacc.c */ -+#line 20 "dtc-parser.y" - - #include -+#include - - #include "dtc.h" - #include "srcpos.h" -@@ -80,15 +85,14 @@ extern void yyerror(char const *s); - extern struct boot_info *the_boot_info; - extern bool treesource_error; - --#line 84 "dtc-parser.tab.c" /* yacc.c:339 */ - --# ifndef YY_NULLPTR --# if defined __cplusplus && 201103L <= __cplusplus --# define YY_NULLPTR nullptr --# else --# define YY_NULLPTR 0 --# endif --# endif -+/* Line 268 of yacc.c */ -+#line 91 "dtc-parser.tab.c" -+ -+/* Enabling traces. */ -+#ifndef YYDEBUG -+# define YYDEBUG 0 -+#endif - - /* Enabling verbose error messages. */ - #ifdef YYERROR_VERBOSE -@@ -98,53 +102,51 @@ extern bool treesource_error; - # define YYERROR_VERBOSE 0 - #endif - --/* In a future release of Bison, this section will be replaced -- by #include "dtc-parser.tab.h". */ --#ifndef YY_YY_DTC_PARSER_TAB_H_INCLUDED --# define YY_YY_DTC_PARSER_TAB_H_INCLUDED --/* Debug traces. */ --#ifndef YYDEBUG --# define YYDEBUG 0 --#endif --#if YYDEBUG --extern int yydebug; -+/* Enabling the token table. */ -+#ifndef YYTOKEN_TABLE -+# define YYTOKEN_TABLE 0 - #endif - --/* Token type. */ -+ -+/* Tokens. */ - #ifndef YYTOKENTYPE - # define YYTOKENTYPE -- enum yytokentype -- { -- DT_V1 = 258, -- DT_MEMRESERVE = 259, -- DT_LSHIFT = 260, -- DT_RSHIFT = 261, -- DT_LE = 262, -- DT_GE = 263, -- DT_EQ = 264, -- DT_NE = 265, -- DT_AND = 266, -- DT_OR = 267, -- DT_BITS = 268, -- DT_DEL_PROP = 269, -- DT_DEL_NODE = 270, -- DT_PROPNODENAME = 271, -- DT_LITERAL = 272, -- DT_CHAR_LITERAL = 273, -- DT_BYTE = 274, -- DT_STRING = 275, -- DT_LABEL = 276, -- DT_REF = 277, -- DT_INCBIN = 278 -- }; -+ /* Put the tokens into the symbol table, so that GDB and other debuggers -+ know about them. */ -+ enum yytokentype { -+ DT_V1 = 258, -+ DT_PLUGIN = 259, -+ DT_MEMRESERVE = 260, -+ DT_LSHIFT = 261, -+ DT_RSHIFT = 262, -+ DT_LE = 263, -+ DT_GE = 264, -+ DT_EQ = 265, -+ DT_NE = 266, -+ DT_AND = 267, -+ DT_OR = 268, -+ DT_BITS = 269, -+ DT_DEL_PROP = 270, -+ DT_DEL_NODE = 271, -+ DT_PROPNODENAME = 272, -+ DT_LITERAL = 273, -+ DT_CHAR_LITERAL = 274, -+ DT_BYTE = 275, -+ DT_STRING = 276, -+ DT_LABEL = 277, -+ DT_REF = 278, -+ DT_INCBIN = 279 -+ }; - #endif - --/* Value type. */ -+ -+ - #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED --typedef union YYSTYPE YYSTYPE; --union YYSTYPE -+typedef union YYSTYPE - { --#line 38 "dtc-parser.y" /* yacc.c:355 */ -+ -+/* Line 293 of yacc.c */ -+#line 39 "dtc-parser.y" - - char *propnodename; - char *labelref; -@@ -162,37 +164,37 @@ union YYSTYPE - struct node *nodelist; - struct reserve_info *re; - uint64_t integer; -+ int is_plugin; - --#line 167 "dtc-parser.tab.c" /* yacc.c:355 */ --}; -+ -+ -+/* Line 293 of yacc.c */ -+#line 173 "dtc-parser.tab.c" -+} YYSTYPE; - # define YYSTYPE_IS_TRIVIAL 1 -+# define yystype YYSTYPE /* obsolescent; will be withdrawn */ - # define YYSTYPE_IS_DECLARED 1 - #endif - --/* Location type. */ - #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED --typedef struct YYLTYPE YYLTYPE; --struct YYLTYPE -+typedef struct YYLTYPE - { - int first_line; - int first_column; - int last_line; - int last_column; --}; -+} YYLTYPE; -+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ - # define YYLTYPE_IS_DECLARED 1 - # define YYLTYPE_IS_TRIVIAL 1 - #endif - - --extern YYSTYPE yylval; --extern YYLTYPE yylloc; --int yyparse (void); -- --#endif /* !YY_YY_DTC_PARSER_TAB_H_INCLUDED */ -- - /* Copy the second part of user declarations. */ - --#line 196 "dtc-parser.tab.c" /* yacc.c:358 */ -+ -+/* Line 343 of yacc.c */ -+#line 198 "dtc-parser.tab.c" - - #ifdef short - # undef short -@@ -206,8 +208,11 @@ typedef unsigned char yytype_uint8; - - #ifdef YYTYPE_INT8 - typedef YYTYPE_INT8 yytype_int8; --#else -+#elif (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) - typedef signed char yytype_int8; -+#else -+typedef short int yytype_int8; - #endif - - #ifdef YYTYPE_UINT16 -@@ -227,7 +232,8 @@ typedef short int yytype_int16; - # define YYSIZE_T __SIZE_TYPE__ - # elif defined size_t - # define YYSIZE_T size_t --# elif ! defined YYSIZE_T -+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) - # include /* INFRINGES ON USER NAME SPACE */ - # define YYSIZE_T size_t - # else -@@ -241,68 +247,39 @@ typedef short int yytype_int16; - # if defined YYENABLE_NLS && YYENABLE_NLS - # if ENABLE_NLS - # include /* INFRINGES ON USER NAME SPACE */ --# define YY_(Msgid) dgettext ("bison-runtime", Msgid) -+# define YY_(msgid) dgettext ("bison-runtime", msgid) - # endif - # endif - # ifndef YY_ --# define YY_(Msgid) Msgid --# endif --#endif -- --#ifndef YY_ATTRIBUTE --# if (defined __GNUC__ \ -- && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ -- || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C --# define YY_ATTRIBUTE(Spec) __attribute__(Spec) --# else --# define YY_ATTRIBUTE(Spec) /* empty */ --# endif --#endif -- --#ifndef YY_ATTRIBUTE_PURE --# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) --#endif -- --#ifndef YY_ATTRIBUTE_UNUSED --# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) --#endif -- --#if !defined _Noreturn \ -- && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112) --# if defined _MSC_VER && 1200 <= _MSC_VER --# define _Noreturn __declspec (noreturn) --# else --# define _Noreturn YY_ATTRIBUTE ((__noreturn__)) -+# define YY_(msgid) msgid - # endif - #endif - - /* Suppress unused-variable warnings by "using" E. */ - #if ! defined lint || defined __GNUC__ --# define YYUSE(E) ((void) (E)) -+# define YYUSE(e) ((void) (e)) - #else --# define YYUSE(E) /* empty */ -+# define YYUSE(e) /* empty */ - #endif - --#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ --/* Suppress an incorrect diagnostic about yylval being uninitialized. */ --# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ -- _Pragma ("GCC diagnostic push") \ -- _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ -- _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") --# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ -- _Pragma ("GCC diagnostic pop") -+/* Identity function, used to suppress warnings about constant conditions. */ -+#ifndef lint -+# define YYID(n) (n) - #else --# define YY_INITIAL_VALUE(Value) Value --#endif --#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN --# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN --# define YY_IGNORE_MAYBE_UNINITIALIZED_END -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) -+static int -+YYID (int yyi) -+#else -+static int -+YYID (yyi) -+ int yyi; - #endif --#ifndef YY_INITIAL_VALUE --# define YY_INITIAL_VALUE(Value) /* Nothing. */ -+{ -+ return yyi; -+} - #endif - -- - #if ! defined yyoverflow || YYERROR_VERBOSE - - /* The parser invokes alloca or malloc; define the necessary symbols. */ -@@ -320,9 +297,9 @@ typedef short int yytype_int16; - # define alloca _alloca - # else - # define YYSTACK_ALLOC alloca --# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS -+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) - # include /* INFRINGES ON USER NAME SPACE */ -- /* Use EXIT_SUCCESS as a witness for stdlib.h. */ - # ifndef EXIT_SUCCESS - # define EXIT_SUCCESS 0 - # endif -@@ -332,8 +309,8 @@ typedef short int yytype_int16; - # endif - - # ifdef YYSTACK_ALLOC -- /* Pacify GCC's 'empty if-body' warning. */ --# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) -+ /* Pacify GCC's `empty if-body' warning. */ -+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) - # ifndef YYSTACK_ALLOC_MAXIMUM - /* The OS might guarantee only one guard page at the bottom of the stack, - and a page size can be as small as 4096 bytes. So we cannot safely -@@ -349,7 +326,7 @@ typedef short int yytype_int16; - # endif - # if (defined __cplusplus && ! defined EXIT_SUCCESS \ - && ! ((defined YYMALLOC || defined malloc) \ -- && (defined YYFREE || defined free))) -+ && (defined YYFREE || defined free))) - # include /* INFRINGES ON USER NAME SPACE */ - # ifndef EXIT_SUCCESS - # define EXIT_SUCCESS 0 -@@ -357,13 +334,15 @@ typedef short int yytype_int16; - # endif - # ifndef YYMALLOC - # define YYMALLOC malloc --# if ! defined malloc && ! defined EXIT_SUCCESS -+# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) - void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ - # endif - # endif - # ifndef YYFREE - # define YYFREE free --# if ! defined free && ! defined EXIT_SUCCESS -+# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) - void free (void *); /* INFRINGES ON USER NAME SPACE */ - # endif - # endif -@@ -373,8 +352,8 @@ void free (void *); /* INFRINGES ON USER - - #if (! defined yyoverflow \ - && (! defined __cplusplus \ -- || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ -- && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) -+ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ -+ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) - - /* A type that is properly aligned for any stack member. */ - union yyalloc -@@ -400,35 +379,35 @@ union yyalloc - elements in the stack, and YYPTR gives the new location of the - stack. Advance YYPTR to a properly aligned location for the next - stack. */ --# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ -- do \ -- { \ -- YYSIZE_T yynewbytes; \ -- YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ -- Stack = &yyptr->Stack_alloc; \ -- yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ -- yyptr += yynewbytes / sizeof (*yyptr); \ -- } \ -- while (0) -+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ -+ do \ -+ { \ -+ YYSIZE_T yynewbytes; \ -+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ -+ Stack = &yyptr->Stack_alloc; \ -+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ -+ yyptr += yynewbytes / sizeof (*yyptr); \ -+ } \ -+ while (YYID (0)) - - #endif - - #if defined YYCOPY_NEEDED && YYCOPY_NEEDED --/* Copy COUNT objects from SRC to DST. The source and destination do -+/* Copy COUNT objects from FROM to TO. The source and destination do - not overlap. */ - # ifndef YYCOPY - # if defined __GNUC__ && 1 < __GNUC__ --# define YYCOPY(Dst, Src, Count) \ -- __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) -+# define YYCOPY(To, From, Count) \ -+ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) - # else --# define YYCOPY(Dst, Src, Count) \ -- do \ -- { \ -- YYSIZE_T yyi; \ -- for (yyi = 0; yyi < (Count); yyi++) \ -- (Dst)[yyi] = (Src)[yyi]; \ -- } \ -- while (0) -+# define YYCOPY(To, From, Count) \ -+ do \ -+ { \ -+ YYSIZE_T yyi; \ -+ for (yyi = 0; yyi < (Count); yyi++) \ -+ (To)[yyi] = (From)[yyi]; \ -+ } \ -+ while (YYID (0)) - # endif - # endif - #endif /* !YYCOPY_NEEDED */ -@@ -439,39 +418,37 @@ union yyalloc - #define YYLAST 136 - - /* YYNTOKENS -- Number of terminals. */ --#define YYNTOKENS 47 -+#define YYNTOKENS 48 - /* YYNNTS -- Number of nonterminals. */ --#define YYNNTS 28 -+#define YYNNTS 29 - /* YYNRULES -- Number of rules. */ --#define YYNRULES 80 --/* YYNSTATES -- Number of states. */ --#define YYNSTATES 144 -+#define YYNRULES 82 -+/* YYNRULES -- Number of states. */ -+#define YYNSTATES 147 - --/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned -- by yylex, with out-of-bounds checking. */ -+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ - #define YYUNDEFTOK 2 --#define YYMAXUTOK 278 -+#define YYMAXUTOK 279 - --#define YYTRANSLATE(YYX) \ -+#define YYTRANSLATE(YYX) \ - ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) - --/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM -- as returned by yylex, without out-of-bounds checking. */ -+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ - static const yytype_uint8 yytranslate[] = - { - 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, 46, 2, 2, 2, 44, 40, 2, -- 32, 34, 43, 41, 33, 42, 2, 25, 2, 2, -- 2, 2, 2, 2, 2, 2, 2, 2, 37, 24, -- 35, 28, 29, 36, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 47, 2, 2, 2, 45, 41, 2, -+ 33, 35, 44, 42, 34, 43, 2, 26, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 38, 25, -+ 36, 29, 30, 37, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -- 2, 30, 2, 31, 39, 2, 2, 2, 2, 2, -+ 2, 31, 2, 32, 40, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -- 2, 2, 2, 26, 38, 27, 45, 2, 2, 2, -+ 2, 2, 2, 27, 39, 28, 46, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -@@ -486,292 +463,335 @@ static const yytype_uint8 yytranslate[] - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -- 15, 16, 17, 18, 19, 20, 21, 22, 23 -+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 - }; - - #if YYDEBUG -- /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ -+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in -+ YYRHS. */ -+static const yytype_uint16 yyprhs[] = -+{ -+ 0, 0, 3, 9, 10, 13, 14, 17, 22, 25, -+ 28, 32, 37, 41, 46, 52, 53, 56, 61, 64, -+ 68, 71, 74, 78, 83, 86, 96, 102, 105, 106, -+ 109, 112, 116, 118, 121, 124, 127, 129, 131, 135, -+ 137, 139, 145, 147, 151, 153, 157, 159, 163, 165, -+ 169, 171, 175, 177, 181, 185, 187, 191, 195, 199, -+ 203, 207, 211, 213, 217, 221, 223, 227, 231, 235, -+ 237, 239, 242, 245, 248, 249, 252, 255, 256, 259, -+ 262, 265, 269 -+}; -+ -+/* YYRHS -- A `-1'-separated list of the rules' RHS. */ -+static const yytype_int8 yyrhs[] = -+{ -+ 49, 0, -1, 3, 25, 50, 51, 53, -1, -1, -+ 4, 25, -1, -1, 52, 51, -1, 5, 60, 60, -+ 25, -1, 22, 52, -1, 26, 54, -1, 53, 26, -+ 54, -1, 53, 22, 23, 54, -1, 53, 23, 54, -+ -1, 53, 16, 23, 25, -1, 27, 55, 75, 28, -+ 25, -1, -1, 55, 56, -1, 17, 29, 57, 25, -+ -1, 17, 25, -1, 15, 17, 25, -1, 22, 56, -+ -1, 58, 21, -1, 58, 59, 30, -1, 58, 31, -+ 74, 32, -1, 58, 23, -1, 58, 24, 33, 21, -+ 34, 60, 34, 60, 35, -1, 58, 24, 33, 21, -+ 35, -1, 57, 22, -1, -1, 57, 34, -1, 58, -+ 22, -1, 14, 18, 36, -1, 36, -1, 59, 60, -+ -1, 59, 23, -1, 59, 22, -1, 18, -1, 19, -+ -1, 33, 61, 35, -1, 62, -1, 63, -1, 63, -+ 37, 61, 38, 62, -1, 64, -1, 63, 13, 64, -+ -1, 65, -1, 64, 12, 65, -1, 66, -1, 65, -+ 39, 66, -1, 67, -1, 66, 40, 67, -1, 68, -+ -1, 67, 41, 68, -1, 69, -1, 68, 10, 69, -+ -1, 68, 11, 69, -1, 70, -1, 69, 36, 70, -+ -1, 69, 30, 70, -1, 69, 8, 70, -1, 69, -+ 9, 70, -1, 70, 6, 71, -1, 70, 7, 71, -+ -1, 71, -1, 71, 42, 72, -1, 71, 43, 72, -+ -1, 72, -1, 72, 44, 73, -1, 72, 26, 73, -+ -1, 72, 45, 73, -1, 73, -1, 60, -1, 43, -+ 73, -1, 46, 73, -1, 47, 73, -1, -1, 74, -+ 20, -1, 74, 22, -1, -1, 76, 75, -1, 76, -+ 56, -1, 17, 54, -1, 16, 17, 25, -1, 22, -+ 76, -1 -+}; -+ -+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ - static const yytype_uint16 yyrline[] = - { -- 0, 104, 104, 113, 116, 123, 127, 135, 139, 144, -- 155, 165, 180, 188, 191, 198, 202, 206, 210, 218, -- 222, 226, 230, 234, 250, 260, 268, 271, 275, 282, -- 298, 303, 322, 336, 343, 344, 345, 352, 356, 357, -- 361, 362, 366, 367, 371, 372, 376, 377, 381, 382, -- 386, 387, 388, 392, 393, 394, 395, 396, 400, 401, -- 402, 406, 407, 408, 412, 413, 414, 415, 419, 420, -- 421, 422, 427, 430, 434, 442, 445, 449, 457, 461, -- 465 -+ 0, 108, 108, 119, 122, 130, 133, 140, 144, 152, -+ 156, 161, 172, 182, 197, 205, 208, 215, 219, 223, -+ 227, 235, 239, 243, 247, 251, 267, 277, 285, 288, -+ 292, 299, 315, 320, 339, 353, 360, 361, 362, 369, -+ 373, 374, 378, 379, 383, 384, 388, 389, 393, 394, -+ 398, 399, 403, 404, 405, 409, 410, 411, 412, 413, -+ 417, 418, 419, 423, 424, 425, 429, 430, 431, 432, -+ 436, 437, 438, 439, 444, 447, 451, 459, 462, 466, -+ 474, 478, 482 - }; - #endif - --#if YYDEBUG || YYERROR_VERBOSE || 0 -+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE - /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. - First, the terminals, then, starting at YYNTOKENS, nonterminals. */ - static const char *const yytname[] = - { -- "$end", "error", "$undefined", "DT_V1", "DT_MEMRESERVE", "DT_LSHIFT", -- "DT_RSHIFT", "DT_LE", "DT_GE", "DT_EQ", "DT_NE", "DT_AND", "DT_OR", -- "DT_BITS", "DT_DEL_PROP", "DT_DEL_NODE", "DT_PROPNODENAME", "DT_LITERAL", -- "DT_CHAR_LITERAL", "DT_BYTE", "DT_STRING", "DT_LABEL", "DT_REF", -- "DT_INCBIN", "';'", "'/'", "'{'", "'}'", "'='", "'>'", "'['", "']'", -- "'('", "','", "')'", "'<'", "'?'", "':'", "'|'", "'^'", "'&'", "'+'", -- "'-'", "'*'", "'%'", "'~'", "'!'", "$accept", "sourcefile", -- "memreserves", "memreserve", "devicetree", "nodedef", "proplist", -- "propdef", "propdata", "propdataprefix", "arrayprefix", "integer_prim", -- "integer_expr", "integer_trinary", "integer_or", "integer_and", -- "integer_bitor", "integer_bitxor", "integer_bitand", "integer_eq", -- "integer_rela", "integer_shift", "integer_add", "integer_mul", -- "integer_unary", "bytestring", "subnodes", "subnode", YY_NULLPTR -+ "$end", "error", "$undefined", "DT_V1", "DT_PLUGIN", "DT_MEMRESERVE", -+ "DT_LSHIFT", "DT_RSHIFT", "DT_LE", "DT_GE", "DT_EQ", "DT_NE", "DT_AND", -+ "DT_OR", "DT_BITS", "DT_DEL_PROP", "DT_DEL_NODE", "DT_PROPNODENAME", -+ "DT_LITERAL", "DT_CHAR_LITERAL", "DT_BYTE", "DT_STRING", "DT_LABEL", -+ "DT_REF", "DT_INCBIN", "';'", "'/'", "'{'", "'}'", "'='", "'>'", "'['", -+ "']'", "'('", "','", "')'", "'<'", "'?'", "':'", "'|'", "'^'", "'&'", -+ "'+'", "'-'", "'*'", "'%'", "'~'", "'!'", "$accept", "sourcefile", -+ "plugindecl", "memreserves", "memreserve", "devicetree", "nodedef", -+ "proplist", "propdef", "propdata", "propdataprefix", "arrayprefix", -+ "integer_prim", "integer_expr", "integer_trinary", "integer_or", -+ "integer_and", "integer_bitor", "integer_bitxor", "integer_bitand", -+ "integer_eq", "integer_rela", "integer_shift", "integer_add", -+ "integer_mul", "integer_unary", "bytestring", "subnodes", "subnode", 0 - }; - #endif - - # ifdef YYPRINT --/* YYTOKNUM[NUM] -- (External) token number corresponding to the -- (internal) symbol number NUM (which must be that of a token). */ -+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to -+ token YYLEX-NUM. */ - static const yytype_uint16 yytoknum[] = - { - 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, - 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, -- 275, 276, 277, 278, 59, 47, 123, 125, 61, 62, -- 91, 93, 40, 44, 41, 60, 63, 58, 124, 94, -- 38, 43, 45, 42, 37, 126, 33 -+ 275, 276, 277, 278, 279, 59, 47, 123, 125, 61, -+ 62, 91, 93, 40, 44, 41, 60, 63, 58, 124, -+ 94, 38, 43, 45, 42, 37, 126, 33 - }; - # endif - --#define YYPACT_NINF -81 -- --#define yypact_value_is_default(Yystate) \ -- (!!((Yystate) == (-81))) -- --#define YYTABLE_NINF -1 -- --#define yytable_value_is_error(Yytable_value) \ -- 0 -- -- /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing -- STATE-NUM. */ --static const yytype_int8 yypact[] = -+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ -+static const yytype_uint8 yyr1[] = - { -- 16, -11, 21, 10, -81, 25, 10, 19, 10, -81, -- -81, -9, 25, -81, 2, 51, -81, -9, -9, -9, -- -81, 1, -81, -6, 50, 14, 28, 29, 36, 3, -- 58, 44, -3, -81, 47, -81, -81, 65, 68, 2, -- 2, -81, -81, -81, -81, -9, -9, -9, -9, -9, -- -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -- -9, -9, -9, -9, -81, 63, 69, 2, -81, -81, -- 50, 57, 14, 28, 29, 36, 3, 3, 58, 58, -- 58, 58, 44, 44, -3, -3, -81, -81, -81, 79, -- 80, -8, 63, -81, 72, 63, -81, -81, -9, 76, -- 77, -81, -81, -81, -81, -81, 78, -81, -81, -81, -- -81, -81, 35, 4, -81, -81, -81, -81, 86, -81, -- -81, -81, 73, -81, -81, 33, 71, 84, 39, -81, -- -81, -81, -81, -81, 41, -81, -81, -81, 25, -81, -- 74, 25, 75, -81 -+ 0, 48, 49, 50, 50, 51, 51, 52, 52, 53, -+ 53, 53, 53, 53, 54, 55, 55, 56, 56, 56, -+ 56, 57, 57, 57, 57, 57, 57, 57, 58, 58, -+ 58, 59, 59, 59, 59, 59, 60, 60, 60, 61, -+ 62, 62, 63, 63, 64, 64, 65, 65, 66, 66, -+ 67, 67, 68, 68, 68, 69, 69, 69, 69, 69, -+ 70, 70, 70, 71, 71, 71, 72, 72, 72, 72, -+ 73, 73, 73, 73, 74, 74, 74, 75, 75, 75, -+ 76, 76, 76 - }; - -- /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. -- Performed when YYTABLE does not specify something else to do. Zero -- means the default is an error. */ --static const yytype_uint8 yydefact[] = -+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ -+static const yytype_uint8 yyr2[] = - { -- 0, 0, 0, 3, 1, 0, 0, 0, 3, 34, -- 35, 0, 0, 6, 0, 2, 4, 0, 0, 0, -- 68, 0, 37, 38, 40, 42, 44, 46, 48, 50, -- 53, 60, 63, 67, 0, 13, 7, 0, 0, 0, -- 0, 69, 70, 71, 36, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 5, 75, 0, 0, 10, 8, -- 41, 0, 43, 45, 47, 49, 51, 52, 56, 57, -- 55, 54, 58, 59, 61, 62, 65, 64, 66, 0, -- 0, 0, 0, 14, 0, 75, 11, 9, 0, 0, -- 0, 16, 26, 78, 18, 80, 0, 77, 76, 39, -- 17, 79, 0, 0, 12, 25, 15, 27, 0, 19, -- 28, 22, 0, 72, 30, 0, 0, 0, 0, 33, -- 32, 20, 31, 29, 0, 73, 74, 21, 0, 24, -- 0, 0, 0, 23 -+ 0, 2, 5, 0, 2, 0, 2, 4, 2, 2, -+ 3, 4, 3, 4, 5, 0, 2, 4, 2, 3, -+ 2, 2, 3, 4, 2, 9, 5, 2, 0, 2, -+ 2, 3, 1, 2, 2, 2, 1, 1, 3, 1, -+ 1, 5, 1, 3, 1, 3, 1, 3, 1, 3, -+ 1, 3, 1, 3, 3, 1, 3, 3, 3, 3, -+ 3, 3, 1, 3, 3, 1, 3, 3, 3, 1, -+ 1, 2, 2, 2, 0, 2, 2, 0, 2, 2, -+ 2, 3, 2 - }; - -- /* YYPGOTO[NTERM-NUM]. */ --static const yytype_int8 yypgoto[] = -+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. -+ Performed when YYTABLE doesn't specify something else to do. Zero -+ means the default is an error. */ -+static const yytype_uint8 yydefact[] = - { -- -81, -81, 100, 104, -81, -38, -81, -80, -81, -81, -- -81, -5, 66, 13, -81, 70, 67, 81, 64, 82, -- 37, 27, 34, 38, -14, -81, 22, 24 -+ 0, 0, 0, 3, 1, 0, 5, 4, 0, 0, -+ 0, 5, 36, 37, 0, 0, 8, 0, 2, 6, -+ 0, 0, 0, 70, 0, 39, 40, 42, 44, 46, -+ 48, 50, 52, 55, 62, 65, 69, 0, 15, 9, -+ 0, 0, 0, 0, 71, 72, 73, 38, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 7, 77, 0, -+ 0, 12, 10, 43, 0, 45, 47, 49, 51, 53, -+ 54, 58, 59, 57, 56, 60, 61, 63, 64, 67, -+ 66, 68, 0, 0, 0, 0, 16, 0, 77, 13, -+ 11, 0, 0, 0, 18, 28, 80, 20, 82, 0, -+ 79, 78, 41, 19, 81, 0, 0, 14, 27, 17, -+ 29, 0, 21, 30, 24, 0, 74, 32, 0, 0, -+ 0, 0, 35, 34, 22, 33, 31, 0, 75, 76, -+ 23, 0, 26, 0, 0, 0, 25 - }; - -- /* YYDEFGOTO[NTERM-NUM]. */ -+/* YYDEFGOTO[NTERM-NUM]. */ - static const yytype_int16 yydefgoto[] = - { -- -1, 2, 7, 8, 15, 36, 65, 93, 112, 113, -- 125, 20, 21, 22, 23, 24, 25, 26, 27, 28, -- 29, 30, 31, 32, 33, 128, 94, 95 -+ -1, 2, 6, 10, 11, 18, 39, 68, 96, 115, -+ 116, 128, 23, 24, 25, 26, 27, 28, 29, 30, -+ 31, 32, 33, 34, 35, 36, 131, 97, 98 - }; - -- /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If -- positive, shift that token. If negative, reduce the rule whose -- number is the opposite. If YYTABLE_NINF, syntax error. */ --static const yytype_uint8 yytable[] = -+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing -+ STATE-NUM. */ -+#define YYPACT_NINF -84 -+static const yytype_int8 yypact[] = - { -- 12, 68, 69, 41, 42, 43, 45, 34, 9, 10, -- 53, 54, 104, 3, 5, 107, 101, 118, 35, 1, -- 102, 4, 61, 11, 119, 120, 121, 122, 35, 97, -- 46, 6, 55, 17, 123, 44, 18, 19, 56, 124, -- 62, 63, 9, 10, 14, 51, 52, 86, 87, 88, -- 9, 10, 48, 103, 129, 130, 115, 11, 135, 116, -- 136, 47, 131, 57, 58, 11, 37, 49, 117, 50, -- 137, 64, 38, 39, 138, 139, 40, 89, 90, 91, -- 78, 79, 80, 81, 92, 59, 60, 66, 76, 77, -- 67, 82, 83, 96, 98, 99, 100, 84, 85, 106, -- 110, 111, 114, 126, 134, 127, 133, 141, 16, 143, -- 13, 109, 71, 74, 72, 70, 105, 108, 0, 0, -- 132, 0, 0, 0, 0, 0, 0, 0, 0, 73, -- 0, 0, 75, 140, 0, 0, 142 -+ 15, -12, 35, 42, -84, 27, 9, -84, 24, 9, -+ 43, 9, -84, -84, -10, 24, -84, 60, 44, -84, -+ -10, -10, -10, -84, 55, -84, -7, 52, 53, 51, -+ 54, 10, 2, 38, 37, -4, -84, 68, -84, -84, -+ 71, 73, 60, 60, -84, -84, -84, -84, -10, -10, -+ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -+ -10, -10, -10, -10, -10, -10, -10, -84, 56, 72, -+ 60, -84, -84, 52, 61, 53, 51, 54, 10, 2, -+ 2, 38, 38, 38, 38, 37, 37, -4, -4, -84, -+ -84, -84, 81, 83, 34, 56, -84, 74, 56, -84, -+ -84, -10, 76, 78, -84, -84, -84, -84, -84, 79, -+ -84, -84, -84, -84, -84, -6, 3, -84, -84, -84, -+ -84, 87, -84, -84, -84, 75, -84, -84, 32, 70, -+ 86, 36, -84, -84, -84, -84, -84, 47, -84, -84, -+ -84, 24, -84, 77, 24, 80, -84 - }; - --static const yytype_int16 yycheck[] = -+/* YYPGOTO[NTERM-NUM]. */ -+static const yytype_int8 yypgoto[] = - { -- 5, 39, 40, 17, 18, 19, 12, 12, 17, 18, -- 7, 8, 92, 24, 4, 95, 24, 13, 26, 3, -- 28, 0, 25, 32, 20, 21, 22, 23, 26, 67, -- 36, 21, 29, 42, 30, 34, 45, 46, 35, 35, -- 43, 44, 17, 18, 25, 9, 10, 61, 62, 63, -- 17, 18, 38, 91, 21, 22, 21, 32, 19, 24, -- 21, 11, 29, 5, 6, 32, 15, 39, 33, 40, -- 31, 24, 21, 22, 33, 34, 25, 14, 15, 16, -- 53, 54, 55, 56, 21, 41, 42, 22, 51, 52, -- 22, 57, 58, 24, 37, 16, 16, 59, 60, 27, -- 24, 24, 24, 17, 20, 32, 35, 33, 8, 34, -- 6, 98, 46, 49, 47, 45, 92, 95, -1, -1, -- 125, -1, -1, -1, -1, -1, -1, -1, -1, 48, -- -1, -1, 50, 138, -1, -1, 141 -+ -84, -84, -84, 98, 101, -84, -41, -84, -83, -84, -+ -84, -84, -8, 63, 12, -84, 66, 67, 65, 69, -+ 82, 29, 18, 25, 26, -17, -84, 20, 28 - }; - -- /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing -- symbol of state STATE-NUM. */ --static const yytype_uint8 yystos[] = -+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If -+ positive, shift that token. If negative, reduce the rule which -+ number is the opposite. If YYTABLE_NINF, syntax error. */ -+#define YYTABLE_NINF -1 -+static const yytype_uint8 yytable[] = - { -- 0, 3, 48, 24, 0, 4, 21, 49, 50, 17, -- 18, 32, 58, 50, 25, 51, 49, 42, 45, 46, -- 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, -- 68, 69, 70, 71, 58, 26, 52, 15, 21, 22, -- 25, 71, 71, 71, 34, 12, 36, 11, 38, 39, -- 40, 9, 10, 7, 8, 29, 35, 5, 6, 41, -- 42, 25, 43, 44, 24, 53, 22, 22, 52, 52, -- 62, 59, 63, 64, 65, 66, 67, 67, 68, 68, -- 68, 68, 69, 69, 70, 70, 71, 71, 71, 14, -- 15, 16, 21, 54, 73, 74, 24, 52, 37, 16, -- 16, 24, 28, 52, 54, 74, 27, 54, 73, 60, -- 24, 24, 55, 56, 24, 21, 24, 33, 13, 20, -- 21, 22, 23, 30, 35, 57, 17, 32, 72, 21, -- 22, 29, 58, 35, 20, 19, 21, 31, 33, 34, -- 58, 33, 58, 34 -+ 15, 71, 72, 44, 45, 46, 48, 37, 12, 13, -+ 56, 57, 107, 3, 8, 110, 118, 121, 1, 119, -+ 54, 55, 64, 14, 122, 123, 124, 125, 120, 100, -+ 49, 9, 58, 20, 126, 4, 21, 22, 59, 127, -+ 65, 66, 12, 13, 60, 61, 5, 89, 90, 91, -+ 12, 13, 7, 106, 132, 133, 138, 14, 139, 104, -+ 40, 38, 134, 105, 50, 14, 41, 42, 140, 17, -+ 43, 92, 93, 94, 81, 82, 83, 84, 95, 62, -+ 63, 141, 142, 79, 80, 85, 86, 38, 87, 88, -+ 47, 52, 51, 67, 69, 53, 70, 99, 102, 101, -+ 103, 113, 109, 114, 117, 129, 136, 137, 130, 19, -+ 16, 144, 74, 112, 73, 146, 76, 75, 111, 0, -+ 135, 77, 0, 108, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 143, 0, 78, 145 - }; - -- /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ --static const yytype_uint8 yyr1[] = -+#define yypact_value_is_default(yystate) \ -+ ((yystate) == (-84)) -+ -+#define yytable_value_is_error(yytable_value) \ -+ YYID (0) -+ -+static const yytype_int16 yycheck[] = - { -- 0, 47, 48, 49, 49, 50, 50, 51, 51, 51, -- 51, 51, 52, 53, 53, 54, 54, 54, 54, 55, -- 55, 55, 55, 55, 55, 55, 56, 56, 56, 57, -- 57, 57, 57, 57, 58, 58, 58, 59, 60, 60, -- 61, 61, 62, 62, 63, 63, 64, 64, 65, 65, -- 66, 66, 66, 67, 67, 67, 67, 67, 68, 68, -- 68, 69, 69, 69, 70, 70, 70, 70, 71, 71, -- 71, 71, 72, 72, 72, 73, 73, 73, 74, 74, -- 74 -+ 8, 42, 43, 20, 21, 22, 13, 15, 18, 19, -+ 8, 9, 95, 25, 5, 98, 22, 14, 3, 25, -+ 10, 11, 26, 33, 21, 22, 23, 24, 34, 70, -+ 37, 22, 30, 43, 31, 0, 46, 47, 36, 36, -+ 44, 45, 18, 19, 6, 7, 4, 64, 65, 66, -+ 18, 19, 25, 94, 22, 23, 20, 33, 22, 25, -+ 16, 27, 30, 29, 12, 33, 22, 23, 32, 26, -+ 26, 15, 16, 17, 56, 57, 58, 59, 22, 42, -+ 43, 34, 35, 54, 55, 60, 61, 27, 62, 63, -+ 35, 40, 39, 25, 23, 41, 23, 25, 17, 38, -+ 17, 25, 28, 25, 25, 18, 36, 21, 33, 11, -+ 9, 34, 49, 101, 48, 35, 51, 50, 98, -1, -+ 128, 52, -1, 95, -1, -1, -1, -1, -1, -1, -+ -1, -1, -1, 141, -1, 53, 144 - }; - -- /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ --static const yytype_uint8 yyr2[] = -+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing -+ symbol of state STATE-NUM. */ -+static const yytype_uint8 yystos[] = - { -- 0, 2, 4, 0, 2, 4, 2, 2, 3, 4, -- 3, 4, 5, 0, 2, 4, 2, 3, 2, 2, -- 3, 4, 2, 9, 5, 2, 0, 2, 2, 3, -- 1, 2, 2, 2, 1, 1, 3, 1, 1, 5, -- 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, -- 1, 3, 3, 1, 3, 3, 3, 3, 3, 3, -- 1, 3, 3, 1, 3, 3, 3, 1, 1, 2, -- 2, 2, 0, 2, 2, 0, 2, 2, 2, 3, -- 2 -+ 0, 3, 49, 25, 0, 4, 50, 25, 5, 22, -+ 51, 52, 18, 19, 33, 60, 52, 26, 53, 51, -+ 43, 46, 47, 60, 61, 62, 63, 64, 65, 66, -+ 67, 68, 69, 70, 71, 72, 73, 60, 27, 54, -+ 16, 22, 23, 26, 73, 73, 73, 35, 13, 37, -+ 12, 39, 40, 41, 10, 11, 8, 9, 30, 36, -+ 6, 7, 42, 43, 26, 44, 45, 25, 55, 23, -+ 23, 54, 54, 64, 61, 65, 66, 67, 68, 69, -+ 69, 70, 70, 70, 70, 71, 71, 72, 72, 73, -+ 73, 73, 15, 16, 17, 22, 56, 75, 76, 25, -+ 54, 38, 17, 17, 25, 29, 54, 56, 76, 28, -+ 56, 75, 62, 25, 25, 57, 58, 25, 22, 25, -+ 34, 14, 21, 22, 23, 24, 31, 36, 59, 18, -+ 33, 74, 22, 23, 30, 60, 36, 21, 20, 22, -+ 32, 34, 35, 60, 34, 60, 35 - }; - -- --#define yyerrok (yyerrstatus = 0) --#define yyclearin (yychar = YYEMPTY) --#define YYEMPTY (-2) --#define YYEOF 0 -- --#define YYACCEPT goto yyacceptlab --#define YYABORT goto yyabortlab --#define YYERROR goto yyerrorlab -- -+#define yyerrok (yyerrstatus = 0) -+#define yyclearin (yychar = YYEMPTY) -+#define YYEMPTY (-2) -+#define YYEOF 0 -+ -+#define YYACCEPT goto yyacceptlab -+#define YYABORT goto yyabortlab -+#define YYERROR goto yyerrorlab -+ -+ -+/* Like YYERROR except do call yyerror. This remains here temporarily -+ to ease the transition to the new meaning of YYERROR, for GCC. -+ Once GCC version 2 has supplanted version 1, this can go. However, -+ YYFAIL appears to be in use. Nevertheless, it is formally deprecated -+ in Bison 2.4.2's NEWS entry, where a plan to phase it out is -+ discussed. */ -+ -+#define YYFAIL goto yyerrlab -+#if defined YYFAIL -+ /* This is here to suppress warnings from the GCC cpp's -+ -Wunused-macros. Normally we don't worry about that warning, but -+ some users do, and we want to make it easy for users to remove -+ YYFAIL uses, which will produce warnings from Bison 2.5. */ -+#endif - - #define YYRECOVERING() (!!yyerrstatus) - --#define YYBACKUP(Token, Value) \ --do \ -- if (yychar == YYEMPTY) \ -- { \ -- yychar = (Token); \ -- yylval = (Value); \ -- YYPOPSTACK (yylen); \ -- yystate = *yyssp; \ -- goto yybackup; \ -- } \ -- else \ -- { \ -+#define YYBACKUP(Token, Value) \ -+do \ -+ if (yychar == YYEMPTY && yylen == 1) \ -+ { \ -+ yychar = (Token); \ -+ yylval = (Value); \ -+ YYPOPSTACK (1); \ -+ goto yybackup; \ -+ } \ -+ else \ -+ { \ - yyerror (YY_("syntax error: cannot back up")); \ -- YYERROR; \ -- } \ --while (0) -- --/* Error token number */ --#define YYTERROR 1 --#define YYERRCODE 256 -+ YYERROR; \ -+ } \ -+while (YYID (0)) -+ -+ -+#define YYTERROR 1 -+#define YYERRCODE 256 - - - /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. - If N is 0, then set CURRENT to the empty location which ends - the previous symbol: RHS[0] (always defined). */ - -+#define YYRHSLOC(Rhs, K) ((Rhs)[K]) - #ifndef YYLLOC_DEFAULT --# define YYLLOC_DEFAULT(Current, Rhs, N) \ -- do \ -- if (N) \ -- { \ -- (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ -- (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ -- (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ -- (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ -- } \ -- else \ -- { \ -- (Current).first_line = (Current).last_line = \ -- YYRHSLOC (Rhs, 0).last_line; \ -- (Current).first_column = (Current).last_column = \ -- YYRHSLOC (Rhs, 0).last_column; \ -- } \ -- while (0) -+# define YYLLOC_DEFAULT(Current, Rhs, N) \ -+ do \ -+ if (YYID (N)) \ -+ { \ -+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ -+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ -+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ -+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ -+ } \ -+ else \ -+ { \ -+ (Current).first_line = (Current).last_line = \ -+ YYRHSLOC (Rhs, 0).last_line; \ -+ (Current).first_column = (Current).last_column = \ -+ YYRHSLOC (Rhs, 0).last_column; \ -+ } \ -+ while (YYID (0)) - #endif - --#define YYRHSLOC(Rhs, K) ((Rhs)[K]) -- -- --/* Enable debugging if requested. */ --#if YYDEBUG -- --# ifndef YYFPRINTF --# include /* INFRINGES ON USER NAME SPACE */ --# define YYFPRINTF fprintf --# endif -- --# define YYDPRINTF(Args) \ --do { \ -- if (yydebug) \ -- YYFPRINTF Args; \ --} while (0) -- - - /* YY_LOCATION_PRINT -- Print the location on the stream. - This macro was not mandated originally: define only if we know -@@ -779,73 +799,82 @@ do { - - #ifndef YY_LOCATION_PRINT - # if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL -- --/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ -- --YY_ATTRIBUTE_UNUSED --static unsigned --yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) --{ -- unsigned res = 0; -- int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; -- if (0 <= yylocp->first_line) -- { -- res += YYFPRINTF (yyo, "%d", yylocp->first_line); -- if (0 <= yylocp->first_column) -- res += YYFPRINTF (yyo, ".%d", yylocp->first_column); -- } -- if (0 <= yylocp->last_line) -- { -- if (yylocp->first_line < yylocp->last_line) -- { -- res += YYFPRINTF (yyo, "-%d", yylocp->last_line); -- if (0 <= end_col) -- res += YYFPRINTF (yyo, ".%d", end_col); -- } -- else if (0 <= end_col && yylocp->first_column < end_col) -- res += YYFPRINTF (yyo, "-%d", end_col); -- } -- return res; -- } -- --# define YY_LOCATION_PRINT(File, Loc) \ -- yy_location_print_ (File, &(Loc)) -- -+# define YY_LOCATION_PRINT(File, Loc) \ -+ fprintf (File, "%d.%d-%d.%d", \ -+ (Loc).first_line, (Loc).first_column, \ -+ (Loc).last_line, (Loc).last_column) - # else - # define YY_LOCATION_PRINT(File, Loc) ((void) 0) - # endif - #endif - - --# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ --do { \ -- if (yydebug) \ -- { \ -- YYFPRINTF (stderr, "%s ", Title); \ -- yy_symbol_print (stderr, \ -- Type, Value, Location); \ -- YYFPRINTF (stderr, "\n"); \ -- } \ --} while (0) -+/* YYLEX -- calling `yylex' with the right arguments. */ - -+#ifdef YYLEX_PARAM -+# define YYLEX yylex (YYLEX_PARAM) -+#else -+# define YYLEX yylex () -+#endif - --/*----------------------------------------. --| Print this symbol's value on YYOUTPUT. | --`----------------------------------------*/ -+/* Enable debugging if requested. */ -+#if YYDEBUG - -+# ifndef YYFPRINTF -+# include /* INFRINGES ON USER NAME SPACE */ -+# define YYFPRINTF fprintf -+# endif -+ -+# define YYDPRINTF(Args) \ -+do { \ -+ if (yydebug) \ -+ YYFPRINTF Args; \ -+} while (YYID (0)) -+ -+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ -+do { \ -+ if (yydebug) \ -+ { \ -+ YYFPRINTF (stderr, "%s ", Title); \ -+ yy_symbol_print (stderr, \ -+ Type, Value, Location); \ -+ YYFPRINTF (stderr, "\n"); \ -+ } \ -+} while (YYID (0)) -+ -+ -+/*--------------------------------. -+| Print this symbol on YYOUTPUT. | -+`--------------------------------*/ -+ -+/*ARGSUSED*/ -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) - static void - yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp) -+#else -+static void -+yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp) -+ FILE *yyoutput; -+ int yytype; -+ YYSTYPE const * const yyvaluep; -+ YYLTYPE const * const yylocationp; -+#endif - { -- FILE *yyo = yyoutput; -- YYUSE (yyo); -- YYUSE (yylocationp); - if (!yyvaluep) - return; -+ YYUSE (yylocationp); - # ifdef YYPRINT - if (yytype < YYNTOKENS) - YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); -+# else -+ YYUSE (yyoutput); - # endif -- YYUSE (yytype); -+ switch (yytype) -+ { -+ default: -+ break; -+ } - } - - -@@ -853,11 +882,23 @@ yy_symbol_value_print (FILE *yyoutput, i - | Print this symbol on YYOUTPUT. | - `--------------------------------*/ - -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) - static void - yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp) -+#else -+static void -+yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp) -+ FILE *yyoutput; -+ int yytype; -+ YYSTYPE const * const yyvaluep; -+ YYLTYPE const * const yylocationp; -+#endif - { -- YYFPRINTF (yyoutput, "%s %s (", -- yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); -+ if (yytype < YYNTOKENS) -+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); -+ else -+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); - - YY_LOCATION_PRINT (yyoutput, *yylocationp); - YYFPRINTF (yyoutput, ": "); -@@ -870,8 +911,16 @@ yy_symbol_print (FILE *yyoutput, int yyt - | TOP (included). | - `------------------------------------------------------------------*/ - -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) - static void - yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) -+#else -+static void -+yy_stack_print (yybottom, yytop) -+ yytype_int16 *yybottom; -+ yytype_int16 *yytop; -+#endif - { - YYFPRINTF (stderr, "Stack now"); - for (; yybottom <= yytop; yybottom++) -@@ -882,42 +931,50 @@ yy_stack_print (yytype_int16 *yybottom, - YYFPRINTF (stderr, "\n"); - } - --# define YY_STACK_PRINT(Bottom, Top) \ --do { \ -- if (yydebug) \ -- yy_stack_print ((Bottom), (Top)); \ --} while (0) -+# define YY_STACK_PRINT(Bottom, Top) \ -+do { \ -+ if (yydebug) \ -+ yy_stack_print ((Bottom), (Top)); \ -+} while (YYID (0)) - - - /*------------------------------------------------. - | Report that the YYRULE is going to be reduced. | - `------------------------------------------------*/ - -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) -+static void -+yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule) -+#else - static void --yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule) -+yy_reduce_print (yyvsp, yylsp, yyrule) -+ YYSTYPE *yyvsp; -+ YYLTYPE *yylsp; -+ int yyrule; -+#endif - { -- unsigned long int yylno = yyrline[yyrule]; - int yynrhs = yyr2[yyrule]; - int yyi; -+ unsigned long int yylno = yyrline[yyrule]; - YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", -- yyrule - 1, yylno); -+ yyrule - 1, yylno); - /* The symbols being reduced. */ - for (yyi = 0; yyi < yynrhs; yyi++) - { - YYFPRINTF (stderr, " $%d = ", yyi + 1); -- yy_symbol_print (stderr, -- yystos[yyssp[yyi + 1 - yynrhs]], -- &(yyvsp[(yyi + 1) - (yynrhs)]) -- , &(yylsp[(yyi + 1) - (yynrhs)]) ); -+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], -+ &(yyvsp[(yyi + 1) - (yynrhs)]) -+ , &(yylsp[(yyi + 1) - (yynrhs)]) ); - YYFPRINTF (stderr, "\n"); - } - } - --# define YY_REDUCE_PRINT(Rule) \ --do { \ -- if (yydebug) \ -- yy_reduce_print (yyssp, yyvsp, yylsp, Rule); \ --} while (0) -+# define YY_REDUCE_PRINT(Rule) \ -+do { \ -+ if (yydebug) \ -+ yy_reduce_print (yyvsp, yylsp, Rule); \ -+} while (YYID (0)) - - /* Nonzero means print parse trace. It is left uninitialized so that - multiple parsers can coexist. */ -@@ -931,7 +988,7 @@ int yydebug; - - - /* YYINITDEPTH -- initial size of the parser's stacks. */ --#ifndef YYINITDEPTH -+#ifndef YYINITDEPTH - # define YYINITDEPTH 200 - #endif - -@@ -954,8 +1011,15 @@ int yydebug; - # define yystrlen strlen - # else - /* Return the length of YYSTR. */ -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) - static YYSIZE_T - yystrlen (const char *yystr) -+#else -+static YYSIZE_T -+yystrlen (yystr) -+ const char *yystr; -+#endif - { - YYSIZE_T yylen; - for (yylen = 0; yystr[yylen]; yylen++) -@@ -971,8 +1035,16 @@ yystrlen (const char *yystr) - # else - /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in - YYDEST. */ -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) - static char * - yystpcpy (char *yydest, const char *yysrc) -+#else -+static char * -+yystpcpy (yydest, yysrc) -+ char *yydest; -+ const char *yysrc; -+#endif - { - char *yyd = yydest; - const char *yys = yysrc; -@@ -1002,27 +1074,27 @@ yytnamerr (char *yyres, const char *yyst - char const *yyp = yystr; - - for (;;) -- switch (*++yyp) -- { -- case '\'': -- case ',': -- goto do_not_strip_quotes; -- -- case '\\': -- if (*++yyp != '\\') -- goto do_not_strip_quotes; -- /* Fall through. */ -- default: -- if (yyres) -- yyres[yyn] = *yyp; -- yyn++; -- break; -- -- case '"': -- if (yyres) -- yyres[yyn] = '\0'; -- return yyn; -- } -+ switch (*++yyp) -+ { -+ case '\'': -+ case ',': -+ goto do_not_strip_quotes; -+ -+ case '\\': -+ if (*++yyp != '\\') -+ goto do_not_strip_quotes; -+ /* Fall through. */ -+ default: -+ if (yyres) -+ yyres[yyn] = *yyp; -+ yyn++; -+ break; -+ -+ case '"': -+ if (yyres) -+ yyres[yyn] = '\0'; -+ return yyn; -+ } - do_not_strip_quotes: ; - } - -@@ -1045,11 +1117,12 @@ static int - yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, - yytype_int16 *yyssp, int yytoken) - { -- YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); -+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]); - YYSIZE_T yysize = yysize0; -+ YYSIZE_T yysize1; - enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; - /* Internationalized format string. */ -- const char *yyformat = YY_NULLPTR; -+ const char *yyformat = 0; - /* Arguments of yyformat. */ - char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - /* Number of reported tokens (one for the "unexpected", one per -@@ -1057,6 +1130,10 @@ yysyntax_error (YYSIZE_T *yymsg_alloc, c - int yycount = 0; - - /* There are many possibilities here to consider: -+ - Assume YYFAIL is not used. It's too flawed to consider. See -+ -+ for details. YYERROR is fine as it does not invoke this -+ function. - - If this state is a consistent state with a default action, then - the only way this function was invoked is if the default action - is an error action. In that case, don't check for expected -@@ -1105,13 +1182,11 @@ yysyntax_error (YYSIZE_T *yymsg_alloc, c - break; - } - yyarg[yycount++] = yytname[yyx]; -- { -- YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); -- if (! (yysize <= yysize1 -- && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) -- return 2; -- yysize = yysize1; -- } -+ yysize1 = yysize + yytnamerr (0, yytname[yyx]); -+ if (! (yysize <= yysize1 -+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) -+ return 2; -+ yysize = yysize1; - } - } - } -@@ -1131,12 +1206,10 @@ yysyntax_error (YYSIZE_T *yymsg_alloc, c - # undef YYCASE_ - } - -- { -- YYSIZE_T yysize1 = yysize + yystrlen (yyformat); -- if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) -- return 2; -- yysize = yysize1; -- } -+ yysize1 = yysize + yystrlen (yyformat); -+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) -+ return 2; -+ yysize = yysize1; - - if (*yymsg_alloc < yysize) - { -@@ -1173,21 +1246,50 @@ yysyntax_error (YYSIZE_T *yymsg_alloc, c - | Release the memory associated to this symbol. | - `-----------------------------------------------*/ - -+/*ARGSUSED*/ -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) - static void - yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp) -+#else -+static void -+yydestruct (yymsg, yytype, yyvaluep, yylocationp) -+ const char *yymsg; -+ int yytype; -+ YYSTYPE *yyvaluep; -+ YYLTYPE *yylocationp; -+#endif - { - YYUSE (yyvaluep); - YYUSE (yylocationp); -+ - if (!yymsg) - yymsg = "Deleting"; - YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); - -- YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN -- YYUSE (yytype); -- YY_IGNORE_MAYBE_UNINITIALIZED_END -+ switch (yytype) -+ { -+ -+ default: -+ break; -+ } - } - - -+/* Prevent warnings from -Wmissing-prototypes. */ -+#ifdef YYPARSE_PARAM -+#if defined __STDC__ || defined __cplusplus -+int yyparse (void *YYPARSE_PARAM); -+#else -+int yyparse (); -+#endif -+#else /* ! YYPARSE_PARAM */ -+#if defined __STDC__ || defined __cplusplus -+int yyparse (void); -+#else -+int yyparse (); -+#endif -+#endif /* ! YYPARSE_PARAM */ - - - /* The lookahead symbol. */ -@@ -1195,12 +1297,10 @@ int yychar; - - /* The semantic value of the lookahead symbol. */ - YYSTYPE yylval; -+ - /* Location data for the lookahead symbol. */ --YYLTYPE yylloc --# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL -- = { 1, 1, 1, 1 } --# endif --; -+YYLTYPE yylloc; -+ - /* Number of syntax errors so far. */ - int yynerrs; - -@@ -1209,19 +1309,38 @@ int yynerrs; - | yyparse. | - `----------*/ - -+#ifdef YYPARSE_PARAM -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) -+int -+yyparse (void *YYPARSE_PARAM) -+#else -+int -+yyparse (YYPARSE_PARAM) -+ void *YYPARSE_PARAM; -+#endif -+#else /* ! YYPARSE_PARAM */ -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) - int - yyparse (void) -+#else -+int -+yyparse () -+ -+#endif -+#endif - { - int yystate; - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus; - - /* The stacks and their tools: -- 'yyss': related to states. -- 'yyvs': related to semantic values. -- 'yyls': related to locations. -+ `yyss': related to states. -+ `yyvs': related to semantic values. -+ `yyls': related to locations. - -- Refer to the stacks through separate pointers, to allow yyoverflow -+ Refer to the stacks thru separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ - - /* The state stack. */ -@@ -1247,7 +1366,7 @@ yyparse (void) - int yyn; - int yyresult; - /* Lookahead token as an internal (translated) token number. */ -- int yytoken = 0; -+ int yytoken; - /* The variables used to return semantic value and location from the - action routines. */ - YYSTYPE yyval; -@@ -1266,9 +1385,10 @@ yyparse (void) - Keep to zero when no symbol should be popped. */ - int yylen = 0; - -- yyssp = yyss = yyssa; -- yyvsp = yyvs = yyvsa; -- yylsp = yyls = yylsa; -+ yytoken = 0; -+ yyss = yyssa; -+ yyvs = yyvsa; -+ yyls = yylsa; - yystacksize = YYINITDEPTH; - - YYDPRINTF ((stderr, "Starting parse\n")); -@@ -1277,7 +1397,21 @@ yyparse (void) - yyerrstatus = 0; - yynerrs = 0; - yychar = YYEMPTY; /* Cause a token to be read. */ -- yylsp[0] = yylloc; -+ -+ /* Initialize stack pointers. -+ Waste one element of value and location stack -+ so that they stay on the same level as the state stack. -+ The wasted elements are never initialized. */ -+ yyssp = yyss; -+ yyvsp = yyvs; -+ yylsp = yyls; -+ -+#if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL -+ /* Initialize the default location before parsing starts. */ -+ yylloc.first_line = yylloc.last_line = 1; -+ yylloc.first_column = yylloc.last_column = 1; -+#endif -+ - goto yysetstate; - - /*------------------------------------------------------------. -@@ -1298,26 +1432,26 @@ yyparse (void) - - #ifdef yyoverflow - { -- /* Give user a chance to reallocate the stack. Use copies of -- these so that the &'s don't force the real ones into -- memory. */ -- YYSTYPE *yyvs1 = yyvs; -- yytype_int16 *yyss1 = yyss; -- YYLTYPE *yyls1 = yyls; -- -- /* Each stack pointer address is followed by the size of the -- data in use in that stack, in bytes. This used to be a -- conditional around just the two extra args, but that might -- be undefined if yyoverflow is a macro. */ -- yyoverflow (YY_("memory exhausted"), -- &yyss1, yysize * sizeof (*yyssp), -- &yyvs1, yysize * sizeof (*yyvsp), -- &yyls1, yysize * sizeof (*yylsp), -- &yystacksize); -- -- yyls = yyls1; -- yyss = yyss1; -- yyvs = yyvs1; -+ /* Give user a chance to reallocate the stack. Use copies of -+ these so that the &'s don't force the real ones into -+ memory. */ -+ YYSTYPE *yyvs1 = yyvs; -+ yytype_int16 *yyss1 = yyss; -+ YYLTYPE *yyls1 = yyls; -+ -+ /* Each stack pointer address is followed by the size of the -+ data in use in that stack, in bytes. This used to be a -+ conditional around just the two extra args, but that might -+ be undefined if yyoverflow is a macro. */ -+ yyoverflow (YY_("memory exhausted"), -+ &yyss1, yysize * sizeof (*yyssp), -+ &yyvs1, yysize * sizeof (*yyvsp), -+ &yyls1, yysize * sizeof (*yylsp), -+ &yystacksize); -+ -+ yyls = yyls1; -+ yyss = yyss1; -+ yyvs = yyvs1; - } - #else /* no yyoverflow */ - # ifndef YYSTACK_RELOCATE -@@ -1325,23 +1459,23 @@ yyparse (void) - # else - /* Extend the stack our own way. */ - if (YYMAXDEPTH <= yystacksize) -- goto yyexhaustedlab; -+ goto yyexhaustedlab; - yystacksize *= 2; - if (YYMAXDEPTH < yystacksize) -- yystacksize = YYMAXDEPTH; -+ yystacksize = YYMAXDEPTH; - - { -- yytype_int16 *yyss1 = yyss; -- union yyalloc *yyptr = -- (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); -- if (! yyptr) -- goto yyexhaustedlab; -- YYSTACK_RELOCATE (yyss_alloc, yyss); -- YYSTACK_RELOCATE (yyvs_alloc, yyvs); -- YYSTACK_RELOCATE (yyls_alloc, yyls); -+ yytype_int16 *yyss1 = yyss; -+ union yyalloc *yyptr = -+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); -+ if (! yyptr) -+ goto yyexhaustedlab; -+ YYSTACK_RELOCATE (yyss_alloc, yyss); -+ YYSTACK_RELOCATE (yyvs_alloc, yyvs); -+ YYSTACK_RELOCATE (yyls_alloc, yyls); - # undef YYSTACK_RELOCATE -- if (yyss1 != yyssa) -- YYSTACK_FREE (yyss1); -+ if (yyss1 != yyssa) -+ YYSTACK_FREE (yyss1); - } - # endif - #endif /* no yyoverflow */ -@@ -1351,10 +1485,10 @@ yyparse (void) - yylsp = yyls + yysize - 1; - - YYDPRINTF ((stderr, "Stack size increased to %lu\n", -- (unsigned long int) yystacksize)); -+ (unsigned long int) yystacksize)); - - if (yyss + yystacksize - 1 <= yyssp) -- YYABORT; -+ YYABORT; - } - - YYDPRINTF ((stderr, "Entering state %d\n", yystate)); -@@ -1383,7 +1517,7 @@ yybackup: - if (yychar == YYEMPTY) - { - YYDPRINTF ((stderr, "Reading a token: ")); -- yychar = yylex (); -+ yychar = YYLEX; - } - - if (yychar <= YYEOF) -@@ -1423,9 +1557,7 @@ yybackup: - yychar = YYEMPTY; - - yystate = yyn; -- YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - *++yyvsp = yylval; -- YY_IGNORE_MAYBE_UNINITIALIZED_END - *++yylsp = yylloc; - goto yynewstate; - -@@ -1448,7 +1580,7 @@ yyreduce: - yylen = yyr2[yyn]; - - /* If YYLEN is nonzero, implement the default value of the action: -- '$$ = $1'. -+ `$$ = $1'. - - Otherwise, the following line sets YYVAL to garbage. - This behavior is undocumented and Bison -@@ -1463,273 +1595,322 @@ yyreduce: - switch (yyn) - { - case 2: --#line 105 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 109 "dtc-parser.y" - { -- the_boot_info = build_boot_info((yyvsp[-1].re), (yyvsp[0].node), -- guess_boot_cpuid((yyvsp[0].node))); -+ (yyvsp[(5) - (5)].node)->is_plugin = (yyvsp[(3) - (5)].is_plugin); -+ (yyvsp[(5) - (5)].node)->is_root = 1; -+ the_boot_info = build_boot_info((yyvsp[(4) - (5)].re), (yyvsp[(5) - (5)].node), -+ guess_boot_cpuid((yyvsp[(5) - (5)].node))); - } --#line 1472 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 3: --#line 113 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 119 "dtc-parser.y" - { -- (yyval.re) = NULL; -+ (yyval.is_plugin) = 0; - } --#line 1480 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 4: --#line 117 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 123 "dtc-parser.y" - { -- (yyval.re) = chain_reserve_entry((yyvsp[-1].re), (yyvsp[0].re)); -+ (yyval.is_plugin) = 1; - } --#line 1488 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 5: --#line 124 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 130 "dtc-parser.y" - { -- (yyval.re) = build_reserve_entry((yyvsp[-2].integer), (yyvsp[-1].integer)); -+ (yyval.re) = NULL; - } --#line 1496 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 6: --#line 128 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 134 "dtc-parser.y" - { -- add_label(&(yyvsp[0].re)->labels, (yyvsp[-1].labelref)); -- (yyval.re) = (yyvsp[0].re); -+ (yyval.re) = chain_reserve_entry((yyvsp[(1) - (2)].re), (yyvsp[(2) - (2)].re)); - } --#line 1505 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 7: --#line 136 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 141 "dtc-parser.y" - { -- (yyval.node) = name_node((yyvsp[0].node), ""); -+ (yyval.re) = build_reserve_entry((yyvsp[(2) - (4)].integer), (yyvsp[(3) - (4)].integer)); - } --#line 1513 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 8: --#line 140 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 145 "dtc-parser.y" - { -- (yyval.node) = merge_nodes((yyvsp[-2].node), (yyvsp[0].node)); -+ add_label(&(yyvsp[(2) - (2)].re)->labels, (yyvsp[(1) - (2)].labelref)); -+ (yyval.re) = (yyvsp[(2) - (2)].re); - } --#line 1521 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 9: --#line 145 "dtc-parser.y" /* yacc.c:1646 */ -- { -- struct node *target = get_node_by_ref((yyvsp[-3].node), (yyvsp[-1].labelref)); - -- add_label(&target->labels, (yyvsp[-2].labelref)); -- if (target) -- merge_nodes(target, (yyvsp[0].node)); -- else -- ERROR(&(yylsp[-1]), "Label or path %s not found", (yyvsp[-1].labelref)); -- (yyval.node) = (yyvsp[-3].node); -+/* Line 1806 of yacc.c */ -+#line 153 "dtc-parser.y" -+ { -+ (yyval.node) = name_node((yyvsp[(2) - (2)].node), ""); - } --#line 1536 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 10: --#line 156 "dtc-parser.y" /* yacc.c:1646 */ -- { -- struct node *target = get_node_by_ref((yyvsp[-2].node), (yyvsp[-1].labelref)); - -- if (target) -- merge_nodes(target, (yyvsp[0].node)); -- else -- ERROR(&(yylsp[-1]), "Label or path %s not found", (yyvsp[-1].labelref)); -- (yyval.node) = (yyvsp[-2].node); -+/* Line 1806 of yacc.c */ -+#line 157 "dtc-parser.y" -+ { -+ (yyval.node) = merge_nodes((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); - } --#line 1550 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 11: --#line 166 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 162 "dtc-parser.y" - { -- struct node *target = get_node_by_ref((yyvsp[-3].node), (yyvsp[-1].labelref)); -+ struct node *target = get_node_by_ref((yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].labelref)); - -+ add_label(&target->labels, (yyvsp[(2) - (4)].labelref)); - if (target) -- delete_node(target); -+ merge_nodes(target, (yyvsp[(4) - (4)].node)); - else -- ERROR(&(yylsp[-1]), "Label or path %s not found", (yyvsp[-1].labelref)); -- -- -- (yyval.node) = (yyvsp[-3].node); -+ ERROR(&(yylsp[(3) - (4)]), "Label or path %s not found", (yyvsp[(3) - (4)].labelref)); -+ (yyval.node) = (yyvsp[(1) - (4)].node); - } --#line 1566 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 12: --#line 181 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 173 "dtc-parser.y" - { -- (yyval.node) = build_node((yyvsp[-3].proplist), (yyvsp[-2].nodelist)); -+ struct node *target = get_node_by_ref((yyvsp[(1) - (3)].node), (yyvsp[(2) - (3)].labelref)); -+ -+ if (target) -+ merge_nodes(target, (yyvsp[(3) - (3)].node)); -+ else -+ ERROR(&(yylsp[(2) - (3)]), "Label or path %s not found", (yyvsp[(2) - (3)].labelref)); -+ (yyval.node) = (yyvsp[(1) - (3)].node); - } --#line 1574 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 13: --#line 188 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 183 "dtc-parser.y" - { -- (yyval.proplist) = NULL; -+ struct node *target = get_node_by_ref((yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].labelref)); -+ -+ if (target) -+ delete_node(target); -+ else -+ ERROR(&(yylsp[(3) - (4)]), "Label or path %s not found", (yyvsp[(3) - (4)].labelref)); -+ -+ -+ (yyval.node) = (yyvsp[(1) - (4)].node); - } --#line 1582 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 14: --#line 192 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 198 "dtc-parser.y" - { -- (yyval.proplist) = chain_property((yyvsp[0].prop), (yyvsp[-1].proplist)); -+ (yyval.node) = build_node((yyvsp[(2) - (5)].proplist), (yyvsp[(3) - (5)].nodelist)); - } --#line 1590 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 15: --#line 199 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 205 "dtc-parser.y" - { -- (yyval.prop) = build_property((yyvsp[-3].propnodename), (yyvsp[-1].data)); -+ (yyval.proplist) = NULL; - } --#line 1598 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 16: --#line 203 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 209 "dtc-parser.y" - { -- (yyval.prop) = build_property((yyvsp[-1].propnodename), empty_data); -+ (yyval.proplist) = chain_property((yyvsp[(2) - (2)].prop), (yyvsp[(1) - (2)].proplist)); - } --#line 1606 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 17: --#line 207 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 216 "dtc-parser.y" - { -- (yyval.prop) = build_property_delete((yyvsp[-1].propnodename)); -+ (yyval.prop) = build_property((yyvsp[(1) - (4)].propnodename), (yyvsp[(3) - (4)].data)); - } --#line 1614 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 18: --#line 211 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 220 "dtc-parser.y" - { -- add_label(&(yyvsp[0].prop)->labels, (yyvsp[-1].labelref)); -- (yyval.prop) = (yyvsp[0].prop); -+ (yyval.prop) = build_property((yyvsp[(1) - (2)].propnodename), empty_data); - } --#line 1623 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 19: --#line 219 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 224 "dtc-parser.y" - { -- (yyval.data) = data_merge((yyvsp[-1].data), (yyvsp[0].data)); -+ (yyval.prop) = build_property_delete((yyvsp[(2) - (3)].propnodename)); - } --#line 1631 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 20: --#line 223 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 228 "dtc-parser.y" - { -- (yyval.data) = data_merge((yyvsp[-2].data), (yyvsp[-1].array).data); -+ add_label(&(yyvsp[(2) - (2)].prop)->labels, (yyvsp[(1) - (2)].labelref)); -+ (yyval.prop) = (yyvsp[(2) - (2)].prop); - } --#line 1639 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 21: --#line 227 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 236 "dtc-parser.y" - { -- (yyval.data) = data_merge((yyvsp[-3].data), (yyvsp[-1].data)); -+ (yyval.data) = data_merge((yyvsp[(1) - (2)].data), (yyvsp[(2) - (2)].data)); - } --#line 1647 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 22: --#line 231 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 240 "dtc-parser.y" - { -- (yyval.data) = data_add_marker((yyvsp[-1].data), REF_PATH, (yyvsp[0].labelref)); -+ (yyval.data) = data_merge((yyvsp[(1) - (3)].data), (yyvsp[(2) - (3)].array).data); - } --#line 1655 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 23: --#line 235 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 244 "dtc-parser.y" -+ { -+ (yyval.data) = data_merge((yyvsp[(1) - (4)].data), (yyvsp[(3) - (4)].data)); -+ } -+ break; -+ -+ case 24: -+ -+/* Line 1806 of yacc.c */ -+#line 248 "dtc-parser.y" -+ { -+ (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), REF_PATH, (yyvsp[(2) - (2)].labelref)); -+ } -+ break; -+ -+ case 25: -+ -+/* Line 1806 of yacc.c */ -+#line 252 "dtc-parser.y" - { -- FILE *f = srcfile_relative_open((yyvsp[-5].data).val, NULL); -+ FILE *f = srcfile_relative_open((yyvsp[(4) - (9)].data).val, NULL); - struct data d; - -- if ((yyvsp[-3].integer) != 0) -- if (fseek(f, (yyvsp[-3].integer), SEEK_SET) != 0) -+ if ((yyvsp[(6) - (9)].integer) != 0) -+ if (fseek(f, (yyvsp[(6) - (9)].integer), SEEK_SET) != 0) - die("Couldn't seek to offset %llu in \"%s\": %s", -- (unsigned long long)(yyvsp[-3].integer), (yyvsp[-5].data).val, -+ (unsigned long long)(yyvsp[(6) - (9)].integer), (yyvsp[(4) - (9)].data).val, - strerror(errno)); - -- d = data_copy_file(f, (yyvsp[-1].integer)); -+ d = data_copy_file(f, (yyvsp[(8) - (9)].integer)); - -- (yyval.data) = data_merge((yyvsp[-8].data), d); -+ (yyval.data) = data_merge((yyvsp[(1) - (9)].data), d); - fclose(f); - } --#line 1675 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - -- case 24: --#line 251 "dtc-parser.y" /* yacc.c:1646 */ -+ case 26: -+ -+/* Line 1806 of yacc.c */ -+#line 268 "dtc-parser.y" - { -- FILE *f = srcfile_relative_open((yyvsp[-1].data).val, NULL); -+ FILE *f = srcfile_relative_open((yyvsp[(4) - (5)].data).val, NULL); - struct data d = empty_data; - - d = data_copy_file(f, -1); - -- (yyval.data) = data_merge((yyvsp[-4].data), d); -+ (yyval.data) = data_merge((yyvsp[(1) - (5)].data), d); - fclose(f); - } --#line 1689 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - -- case 25: --#line 261 "dtc-parser.y" /* yacc.c:1646 */ -+ case 27: -+ -+/* Line 1806 of yacc.c */ -+#line 278 "dtc-parser.y" - { -- (yyval.data) = data_add_marker((yyvsp[-1].data), LABEL, (yyvsp[0].labelref)); -+ (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref)); - } --#line 1697 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - -- case 26: --#line 268 "dtc-parser.y" /* yacc.c:1646 */ -+ case 28: -+ -+/* Line 1806 of yacc.c */ -+#line 285 "dtc-parser.y" - { - (yyval.data) = empty_data; - } --#line 1705 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - -- case 27: --#line 272 "dtc-parser.y" /* yacc.c:1646 */ -+ case 29: -+ -+/* Line 1806 of yacc.c */ -+#line 289 "dtc-parser.y" - { -- (yyval.data) = (yyvsp[-1].data); -+ (yyval.data) = (yyvsp[(1) - (2)].data); - } --#line 1713 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - -- case 28: --#line 276 "dtc-parser.y" /* yacc.c:1646 */ -+ case 30: -+ -+/* Line 1806 of yacc.c */ -+#line 293 "dtc-parser.y" - { -- (yyval.data) = data_add_marker((yyvsp[-1].data), LABEL, (yyvsp[0].labelref)); -+ (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref)); - } --#line 1721 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - -- case 29: --#line 283 "dtc-parser.y" /* yacc.c:1646 */ -+ case 31: -+ -+/* Line 1806 of yacc.c */ -+#line 300 "dtc-parser.y" - { - unsigned long long bits; - -- bits = (yyvsp[-1].integer); -+ bits = (yyvsp[(2) - (3)].integer); - - if ((bits != 8) && (bits != 16) && - (bits != 32) && (bits != 64)) { -- ERROR(&(yylsp[-1]), "Array elements must be" -+ ERROR(&(yylsp[(2) - (3)]), "Array elements must be" - " 8, 16, 32 or 64-bits"); - bits = 32; - } -@@ -1737,23 +1918,25 @@ yyreduce: - (yyval.array).data = empty_data; - (yyval.array).bits = bits; - } --#line 1741 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - -- case 30: --#line 299 "dtc-parser.y" /* yacc.c:1646 */ -+ case 32: -+ -+/* Line 1806 of yacc.c */ -+#line 316 "dtc-parser.y" - { - (yyval.array).data = empty_data; - (yyval.array).bits = 32; - } --#line 1750 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - -- case 31: --#line 304 "dtc-parser.y" /* yacc.c:1646 */ -+ case 33: -+ -+/* Line 1806 of yacc.c */ -+#line 321 "dtc-parser.y" - { -- if ((yyvsp[-1].array).bits < 64) { -- uint64_t mask = (1ULL << (yyvsp[-1].array).bits) - 1; -+ if ((yyvsp[(1) - (2)].array).bits < 64) { -+ uint64_t mask = (1ULL << (yyvsp[(1) - (2)].array).bits) - 1; - /* - * Bits above mask must either be all zero - * (positive within range of mask) or all one -@@ -1762,258 +1945,293 @@ yyreduce: - * within the mask to one (i.e. | in the - * mask), all bits are one. - */ -- if (((yyvsp[0].integer) > mask) && (((yyvsp[0].integer) | mask) != -1ULL)) -- ERROR(&(yylsp[0]), "Value out of range for" -- " %d-bit array element", (yyvsp[-1].array).bits); -+ if (((yyvsp[(2) - (2)].integer) > mask) && (((yyvsp[(2) - (2)].integer) | mask) != -1ULL)) -+ ERROR(&(yylsp[(2) - (2)]), "Value out of range for" -+ " %d-bit array element", (yyvsp[(1) - (2)].array).bits); - } - -- (yyval.array).data = data_append_integer((yyvsp[-1].array).data, (yyvsp[0].integer), (yyvsp[-1].array).bits); -+ (yyval.array).data = data_append_integer((yyvsp[(1) - (2)].array).data, (yyvsp[(2) - (2)].integer), (yyvsp[(1) - (2)].array).bits); - } --#line 1773 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - -- case 32: --#line 323 "dtc-parser.y" /* yacc.c:1646 */ -+ case 34: -+ -+/* Line 1806 of yacc.c */ -+#line 340 "dtc-parser.y" - { -- uint64_t val = ~0ULL >> (64 - (yyvsp[-1].array).bits); -+ uint64_t val = ~0ULL >> (64 - (yyvsp[(1) - (2)].array).bits); - -- if ((yyvsp[-1].array).bits == 32) -- (yyvsp[-1].array).data = data_add_marker((yyvsp[-1].array).data, -+ if ((yyvsp[(1) - (2)].array).bits == 32) -+ (yyvsp[(1) - (2)].array).data = data_add_marker((yyvsp[(1) - (2)].array).data, - REF_PHANDLE, -- (yyvsp[0].labelref)); -+ (yyvsp[(2) - (2)].labelref)); - else -- ERROR(&(yylsp[0]), "References are only allowed in " -+ ERROR(&(yylsp[(2) - (2)]), "References are only allowed in " - "arrays with 32-bit elements."); - -- (yyval.array).data = data_append_integer((yyvsp[-1].array).data, val, (yyvsp[-1].array).bits); -+ (yyval.array).data = data_append_integer((yyvsp[(1) - (2)].array).data, val, (yyvsp[(1) - (2)].array).bits); - } --#line 1791 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - -- case 33: --#line 337 "dtc-parser.y" /* yacc.c:1646 */ -+ case 35: -+ -+/* Line 1806 of yacc.c */ -+#line 354 "dtc-parser.y" - { -- (yyval.array).data = data_add_marker((yyvsp[-1].array).data, LABEL, (yyvsp[0].labelref)); -+ (yyval.array).data = data_add_marker((yyvsp[(1) - (2)].array).data, LABEL, (yyvsp[(2) - (2)].labelref)); - } --#line 1799 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - -- case 36: --#line 346 "dtc-parser.y" /* yacc.c:1646 */ -+ case 38: -+ -+/* Line 1806 of yacc.c */ -+#line 363 "dtc-parser.y" - { -- (yyval.integer) = (yyvsp[-1].integer); -+ (yyval.integer) = (yyvsp[(2) - (3)].integer); - } --#line 1807 "dtc-parser.tab.c" /* yacc.c:1646 */ -- break; -- -- case 39: --#line 357 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-4].integer) ? (yyvsp[-2].integer) : (yyvsp[0].integer); } --#line 1813 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 41: --#line 362 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-2].integer) || (yyvsp[0].integer); } --#line 1819 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 374 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (5)].integer) ? (yyvsp[(3) - (5)].integer) : (yyvsp[(5) - (5)].integer); } - break; - - case 43: --#line 367 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-2].integer) && (yyvsp[0].integer); } --#line 1825 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 379 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) || (yyvsp[(3) - (3)].integer); } - break; - - case 45: --#line 372 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-2].integer) | (yyvsp[0].integer); } --#line 1831 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 384 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) && (yyvsp[(3) - (3)].integer); } - break; - - case 47: --#line 377 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-2].integer) ^ (yyvsp[0].integer); } --#line 1837 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 389 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) | (yyvsp[(3) - (3)].integer); } - break; - - case 49: --#line 382 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-2].integer) & (yyvsp[0].integer); } --#line 1843 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 394 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) ^ (yyvsp[(3) - (3)].integer); } - break; - - case 51: --#line 387 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-2].integer) == (yyvsp[0].integer); } --#line 1849 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 399 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) & (yyvsp[(3) - (3)].integer); } - break; - -- case 52: --#line 388 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-2].integer) != (yyvsp[0].integer); } --#line 1855 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ case 53: -+ -+/* Line 1806 of yacc.c */ -+#line 404 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) == (yyvsp[(3) - (3)].integer); } - break; - - case 54: --#line 393 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-2].integer) < (yyvsp[0].integer); } --#line 1861 "dtc-parser.tab.c" /* yacc.c:1646 */ -- break; - -- case 55: --#line 394 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-2].integer) > (yyvsp[0].integer); } --#line 1867 "dtc-parser.tab.c" /* yacc.c:1646 */ -+/* Line 1806 of yacc.c */ -+#line 405 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) != (yyvsp[(3) - (3)].integer); } - break; - - case 56: --#line 395 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-2].integer) <= (yyvsp[0].integer); } --#line 1873 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 410 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) < (yyvsp[(3) - (3)].integer); } - break; - - case 57: --#line 396 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-2].integer) >= (yyvsp[0].integer); } --#line 1879 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 411 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) > (yyvsp[(3) - (3)].integer); } - break; - - case 58: --#line 400 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-2].integer) << (yyvsp[0].integer); } --#line 1885 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 412 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) <= (yyvsp[(3) - (3)].integer); } - break; - - case 59: --#line 401 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-2].integer) >> (yyvsp[0].integer); } --#line 1891 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 413 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) >= (yyvsp[(3) - (3)].integer); } -+ break; -+ -+ case 60: -+ -+/* Line 1806 of yacc.c */ -+#line 417 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) << (yyvsp[(3) - (3)].integer); } - break; - - case 61: --#line 406 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-2].integer) + (yyvsp[0].integer); } --#line 1897 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 418 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) >> (yyvsp[(3) - (3)].integer); } - break; - -- case 62: --#line 407 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-2].integer) - (yyvsp[0].integer); } --#line 1903 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ case 63: -+ -+/* Line 1806 of yacc.c */ -+#line 423 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) + (yyvsp[(3) - (3)].integer); } - break; - - case 64: --#line 412 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-2].integer) * (yyvsp[0].integer); } --#line 1909 "dtc-parser.tab.c" /* yacc.c:1646 */ -- break; - -- case 65: --#line 413 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-2].integer) / (yyvsp[0].integer); } --#line 1915 "dtc-parser.tab.c" /* yacc.c:1646 */ -+/* Line 1806 of yacc.c */ -+#line 424 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) - (yyvsp[(3) - (3)].integer); } - break; - - case 66: --#line 414 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = (yyvsp[-2].integer) % (yyvsp[0].integer); } --#line 1921 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 429 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) * (yyvsp[(3) - (3)].integer); } - break; - -- case 69: --#line 420 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = -(yyvsp[0].integer); } --#line 1927 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ case 67: -+ -+/* Line 1806 of yacc.c */ -+#line 430 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) / (yyvsp[(3) - (3)].integer); } - break; - -- case 70: --#line 421 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = ~(yyvsp[0].integer); } --#line 1933 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ case 68: -+ -+/* Line 1806 of yacc.c */ -+#line 431 "dtc-parser.y" -+ { (yyval.integer) = (yyvsp[(1) - (3)].integer) % (yyvsp[(3) - (3)].integer); } - break; - - case 71: --#line 422 "dtc-parser.y" /* yacc.c:1646 */ -- { (yyval.integer) = !(yyvsp[0].integer); } --#line 1939 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 437 "dtc-parser.y" -+ { (yyval.integer) = -(yyvsp[(2) - (2)].integer); } - break; - - case 72: --#line 427 "dtc-parser.y" /* yacc.c:1646 */ -- { -- (yyval.data) = empty_data; -- } --#line 1947 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 438 "dtc-parser.y" -+ { (yyval.integer) = ~(yyvsp[(2) - (2)].integer); } - break; - - case 73: --#line 431 "dtc-parser.y" /* yacc.c:1646 */ -- { -- (yyval.data) = data_append_byte((yyvsp[-1].data), (yyvsp[0].byte)); -- } --#line 1955 "dtc-parser.tab.c" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 439 "dtc-parser.y" -+ { (yyval.integer) = !(yyvsp[(2) - (2)].integer); } - break; - - case 74: --#line 435 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 444 "dtc-parser.y" - { -- (yyval.data) = data_add_marker((yyvsp[-1].data), LABEL, (yyvsp[0].labelref)); -+ (yyval.data) = empty_data; - } --#line 1963 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 75: --#line 442 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 448 "dtc-parser.y" - { -- (yyval.nodelist) = NULL; -+ (yyval.data) = data_append_byte((yyvsp[(1) - (2)].data), (yyvsp[(2) - (2)].byte)); - } --#line 1971 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 76: --#line 446 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 452 "dtc-parser.y" - { -- (yyval.nodelist) = chain_node((yyvsp[-1].node), (yyvsp[0].nodelist)); -+ (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref)); - } --#line 1979 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 77: --#line 450 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 459 "dtc-parser.y" - { -- ERROR(&(yylsp[0]), "Properties must precede subnodes"); -- YYERROR; -+ (yyval.nodelist) = NULL; - } --#line 1988 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 78: --#line 458 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 463 "dtc-parser.y" - { -- (yyval.node) = name_node((yyvsp[0].node), (yyvsp[-1].propnodename)); -+ (yyval.nodelist) = chain_node((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].nodelist)); - } --#line 1996 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 79: --#line 462 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 467 "dtc-parser.y" - { -- (yyval.node) = name_node(build_node_delete(), (yyvsp[-1].propnodename)); -+ ERROR(&(yylsp[(2) - (2)]), "Properties must precede subnodes"); -+ YYERROR; - } --#line 2004 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - - case 80: --#line 466 "dtc-parser.y" /* yacc.c:1646 */ -+ -+/* Line 1806 of yacc.c */ -+#line 475 "dtc-parser.y" - { -- add_label(&(yyvsp[0].node)->labels, (yyvsp[-1].labelref)); -- (yyval.node) = (yyvsp[0].node); -+ (yyval.node) = name_node((yyvsp[(2) - (2)].node), (yyvsp[(1) - (2)].propnodename)); - } --#line 2013 "dtc-parser.tab.c" /* yacc.c:1646 */ - break; - -+ case 81: - --#line 2017 "dtc-parser.tab.c" /* yacc.c:1646 */ -+/* Line 1806 of yacc.c */ -+#line 479 "dtc-parser.y" -+ { -+ (yyval.node) = name_node(build_node_delete(), (yyvsp[(2) - (3)].propnodename)); -+ } -+ break; -+ -+ case 82: -+ -+/* Line 1806 of yacc.c */ -+#line 483 "dtc-parser.y" -+ { -+ add_label(&(yyvsp[(2) - (2)].node)->labels, (yyvsp[(1) - (2)].labelref)); -+ (yyval.node) = (yyvsp[(2) - (2)].node); -+ } -+ break; -+ -+ -+ -+/* Line 1806 of yacc.c */ -+#line 2235 "dtc-parser.tab.c" - default: break; - } - /* User semantic actions sometimes alter yychar, and that requires -@@ -2036,7 +2254,7 @@ yyreduce: - *++yyvsp = yyval; - *++yylsp = yyloc; - -- /* Now 'shift' the result of the reduction. Determine what state -+ /* Now `shift' the result of the reduction. Determine what state - that goes to, based on the state we popped back to and the rule - number reduced by. */ - -@@ -2051,9 +2269,9 @@ yyreduce: - goto yynewstate; - - --/*--------------------------------------. --| yyerrlab -- here on detecting error. | --`--------------------------------------*/ -+/*------------------------------------. -+| yyerrlab -- here on detecting error | -+`------------------------------------*/ - yyerrlab: - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ -@@ -2104,20 +2322,20 @@ yyerrlab: - if (yyerrstatus == 3) - { - /* If just tried and failed to reuse lookahead token after an -- error, discard it. */ -+ error, discard it. */ - - if (yychar <= YYEOF) -- { -- /* Return failure if at end of input. */ -- if (yychar == YYEOF) -- YYABORT; -- } -+ { -+ /* Return failure if at end of input. */ -+ if (yychar == YYEOF) -+ YYABORT; -+ } - else -- { -- yydestruct ("Error: discarding", -- yytoken, &yylval, &yylloc); -- yychar = YYEMPTY; -- } -+ { -+ yydestruct ("Error: discarding", -+ yytoken, &yylval, &yylloc); -+ yychar = YYEMPTY; -+ } - } - - /* Else will try to reuse lookahead token after shifting the error -@@ -2137,7 +2355,7 @@ yyerrorlab: - goto yyerrorlab; - - yyerror_range[1] = yylsp[1-yylen]; -- /* Do not reclaim the symbols of the rule whose action triggered -+ /* Do not reclaim the symbols of the rule which action triggered - this YYERROR. */ - YYPOPSTACK (yylen); - yylen = 0; -@@ -2150,37 +2368,35 @@ yyerrorlab: - | yyerrlab1 -- common code for both syntax error and YYERROR. | - `-------------------------------------------------------------*/ - yyerrlab1: -- yyerrstatus = 3; /* Each real token shifted decrements this. */ -+ yyerrstatus = 3; /* Each real token shifted decrements this. */ - - for (;;) - { - yyn = yypact[yystate]; - if (!yypact_value_is_default (yyn)) -- { -- yyn += YYTERROR; -- if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) -- { -- yyn = yytable[yyn]; -- if (0 < yyn) -- break; -- } -- } -+ { -+ yyn += YYTERROR; -+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) -+ { -+ yyn = yytable[yyn]; -+ if (0 < yyn) -+ break; -+ } -+ } - - /* Pop the current state because it cannot handle the error token. */ - if (yyssp == yyss) -- YYABORT; -+ YYABORT; - - yyerror_range[1] = *yylsp; - yydestruct ("Error: popping", -- yystos[yystate], yyvsp, yylsp); -+ yystos[yystate], yyvsp, yylsp); - YYPOPSTACK (1); - yystate = *yyssp; - YY_STACK_PRINT (yyss, yyssp); - } - -- YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - *++yyvsp = yylval; -- YY_IGNORE_MAYBE_UNINITIALIZED_END - - yyerror_range[2] = yylloc; - /* Using YYLLOC is tempting, but would change the location of -@@ -2209,7 +2425,7 @@ yyabortlab: - yyresult = 1; - goto yyreturn; - --#if !defined yyoverflow || YYERROR_VERBOSE -+#if !defined(yyoverflow) || YYERROR_VERBOSE - /*-------------------------------------------------. - | yyexhaustedlab -- memory exhaustion comes here. | - `-------------------------------------------------*/ -@@ -2228,14 +2444,14 @@ yyreturn: - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval, &yylloc); - } -- /* Do not reclaim the symbols of the rule whose action triggered -+ /* Do not reclaim the symbols of the rule which action triggered - this YYABORT or YYACCEPT. */ - YYPOPSTACK (yylen); - YY_STACK_PRINT (yyss, yyssp); - while (yyssp != yyss) - { - yydestruct ("Cleanup: popping", -- yystos[*yyssp], yyvsp, yylsp); -+ yystos[*yyssp], yyvsp, yylsp); - YYPOPSTACK (1); - } - #ifndef yyoverflow -@@ -2246,12 +2462,18 @@ yyreturn: - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - #endif -- return yyresult; -+ /* Make sure YYID is used. */ -+ return YYID (yyresult); - } --#line 472 "dtc-parser.y" /* yacc.c:1906 */ -+ -+ -+ -+/* Line 2067 of yacc.c */ -+#line 489 "dtc-parser.y" - - - void yyerror(char const *s) - { - ERROR(&yylloc, "%s", s); - } -+ ---- a/scripts/dtc/dtc-parser.tab.h_shipped -+++ b/scripts/dtc/dtc-parser.tab.h_shipped -@@ -1,19 +1,19 @@ --/* A Bison parser, made by GNU Bison 3.0.2. */ -+/* A Bison parser, made by GNU Bison 2.5. */ - - /* Bison interface for Yacc-like parsers in C -- -- Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. -- -+ -+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. -+ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. -- -+ - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU 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 . */ - -@@ -26,55 +26,50 @@ - special exception, which will cause the skeleton and the resulting - Bison output files to be licensed under the GNU General Public - License without this special exception. -- -+ - This special exception was added by the Free Software Foundation in - version 2.2 of Bison. */ - --#ifndef YY_YY_DTC_PARSER_TAB_H_INCLUDED --# define YY_YY_DTC_PARSER_TAB_H_INCLUDED --/* Debug traces. */ --#ifndef YYDEBUG --# define YYDEBUG 0 --#endif --#if YYDEBUG --extern int yydebug; --#endif - --/* Token type. */ -+/* Tokens. */ - #ifndef YYTOKENTYPE - # define YYTOKENTYPE -- enum yytokentype -- { -- DT_V1 = 258, -- DT_MEMRESERVE = 259, -- DT_LSHIFT = 260, -- DT_RSHIFT = 261, -- DT_LE = 262, -- DT_GE = 263, -- DT_EQ = 264, -- DT_NE = 265, -- DT_AND = 266, -- DT_OR = 267, -- DT_BITS = 268, -- DT_DEL_PROP = 269, -- DT_DEL_NODE = 270, -- DT_PROPNODENAME = 271, -- DT_LITERAL = 272, -- DT_CHAR_LITERAL = 273, -- DT_BYTE = 274, -- DT_STRING = 275, -- DT_LABEL = 276, -- DT_REF = 277, -- DT_INCBIN = 278 -- }; -+ /* Put the tokens into the symbol table, so that GDB and other debuggers -+ know about them. */ -+ enum yytokentype { -+ DT_V1 = 258, -+ DT_PLUGIN = 259, -+ DT_MEMRESERVE = 260, -+ DT_LSHIFT = 261, -+ DT_RSHIFT = 262, -+ DT_LE = 263, -+ DT_GE = 264, -+ DT_EQ = 265, -+ DT_NE = 266, -+ DT_AND = 267, -+ DT_OR = 268, -+ DT_BITS = 269, -+ DT_DEL_PROP = 270, -+ DT_DEL_NODE = 271, -+ DT_PROPNODENAME = 272, -+ DT_LITERAL = 273, -+ DT_CHAR_LITERAL = 274, -+ DT_BYTE = 275, -+ DT_STRING = 276, -+ DT_LABEL = 277, -+ DT_REF = 278, -+ DT_INCBIN = 279 -+ }; - #endif - --/* Value type. */ -+ -+ - #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED --typedef union YYSTYPE YYSTYPE; --union YYSTYPE -+typedef union YYSTYPE - { --#line 38 "dtc-parser.y" /* yacc.c:1909 */ -+ -+/* Line 2068 of yacc.c */ -+#line 39 "dtc-parser.y" - - char *propnodename; - char *labelref; -@@ -92,30 +87,32 @@ union YYSTYPE - struct node *nodelist; - struct reserve_info *re; - uint64_t integer; -+ int is_plugin; -+ -+ - --#line 97 "dtc-parser.tab.h" /* yacc.c:1909 */ --}; -+/* Line 2068 of yacc.c */ -+#line 96 "dtc-parser.tab.h" -+} YYSTYPE; - # define YYSTYPE_IS_TRIVIAL 1 -+# define yystype YYSTYPE /* obsolescent; will be withdrawn */ - # define YYSTYPE_IS_DECLARED 1 - #endif - --/* Location type. */ -+extern YYSTYPE yylval; -+ - #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED --typedef struct YYLTYPE YYLTYPE; --struct YYLTYPE -+typedef struct YYLTYPE - { - int first_line; - int first_column; - int last_line; - int last_column; --}; -+} YYLTYPE; -+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ - # define YYLTYPE_IS_DECLARED 1 - # define YYLTYPE_IS_TRIVIAL 1 - #endif - -- --extern YYSTYPE yylval; - extern YYLTYPE yylloc; --int yyparse (void); - --#endif /* !YY_YY_DTC_PARSER_TAB_H_INCLUDED */ ---- a/scripts/dtc/dtc-parser.y -+++ b/scripts/dtc/dtc-parser.y -@@ -19,6 +19,7 @@ - */ - %{ - #include -+#include - - #include "dtc.h" - #include "srcpos.h" -@@ -52,9 +53,11 @@ extern bool treesource_error; - struct node *nodelist; - struct reserve_info *re; - uint64_t integer; -+ int is_plugin; - } - - %token DT_V1 -+%token DT_PLUGIN - %token DT_MEMRESERVE - %token DT_LSHIFT DT_RSHIFT DT_LE DT_GE DT_EQ DT_NE DT_AND DT_OR - %token DT_BITS -@@ -71,6 +74,7 @@ extern bool treesource_error; - - %type propdata - %type propdataprefix -+%type plugindecl - %type memreserve - %type memreserves - %type arrayprefix -@@ -101,10 +105,23 @@ extern bool treesource_error; - %% - - sourcefile: -- DT_V1 ';' memreserves devicetree -+ DT_V1 ';' plugindecl memreserves devicetree - { -- the_boot_info = build_boot_info($3, $4, -- guess_boot_cpuid($4)); -+ $5->is_plugin = $3; -+ $5->is_root = 1; -+ the_boot_info = build_boot_info($4, $5, -+ guess_boot_cpuid($5)); -+ } -+ ; -+ -+plugindecl: -+ /* empty */ -+ { -+ $$ = 0; -+ } -+ | DT_PLUGIN ';' -+ { -+ $$ = 1; - } - ; - ---- a/scripts/dtc/dtc.c -+++ b/scripts/dtc/dtc.c -@@ -29,6 +29,7 @@ int reservenum; /* Number of memory res - int minsize; /* Minimum blob size */ - int padsize; /* Additional padding to blob */ - int phandle_format = PHANDLE_BOTH; /* Use linux,phandle or phandle properties */ -+int symbol_fixup_support = 0; - - static void fill_fullpaths(struct node *tree, const char *prefix) - { -@@ -51,7 +52,7 @@ static void fill_fullpaths(struct node * - #define FDT_VERSION(version) _FDT_VERSION(version) - #define _FDT_VERSION(version) #version - static const char usage_synopsis[] = "dtc [options] "; --static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:hv"; -+static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:hv@"; - static struct option const usage_long_opts[] = { - {"quiet", no_argument, NULL, 'q'}, - {"in-format", a_argument, NULL, 'I'}, -@@ -69,6 +70,7 @@ static struct option const usage_long_op - {"phandle", a_argument, NULL, 'H'}, - {"warning", a_argument, NULL, 'W'}, - {"error", a_argument, NULL, 'E'}, -+ {"symbols", a_argument, NULL, '@'}, - {"help", no_argument, NULL, 'h'}, - {"version", no_argument, NULL, 'v'}, - {NULL, no_argument, NULL, 0x0}, -@@ -99,6 +101,7 @@ static const char * const usage_opts_hel - "\t\tboth - Both \"linux,phandle\" and \"phandle\" properties", - "\n\tEnable/disable warnings (prefix with \"no-\")", - "\n\tEnable/disable errors (prefix with \"no-\")", -+ "\n\tSymbols and Fixups support", - "\n\tPrint this help and exit", - "\n\tPrint version and exit", - NULL, -@@ -186,7 +189,9 @@ int main(int argc, char *argv[]) - case 'E': - parse_checks_option(false, true, optarg); - break; -- -+ case '@': -+ symbol_fixup_support = 1; -+ break; - case 'h': - usage(NULL); - default: ---- a/scripts/dtc/dtc.h -+++ b/scripts/dtc/dtc.h -@@ -54,6 +54,7 @@ extern int reservenum; /* Number of mem - extern int minsize; /* Minimum blob size */ - extern int padsize; /* Additional padding to blob */ - extern int phandle_format; /* Use linux,phandle or phandle properties */ -+extern int symbol_fixup_support;/* enable symbols & fixup support */ - - #define PHANDLE_LEGACY 0x1 - #define PHANDLE_EPAPR 0x2 -@@ -132,6 +133,25 @@ struct label { - struct label *next; - }; - -+struct fixup_entry { -+ int offset; -+ struct node *node; -+ struct property *prop; -+ struct fixup_entry *next; -+}; -+ -+struct fixup { -+ char *ref; -+ struct fixup_entry *entries; -+ struct fixup *next; -+}; -+ -+struct symbol { -+ struct label *label; -+ struct node *node; -+ struct symbol *next; -+}; -+ - struct property { - bool deleted; - char *name; -@@ -158,6 +178,12 @@ struct node { - int addr_cells, size_cells; - - struct label *labels; -+ -+ int is_root; -+ int is_plugin; -+ struct fixup *fixups; -+ struct symbol *symbols; -+ struct fixup_entry *local_fixups; - }; - - #define for_each_label_withdel(l0, l) \ -@@ -181,6 +207,18 @@ struct node { - for_each_child_withdel(n, c) \ - if (!(c)->deleted) - -+#define for_each_fixup(n, f) \ -+ for ((f) = (n)->fixups; (f); (f) = (f)->next) -+ -+#define for_each_fixup_entry(f, fe) \ -+ for ((fe) = (f)->entries; (fe); (fe) = (fe)->next) -+ -+#define for_each_symbol(n, s) \ -+ for ((s) = (n)->symbols; (s); (s) = (s)->next) -+ -+#define for_each_local_fixup_entry(n, fe) \ -+ for ((fe) = (n)->local_fixups; (fe); (fe) = (fe)->next) -+ - void add_label(struct label **labels, char *label); - void delete_labels(struct label **labels); - ---- a/scripts/dtc/flattree.c -+++ b/scripts/dtc/flattree.c -@@ -262,6 +262,12 @@ static void flatten_tree(struct node *tr - struct property *prop; - struct node *child; - bool seen_name_prop = false; -+ struct symbol *sym; -+ struct fixup *f; -+ struct fixup_entry *fe; -+ char *name, *s; -+ const char *fullpath; -+ int namesz, nameoff, vallen; - - if (tree->deleted) - return; -@@ -276,8 +282,6 @@ static void flatten_tree(struct node *tr - emit->align(etarget, sizeof(cell_t)); - - for_each_property(tree, prop) { -- int nameoff; -- - if (streq(prop->name, "name")) - seen_name_prop = true; - -@@ -310,6 +314,139 @@ static void flatten_tree(struct node *tr - flatten_tree(child, emit, etarget, strbuf, vi); - } - -+ if (!symbol_fixup_support) -+ goto no_symbols; -+ -+ /* add the symbol nodes (if any) */ -+ if (tree->symbols) { -+ -+ emit->beginnode(etarget, NULL); -+ emit->string(etarget, "__symbols__", 0); -+ emit->align(etarget, sizeof(cell_t)); -+ -+ for_each_symbol(tree, sym) { -+ -+ vallen = strlen(sym->node->fullpath); -+ -+ nameoff = stringtable_insert(strbuf, sym->label->label); -+ -+ emit->property(etarget, NULL); -+ emit->cell(etarget, vallen + 1); -+ emit->cell(etarget, nameoff); -+ -+ if ((vi->flags & FTF_VARALIGN) && vallen >= 8) -+ emit->align(etarget, 8); -+ -+ emit->string(etarget, sym->node->fullpath, -+ strlen(sym->node->fullpath)); -+ emit->align(etarget, sizeof(cell_t)); -+ } -+ -+ emit->endnode(etarget, NULL); -+ } -+ -+ /* add the fixup nodes */ -+ if (tree->fixups) { -+ -+ /* emit the external fixups */ -+ emit->beginnode(etarget, NULL); -+ emit->string(etarget, "__fixups__", 0); -+ emit->align(etarget, sizeof(cell_t)); -+ -+ for_each_fixup(tree, f) { -+ -+ namesz = 0; -+ for_each_fixup_entry(f, fe) { -+ fullpath = fe->node->fullpath; -+ if (fullpath[0] == '\0') -+ fullpath = "/"; -+ namesz += strlen(fullpath) + 1; -+ namesz += strlen(fe->prop->name) + 1; -+ namesz += 32; /* space for : + '\0' */ -+ } -+ -+ name = xmalloc(namesz); -+ -+ s = name; -+ for_each_fixup_entry(f, fe) { -+ fullpath = fe->node->fullpath; -+ if (fullpath[0] == '\0') -+ fullpath = "/"; -+ snprintf(s, name + namesz - s, "%s:%s:%d", -+ fullpath, -+ fe->prop->name, fe->offset); -+ s += strlen(s) + 1; -+ } -+ -+ nameoff = stringtable_insert(strbuf, f->ref); -+ vallen = s - name - 1; -+ -+ emit->property(etarget, NULL); -+ emit->cell(etarget, vallen + 1); -+ emit->cell(etarget, nameoff); -+ -+ if ((vi->flags & FTF_VARALIGN) && vallen >= 8) -+ emit->align(etarget, 8); -+ -+ emit->string(etarget, name, vallen); -+ emit->align(etarget, sizeof(cell_t)); -+ -+ free(name); -+ } -+ -+ emit->endnode(etarget, tree->labels); -+ } -+ -+ /* add the local fixup property */ -+ if (tree->local_fixups) { -+ -+ /* emit the external fixups */ -+ emit->beginnode(etarget, NULL); -+ emit->string(etarget, "__local_fixups__", 0); -+ emit->align(etarget, sizeof(cell_t)); -+ -+ namesz = 0; -+ for_each_local_fixup_entry(tree, fe) { -+ fullpath = fe->node->fullpath; -+ if (fullpath[0] == '\0') -+ fullpath = "/"; -+ namesz += strlen(fullpath) + 1; -+ namesz += strlen(fe->prop->name) + 1; -+ namesz += 32; /* space for : + '\0' */ -+ } -+ -+ name = xmalloc(namesz); -+ -+ s = name; -+ for_each_local_fixup_entry(tree, fe) { -+ fullpath = fe->node->fullpath; -+ if (fullpath[0] == '\0') -+ fullpath = "/"; -+ snprintf(s, name + namesz - s, "%s:%s:%d", -+ fullpath, fe->prop->name, -+ fe->offset); -+ s += strlen(s) + 1; -+ } -+ -+ nameoff = stringtable_insert(strbuf, "fixup"); -+ vallen = s - name - 1; -+ -+ emit->property(etarget, NULL); -+ emit->cell(etarget, vallen + 1); -+ emit->cell(etarget, nameoff); -+ -+ if ((vi->flags & FTF_VARALIGN) && vallen >= 8) -+ emit->align(etarget, 8); -+ -+ emit->string(etarget, name, vallen); -+ emit->align(etarget, sizeof(cell_t)); -+ -+ free(name); -+ -+ emit->endnode(etarget, tree->labels); -+ } -+ -+no_symbols: - emit->endnode(etarget, tree->labels); - } - ---- a/scripts/dtc/version_gen.h -+++ b/scripts/dtc/version_gen.h -@@ -1 +1 @@ --#define DTC_VERSION "DTC 1.4.1-g9d3649bd" -+#define DTC_VERSION "DTC 1.4.1-g9d3649bd-dirty" diff --git a/target/linux/brcm2708/patches-4.4/0084-RaspiDAC3-support.patch b/target/linux/brcm2708/patches-4.4/0084-RaspiDAC3-support.patch new file mode 100644 index 0000000000..3da75d31ea --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0084-RaspiDAC3-support.patch @@ -0,0 +1,243 @@ +From 5133d16d705f9ccf7f558ebc43861e1800512969 Mon Sep 17 00:00:00 2001 +From: Jan Grulich +Date: Mon, 24 Aug 2015 16:03:47 +0100 +Subject: [PATCH 084/232] RaspiDAC3 support + +Signed-off-by: Jan Grulich + +config: fix RaspiDAC Rev.3x dependencies + +Change depends to SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S +like the other I2S soundcard drivers. + +Signed-off-by: Matthias Reichl +--- + sound/soc/bcm/Kconfig | 8 ++ + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/raspidac3.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 201 insertions(+) + create mode 100644 sound/soc/bcm/raspidac3.c + +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -56,3 +56,11 @@ config SND_BCM2708_SOC_IQAUDIO_DAC + select SND_SOC_PCM512x_I2C + help + Say Y or M if you want to add support for IQaudIO-DAC. ++ ++config SND_BCM2708_SOC_RASPIDAC3 ++ tristate "Support for RaspiDAC Rev.3x" ++ depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S ++ select SND_SOC_PCM512x_I2C ++ select SND_SOC_TPA6130A2 ++ help ++ Say Y or M if you want to add support for RaspiDAC Rev.3x. +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -11,6 +11,7 @@ snd-soc-hifiberry-amp-objs := hifiberry_ + snd-soc-rpi-dac-objs := rpi-dac.o + snd-soc-rpi-proto-objs := rpi-proto.o + snd-soc-iqaudio-dac-objs := iqaudio-dac.o ++snd-soc-raspidac3-objs := raspidac3.o + + obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o + obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) += snd-soc-hifiberry-dacplus.o +@@ -19,3 +20,4 @@ obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_A + 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 ++obj-$(CONFIG_SND_BCM2708_SOC_RASPIDAC3) += snd-soc-raspidac3.o +--- /dev/null ++++ b/sound/soc/bcm/raspidac3.c +@@ -0,0 +1,191 @@ ++/* ++ * ASoC Driver for RaspiDAC v3 ++ * ++ * Author: Jan Grulich ++ * Copyright 2015 ++ * based on code by Daniel Matuschek ++ * based on code by Florian Meier ++ * ++ * 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 ++ ++#include "../codecs/pcm512x.h" ++#include "../codecs/tpa6130a2.h" ++ ++/* sound card init */ ++static int snd_rpi_raspidac3_init(struct snd_soc_pcm_runtime *rtd) ++{ ++ int ret; ++ struct snd_soc_card *card = rtd->card; ++ struct snd_soc_codec *codec = rtd->codec; ++ snd_soc_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08); ++ snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0xf, 0x02); ++ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08,0x00); ++ ++ ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207); ++ if (ret < 0) ++ dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); ++ else { ++ struct snd_kcontrol *kctl; ++ ++ ret = tpa6130a2_add_controls(codec); ++ if (ret < 0) ++ dev_warn(card->dev, "Failed to add TPA6130A2 controls: %d\n", ++ ret); ++ ret = snd_soc_limit_volume(card, ++ "TPA6130A2 Headphone Playback Volume", ++ 54); ++ if (ret < 0) ++ dev_warn(card->dev, "Failed to set TPA6130A2 volume limit: %d\n", ++ ret); ++ kctl = snd_soc_card_get_kcontrol(card, ++ "TPA6130A2 Headphone Playback Volume"); ++ if (kctl) { ++ strcpy(kctl->id.name, "Headphones Playback Volume"); ++ /* disable the volume dB scale so alsamixer works */ ++ kctl->vd[0].access = SNDRV_CTL_ELEM_ACCESS_READWRITE; ++ } ++ ++ kctl = snd_soc_card_get_kcontrol(card, ++ "TPA6130A2 Headphone Playback Switch"); ++ if (kctl) ++ strcpy(kctl->id.name, "Headphones Playback Switch"); ++ } ++ ++ return 0; ++} ++ ++/* set hw parameters */ ++static int snd_rpi_raspidac3_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); ++} ++ ++/* startup */ ++static int snd_rpi_raspidac3_startup(struct snd_pcm_substream *substream) { ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_codec *codec = rtd->codec; ++ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08,0x08); ++ tpa6130a2_stereo_enable(codec, 1); ++ return 0; ++} ++ ++/* shutdown */ ++static void snd_rpi_raspidac3_shutdown(struct snd_pcm_substream *substream) { ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_codec *codec = rtd->codec; ++ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08,0x00); ++ tpa6130a2_stereo_enable(codec, 0); ++} ++ ++/* machine stream operations */ ++static struct snd_soc_ops snd_rpi_raspidac3_ops = { ++ .hw_params = snd_rpi_raspidac3_hw_params, ++ .startup = snd_rpi_raspidac3_startup, ++ .shutdown = snd_rpi_raspidac3_shutdown, ++}; ++ ++/* interface setup */ ++static struct snd_soc_dai_link snd_rpi_raspidac3_dai[] = { ++{ ++ .name = "RaspiDAC Rev.3x", ++ .stream_name = "RaspiDAC 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_raspidac3_ops, ++ .init = snd_rpi_raspidac3_init, ++}, ++}; ++ ++/* audio machine driver */ ++static struct snd_soc_card snd_rpi_raspidac3 = { ++ .name = "RaspiDAC Rev.3x HiFi Audio Card", ++ .dai_link = snd_rpi_raspidac3_dai, ++ .num_links = ARRAY_SIZE(snd_rpi_raspidac3_dai), ++}; ++ ++/* sound card test */ ++static int snd_rpi_raspidac3_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ snd_rpi_raspidac3.dev = &pdev->dev; ++ ++ if (pdev->dev.of_node) { ++ struct device_node *i2s_node; ++ struct snd_soc_dai_link *dai = &snd_rpi_raspidac3_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_raspidac3); ++ if (ret) ++ dev_err(&pdev->dev, ++ "snd_soc_register_card() failed: %d\n", ret); ++ ++ return ret; ++} ++ ++/* sound card disconnect */ ++static int snd_rpi_raspidac3_remove(struct platform_device *pdev) ++{ ++ return snd_soc_unregister_card(&snd_rpi_raspidac3); ++} ++ ++static const struct of_device_id raspidac3_of_match[] = { ++ { .compatible = "jg,raspidacv3", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, raspidac3_of_match); ++ ++/* sound card platform driver */ ++static struct platform_driver snd_rpi_raspidac3_driver = { ++ .driver = { ++ .name = "snd-rpi-raspidac3", ++ .owner = THIS_MODULE, ++ .of_match_table = raspidac3_of_match, ++ }, ++ .probe = snd_rpi_raspidac3_probe, ++ .remove = snd_rpi_raspidac3_remove, ++}; ++ ++module_platform_driver(snd_rpi_raspidac3_driver); ++ ++MODULE_AUTHOR("Jan Grulich "); ++MODULE_DESCRIPTION("ASoC Driver for RaspiDAC Rev.3x"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/brcm2708/patches-4.4/0084-mfd-Add-Raspberry-Pi-Sense-HAT-core-driver.patch b/target/linux/brcm2708/patches-4.4/0084-mfd-Add-Raspberry-Pi-Sense-HAT-core-driver.patch deleted file mode 100644 index 9e41391993..0000000000 --- a/target/linux/brcm2708/patches-4.4/0084-mfd-Add-Raspberry-Pi-Sense-HAT-core-driver.patch +++ /dev/null @@ -1,838 +0,0 @@ -From b065c580329ecb7c7e77bb7019c6059d7db83398 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Tue, 14 Jul 2015 14:32:47 +0100 -Subject: [PATCH 084/170] mfd: Add Raspberry Pi Sense HAT core driver - ---- - drivers/input/joystick/Kconfig | 8 + - drivers/input/joystick/Makefile | 1 + - drivers/input/joystick/rpisense-js.c | 153 ++++++++++++++++ - drivers/mfd/Kconfig | 8 + - drivers/mfd/Makefile | 2 + - drivers/mfd/rpisense-core.c | 157 +++++++++++++++++ - drivers/video/fbdev/Kconfig | 13 ++ - drivers/video/fbdev/Makefile | 1 + - drivers/video/fbdev/rpisense-fb.c | 293 +++++++++++++++++++++++++++++++ - include/linux/mfd/rpisense/core.h | 47 +++++ - include/linux/mfd/rpisense/framebuffer.h | 32 ++++ - include/linux/mfd/rpisense/joystick.h | 35 ++++ - 12 files changed, 750 insertions(+) - create mode 100644 drivers/input/joystick/rpisense-js.c - create mode 100644 drivers/mfd/rpisense-core.c - create mode 100644 drivers/video/fbdev/rpisense-fb.c - create mode 100644 include/linux/mfd/rpisense/core.h - create mode 100644 include/linux/mfd/rpisense/framebuffer.h - create mode 100644 include/linux/mfd/rpisense/joystick.h - ---- a/drivers/input/joystick/Kconfig -+++ b/drivers/input/joystick/Kconfig -@@ -330,4 +330,12 @@ config JOYSTICK_MAPLE - To compile this as a module choose M here: the module will be called - maplecontrol. - -+config JOYSTICK_RPISENSE -+ tristate "Raspberry Pi Sense HAT joystick" -+ depends on GPIOLIB && INPUT -+ select MFD_RPISENSE_CORE -+ -+ help -+ This is the joystick driver for the Raspberry Pi Sense HAT -+ - endif ---- a/drivers/input/joystick/Makefile -+++ b/drivers/input/joystick/Makefile -@@ -32,4 +32,5 @@ obj-$(CONFIG_JOYSTICK_WARRIOR) += warri - obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o - obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o - obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o -+obj-$(CONFIG_JOYSTICK_RPISENSE) += rpisense-js.o - ---- /dev/null -+++ b/drivers/input/joystick/rpisense-js.c -@@ -0,0 +1,153 @@ -+/* -+ * Raspberry Pi Sense HAT joystick driver -+ * http://raspberrypi.org -+ * -+ * Copyright (C) 2015 Raspberry Pi -+ * -+ * Author: Serge Schneider -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation; either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ */ -+ -+#include -+ -+#include -+#include -+ -+static struct rpisense *rpisense; -+static unsigned char keymap[5] = {KEY_DOWN, KEY_RIGHT, KEY_UP, KEY_ENTER, KEY_LEFT,}; -+ -+static void keys_work_fn(struct work_struct *work) -+{ -+ int i; -+ static s32 prev_keys; -+ struct rpisense_js *rpisense_js = &rpisense->joystick; -+ s32 keys = rpisense_reg_read(rpisense, RPISENSE_KEYS); -+ s32 changes = keys ^ prev_keys; -+ -+ prev_keys = keys; -+ for (i = 0; i < 5; i++) { -+ if (changes & 1) { -+ input_report_key(rpisense_js->keys_dev, -+ keymap[i], keys & 1); -+ } -+ changes >>= 1; -+ keys >>= 1; -+ } -+ input_sync(rpisense_js->keys_dev); -+} -+ -+static irqreturn_t keys_irq_handler(int irq, void *pdev) -+{ -+ struct rpisense_js *rpisense_js = &rpisense->joystick; -+ -+ schedule_work(&rpisense_js->keys_work_s); -+ return IRQ_HANDLED; -+} -+ -+static int rpisense_js_probe(struct platform_device *pdev) -+{ -+ int ret; -+ int i; -+ struct rpisense_js *rpisense_js; -+ -+ rpisense = rpisense_get_dev(); -+ rpisense_js = &rpisense->joystick; -+ -+ INIT_WORK(&rpisense_js->keys_work_s, keys_work_fn); -+ -+ rpisense_js->keys_dev = input_allocate_device(); -+ if (!rpisense_js->keys_dev) { -+ dev_err(&pdev->dev, "Could not allocate input device.\n"); -+ return -ENOMEM; -+ } -+ -+ rpisense_js->keys_dev->evbit[0] = BIT_MASK(EV_KEY); -+ for (i = 0; i < ARRAY_SIZE(keymap); i++) { -+ set_bit(keymap[i], -+ rpisense_js->keys_dev->keybit); -+ } -+ -+ rpisense_js->keys_dev->name = "Raspberry Pi Sense HAT Joystick"; -+ rpisense_js->keys_dev->phys = "rpi-sense-joy/input0"; -+ rpisense_js->keys_dev->id.bustype = BUS_I2C; -+ rpisense_js->keys_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); -+ rpisense_js->keys_dev->keycode = keymap; -+ rpisense_js->keys_dev->keycodesize = sizeof(unsigned char); -+ rpisense_js->keys_dev->keycodemax = ARRAY_SIZE(keymap); -+ -+ ret = input_register_device(rpisense_js->keys_dev); -+ if (ret) { -+ dev_err(&pdev->dev, "Could not register input device.\n"); -+ goto err_keys_alloc; -+ } -+ -+ ret = gpiod_direction_input(rpisense_js->keys_desc); -+ if (ret) { -+ dev_err(&pdev->dev, "Could not set keys-int direction.\n"); -+ goto err_keys_reg; -+ } -+ -+ rpisense_js->keys_irq = gpiod_to_irq(rpisense_js->keys_desc); -+ if (rpisense_js->keys_irq < 0) { -+ dev_err(&pdev->dev, "Could not determine keys-int IRQ.\n"); -+ ret = rpisense_js->keys_irq; -+ goto err_keys_reg; -+ } -+ -+ ret = devm_request_irq(&pdev->dev, rpisense_js->keys_irq, -+ keys_irq_handler, IRQF_TRIGGER_RISING, -+ "keys", &pdev->dev); -+ if (ret) { -+ dev_err(&pdev->dev, "IRQ request failed.\n"); -+ goto err_keys_reg; -+ } -+ return 0; -+err_keys_reg: -+ input_unregister_device(rpisense_js->keys_dev); -+err_keys_alloc: -+ input_free_device(rpisense_js->keys_dev); -+ return ret; -+} -+ -+static int rpisense_js_remove(struct platform_device *pdev) -+{ -+ struct rpisense_js *rpisense_js = &rpisense->joystick; -+ -+ input_unregister_device(rpisense_js->keys_dev); -+ input_free_device(rpisense_js->keys_dev); -+ return 0; -+} -+ -+#ifdef CONFIG_OF -+static const struct of_device_id rpisense_js_id[] = { -+ { .compatible = "rpi,rpi-sense-js" }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(of, rpisense_js_id); -+#endif -+ -+static struct platform_device_id rpisense_js_device_id[] = { -+ { .name = "rpi-sense-js" }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(platform, rpisense_js_device_id); -+ -+static struct platform_driver rpisense_js_driver = { -+ .probe = rpisense_js_probe, -+ .remove = rpisense_js_remove, -+ .driver = { -+ .name = "rpi-sense-js", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+module_platform_driver(rpisense_js_driver); -+ -+MODULE_DESCRIPTION("Raspberry Pi Sense HAT joystick driver"); -+MODULE_AUTHOR("Serge Schneider "); -+MODULE_LICENSE("GPL"); ---- a/drivers/mfd/Kconfig -+++ b/drivers/mfd/Kconfig -@@ -10,6 +10,14 @@ config MFD_CORE - select IRQ_DOMAIN - default n - -+config MFD_RPISENSE_CORE -+ tristate "Raspberry Pi Sense HAT core functions" -+ depends on I2C -+ select MFD_CORE -+ help -+ This is the core driver for the Raspberry Pi Sense HAT. This provides -+ the necessary functions to communicate with the hardware. -+ - config MFD_CS5535 - tristate "AMD CS5535 and CS5536 southbridge core functions" - select MFD_CORE ---- a/drivers/mfd/Makefile -+++ b/drivers/mfd/Makefile -@@ -194,3 +194,5 @@ intel-soc-pmic-objs := intel_soc_pmic_c - intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC) += intel_soc_pmic_bxtwc.o - obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o - obj-$(CONFIG_MFD_MT6397) += mt6397-core.o -+ -+obj-$(CONFIG_MFD_RPISENSE_CORE) += rpisense-core.o ---- /dev/null -+++ b/drivers/mfd/rpisense-core.c -@@ -0,0 +1,157 @@ -+/* -+ * Raspberry Pi Sense HAT core driver -+ * http://raspberrypi.org -+ * -+ * Copyright (C) 2015 Raspberry Pi -+ * -+ * Author: Serge Schneider -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation; either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ * This driver is based on wm8350 implementation. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+static struct rpisense *rpisense; -+ -+static void rpisense_client_dev_register(struct rpisense *rpisense, -+ const char *name, -+ struct platform_device **pdev) -+{ -+ int ret; -+ -+ *pdev = platform_device_alloc(name, -1); -+ if (*pdev == NULL) { -+ dev_err(rpisense->dev, "Failed to allocate %s\n", name); -+ return; -+ } -+ -+ (*pdev)->dev.parent = rpisense->dev; -+ platform_set_drvdata(*pdev, rpisense); -+ ret = platform_device_add(*pdev); -+ if (ret != 0) { -+ dev_err(rpisense->dev, "Failed to register %s: %d\n", -+ name, ret); -+ platform_device_put(*pdev); -+ *pdev = NULL; -+ } -+} -+ -+static int rpisense_probe(struct i2c_client *i2c, -+ const struct i2c_device_id *id) -+{ -+ int ret; -+ struct rpisense_js *rpisense_js; -+ -+ rpisense = devm_kzalloc(&i2c->dev, sizeof(struct rpisense), GFP_KERNEL); -+ if (rpisense == NULL) -+ return -ENOMEM; -+ -+ i2c_set_clientdata(i2c, rpisense); -+ rpisense->dev = &i2c->dev; -+ rpisense->i2c_client = i2c; -+ -+ ret = rpisense_reg_read(rpisense, RPISENSE_WAI); -+ if (ret > 0) { -+ if (ret != 's') -+ return -EINVAL; -+ } else { -+ return ret; -+ } -+ ret = rpisense_reg_read(rpisense, RPISENSE_VER); -+ if (ret < 0) -+ return ret; -+ -+ dev_info(rpisense->dev, -+ "Raspberry Pi Sense HAT firmware version %i\n", ret); -+ -+ rpisense_js = &rpisense->joystick; -+ rpisense_js->keys_desc = devm_gpiod_get(&i2c->dev, -+ "keys-int", GPIOD_IN); -+ if (IS_ERR(rpisense_js->keys_desc)) { -+ dev_warn(&i2c->dev, "Failed to get keys-int descriptor.\n"); -+ rpisense_js->keys_desc = gpio_to_desc(23); -+ if (rpisense_js->keys_desc == NULL) { -+ dev_err(&i2c->dev, "GPIO23 fallback failed.\n"); -+ return PTR_ERR(rpisense_js->keys_desc); -+ } -+ } -+ rpisense_client_dev_register(rpisense, "rpi-sense-js", -+ &(rpisense->joystick.pdev)); -+ rpisense_client_dev_register(rpisense, "rpi-sense-fb", -+ &(rpisense->framebuffer.pdev)); -+ -+ return 0; -+} -+ -+static int rpisense_remove(struct i2c_client *i2c) -+{ -+ struct rpisense *rpisense = i2c_get_clientdata(i2c); -+ -+ platform_device_unregister(rpisense->joystick.pdev); -+ return 0; -+} -+ -+struct rpisense *rpisense_get_dev(void) -+{ -+ return rpisense; -+} -+EXPORT_SYMBOL_GPL(rpisense_get_dev); -+ -+s32 rpisense_reg_read(struct rpisense *rpisense, int reg) -+{ -+ int ret = i2c_smbus_read_byte_data(rpisense->i2c_client, reg); -+ -+ if (ret < 0) -+ dev_err(rpisense->dev, "Read from reg %d failed\n", reg); -+ /* Due to the BCM270x I2C clock stretching bug, some values -+ * may have MSB set. Clear it to avoid incorrect values. -+ * */ -+ return ret & 0x7F; -+} -+EXPORT_SYMBOL_GPL(rpisense_reg_read); -+ -+int rpisense_block_write(struct rpisense *rpisense, const char *buf, int count) -+{ -+ int ret = i2c_master_send(rpisense->i2c_client, buf, count); -+ -+ if (ret < 0) -+ dev_err(rpisense->dev, "Block write failed\n"); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(rpisense_block_write); -+ -+static const struct i2c_device_id rpisense_i2c_id[] = { -+ { "rpi-sense", 0 }, -+ { } -+}; -+MODULE_DEVICE_TABLE(i2c, rpisense_i2c_id); -+ -+ -+static struct i2c_driver rpisense_driver = { -+ .driver = { -+ .name = "rpi-sense", -+ .owner = THIS_MODULE, -+ }, -+ .probe = rpisense_probe, -+ .remove = rpisense_remove, -+ .id_table = rpisense_i2c_id, -+}; -+ -+module_i2c_driver(rpisense_driver); -+ -+MODULE_DESCRIPTION("Raspberry Pi Sense HAT core driver"); -+MODULE_AUTHOR("Serge Schneider "); -+MODULE_LICENSE("GPL"); -+ ---- a/drivers/video/fbdev/Kconfig -+++ b/drivers/video/fbdev/Kconfig -@@ -2506,3 +2506,16 @@ config FB_SM712 - This driver is also available as a module. The module will be - called sm712fb. If you want to compile it as a module, say M - here and read . -+ -+config FB_RPISENSE -+ tristate "Raspberry Pi Sense HAT framebuffer" -+ depends on FB -+ select MFD_RPISENSE_CORE -+ select FB_SYS_FOPS -+ select FB_SYS_FILLRECT -+ select FB_SYS_COPYAREA -+ select FB_SYS_IMAGEBLIT -+ select FB_DEFERRED_IO -+ -+ help -+ This is the framebuffer driver for the Raspberry Pi Sense HAT ---- a/drivers/video/fbdev/Makefile -+++ b/drivers/video/fbdev/Makefile -@@ -150,6 +150,7 @@ obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o - obj-$(CONFIG_FB_MXS) += mxsfb.o - obj-$(CONFIG_FB_SSD1307) += ssd1307fb.o - obj-$(CONFIG_FB_SIMPLE) += simplefb.o -+obj-$(CONFIG_FB_RPISENSE) += rpisense-fb.o - - # the test framebuffer is last - obj-$(CONFIG_FB_VIRTUAL) += vfb.o ---- /dev/null -+++ b/drivers/video/fbdev/rpisense-fb.c -@@ -0,0 +1,293 @@ -+/* -+ * Raspberry Pi Sense HAT framebuffer driver -+ * http://raspberrypi.org -+ * -+ * Copyright (C) 2015 Raspberry Pi -+ * -+ * Author: Serge Schneider -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation; either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+static bool lowlight; -+module_param(lowlight, bool, 0); -+MODULE_PARM_DESC(lowlight, "Reduce LED matrix brightness to one third"); -+ -+static struct rpisense *rpisense; -+ -+struct rpisense_fb_param { -+ char __iomem *vmem; -+ u8 *vmem_work; -+ u32 vmemsize; -+ u8 *gamma; -+}; -+ -+static u8 gamma_default[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, -+ 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, -+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0E, 0x0F, 0x11, -+ 0x12, 0x14, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F,}; -+ -+static u8 gamma_low[32] = {0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, -+ 0x03, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, -+ 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x0A, 0x0A,}; -+ -+static u8 gamma_user[32]; -+ -+static struct rpisense_fb_param rpisense_fb_param = { -+ .vmem = NULL, -+ .vmemsize = 128, -+ .gamma = gamma_default, -+}; -+ -+static struct fb_deferred_io rpisense_fb_defio; -+ -+static struct fb_fix_screeninfo rpisense_fb_fix = { -+ .id = "RPi-Sense FB", -+ .type = FB_TYPE_PACKED_PIXELS, -+ .visual = FB_VISUAL_TRUECOLOR, -+ .xpanstep = 0, -+ .ypanstep = 0, -+ .ywrapstep = 0, -+ .accel = FB_ACCEL_NONE, -+ .line_length = 16, -+}; -+ -+static struct fb_var_screeninfo rpisense_fb_var = { -+ .xres = 8, -+ .yres = 8, -+ .xres_virtual = 8, -+ .yres_virtual = 8, -+ .bits_per_pixel = 16, -+ .red = {11, 5, 0}, -+ .green = {5, 6, 0}, -+ .blue = {0, 5, 0}, -+}; -+ -+static ssize_t rpisense_fb_write(struct fb_info *info, -+ const char __user *buf, size_t count, -+ loff_t *ppos) -+{ -+ ssize_t res = fb_sys_write(info, buf, count, ppos); -+ -+ schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay); -+ return res; -+} -+ -+static void rpisense_fb_fillrect(struct fb_info *info, -+ const struct fb_fillrect *rect) -+{ -+ sys_fillrect(info, rect); -+ schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay); -+} -+ -+static void rpisense_fb_copyarea(struct fb_info *info, -+ const struct fb_copyarea *area) -+{ -+ sys_copyarea(info, area); -+ schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay); -+} -+ -+static void rpisense_fb_imageblit(struct fb_info *info, -+ const struct fb_image *image) -+{ -+ sys_imageblit(info, image); -+ schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay); -+} -+ -+static void rpisense_fb_deferred_io(struct fb_info *info, -+ struct list_head *pagelist) -+{ -+ int i; -+ int j; -+ u8 *vmem_work = rpisense_fb_param.vmem_work; -+ u16 *mem = (u16 *)rpisense_fb_param.vmem; -+ u8 *gamma = rpisense_fb_param.gamma; -+ -+ vmem_work[0] = 0; -+ for (j = 0; j < 8; j++) { -+ for (i = 0; i < 8; i++) { -+ vmem_work[(j * 24) + i + 1] = -+ gamma[(mem[(j * 8) + i] >> 11) & 0x1F]; -+ vmem_work[(j * 24) + (i + 8) + 1] = -+ gamma[(mem[(j * 8) + i] >> 6) & 0x1F]; -+ vmem_work[(j * 24) + (i + 16) + 1] = -+ gamma[(mem[(j * 8) + i]) & 0x1F]; -+ } -+ } -+ rpisense_block_write(rpisense, vmem_work, 193); -+} -+ -+static struct fb_deferred_io rpisense_fb_defio = { -+ .delay = HZ/100, -+ .deferred_io = rpisense_fb_deferred_io, -+}; -+ -+static int rpisense_fb_ioctl(struct fb_info *info, unsigned int cmd, -+ unsigned long arg) -+{ -+ switch (cmd) { -+ case SENSEFB_FBIOGET_GAMMA: -+ if (copy_to_user((void __user *) arg, rpisense_fb_param.gamma, -+ sizeof(u8[32]))) -+ return -EFAULT; -+ return 0; -+ case SENSEFB_FBIOSET_GAMMA: -+ if (copy_from_user(gamma_user, (void __user *)arg, -+ sizeof(u8[32]))) -+ return -EFAULT; -+ rpisense_fb_param.gamma = gamma_user; -+ schedule_delayed_work(&info->deferred_work, -+ rpisense_fb_defio.delay); -+ return 0; -+ case SENSEFB_FBIORESET_GAMMA: -+ switch (arg) { -+ case 0: -+ rpisense_fb_param.gamma = gamma_default; -+ break; -+ case 1: -+ rpisense_fb_param.gamma = gamma_low; -+ break; -+ case 2: -+ rpisense_fb_param.gamma = gamma_user; -+ break; -+ default: -+ return -EINVAL; -+ } -+ schedule_delayed_work(&info->deferred_work, -+ rpisense_fb_defio.delay); -+ break; -+ default: -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+static struct fb_ops rpisense_fb_ops = { -+ .owner = THIS_MODULE, -+ .fb_read = fb_sys_read, -+ .fb_write = rpisense_fb_write, -+ .fb_fillrect = rpisense_fb_fillrect, -+ .fb_copyarea = rpisense_fb_copyarea, -+ .fb_imageblit = rpisense_fb_imageblit, -+ .fb_ioctl = rpisense_fb_ioctl, -+}; -+ -+static int rpisense_fb_probe(struct platform_device *pdev) -+{ -+ struct fb_info *info; -+ int ret = -ENOMEM; -+ struct rpisense_fb *rpisense_fb; -+ -+ rpisense = rpisense_get_dev(); -+ rpisense_fb = &rpisense->framebuffer; -+ -+ rpisense_fb_param.vmem = vzalloc(rpisense_fb_param.vmemsize); -+ if (!rpisense_fb_param.vmem) -+ return ret; -+ -+ rpisense_fb_param.vmem_work = devm_kmalloc(&pdev->dev, 193, GFP_KERNEL); -+ if (!rpisense_fb_param.vmem_work) -+ goto err_malloc; -+ -+ info = framebuffer_alloc(0, &pdev->dev); -+ if (!info) { -+ dev_err(&pdev->dev, "Could not allocate framebuffer.\n"); -+ goto err_malloc; -+ } -+ rpisense_fb->info = info; -+ -+ rpisense_fb_fix.smem_start = (unsigned long)rpisense_fb_param.vmem; -+ rpisense_fb_fix.smem_len = rpisense_fb_param.vmemsize; -+ -+ info->fbops = &rpisense_fb_ops; -+ info->fix = rpisense_fb_fix; -+ info->var = rpisense_fb_var; -+ info->fbdefio = &rpisense_fb_defio; -+ info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; -+ info->screen_base = rpisense_fb_param.vmem; -+ info->screen_size = rpisense_fb_param.vmemsize; -+ -+ if (lowlight) -+ rpisense_fb_param.gamma = gamma_low; -+ -+ fb_deferred_io_init(info); -+ -+ ret = register_framebuffer(info); -+ if (ret < 0) { -+ dev_err(&pdev->dev, "Could not register framebuffer.\n"); -+ goto err_fballoc; -+ } -+ -+ fb_info(info, "%s frame buffer device\n", info->fix.id); -+ schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay); -+ return 0; -+err_fballoc: -+ framebuffer_release(info); -+err_malloc: -+ vfree(rpisense_fb_param.vmem); -+ return ret; -+} -+ -+static int rpisense_fb_remove(struct platform_device *pdev) -+{ -+ struct rpisense_fb *rpisense_fb = &rpisense->framebuffer; -+ struct fb_info *info = rpisense_fb->info; -+ -+ if (info) { -+ unregister_framebuffer(info); -+ fb_deferred_io_cleanup(info); -+ framebuffer_release(info); -+ vfree(rpisense_fb_param.vmem); -+ } -+ -+ return 0; -+} -+ -+#ifdef CONFIG_OF -+static const struct of_device_id rpisense_fb_id[] = { -+ { .compatible = "rpi,rpi-sense-fb" }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(of, rpisense_fb_id); -+#endif -+ -+static struct platform_device_id rpisense_fb_device_id[] = { -+ { .name = "rpi-sense-fb" }, -+ { }, -+}; -+MODULE_DEVICE_TABLE(platform, rpisense_fb_device_id); -+ -+static struct platform_driver rpisense_fb_driver = { -+ .probe = rpisense_fb_probe, -+ .remove = rpisense_fb_remove, -+ .driver = { -+ .name = "rpi-sense-fb", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+module_platform_driver(rpisense_fb_driver); -+ -+MODULE_DESCRIPTION("Raspberry Pi Sense HAT framebuffer driver"); -+MODULE_AUTHOR("Serge Schneider "); -+MODULE_LICENSE("GPL"); -+ ---- /dev/null -+++ b/include/linux/mfd/rpisense/core.h -@@ -0,0 +1,47 @@ -+/* -+ * Raspberry Pi Sense HAT core driver -+ * http://raspberrypi.org -+ * -+ * Copyright (C) 2015 Raspberry Pi -+ * -+ * Author: Serge Schneider -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation; either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ */ -+ -+#ifndef __LINUX_MFD_RPISENSE_CORE_H_ -+#define __LINUX_MFD_RPISENSE_CORE_H_ -+ -+#include -+#include -+ -+/* -+ * Register values. -+ */ -+#define RPISENSE_FB 0x00 -+#define RPISENSE_WAI 0xF0 -+#define RPISENSE_VER 0xF1 -+#define RPISENSE_KEYS 0xF2 -+#define RPISENSE_EE_WP 0xF3 -+ -+#define RPISENSE_ID 's' -+ -+struct rpisense { -+ struct device *dev; -+ struct i2c_client *i2c_client; -+ -+ /* Client devices */ -+ struct rpisense_js joystick; -+ struct rpisense_fb framebuffer; -+}; -+ -+struct rpisense *rpisense_get_dev(void); -+s32 rpisense_reg_read(struct rpisense *rpisense, int reg); -+int rpisense_reg_write(struct rpisense *rpisense, int reg, u16 val); -+int rpisense_block_write(struct rpisense *rpisense, const char *buf, int count); -+ -+#endif ---- /dev/null -+++ b/include/linux/mfd/rpisense/framebuffer.h -@@ -0,0 +1,32 @@ -+/* -+ * Raspberry Pi Sense HAT framebuffer driver -+ * http://raspberrypi.org -+ * -+ * Copyright (C) 2015 Raspberry Pi -+ * -+ * Author: Serge Schneider -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation; either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ */ -+ -+#ifndef __LINUX_RPISENSE_FB_H_ -+#define __LINUX_RPISENSE_FB_H_ -+ -+#define SENSEFB_FBIO_IOC_MAGIC 0xF1 -+ -+#define SENSEFB_FBIOGET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 0) -+#define SENSEFB_FBIOSET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 1) -+#define SENSEFB_FBIORESET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 2) -+ -+struct rpisense; -+ -+struct rpisense_fb { -+ struct platform_device *pdev; -+ struct fb_info *info; -+}; -+ -+#endif ---- /dev/null -+++ b/include/linux/mfd/rpisense/joystick.h -@@ -0,0 +1,35 @@ -+/* -+ * Raspberry Pi Sense HAT joystick driver -+ * http://raspberrypi.org -+ * -+ * Copyright (C) 2015 Raspberry Pi -+ * -+ * Author: Serge Schneider -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation; either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ */ -+ -+#ifndef __LINUX_RPISENSE_JOYSTICK_H_ -+#define __LINUX_RPISENSE_JOYSTICK_H_ -+ -+#include -+#include -+#include -+#include -+ -+struct rpisense; -+ -+struct rpisense_js { -+ struct platform_device *pdev; -+ struct input_dev *keys_dev; -+ struct gpio_desc *keys_desc; -+ struct work_struct keys_work_s; -+ int keys_irq; -+}; -+ -+ -+#endif diff --git a/target/linux/brcm2708/patches-4.4/0085-RaspiDAC3-support.patch b/target/linux/brcm2708/patches-4.4/0085-RaspiDAC3-support.patch deleted file mode 100644 index f161a53394..0000000000 --- a/target/linux/brcm2708/patches-4.4/0085-RaspiDAC3-support.patch +++ /dev/null @@ -1,243 +0,0 @@ -From c86a74aaff9bc42b3cb04881b3aaf7f822edda04 Mon Sep 17 00:00:00 2001 -From: Jan Grulich -Date: Mon, 24 Aug 2015 16:03:47 +0100 -Subject: [PATCH 085/170] RaspiDAC3 support - -Signed-off-by: Jan Grulich - -config: fix RaspiDAC Rev.3x dependencies - -Change depends to SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S -like the other I2S soundcard drivers. - -Signed-off-by: Matthias Reichl ---- - sound/soc/bcm/Kconfig | 8 ++ - sound/soc/bcm/Makefile | 2 + - sound/soc/bcm/raspidac3.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 201 insertions(+) - create mode 100644 sound/soc/bcm/raspidac3.c - ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -56,3 +56,11 @@ config SND_BCM2708_SOC_IQAUDIO_DAC - select SND_SOC_PCM512x_I2C - help - Say Y or M if you want to add support for IQaudIO-DAC. -+ -+config SND_BCM2708_SOC_RASPIDAC3 -+ tristate "Support for RaspiDAC Rev.3x" -+ depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S -+ select SND_SOC_PCM512x_I2C -+ select SND_SOC_TPA6130A2 -+ help -+ Say Y or M if you want to add support for RaspiDAC Rev.3x. ---- a/sound/soc/bcm/Makefile -+++ b/sound/soc/bcm/Makefile -@@ -11,6 +11,7 @@ snd-soc-hifiberry-amp-objs := hifiberry_ - snd-soc-rpi-dac-objs := rpi-dac.o - snd-soc-rpi-proto-objs := rpi-proto.o - snd-soc-iqaudio-dac-objs := iqaudio-dac.o -+snd-soc-raspidac3-objs := raspidac3.o - - obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o - obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) += snd-soc-hifiberry-dacplus.o -@@ -19,3 +20,4 @@ obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_A - 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 -+obj-$(CONFIG_SND_BCM2708_SOC_RASPIDAC3) += snd-soc-raspidac3.o ---- /dev/null -+++ b/sound/soc/bcm/raspidac3.c -@@ -0,0 +1,191 @@ -+/* -+ * ASoC Driver for RaspiDAC v3 -+ * -+ * Author: Jan Grulich -+ * Copyright 2015 -+ * based on code by Daniel Matuschek -+ * based on code by Florian Meier -+ * -+ * 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 -+ -+#include "../codecs/pcm512x.h" -+#include "../codecs/tpa6130a2.h" -+ -+/* sound card init */ -+static int snd_rpi_raspidac3_init(struct snd_soc_pcm_runtime *rtd) -+{ -+ int ret; -+ struct snd_soc_card *card = rtd->card; -+ struct snd_soc_codec *codec = rtd->codec; -+ snd_soc_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08); -+ snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0xf, 0x02); -+ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08,0x00); -+ -+ ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207); -+ if (ret < 0) -+ dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); -+ else { -+ struct snd_kcontrol *kctl; -+ -+ ret = tpa6130a2_add_controls(codec); -+ if (ret < 0) -+ dev_warn(card->dev, "Failed to add TPA6130A2 controls: %d\n", -+ ret); -+ ret = snd_soc_limit_volume(card, -+ "TPA6130A2 Headphone Playback Volume", -+ 54); -+ if (ret < 0) -+ dev_warn(card->dev, "Failed to set TPA6130A2 volume limit: %d\n", -+ ret); -+ kctl = snd_soc_card_get_kcontrol(card, -+ "TPA6130A2 Headphone Playback Volume"); -+ if (kctl) { -+ strcpy(kctl->id.name, "Headphones Playback Volume"); -+ /* disable the volume dB scale so alsamixer works */ -+ kctl->vd[0].access = SNDRV_CTL_ELEM_ACCESS_READWRITE; -+ } -+ -+ kctl = snd_soc_card_get_kcontrol(card, -+ "TPA6130A2 Headphone Playback Switch"); -+ if (kctl) -+ strcpy(kctl->id.name, "Headphones Playback Switch"); -+ } -+ -+ return 0; -+} -+ -+/* set hw parameters */ -+static int snd_rpi_raspidac3_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); -+} -+ -+/* startup */ -+static int snd_rpi_raspidac3_startup(struct snd_pcm_substream *substream) { -+ struct snd_soc_pcm_runtime *rtd = substream->private_data; -+ struct snd_soc_codec *codec = rtd->codec; -+ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08,0x08); -+ tpa6130a2_stereo_enable(codec, 1); -+ return 0; -+} -+ -+/* shutdown */ -+static void snd_rpi_raspidac3_shutdown(struct snd_pcm_substream *substream) { -+ struct snd_soc_pcm_runtime *rtd = substream->private_data; -+ struct snd_soc_codec *codec = rtd->codec; -+ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08,0x00); -+ tpa6130a2_stereo_enable(codec, 0); -+} -+ -+/* machine stream operations */ -+static struct snd_soc_ops snd_rpi_raspidac3_ops = { -+ .hw_params = snd_rpi_raspidac3_hw_params, -+ .startup = snd_rpi_raspidac3_startup, -+ .shutdown = snd_rpi_raspidac3_shutdown, -+}; -+ -+/* interface setup */ -+static struct snd_soc_dai_link snd_rpi_raspidac3_dai[] = { -+{ -+ .name = "RaspiDAC Rev.3x", -+ .stream_name = "RaspiDAC 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_raspidac3_ops, -+ .init = snd_rpi_raspidac3_init, -+}, -+}; -+ -+/* audio machine driver */ -+static struct snd_soc_card snd_rpi_raspidac3 = { -+ .name = "RaspiDAC Rev.3x HiFi Audio Card", -+ .dai_link = snd_rpi_raspidac3_dai, -+ .num_links = ARRAY_SIZE(snd_rpi_raspidac3_dai), -+}; -+ -+/* sound card test */ -+static int snd_rpi_raspidac3_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ -+ snd_rpi_raspidac3.dev = &pdev->dev; -+ -+ if (pdev->dev.of_node) { -+ struct device_node *i2s_node; -+ struct snd_soc_dai_link *dai = &snd_rpi_raspidac3_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_raspidac3); -+ if (ret) -+ dev_err(&pdev->dev, -+ "snd_soc_register_card() failed: %d\n", ret); -+ -+ return ret; -+} -+ -+/* sound card disconnect */ -+static int snd_rpi_raspidac3_remove(struct platform_device *pdev) -+{ -+ return snd_soc_unregister_card(&snd_rpi_raspidac3); -+} -+ -+static const struct of_device_id raspidac3_of_match[] = { -+ { .compatible = "jg,raspidacv3", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, raspidac3_of_match); -+ -+/* sound card platform driver */ -+static struct platform_driver snd_rpi_raspidac3_driver = { -+ .driver = { -+ .name = "snd-rpi-raspidac3", -+ .owner = THIS_MODULE, -+ .of_match_table = raspidac3_of_match, -+ }, -+ .probe = snd_rpi_raspidac3_probe, -+ .remove = snd_rpi_raspidac3_remove, -+}; -+ -+module_platform_driver(snd_rpi_raspidac3_driver); -+ -+MODULE_AUTHOR("Jan Grulich "); -+MODULE_DESCRIPTION("ASoC Driver for RaspiDAC Rev.3x"); -+MODULE_LICENSE("GPL v2"); diff --git a/target/linux/brcm2708/patches-4.4/0085-tpa6130a2-Add-headphone-switch-control.patch b/target/linux/brcm2708/patches-4.4/0085-tpa6130a2-Add-headphone-switch-control.patch new file mode 100644 index 0000000000..34ed178948 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0085-tpa6130a2-Add-headphone-switch-control.patch @@ -0,0 +1,91 @@ +From 570d2d99cbf2b830e0d1be01dcc6fdd339b4d157 Mon Sep 17 00:00:00 2001 +From: Jan Grulich +Date: Mon, 24 Aug 2015 16:02:34 +0100 +Subject: [PATCH 085/232] tpa6130a2: Add headphone switch control + +Signed-off-by: Jan Grulich +--- + sound/soc/codecs/tpa6130a2.c | 29 ++++++++++++++++++++++++++--- + 1 file changed, 26 insertions(+), 3 deletions(-) + +--- a/sound/soc/codecs/tpa6130a2.c ++++ b/sound/soc/codecs/tpa6130a2.c +@@ -4,6 +4,7 @@ + * Copyright (C) Nokia Corporation + * + * Author: Peter Ujfalusi ++ * Modified: Jan Grulich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License +@@ -52,6 +53,8 @@ struct tpa6130a2_data { + enum tpa_model id; + }; + ++static void tpa6130a2_channel_enable(u8 channel, int enable); ++ + static int tpa6130a2_i2c_read(int reg) + { + struct tpa6130a2_data *data; +@@ -189,7 +192,7 @@ exit: + } + + static int tpa6130a2_get_volsw(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) ++ struct snd_ctl_elem_value *ucontrol) + { + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; +@@ -218,7 +221,7 @@ static int tpa6130a2_get_volsw(struct sn + } + + static int tpa6130a2_put_volsw(struct snd_kcontrol *kcontrol, +- struct snd_ctl_elem_value *ucontrol) ++ struct snd_ctl_elem_value *ucontrol) + { + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; +@@ -255,8 +258,22 @@ static int tpa6130a2_put_volsw(struct sn + return 1; + } + ++static int tpa6130a2_put_hp_sw(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int enable = ucontrol->value.integer.value[0]; ++ unsigned int state; ++ ++ state = (tpa6130a2_read(TPA6130A2_REG_VOL_MUTE) & 0x80) == 0; ++ if (state == enable) ++ return 0; /* No change */ ++ ++ tpa6130a2_channel_enable(TPA6130A2_HP_EN_R | TPA6130A2_HP_EN_L, enable); ++ return 1; /* Changed */ ++} ++ + /* +- * TPA6130 volume. From -59.5 to 4 dB with increasing step size when going ++ * TPA6130 volume. From -59.5 to +4.0 dB with increasing step size when going + * down in gain. + */ + static const DECLARE_TLV_DB_RANGE(tpa6130_tlv, +@@ -277,6 +294,9 @@ static const struct snd_kcontrol_new tpa + TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0, + tpa6130a2_get_volsw, tpa6130a2_put_volsw, + tpa6130_tlv), ++ SOC_SINGLE_EXT("TPA6130A2 Headphone Playback Switch", ++ TPA6130A2_REG_VOL_MUTE, 7, 1, 1, ++ tpa6130a2_get_volsw, tpa6130a2_put_hp_sw), + }; + + static const DECLARE_TLV_DB_RANGE(tpa6140_tlv, +@@ -290,6 +310,9 @@ static const struct snd_kcontrol_new tpa + TPA6130A2_REG_VOL_MUTE, 1, 0x1f, 0, + tpa6130a2_get_volsw, tpa6130a2_put_volsw, + tpa6140_tlv), ++ SOC_SINGLE_EXT("TPA6140A2 Headphone Playback Switch", ++ TPA6130A2_REG_VOL_MUTE, 7, 1, 1, ++ tpa6130a2_get_volsw, tpa6130a2_put_hp_sw), + }; + + /* diff --git a/target/linux/brcm2708/patches-4.4/0086-irq-bcm2835-Fix-building-with-2708.patch b/target/linux/brcm2708/patches-4.4/0086-irq-bcm2835-Fix-building-with-2708.patch new file mode 100644 index 0000000000..125c30ede8 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0086-irq-bcm2835-Fix-building-with-2708.patch @@ -0,0 +1,28 @@ +From a9e2bfff9a39efe94a066735509643fc3bed1ba0 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Mon, 28 Sep 2015 23:38:59 +0100 +Subject: [PATCH 086/232] irq-bcm2835: Fix building with 2708 + +--- + drivers/irqchip/irq-bcm2835.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/irqchip/irq-bcm2835.c ++++ b/drivers/irqchip/irq-bcm2835.c +@@ -82,6 +82,7 @@ + #define NR_BANKS 3 + #define IRQS_PER_BANK 32 + #define NUMBER_IRQS MAKE_HWIRQ(NR_BANKS, 0) ++#undef FIQ_START + #define FIQ_START (NR_IRQS_BANK0 + MAKE_HWIRQ(NR_BANKS - 1, 0)) + + static const int reg_pending[] __initconst = { 0x00, 0x04, 0x08 }; +@@ -256,7 +257,7 @@ static int __init armctrl_of_init(struct + MAKE_HWIRQ(b, i) + NUMBER_IRQS); + BUG_ON(irq <= 0); + irq_set_chip(irq, &armctrl_chip); +- set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); ++ irq_set_probe(irq); + } + } + init_FIQ(FIQ_START); diff --git a/target/linux/brcm2708/patches-4.4/0086-tpa6130a2-Add-headphone-switch-control.patch b/target/linux/brcm2708/patches-4.4/0086-tpa6130a2-Add-headphone-switch-control.patch deleted file mode 100644 index 0daaee22c6..0000000000 --- a/target/linux/brcm2708/patches-4.4/0086-tpa6130a2-Add-headphone-switch-control.patch +++ /dev/null @@ -1,91 +0,0 @@ -From 2d340e2fbbc9f4735b798601a43ae9f0f4cc5742 Mon Sep 17 00:00:00 2001 -From: Jan Grulich -Date: Mon, 24 Aug 2015 16:02:34 +0100 -Subject: [PATCH 086/170] tpa6130a2: Add headphone switch control - -Signed-off-by: Jan Grulich ---- - sound/soc/codecs/tpa6130a2.c | 29 ++++++++++++++++++++++++++--- - 1 file changed, 26 insertions(+), 3 deletions(-) - ---- a/sound/soc/codecs/tpa6130a2.c -+++ b/sound/soc/codecs/tpa6130a2.c -@@ -4,6 +4,7 @@ - * Copyright (C) Nokia Corporation - * - * Author: Peter Ujfalusi -+ * Modified: Jan Grulich - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License -@@ -52,6 +53,8 @@ struct tpa6130a2_data { - enum tpa_model id; - }; - -+static void tpa6130a2_channel_enable(u8 channel, int enable); -+ - static int tpa6130a2_i2c_read(int reg) - { - struct tpa6130a2_data *data; -@@ -189,7 +192,7 @@ exit: - } - - static int tpa6130a2_get_volsw(struct snd_kcontrol *kcontrol, -- struct snd_ctl_elem_value *ucontrol) -+ struct snd_ctl_elem_value *ucontrol) - { - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; -@@ -218,7 +221,7 @@ static int tpa6130a2_get_volsw(struct sn - } - - static int tpa6130a2_put_volsw(struct snd_kcontrol *kcontrol, -- struct snd_ctl_elem_value *ucontrol) -+ struct snd_ctl_elem_value *ucontrol) - { - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; -@@ -255,8 +258,22 @@ static int tpa6130a2_put_volsw(struct sn - return 1; - } - -+static int tpa6130a2_put_hp_sw(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ int enable = ucontrol->value.integer.value[0]; -+ unsigned int state; -+ -+ state = (tpa6130a2_read(TPA6130A2_REG_VOL_MUTE) & 0x80) == 0; -+ if (state == enable) -+ return 0; /* No change */ -+ -+ tpa6130a2_channel_enable(TPA6130A2_HP_EN_R | TPA6130A2_HP_EN_L, enable); -+ return 1; /* Changed */ -+} -+ - /* -- * TPA6130 volume. From -59.5 to 4 dB with increasing step size when going -+ * TPA6130 volume. From -59.5 to +4.0 dB with increasing step size when going - * down in gain. - */ - static const DECLARE_TLV_DB_RANGE(tpa6130_tlv, -@@ -277,6 +294,9 @@ static const struct snd_kcontrol_new tpa - TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0, - tpa6130a2_get_volsw, tpa6130a2_put_volsw, - tpa6130_tlv), -+ SOC_SINGLE_EXT("TPA6130A2 Headphone Playback Switch", -+ TPA6130A2_REG_VOL_MUTE, 7, 1, 1, -+ tpa6130a2_get_volsw, tpa6130a2_put_hp_sw), - }; - - static const DECLARE_TLV_DB_RANGE(tpa6140_tlv, -@@ -290,6 +310,9 @@ static const struct snd_kcontrol_new tpa - TPA6130A2_REG_VOL_MUTE, 1, 0x1f, 0, - tpa6130a2_get_volsw, tpa6130a2_put_volsw, - tpa6140_tlv), -+ SOC_SINGLE_EXT("TPA6140A2 Headphone Playback Switch", -+ TPA6130A2_REG_VOL_MUTE, 7, 1, 1, -+ tpa6130a2_get_volsw, tpa6130a2_put_hp_sw), - }; - - /* diff --git a/target/linux/brcm2708/patches-4.4/0087-irq-bcm2835-Fix-building-with-2708.patch b/target/linux/brcm2708/patches-4.4/0087-irq-bcm2835-Fix-building-with-2708.patch deleted file mode 100644 index 5c7c661363..0000000000 --- a/target/linux/brcm2708/patches-4.4/0087-irq-bcm2835-Fix-building-with-2708.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 6fc9d40bec5dbcf3ae8b52ab6850ed0b18193c82 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Mon, 28 Sep 2015 23:38:59 +0100 -Subject: [PATCH 087/170] irq-bcm2835: Fix building with 2708 - ---- - drivers/irqchip/irq-bcm2835.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - ---- a/drivers/irqchip/irq-bcm2835.c -+++ b/drivers/irqchip/irq-bcm2835.c -@@ -82,6 +82,7 @@ - #define NR_BANKS 3 - #define IRQS_PER_BANK 32 - #define NUMBER_IRQS MAKE_HWIRQ(NR_BANKS, 0) -+#undef FIQ_START - #define FIQ_START (NR_IRQS_BANK0 + MAKE_HWIRQ(NR_BANKS - 1, 0)) - - static const int reg_pending[] __initconst = { 0x00, 0x04, 0x08 }; -@@ -256,7 +257,7 @@ static int __init armctrl_of_init(struct - MAKE_HWIRQ(b, i) + NUMBER_IRQS); - BUG_ON(irq <= 0); - irq_set_chip(irq, &armctrl_chip); -- set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); -+ irq_set_probe(irq); - } - } - init_FIQ(FIQ_START); diff --git a/target/linux/brcm2708/patches-4.4/0087-rpi_display-add-backlight-driver-and-overlay.patch b/target/linux/brcm2708/patches-4.4/0087-rpi_display-add-backlight-driver-and-overlay.patch new file mode 100644 index 0000000000..f9c84bb9ee --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0087-rpi_display-add-backlight-driver-and-overlay.patch @@ -0,0 +1,250 @@ +From 0a8cace2c9d473ac347cc08dfe19b24ad5ca3d2b Mon Sep 17 00:00:00 2001 +From: P33M +Date: Wed, 21 Oct 2015 14:55:21 +0100 +Subject: [PATCH 087/232] rpi_display: add backlight driver and overlay + +Add a mailbox-driven backlight controller for the Raspberry Pi DSI +touchscreen display. Requires updated GPU firmware to recognise the +mailbox request. + +Signed-off-by: Gordon Hollingworth +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 6 ++ + .../boot/dts/overlays/rpi-backlight-overlay.dts | 21 ++++ + arch/arm/configs/bcm2709_defconfig | 1 + + arch/arm/configs/bcmrpi_defconfig | 1 + + drivers/video/backlight/Kconfig | 6 ++ + drivers/video/backlight/Makefile | 1 + + drivers/video/backlight/rpi_backlight.c | 119 +++++++++++++++++++++ + include/soc/bcm2835/raspberrypi-firmware.h | 1 + + 9 files changed, 157 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/rpi-backlight-overlay.dts + create mode 100644 drivers/video/backlight/rpi_backlight.c + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -38,6 +38,7 @@ dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overl + dtb-$(RPI_DT_OVERLAYS) += pwm-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += pwm-2chan-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += raspidac3-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += rpi-backlight-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += rpi-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += rpi-display-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += rpi-ft5406-overlay.dtb +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -462,6 +462,12 @@ Load: dtoverlay=raspidac3 + Params: + + ++Name: rpi-backlight ++Info: Raspberry Pi official display backlight driver ++Load: dtoverlay=rpi-backlight ++Params: ++ ++ + Name: rpi-dac + Info: Configures the RPi DAC audio card + Load: dtoverlay=rpi-dac +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/rpi-backlight-overlay.dts +@@ -0,0 +1,21 @@ ++/* ++ * Devicetree overlay for mailbox-driven Raspberry Pi DSI Display ++ * backlight controller ++ */ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target-path = "/"; ++ __overlay__ { ++ rpi_backlight: rpi_backlight { ++ compatible = "raspberrypi,rpi-backlight"; ++ firmware = <&firmware>; ++ status = "okay"; ++ }; ++ }; ++ }; ++}; +--- a/arch/arm/configs/bcm2709_defconfig ++++ b/arch/arm/configs/bcm2709_defconfig +@@ -808,6 +808,7 @@ CONFIG_FB_UDL=m + CONFIG_FB_SSD1307=m + CONFIG_FB_RPISENSE=m + # CONFIG_BACKLIGHT_GENERIC is not set ++CONFIG_BACKLIGHT_RPI=m + CONFIG_BACKLIGHT_GPIO=m + CONFIG_FRAMEBUFFER_CONSOLE=y + CONFIG_LOGO=y +--- a/arch/arm/configs/bcmrpi_defconfig ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -801,6 +801,7 @@ CONFIG_FB_UDL=m + CONFIG_FB_SSD1307=m + CONFIG_FB_RPISENSE=m + # CONFIG_BACKLIGHT_GENERIC is not set ++CONFIG_BACKLIGHT_RPI=m + CONFIG_BACKLIGHT_GPIO=m + CONFIG_FRAMEBUFFER_CONSOLE=y + CONFIG_LOGO=y +--- a/drivers/video/backlight/Kconfig ++++ b/drivers/video/backlight/Kconfig +@@ -265,6 +265,12 @@ config BACKLIGHT_PWM + If you have a LCD backlight adjustable by PWM, say Y to enable + this driver. + ++config BACKLIGHT_RPI ++ tristate "Raspberry Pi display firmware driven backlight" ++ help ++ If you have the Raspberry Pi DSI touchscreen display, say Y to ++ enable the mailbox-controlled backlight driver. ++ + config BACKLIGHT_DA903X + tristate "Backlight Driver for DA9030/DA9034 using WLED" + depends on PMIC_DA903X +--- a/drivers/video/backlight/Makefile ++++ b/drivers/video/backlight/Makefile +@@ -50,6 +50,7 @@ obj-$(CONFIG_BACKLIGHT_PANDORA) += pand + obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o + obj-$(CONFIG_BACKLIGHT_PM8941_WLED) += pm8941-wled.o + obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o ++obj-$(CONFIG_BACKLIGHT_RPI) += rpi_backlight.o + obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o + obj-$(CONFIG_BACKLIGHT_SKY81452) += sky81452-backlight.o + obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o +--- /dev/null ++++ b/drivers/video/backlight/rpi_backlight.c +@@ -0,0 +1,119 @@ ++/* ++ * rpi_bl.c - Backlight controller through VPU ++ * ++ * 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 ++ ++struct rpi_backlight { ++ struct device *dev; ++ struct device *fbdev; ++ struct rpi_firmware *fw; ++}; ++ ++static int rpi_backlight_update_status(struct backlight_device *bl) ++{ ++ struct rpi_backlight *gbl = bl_get_data(bl); ++ int brightness = bl->props.brightness; ++ int ret; ++ ++ if (bl->props.power != FB_BLANK_UNBLANK || ++ bl->props.fb_blank != FB_BLANK_UNBLANK || ++ bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) ++ brightness = 0; ++ ++ ret = rpi_firmware_property(gbl->fw, ++ RPI_FIRMWARE_FRAMEBUFFER_SET_BACKLIGHT, ++ &brightness, sizeof(brightness)); ++ if (ret) { ++ dev_err(gbl->dev, "Failed to set brightness\n"); ++ return ret; ++ } ++ ++ if (brightness < 0) { ++ dev_err(gbl->dev, "Backlight change failed\n"); ++ return -EAGAIN; ++ } ++ ++ return 0; ++} ++ ++static const struct backlight_ops rpi_backlight_ops = { ++ .options = BL_CORE_SUSPENDRESUME, ++ .update_status = rpi_backlight_update_status, ++}; ++ ++static int rpi_backlight_probe(struct platform_device *pdev) ++{ ++ struct backlight_properties props; ++ struct backlight_device *bl; ++ struct rpi_backlight *gbl; ++ struct device_node *fw_node; ++ ++ gbl = devm_kzalloc(&pdev->dev, sizeof(*gbl), GFP_KERNEL); ++ if (gbl == NULL) ++ return -ENOMEM; ++ ++ gbl->dev = &pdev->dev; ++ ++ fw_node = of_parse_phandle(pdev->dev.of_node, "firmware", 0); ++ if (!fw_node) { ++ dev_err(&pdev->dev, "Missing firmware node\n"); ++ return -ENOENT; ++ } ++ ++ gbl->fw = rpi_firmware_get(fw_node); ++ if (!gbl->fw) ++ return -EPROBE_DEFER; ++ ++ memset(&props, 0, sizeof(props)); ++ props.type = BACKLIGHT_RAW; ++ props.max_brightness = 255; ++ bl = devm_backlight_device_register(&pdev->dev, dev_name(&pdev->dev), ++ &pdev->dev, gbl, &rpi_backlight_ops, ++ &props); ++ if (IS_ERR(bl)) { ++ dev_err(&pdev->dev, "failed to register backlight\n"); ++ return PTR_ERR(bl); ++ } ++ ++ bl->props.brightness = 255; ++ backlight_update_status(bl); ++ ++ platform_set_drvdata(pdev, bl); ++ return 0; ++} ++ ++static const struct of_device_id rpi_backlight_of_match[] = { ++ { .compatible = "raspberrypi,rpi-backlight" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, rpi_backlight_of_match); ++ ++static struct platform_driver rpi_backlight_driver = { ++ .driver = { ++ .name = "rpi-backlight", ++ .of_match_table = of_match_ptr(rpi_backlight_of_match), ++ }, ++ .probe = rpi_backlight_probe, ++}; ++ ++module_platform_driver(rpi_backlight_driver); ++ ++MODULE_AUTHOR("Gordon Hollingworth "); ++MODULE_DESCRIPTION("Raspberry Pi mailbox based Backlight Driver"); ++MODULE_LICENSE("GPL"); +--- a/include/soc/bcm2835/raspberrypi-firmware.h ++++ b/include/soc/bcm2835/raspberrypi-firmware.h +@@ -112,6 +112,7 @@ enum rpi_firmware_property_tag { + RPI_FIRMWARE_FRAMEBUFFER_SET_OVERSCAN = 0x0004800a, + RPI_FIRMWARE_FRAMEBUFFER_SET_PALETTE = 0x0004800b, + RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC = 0x0004800e, ++ RPI_FIRMWARE_FRAMEBUFFER_SET_BACKLIGHT = 0x0004800f, + + RPI_FIRMWARE_VCHIQ_INIT = 0x00048010, + diff --git a/target/linux/brcm2708/patches-4.4/0088-bcm2835-dma-Fix-up-convert-to-DMA-pool.patch b/target/linux/brcm2708/patches-4.4/0088-bcm2835-dma-Fix-up-convert-to-DMA-pool.patch new file mode 100644 index 0000000000..d7263d1624 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0088-bcm2835-dma-Fix-up-convert-to-DMA-pool.patch @@ -0,0 +1,85 @@ +From ec2e48fda22c57cab56a4332d1a095f91c919493 Mon Sep 17 00:00:00 2001 +From: Matthias Reichl +Date: Mon, 16 Nov 2015 14:05:35 +0000 +Subject: [PATCH 088/232] bcm2835-dma: Fix up convert to DMA pool + +--- + drivers/dma/bcm2835-dma.c | 36 ++++++++++++++++++++++++++---------- + 1 file changed, 26 insertions(+), 10 deletions(-) + +--- a/drivers/dma/bcm2835-dma.c ++++ b/drivers/dma/bcm2835-dma.c +@@ -488,6 +488,17 @@ static struct dma_async_tx_descriptor *b + c->cyclic = true; + + return vchan_tx_prep(&c->vc, &d->vd, flags); ++error_cb: ++ i--; ++ for (; i >= 0; i--) { ++ struct bcm2835_cb_entry *cb_entry = &d->cb_list[i]; ++ ++ dma_pool_free(c->cb_pool, cb_entry->cb, cb_entry->paddr); ++ } ++ ++ kfree(d->cb_list); ++ kfree(d); ++ return NULL; + } + + static struct dma_async_tx_descriptor * +@@ -534,6 +545,7 @@ bcm2835_dma_prep_slave_sg(struct dma_cha + if (!d) + return NULL; + ++ d->c = c; + d->dir = direction; + + if (c->ch >= 8) /* LITE channel */ +@@ -553,15 +565,21 @@ bcm2835_dma_prep_slave_sg(struct dma_cha + d->frames += len / max_size + 1; + } + +- /* 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) { ++ d->cb_list = kcalloc(d->frames, sizeof(*d->cb_list), GFP_KERNEL); ++ if (!d->cb_list) { + kfree(d); + return NULL; + } ++ /* Allocate memory for control blocks */ ++ for (i = 0; i < d->frames; i++) { ++ struct bcm2835_cb_entry *cb_entry = &d->cb_list[i]; ++ ++ cb_entry->cb = dma_pool_zalloc(c->cb_pool, GFP_ATOMIC, ++ &cb_entry->paddr); ++ ++ if (!cb_entry->cb) ++ goto error_cb; ++ } + + /* + * Iterate over all SG entries, create a control block +@@ -578,7 +596,7 @@ bcm2835_dma_prep_slave_sg(struct dma_cha + + for (j = 0; j < len; j += max_size) { + struct bcm2835_dma_cb *control_block = +- &d->control_block_base[i + split_cnt]; ++ d->cb_list[i + split_cnt].cb; + + /* Setup addresses */ + if (d->dir == DMA_DEV_TO_MEM) { +@@ -620,9 +638,7 @@ bcm2835_dma_prep_slave_sg(struct dma_cha + 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 + split_cnt + 1); ++ d->cb_list[i + split_cnt + 1].paddr; + } else { + /* Next block is empty. */ + control_block->next = 0; diff --git a/target/linux/brcm2708/patches-4.4/0088-rpi_display-add-backlight-driver-and-overlay.patch b/target/linux/brcm2708/patches-4.4/0088-rpi_display-add-backlight-driver-and-overlay.patch deleted file mode 100644 index 02a78c8e07..0000000000 --- a/target/linux/brcm2708/patches-4.4/0088-rpi_display-add-backlight-driver-and-overlay.patch +++ /dev/null @@ -1,250 +0,0 @@ -From 0e7ec1e61b149567d5af63169cd4d701c133356f Mon Sep 17 00:00:00 2001 -From: P33M -Date: Wed, 21 Oct 2015 14:55:21 +0100 -Subject: [PATCH 088/170] rpi_display: add backlight driver and overlay - -Add a mailbox-driven backlight controller for the Raspberry Pi DSI -touchscreen display. Requires updated GPU firmware to recognise the -mailbox request. - -Signed-off-by: Gordon Hollingworth ---- - arch/arm/boot/dts/overlays/Makefile | 1 + - arch/arm/boot/dts/overlays/README | 6 ++ - .../boot/dts/overlays/rpi-backlight-overlay.dts | 21 ++++ - arch/arm/configs/bcm2709_defconfig | 1 + - arch/arm/configs/bcmrpi_defconfig | 1 + - drivers/video/backlight/Kconfig | 6 ++ - drivers/video/backlight/Makefile | 1 + - drivers/video/backlight/rpi_backlight.c | 119 +++++++++++++++++++++ - include/soc/bcm2835/raspberrypi-firmware.h | 1 + - 9 files changed, 157 insertions(+) - create mode 100644 arch/arm/boot/dts/overlays/rpi-backlight-overlay.dts - create mode 100644 drivers/video/backlight/rpi_backlight.c - ---- a/arch/arm/boot/dts/overlays/Makefile -+++ b/arch/arm/boot/dts/overlays/Makefile -@@ -38,6 +38,7 @@ dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overl - dtb-$(RPI_DT_OVERLAYS) += pwm-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += pwm-2chan-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += raspidac3-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += rpi-backlight-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += rpi-dac-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += rpi-display-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += rpi-ft5406-overlay.dtb ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -462,6 +462,12 @@ Load: dtoverlay=raspidac3 - Params: - - -+Name: rpi-backlight -+Info: Raspberry Pi official display backlight driver -+Load: dtoverlay=rpi-backlight -+Params: -+ -+ - Name: rpi-dac - Info: Configures the RPi DAC audio card - Load: dtoverlay=rpi-dac ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/rpi-backlight-overlay.dts -@@ -0,0 +1,21 @@ -+/* -+ * Devicetree overlay for mailbox-driven Raspberry Pi DSI Display -+ * backlight controller -+ */ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target-path = "/"; -+ __overlay__ { -+ rpi_backlight: rpi_backlight { -+ compatible = "raspberrypi,rpi-backlight"; -+ firmware = <&firmware>; -+ status = "okay"; -+ }; -+ }; -+ }; -+}; ---- a/arch/arm/configs/bcm2709_defconfig -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -808,6 +808,7 @@ CONFIG_FB_UDL=m - CONFIG_FB_SSD1307=m - CONFIG_FB_RPISENSE=m - # CONFIG_BACKLIGHT_GENERIC is not set -+CONFIG_BACKLIGHT_RPI=m - CONFIG_BACKLIGHT_GPIO=m - CONFIG_FRAMEBUFFER_CONSOLE=y - CONFIG_LOGO=y ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -801,6 +801,7 @@ CONFIG_FB_UDL=m - CONFIG_FB_SSD1307=m - CONFIG_FB_RPISENSE=m - # CONFIG_BACKLIGHT_GENERIC is not set -+CONFIG_BACKLIGHT_RPI=m - CONFIG_BACKLIGHT_GPIO=m - CONFIG_FRAMEBUFFER_CONSOLE=y - CONFIG_LOGO=y ---- a/drivers/video/backlight/Kconfig -+++ b/drivers/video/backlight/Kconfig -@@ -265,6 +265,12 @@ config BACKLIGHT_PWM - If you have a LCD backlight adjustable by PWM, say Y to enable - this driver. - -+config BACKLIGHT_RPI -+ tristate "Raspberry Pi display firmware driven backlight" -+ help -+ If you have the Raspberry Pi DSI touchscreen display, say Y to -+ enable the mailbox-controlled backlight driver. -+ - config BACKLIGHT_DA903X - tristate "Backlight Driver for DA9030/DA9034 using WLED" - depends on PMIC_DA903X ---- a/drivers/video/backlight/Makefile -+++ b/drivers/video/backlight/Makefile -@@ -50,6 +50,7 @@ obj-$(CONFIG_BACKLIGHT_PANDORA) += pand - obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o - obj-$(CONFIG_BACKLIGHT_PM8941_WLED) += pm8941-wled.o - obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o -+obj-$(CONFIG_BACKLIGHT_RPI) += rpi_backlight.o - obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o - obj-$(CONFIG_BACKLIGHT_SKY81452) += sky81452-backlight.o - obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o ---- /dev/null -+++ b/drivers/video/backlight/rpi_backlight.c -@@ -0,0 +1,119 @@ -+/* -+ * rpi_bl.c - Backlight controller through VPU -+ * -+ * 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 -+ -+struct rpi_backlight { -+ struct device *dev; -+ struct device *fbdev; -+ struct rpi_firmware *fw; -+}; -+ -+static int rpi_backlight_update_status(struct backlight_device *bl) -+{ -+ struct rpi_backlight *gbl = bl_get_data(bl); -+ int brightness = bl->props.brightness; -+ int ret; -+ -+ if (bl->props.power != FB_BLANK_UNBLANK || -+ bl->props.fb_blank != FB_BLANK_UNBLANK || -+ bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) -+ brightness = 0; -+ -+ ret = rpi_firmware_property(gbl->fw, -+ RPI_FIRMWARE_FRAMEBUFFER_SET_BACKLIGHT, -+ &brightness, sizeof(brightness)); -+ if (ret) { -+ dev_err(gbl->dev, "Failed to set brightness\n"); -+ return ret; -+ } -+ -+ if (brightness < 0) { -+ dev_err(gbl->dev, "Backlight change failed\n"); -+ return -EAGAIN; -+ } -+ -+ return 0; -+} -+ -+static const struct backlight_ops rpi_backlight_ops = { -+ .options = BL_CORE_SUSPENDRESUME, -+ .update_status = rpi_backlight_update_status, -+}; -+ -+static int rpi_backlight_probe(struct platform_device *pdev) -+{ -+ struct backlight_properties props; -+ struct backlight_device *bl; -+ struct rpi_backlight *gbl; -+ struct device_node *fw_node; -+ -+ gbl = devm_kzalloc(&pdev->dev, sizeof(*gbl), GFP_KERNEL); -+ if (gbl == NULL) -+ return -ENOMEM; -+ -+ gbl->dev = &pdev->dev; -+ -+ fw_node = of_parse_phandle(pdev->dev.of_node, "firmware", 0); -+ if (!fw_node) { -+ dev_err(&pdev->dev, "Missing firmware node\n"); -+ return -ENOENT; -+ } -+ -+ gbl->fw = rpi_firmware_get(fw_node); -+ if (!gbl->fw) -+ return -EPROBE_DEFER; -+ -+ memset(&props, 0, sizeof(props)); -+ props.type = BACKLIGHT_RAW; -+ props.max_brightness = 255; -+ bl = devm_backlight_device_register(&pdev->dev, dev_name(&pdev->dev), -+ &pdev->dev, gbl, &rpi_backlight_ops, -+ &props); -+ if (IS_ERR(bl)) { -+ dev_err(&pdev->dev, "failed to register backlight\n"); -+ return PTR_ERR(bl); -+ } -+ -+ bl->props.brightness = 255; -+ backlight_update_status(bl); -+ -+ platform_set_drvdata(pdev, bl); -+ return 0; -+} -+ -+static const struct of_device_id rpi_backlight_of_match[] = { -+ { .compatible = "raspberrypi,rpi-backlight" }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(of, rpi_backlight_of_match); -+ -+static struct platform_driver rpi_backlight_driver = { -+ .driver = { -+ .name = "rpi-backlight", -+ .of_match_table = of_match_ptr(rpi_backlight_of_match), -+ }, -+ .probe = rpi_backlight_probe, -+}; -+ -+module_platform_driver(rpi_backlight_driver); -+ -+MODULE_AUTHOR("Gordon Hollingworth "); -+MODULE_DESCRIPTION("Raspberry Pi mailbox based Backlight Driver"); -+MODULE_LICENSE("GPL"); ---- a/include/soc/bcm2835/raspberrypi-firmware.h -+++ b/include/soc/bcm2835/raspberrypi-firmware.h -@@ -112,6 +112,7 @@ enum rpi_firmware_property_tag { - RPI_FIRMWARE_FRAMEBUFFER_SET_OVERSCAN = 0x0004800a, - RPI_FIRMWARE_FRAMEBUFFER_SET_PALETTE = 0x0004800b, - RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC = 0x0004800e, -+ RPI_FIRMWARE_FRAMEBUFFER_SET_BACKLIGHT = 0x0004800f, - - RPI_FIRMWARE_VCHIQ_INIT = 0x00048010, - diff --git a/target/linux/brcm2708/patches-4.4/0089-bcm2835-dma-Fix-up-convert-to-DMA-pool.patch b/target/linux/brcm2708/patches-4.4/0089-bcm2835-dma-Fix-up-convert-to-DMA-pool.patch deleted file mode 100644 index cfeccd2a43..0000000000 --- a/target/linux/brcm2708/patches-4.4/0089-bcm2835-dma-Fix-up-convert-to-DMA-pool.patch +++ /dev/null @@ -1,85 +0,0 @@ -From 0459ca97c1f2753ce805ab58ff17847cb4c142e0 Mon Sep 17 00:00:00 2001 -From: Matthias Reichl -Date: Mon, 16 Nov 2015 14:05:35 +0000 -Subject: [PATCH 089/170] bcm2835-dma: Fix up convert to DMA pool - ---- - drivers/dma/bcm2835-dma.c | 36 ++++++++++++++++++++++++++---------- - 1 file changed, 26 insertions(+), 10 deletions(-) - ---- a/drivers/dma/bcm2835-dma.c -+++ b/drivers/dma/bcm2835-dma.c -@@ -488,6 +488,17 @@ static struct dma_async_tx_descriptor *b - c->cyclic = true; - - return vchan_tx_prep(&c->vc, &d->vd, flags); -+error_cb: -+ i--; -+ for (; i >= 0; i--) { -+ struct bcm2835_cb_entry *cb_entry = &d->cb_list[i]; -+ -+ dma_pool_free(c->cb_pool, cb_entry->cb, cb_entry->paddr); -+ } -+ -+ kfree(d->cb_list); -+ kfree(d); -+ return NULL; - } - - static struct dma_async_tx_descriptor * -@@ -534,6 +545,7 @@ bcm2835_dma_prep_slave_sg(struct dma_cha - if (!d) - return NULL; - -+ d->c = c; - d->dir = direction; - - if (c->ch >= 8) /* LITE channel */ -@@ -553,15 +565,21 @@ bcm2835_dma_prep_slave_sg(struct dma_cha - d->frames += len / max_size + 1; - } - -- /* 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) { -+ d->cb_list = kcalloc(d->frames, sizeof(*d->cb_list), GFP_KERNEL); -+ if (!d->cb_list) { - kfree(d); - return NULL; - } -+ /* Allocate memory for control blocks */ -+ for (i = 0; i < d->frames; i++) { -+ struct bcm2835_cb_entry *cb_entry = &d->cb_list[i]; -+ -+ cb_entry->cb = dma_pool_zalloc(c->cb_pool, GFP_ATOMIC, -+ &cb_entry->paddr); -+ -+ if (!cb_entry->cb) -+ goto error_cb; -+ } - - /* - * Iterate over all SG entries, create a control block -@@ -578,7 +596,7 @@ bcm2835_dma_prep_slave_sg(struct dma_cha - - for (j = 0; j < len; j += max_size) { - struct bcm2835_dma_cb *control_block = -- &d->control_block_base[i + split_cnt]; -+ d->cb_list[i + split_cnt].cb; - - /* Setup addresses */ - if (d->dir == DMA_DEV_TO_MEM) { -@@ -620,9 +638,7 @@ bcm2835_dma_prep_slave_sg(struct dma_cha - 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 + split_cnt + 1); -+ d->cb_list[i + split_cnt + 1].paddr; - } else { - /* Next block is empty. */ - control_block->next = 0; diff --git a/target/linux/brcm2708/patches-4.4/0089-scripts-Multi-platform-support-for-mkknlimg-and-knli.patch b/target/linux/brcm2708/patches-4.4/0089-scripts-Multi-platform-support-for-mkknlimg-and-knli.patch new file mode 100644 index 0000000000..a3f7e98905 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0089-scripts-Multi-platform-support-for-mkknlimg-and-knli.patch @@ -0,0 +1,247 @@ +From 9a67f8f17bb07bbfeee650327c88b6d381ab4af1 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 11 Nov 2015 11:38:59 +0000 +Subject: [PATCH 089/232] scripts: Multi-platform support for mkknlimg and + knlinfo + +The firmware uses tags in the kernel trailer to choose which dtb file +to load. Current firmware loads bcm2835-*.dtb if the '283x' tag is true, +otherwise it loads bcm270*.dtb. This scheme breaks if an image supports +multiple platforms. + +This patch adds '270X' and '283X' tags to indicate support for RPi and +upstream platforms, respectively. '283x' (note lower case 'x') is left +for old firmware, and is only set if the image only supports upstream +builds. +--- + scripts/knlinfo | 2 + + scripts/mkknlimg | 136 +++++++++++++++++++++++++++++++------------------------ + 2 files changed, 80 insertions(+), 58 deletions(-) + +--- a/scripts/knlinfo ++++ b/scripts/knlinfo +@@ -18,6 +18,8 @@ my %atom_formats = + ( + 'DTOK' => \&format_bool, + 'KVer' => \&format_string, ++ '270X' => \&format_bool, ++ '283X' => \&format_bool, + '283x' => \&format_bool, + ); + +--- a/scripts/mkknlimg ++++ b/scripts/mkknlimg +@@ -13,12 +13,20 @@ use strict; + use warnings; + use integer; + ++use constant FLAG_PI => 0x01; ++use constant FLAG_DTOK => 0x02; ++use constant FLAG_DDTK => 0x04; ++use constant FLAG_270X => 0x08; ++use constant FLAG_283X => 0x10; ++ + my $trailer_magic = 'RPTL'; + + my $tmpfile1 = "/tmp/mkknlimg_$$.1"; + my $tmpfile2 = "/tmp/mkknlimg_$$.2"; + + my $dtok = 0; ++my $ddtk = 0; ++my $is_270x = 0; + my $is_283x = 0; + + while (@ARGV && ($ARGV[0] =~ /^-/)) +@@ -28,6 +36,14 @@ while (@ARGV && ($ARGV[0] =~ /^-/)) + { + $dtok = 1; + } ++ elsif ($arg eq '--ddtk') ++ { ++ $ddtk = 1; ++ } ++ elsif ($arg eq '--270x') ++ { ++ $is_270x = 1; ++ } + elsif ($arg eq '--283x') + { + $is_283x = 1; +@@ -50,30 +66,33 @@ if (! -r $kernel_file) + usage(); + } + +-my @wanted_strings = +-( +- 'bcm2708_fb', +- 'brcm,bcm2835-mmc', +- 'brcm,bcm2835-sdhost', +- 'brcm,bcm2708-pinctrl', +- 'brcm,bcm2835-gpio', +- 'brcm,bcm2835', +- 'brcm,bcm2836' +-); ++my $wanted_strings = ++{ ++ 'bcm2708_fb' => FLAG_PI, ++ 'brcm,bcm2835-mmc' => FLAG_PI, ++ 'brcm,bcm2835-sdhost' => FLAG_PI, ++ 'brcm,bcm2708-pinctrl' => FLAG_PI | FLAG_DTOK, ++ 'brcm,bcm2835-gpio' => FLAG_PI | FLAG_DTOK, ++ 'brcm,bcm2708' => FLAG_PI | FLAG_DTOK | FLAG_270X, ++ 'brcm,bcm2709' => FLAG_PI | FLAG_DTOK | FLAG_270X, ++ 'brcm,bcm2835' => FLAG_PI | FLAG_DTOK | FLAG_283X, ++ 'brcm,bcm2836' => FLAG_PI | FLAG_DTOK | FLAG_283X, ++ 'of_overlay_apply' => FLAG_DTOK | FLAG_DDTK, ++}; + + my $res = try_extract($kernel_file, $tmpfile1); +-$res = try_decompress('\037\213\010', 'xy', 'gunzip', 0, +- $kernel_file, $tmpfile1, $tmpfile2) if (!$res); +-$res = try_decompress('\3757zXZ\000', 'abcde', 'unxz --single-stream', -1, +- $kernel_file, $tmpfile1, $tmpfile2) if (!$res); +-$res = try_decompress('BZh', 'xy', 'bunzip2', 0, +- $kernel_file, $tmpfile1, $tmpfile2) if (!$res); +-$res = try_decompress('\135\0\0\0', 'xxx', 'unlzma', 0, +- $kernel_file, $tmpfile1, $tmpfile2) if (!$res); +-$res = try_decompress('\211\114\132', 'xy', 'lzop -d', 0, +- $kernel_file, $tmpfile1, $tmpfile2) if (!$res); +-$res = try_decompress('\002\041\114\030', 'xy', 'lz4 -d', 1, +- $kernel_file, $tmpfile1, $tmpfile2) if (!$res); ++$res ||= try_decompress('\037\213\010', 'xy', 'gunzip', 0, ++ $kernel_file, $tmpfile1, $tmpfile2); ++$res ||= try_decompress('\3757zXZ\000', 'abcde', 'unxz --single-stream', -1, ++ $kernel_file, $tmpfile1, $tmpfile2); ++$res ||= try_decompress('BZh', 'xy', 'bunzip2', 0, ++ $kernel_file, $tmpfile1, $tmpfile2); ++$res ||= try_decompress('\135\0\0\0', 'xxx', 'unlzma', 0, ++ $kernel_file, $tmpfile1, $tmpfile2); ++$res ||= try_decompress('\211\114\132', 'xy', 'lzop -d', 0, ++ $kernel_file, $tmpfile1, $tmpfile2); ++$res ||= try_decompress('\002\041\114\030', 'xy', 'lz4 -d', 1, ++ $kernel_file, $tmpfile1, $tmpfile2); + + my $append_trailer; + my $trailer; +@@ -83,27 +102,21 @@ $append_trailer = $dtok; + + if ($res) + { +- $kver = $res->{''} || '?'; ++ $kver = $res->{'kver'} || '?'; ++ my $flags = $res->{'flags'}; + print("Version: $kver\n"); + +- $append_trailer = $dtok; +- if (!$dtok) ++ if ($flags & FLAG_PI) + { +- if (config_bool($res, 'bcm2708_fb') || +- config_bool($res, 'brcm,bcm2835-mmc') || +- config_bool($res, 'brcm,bcm2835-sdhost')) +- { +- $dtok ||= config_bool($res, 'brcm,bcm2708-pinctrl'); +- $dtok ||= config_bool($res, 'brcm,bcm2835-gpio'); +- $is_283x ||= config_bool($res, 'brcm,bcm2835'); +- $is_283x ||= config_bool($res, 'brcm,bcm2836'); +- $dtok ||= $is_283x; +- $append_trailer = 1; +- } +- else +- { +- print ("* This doesn't look like a Raspberry Pi kernel. In pass-through mode.\n"); +- } ++ $append_trailer = 1; ++ $dtok ||= ($flags & FLAG_DTOK) != 0; ++ $is_270x ||= ($flags & FLAG_270X) != 0; ++ $is_283x ||= ($flags & FLAG_283X) != 0; ++ $ddtk ||= ($flags & FLAG_DDTK) != 0; ++ } ++ else ++ { ++ print ("* This doesn't look like a Raspberry Pi kernel. In pass-through mode.\n"); + } + } + elsif (!$dtok) +@@ -114,6 +127,8 @@ elsif (!$dtok) + if ($append_trailer) + { + printf("DT: %s\n", $dtok ? "y" : "n"); ++ printf("DDT: %s\n", $ddtk ? "y" : "n") if ($ddtk); ++ printf("270x: %s\n", $is_270x ? "y" : "n"); + printf("283x: %s\n", $is_283x ? "y" : "n"); + + my @atoms; +@@ -121,7 +136,10 @@ if ($append_trailer) + push @atoms, [ $trailer_magic, pack('V', 0) ]; + push @atoms, [ 'KVer', $kver ]; + push @atoms, [ 'DTOK', pack('V', $dtok) ]; +- push @atoms, [ '283x', pack('V', $is_283x) ]; ++ push @atoms, [ 'DDTK', pack('V', $ddtk) ] if ($ddtk); ++ push @atoms, [ '270X', pack('V', $is_270x) ]; ++ push @atoms, [ '283X', pack('V', $is_283x) ]; ++ push @atoms, [ '283x', pack('V', $is_283x && !$is_270x) ]; + + $trailer = pack_trailer(\@atoms); + $atoms[0]->[1] = pack('V', length($trailer)); +@@ -175,7 +193,7 @@ END { + + sub usage + { +- print ("Usage: mkknlimg [--dtok] [--283x] \n"); ++ print ("Usage: mkknlimg [--dtok] [--270x] [--283x] \n"); + exit(1); + } + +@@ -189,15 +207,8 @@ sub try_extract + + chomp($ver); + +- my $res = { ''=>$ver }; +- my $string_pattern = '^('.join('|', @wanted_strings).')$'; +- +- my @matches = `strings \"$knl\" | grep -E \"$string_pattern\"`; +- foreach my $match (@matches) +- { +- chomp($match); +- $res->{$match} = 1; +- } ++ my $res = { 'kver'=>$ver }; ++ $res->{'flags'} = strings_to_flags($knl, $wanted_strings); + + return $res; + } +@@ -224,6 +235,22 @@ sub try_decompress + return undef; + } + ++sub strings_to_flags ++{ ++ my ($knl, $strings) = @_; ++ my $string_pattern = '^('.join('|', keys(%$strings)).')$'; ++ my $flags = 0; ++ ++ my @matches = `strings \"$knl\" | grep -E \"$string_pattern\"`; ++ foreach my $match (@matches) ++ { ++ chomp($match); ++ $flags |= $strings->{$match}; ++ } ++ ++ return $flags; ++} ++ + sub pack_trailer + { + my ($atoms) = @_; +@@ -235,10 +262,3 @@ sub pack_trailer + } + return $trailer; + } +- +-sub config_bool +-{ +- my ($configs, $wanted) = @_; +- my $val = $configs->{$wanted} || 'n'; +- return (($val eq 'y') || ($val eq '1')); +-} diff --git a/target/linux/brcm2708/patches-4.4/0090-drm-vc4-Add-suport-for-3D-rendering-using-the-V3D-en.patch b/target/linux/brcm2708/patches-4.4/0090-drm-vc4-Add-suport-for-3D-rendering-using-the-V3D-en.patch new file mode 100644 index 0000000000..9713718920 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0090-drm-vc4-Add-suport-for-3D-rendering-using-the-V3D-en.patch @@ -0,0 +1,5558 @@ +From 16c40415a78ba028f126b68e2cabf860186ff4f4 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 2 Mar 2015 13:01:12 -0800 +Subject: [PATCH 090/232] drm/vc4: Add suport for 3D rendering using the V3D + engine. + +This is a squash of the out-of-tree development series. Since that +series contained code from the first "get a demo triangle rendered +using a hacked up driver using binary shader code" to "plug the last +known security hole", it's hard to reconstruct a different series of +incremental development that's mergeable without security holes +throughout it. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/Makefile | 11 +- + drivers/gpu/drm/vc4/vc4_bo.c | 476 +++++++++++++- + drivers/gpu/drm/vc4/vc4_crtc.c | 98 ++- + drivers/gpu/drm/vc4/vc4_debugfs.c | 3 + + drivers/gpu/drm/vc4/vc4_drv.c | 45 +- + drivers/gpu/drm/vc4/vc4_drv.h | 317 ++++++++++ + drivers/gpu/drm/vc4/vc4_gem.c | 686 +++++++++++++++++++++ + drivers/gpu/drm/vc4/vc4_irq.c | 211 +++++++ + drivers/gpu/drm/vc4/vc4_kms.c | 148 ++++- + drivers/gpu/drm/vc4/vc4_packet.h | 384 ++++++++++++ + drivers/gpu/drm/vc4/vc4_plane.c | 40 ++ + drivers/gpu/drm/vc4/vc4_qpu_defines.h | 268 ++++++++ + drivers/gpu/drm/vc4/vc4_render_cl.c | 448 ++++++++++++++ + drivers/gpu/drm/vc4/vc4_trace.h | 63 ++ + drivers/gpu/drm/vc4/vc4_trace_points.c | 14 + + drivers/gpu/drm/vc4/vc4_v3d.c | 268 ++++++++ + drivers/gpu/drm/vc4/vc4_validate.c | 958 +++++++++++++++++++++++++++++ + drivers/gpu/drm/vc4/vc4_validate_shaders.c | 521 ++++++++++++++++ + include/uapi/drm/vc4_drm.h | 229 +++++++ + 19 files changed, 5173 insertions(+), 15 deletions(-) + create mode 100644 drivers/gpu/drm/vc4/vc4_gem.c + create mode 100644 drivers/gpu/drm/vc4/vc4_irq.c + create mode 100644 drivers/gpu/drm/vc4/vc4_packet.h + create mode 100644 drivers/gpu/drm/vc4/vc4_qpu_defines.h + create mode 100644 drivers/gpu/drm/vc4/vc4_render_cl.c + create mode 100644 drivers/gpu/drm/vc4/vc4_trace.h + create mode 100644 drivers/gpu/drm/vc4/vc4_trace_points.c + create mode 100644 drivers/gpu/drm/vc4/vc4_v3d.c + create mode 100644 drivers/gpu/drm/vc4/vc4_validate.c + create mode 100644 drivers/gpu/drm/vc4/vc4_validate_shaders.c + create mode 100644 include/uapi/drm/vc4_drm.h + +--- a/drivers/gpu/drm/vc4/Makefile ++++ b/drivers/gpu/drm/vc4/Makefile +@@ -8,10 +8,19 @@ vc4-y := \ + vc4_crtc.o \ + vc4_drv.o \ + vc4_kms.o \ ++ vc4_gem.o \ + vc4_hdmi.o \ + vc4_hvs.o \ +- vc4_plane.o ++ vc4_irq.o \ ++ vc4_plane.o \ ++ vc4_render_cl.o \ ++ vc4_trace_points.o \ ++ vc4_v3d.o \ ++ vc4_validate.o \ ++ vc4_validate_shaders.o + + vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o + + obj-$(CONFIG_DRM_VC4) += vc4.o ++ ++CFLAGS_vc4_trace_points.o := -I$(src) +--- a/drivers/gpu/drm/vc4/vc4_bo.c ++++ b/drivers/gpu/drm/vc4/vc4_bo.c +@@ -15,16 +15,174 @@ + */ + + #include "vc4_drv.h" ++#include "uapi/drm/vc4_drm.h" + +-struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size) ++static void vc4_bo_stats_dump(struct vc4_dev *vc4) + { ++ DRM_INFO("num bos allocated: %d\n", ++ vc4->bo_stats.num_allocated); ++ DRM_INFO("size bos allocated: %dkb\n", ++ vc4->bo_stats.size_allocated / 1024); ++ DRM_INFO("num bos used: %d\n", ++ vc4->bo_stats.num_allocated - vc4->bo_stats.num_cached); ++ DRM_INFO("size bos used: %dkb\n", ++ (vc4->bo_stats.size_allocated - ++ vc4->bo_stats.size_cached) / 1024); ++ DRM_INFO("num bos cached: %d\n", ++ vc4->bo_stats.num_cached); ++ DRM_INFO("size bos cached: %dkb\n", ++ vc4->bo_stats.size_cached / 1024); ++} ++ ++static uint32_t bo_page_index(size_t size) ++{ ++ return (size / PAGE_SIZE) - 1; ++} ++ ++/* Must be called with bo_lock held. */ ++static void vc4_bo_destroy(struct vc4_bo *bo) ++{ ++ struct drm_gem_object *obj = &bo->base.base; ++ struct vc4_dev *vc4 = to_vc4_dev(obj->dev); ++ ++ if (bo->validated_shader) { ++ kfree(bo->validated_shader->texture_samples); ++ kfree(bo->validated_shader); ++ bo->validated_shader = NULL; ++ } ++ ++ vc4->bo_stats.num_allocated--; ++ vc4->bo_stats.size_allocated -= obj->size; ++ drm_gem_cma_free_object(obj); ++} ++ ++/* Must be called with bo_lock held. */ ++static void vc4_bo_remove_from_cache(struct vc4_bo *bo) ++{ ++ struct drm_gem_object *obj = &bo->base.base; ++ struct vc4_dev *vc4 = to_vc4_dev(obj->dev); ++ ++ vc4->bo_stats.num_cached--; ++ vc4->bo_stats.size_cached -= obj->size; ++ ++ list_del(&bo->unref_head); ++ list_del(&bo->size_head); ++} ++ ++static struct list_head *vc4_get_cache_list_for_size(struct drm_device *dev, ++ size_t size) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ uint32_t page_index = bo_page_index(size); ++ ++ if (vc4->bo_cache.size_list_size <= page_index) { ++ uint32_t new_size = max(vc4->bo_cache.size_list_size * 2, ++ page_index + 1); ++ struct list_head *new_list; ++ uint32_t i; ++ ++ new_list = kmalloc(new_size * sizeof(struct list_head), ++ GFP_KERNEL); ++ if (!new_list) ++ return NULL; ++ ++ /* Rebase the old cached BO lists to their new list ++ * head locations. ++ */ ++ for (i = 0; i < vc4->bo_cache.size_list_size; i++) { ++ struct list_head *old_list = &vc4->bo_cache.size_list[i]; ++ if (list_empty(old_list)) ++ INIT_LIST_HEAD(&new_list[i]); ++ else ++ list_replace(old_list, &new_list[i]); ++ } ++ /* And initialize the brand new BO list heads. */ ++ for (i = vc4->bo_cache.size_list_size; i < new_size; i++) ++ INIT_LIST_HEAD(&new_list[i]); ++ ++ kfree(vc4->bo_cache.size_list); ++ vc4->bo_cache.size_list = new_list; ++ vc4->bo_cache.size_list_size = new_size; ++ } ++ ++ return &vc4->bo_cache.size_list[page_index]; ++} ++ ++void vc4_bo_cache_purge(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ spin_lock(&vc4->bo_lock); ++ while (!list_empty(&vc4->bo_cache.time_list)) { ++ struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, ++ struct vc4_bo, unref_head); ++ vc4_bo_remove_from_cache(bo); ++ vc4_bo_destroy(bo); ++ } ++ spin_unlock(&vc4->bo_lock); ++} ++ ++struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ uint32_t size = roundup(unaligned_size, PAGE_SIZE); ++ uint32_t page_index = bo_page_index(size); + struct drm_gem_cma_object *cma_obj; ++ int pass; + +- cma_obj = drm_gem_cma_create(dev, size); +- if (IS_ERR(cma_obj)) ++ if (size == 0) + return NULL; +- else +- return to_vc4_bo(&cma_obj->base); ++ ++ /* First, try to get a vc4_bo from the kernel BO cache. */ ++ spin_lock(&vc4->bo_lock); ++ if (page_index < vc4->bo_cache.size_list_size && ++ !list_empty(&vc4->bo_cache.size_list[page_index])) { ++ struct vc4_bo *bo = ++ list_first_entry(&vc4->bo_cache.size_list[page_index], ++ struct vc4_bo, size_head); ++ vc4_bo_remove_from_cache(bo); ++ spin_unlock(&vc4->bo_lock); ++ kref_init(&bo->base.base.refcount); ++ return bo; ++ } ++ spin_unlock(&vc4->bo_lock); ++ ++ /* Otherwise, make a new BO. */ ++ for (pass = 0; ; pass++) { ++ cma_obj = drm_gem_cma_create(dev, size); ++ if (!IS_ERR(cma_obj)) ++ break; ++ ++ switch (pass) { ++ case 0: ++ /* ++ * If we've run out of CMA memory, kill the cache of ++ * CMA allocations we've got laying around and try again. ++ */ ++ vc4_bo_cache_purge(dev); ++ break; ++ case 1: ++ /* ++ * Getting desperate, so try to wait for any ++ * previous rendering to finish, free its ++ * unreferenced BOs to the cache, and then ++ * free the cache. ++ */ ++ vc4_wait_for_seqno(dev, vc4->emit_seqno, ~0ull, true); ++ vc4_job_handle_completed(vc4); ++ vc4_bo_cache_purge(dev); ++ break; ++ case 3: ++ DRM_ERROR("Failed to allocate from CMA:\n"); ++ vc4_bo_stats_dump(vc4); ++ return NULL; ++ } ++ } ++ ++ vc4->bo_stats.num_allocated++; ++ vc4->bo_stats.size_allocated += size; ++ ++ return to_vc4_bo(&cma_obj->base); + } + + int vc4_dumb_create(struct drm_file *file_priv, +@@ -41,7 +199,129 @@ int vc4_dumb_create(struct drm_file *fil + if (args->size < args->pitch * args->height) + args->size = args->pitch * args->height; + +- bo = vc4_bo_create(dev, roundup(args->size, PAGE_SIZE)); ++ bo = vc4_bo_create(dev, args->size); ++ if (!bo) ++ return -ENOMEM; ++ ++ ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); ++ drm_gem_object_unreference_unlocked(&bo->base.base); ++ ++ return ret; ++} ++ ++static void ++vc4_bo_cache_free_old(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ unsigned long expire_time = jiffies - msecs_to_jiffies(1000); ++ ++ spin_lock(&vc4->bo_lock); ++ while (!list_empty(&vc4->bo_cache.time_list)) { ++ struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, ++ struct vc4_bo, unref_head); ++ if (time_before(expire_time, bo->free_time)) { ++ mod_timer(&vc4->bo_cache.time_timer, ++ round_jiffies_up(jiffies + ++ msecs_to_jiffies(1000))); ++ spin_unlock(&vc4->bo_lock); ++ return; ++ } ++ ++ vc4_bo_remove_from_cache(bo); ++ vc4_bo_destroy(bo); ++ } ++ spin_unlock(&vc4->bo_lock); ++} ++ ++/* Called on the last userspace/kernel unreference of the BO. Returns ++ * it to the BO cache if possible, otherwise frees it. ++ * ++ * Note that this is called with the struct_mutex held. ++ */ ++void vc4_free_object(struct drm_gem_object *gem_bo) ++{ ++ struct drm_device *dev = gem_bo->dev; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct vc4_bo *bo = to_vc4_bo(gem_bo); ++ struct list_head *cache_list; ++ ++ /* If the object references someone else's memory, we can't cache it. ++ */ ++ if (gem_bo->import_attach) { ++ vc4_bo_destroy(bo); ++ return; ++ } ++ ++ /* Don't cache if it was publicly named. */ ++ if (gem_bo->name) { ++ vc4_bo_destroy(bo); ++ return; ++ } ++ ++ spin_lock(&vc4->bo_lock); ++ cache_list = vc4_get_cache_list_for_size(dev, gem_bo->size); ++ if (!cache_list) { ++ vc4_bo_destroy(bo); ++ spin_unlock(&vc4->bo_lock); ++ return; ++ } ++ ++ if (bo->validated_shader) { ++ kfree(bo->validated_shader->texture_samples); ++ kfree(bo->validated_shader); ++ bo->validated_shader = NULL; ++ } ++ ++ bo->free_time = jiffies; ++ list_add(&bo->size_head, cache_list); ++ list_add(&bo->unref_head, &vc4->bo_cache.time_list); ++ ++ vc4->bo_stats.num_cached++; ++ vc4->bo_stats.size_cached += gem_bo->size; ++ spin_unlock(&vc4->bo_lock); ++ ++ vc4_bo_cache_free_old(dev); ++} ++ ++static void vc4_bo_cache_time_work(struct work_struct *work) ++{ ++ struct vc4_dev *vc4 = ++ container_of(work, struct vc4_dev, bo_cache.time_work); ++ struct drm_device *dev = vc4->dev; ++ ++ vc4_bo_cache_free_old(dev); ++} ++ ++static void vc4_bo_cache_time_timer(unsigned long data) ++{ ++ struct drm_device *dev = (struct drm_device *)data; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ schedule_work(&vc4->bo_cache.time_work); ++} ++ ++struct dma_buf * ++vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) ++{ ++ struct vc4_bo *bo = to_vc4_bo(obj); ++ ++ if (bo->validated_shader) { ++ DRM_ERROR("Attempting to export shader BO\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ return drm_gem_prime_export(dev, obj, flags); ++} ++ ++int ++vc4_create_bo_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ struct drm_vc4_create_bo *args = data; ++ struct vc4_bo *bo = NULL; ++ int ret; ++ ++ bo = vc4_bo_create(dev, args->size); + if (!bo) + return -ENOMEM; + +@@ -50,3 +330,187 @@ int vc4_dumb_create(struct drm_file *fil + + return ret; + } ++ ++int ++vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ struct drm_vc4_create_shader_bo *args = data; ++ struct vc4_bo *bo = NULL; ++ int ret; ++ ++ if (args->size == 0) ++ return -EINVAL; ++ ++ if (args->size % sizeof(u64) != 0) ++ return -EINVAL; ++ ++ if (args->flags != 0) { ++ DRM_INFO("Unknown flags set: 0x%08x\n", args->flags); ++ return -EINVAL; ++ } ++ ++ if (args->pad != 0) { ++ DRM_INFO("Pad set: 0x%08x\n", args->pad); ++ return -EINVAL; ++ } ++ ++ bo = vc4_bo_create(dev, args->size); ++ if (!bo) ++ return -ENOMEM; ++ ++ ret = copy_from_user(bo->base.vaddr, ++ (void __user *)(uintptr_t)args->data, ++ args->size); ++ if (ret != 0) ++ goto fail; ++ ++ bo->validated_shader = vc4_validate_shader(&bo->base); ++ if (!bo->validated_shader) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ /* We have to create the handle after validation, to avoid ++ * races for users to do doing things like mmap the shader BO. ++ */ ++ ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); ++ ++ fail: ++ drm_gem_object_unreference_unlocked(&bo->base.base); ++ ++ return ret; ++} ++ ++int ++vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ struct drm_vc4_mmap_bo *args = data; ++ struct drm_gem_object *gem_obj; ++ ++ gem_obj = drm_gem_object_lookup(dev, file_priv, args->handle); ++ if (!gem_obj) { ++ DRM_ERROR("Failed to look up GEM BO %d\n", args->handle); ++ return -EINVAL; ++ } ++ ++ /* The mmap offset was set up at BO allocation time. */ ++ args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); ++ ++ drm_gem_object_unreference(gem_obj); ++ return 0; ++} ++ ++int vc4_mmap(struct file *filp, struct vm_area_struct *vma) ++{ ++ struct drm_gem_object *gem_obj; ++ struct vc4_bo *bo; ++ int ret; ++ ++ ret = drm_gem_mmap(filp, vma); ++ if (ret) ++ return ret; ++ ++ gem_obj = vma->vm_private_data; ++ bo = to_vc4_bo(gem_obj); ++ ++ if (bo->validated_shader) { ++ DRM_ERROR("mmaping of shader BOs not allowed.\n"); ++ return -EINVAL; ++ } ++ ++ /* ++ * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the ++ * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map ++ * the whole buffer. ++ */ ++ vma->vm_flags &= ~VM_PFNMAP; ++ vma->vm_pgoff = 0; ++ ++ ret = dma_mmap_writecombine(bo->base.base.dev->dev, vma, ++ bo->base.vaddr, bo->base.paddr, ++ vma->vm_end - vma->vm_start); ++ if (ret) ++ drm_gem_vm_close(vma); ++ ++ return ret; ++} ++ ++int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) ++{ ++ struct vc4_bo *bo = to_vc4_bo(obj); ++ ++ if (bo->validated_shader) { ++ DRM_ERROR("mmaping of shader BOs not allowed.\n"); ++ return -EINVAL; ++ } ++ ++ return drm_gem_cma_prime_mmap(obj, vma); ++} ++ ++void *vc4_prime_vmap(struct drm_gem_object *obj) ++{ ++ struct vc4_bo *bo = to_vc4_bo(obj); ++ ++ if (bo->validated_shader) { ++ DRM_ERROR("mmaping of shader BOs not allowed.\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ return drm_gem_cma_prime_vmap(obj); ++} ++ ++void vc4_bo_cache_init(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ spin_lock_init(&vc4->bo_lock); ++ ++ INIT_LIST_HEAD(&vc4->bo_cache.time_list); ++ ++ INIT_WORK(&vc4->bo_cache.time_work, vc4_bo_cache_time_work); ++ setup_timer(&vc4->bo_cache.time_timer, ++ vc4_bo_cache_time_timer, ++ (unsigned long) dev); ++} ++ ++void vc4_bo_cache_destroy(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ del_timer(&vc4->bo_cache.time_timer); ++ cancel_work_sync(&vc4->bo_cache.time_work); ++ ++ vc4_bo_cache_purge(dev); ++ ++ if (vc4->bo_stats.num_allocated) { ++ DRM_ERROR("Destroying BO cache while BOs still allocated:\n"); ++ vc4_bo_stats_dump(vc4); ++ } ++} ++ ++#ifdef CONFIG_DEBUG_FS ++int vc4_bo_stats_debugfs(struct seq_file *m, void *unused) ++{ ++ struct drm_info_node *node = (struct drm_info_node *) m->private; ++ struct drm_device *dev = node->minor->dev; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct vc4_bo_stats stats; ++ ++ spin_lock(&vc4->bo_lock); ++ stats = vc4->bo_stats; ++ spin_unlock(&vc4->bo_lock); ++ ++ seq_printf(m, "num bos allocated: %d\n", stats.num_allocated); ++ seq_printf(m, "size bos allocated: %dkb\n", stats.size_allocated / 1024); ++ seq_printf(m, "num bos used: %d\n", (stats.num_allocated - ++ stats.num_cached)); ++ seq_printf(m, "size bos used: %dkb\n", (stats.size_allocated - ++ stats.size_cached) / 1024); ++ seq_printf(m, "num bos cached: %d\n", stats.num_cached); ++ seq_printf(m, "size bos cached: %dkb\n", stats.size_cached / 1024); ++ ++ return 0; ++} ++#endif +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -35,6 +35,7 @@ + #include "drm_atomic_helper.h" + #include "drm_crtc_helper.h" + #include "linux/clk.h" ++#include "drm_fb_cma_helper.h" + #include "linux/component.h" + #include "linux/of_device.h" + #include "vc4_drv.h" +@@ -476,10 +477,105 @@ static irqreturn_t vc4_crtc_irq_handler( + return ret; + } + ++struct vc4_async_flip_state { ++ struct drm_crtc *crtc; ++ struct drm_framebuffer *fb; ++ struct drm_pending_vblank_event *event; ++ ++ struct vc4_seqno_cb cb; ++}; ++ ++/* Called when the V3D execution for the BO being flipped to is done, so that ++ * we can actually update the plane's address to point to it. ++ */ ++static void ++vc4_async_page_flip_complete(struct vc4_seqno_cb *cb) ++{ ++ struct vc4_async_flip_state *flip_state = ++ container_of(cb, struct vc4_async_flip_state, cb); ++ struct drm_crtc *crtc = flip_state->crtc; ++ struct drm_device *dev = crtc->dev; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct drm_plane *plane = crtc->primary; ++ ++ vc4_plane_async_set_fb(plane, flip_state->fb); ++ if (flip_state->event) { ++ unsigned long flags; ++ spin_lock_irqsave(&dev->event_lock, flags); ++ drm_crtc_send_vblank_event(crtc, flip_state->event); ++ spin_unlock_irqrestore(&dev->event_lock, flags); ++ } ++ ++ drm_framebuffer_unreference(flip_state->fb); ++ kfree(flip_state); ++ ++ up(&vc4->async_modeset); ++} ++ ++/* Implements async (non-vblank-synced) page flips. ++ * ++ * The page flip ioctl needs to return immediately, so we grab the ++ * modeset semaphore on the pipe, and queue the address update for ++ * when V3D is done with the BO being flipped to. ++ */ ++static int vc4_async_page_flip(struct drm_crtc *crtc, ++ struct drm_framebuffer *fb, ++ struct drm_pending_vblank_event *event, ++ uint32_t flags) ++{ ++ struct drm_device *dev = crtc->dev; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct drm_plane *plane = crtc->primary; ++ int ret = 0; ++ struct vc4_async_flip_state *flip_state; ++ struct drm_gem_cma_object *cma_bo = drm_fb_cma_get_gem_obj(fb, 0); ++ struct vc4_bo *bo = to_vc4_bo(&cma_bo->base); ++ ++ flip_state = kzalloc(sizeof(*flip_state), GFP_KERNEL); ++ if (!flip_state) ++ return -ENOMEM; ++ ++ drm_framebuffer_reference(fb); ++ flip_state->fb = fb; ++ flip_state->crtc = crtc; ++ flip_state->event = event; ++ ++ /* Make sure all other async modesetes have landed. */ ++ ret = down_interruptible(&vc4->async_modeset); ++ if (ret) { ++ kfree(flip_state); ++ return ret; ++ } ++ ++ /* Immediately update the plane's legacy fb pointer, so that later ++ * modeset prep sees the state that will be present when the semaphore ++ * is released. ++ */ ++ drm_atomic_set_fb_for_plane(plane->state, fb); ++ plane->fb = fb; ++ ++ vc4_queue_seqno_cb(dev, &flip_state->cb, bo->seqno, ++ vc4_async_page_flip_complete); ++ ++ /* Driver takes ownership of state on successful async commit. */ ++ return 0; ++} ++ ++static int vc4_page_flip(struct drm_crtc *crtc, ++ struct drm_framebuffer *fb, ++ struct drm_pending_vblank_event *event, ++ uint32_t flags) ++{ ++ if (flags & DRM_MODE_PAGE_FLIP_ASYNC) ++ return vc4_async_page_flip(crtc, fb, event, flags); ++ else ++ return drm_atomic_helper_page_flip(crtc, fb, event, flags); ++} ++ + static const struct drm_crtc_funcs vc4_crtc_funcs = { + .set_config = drm_atomic_helper_set_config, + .destroy = vc4_crtc_destroy, +- .page_flip = drm_atomic_helper_page_flip, ++ .page_flip = vc4_page_flip, + .set_property = NULL, + .cursor_set = NULL, /* handled by drm_mode_cursor_universal */ + .cursor_move = NULL, /* handled by drm_mode_cursor_universal */ +--- a/drivers/gpu/drm/vc4/vc4_debugfs.c ++++ b/drivers/gpu/drm/vc4/vc4_debugfs.c +@@ -16,11 +16,14 @@ + #include "vc4_regs.h" + + static const struct drm_info_list vc4_debugfs_list[] = { ++ {"bo_stats", vc4_bo_stats_debugfs, 0}, + {"hdmi_regs", vc4_hdmi_debugfs_regs, 0}, + {"hvs_regs", vc4_hvs_debugfs_regs, 0}, + {"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0}, + {"crtc1_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)1}, + {"crtc2_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)2}, ++ {"v3d_ident", vc4_v3d_debugfs_ident, 0}, ++ {"v3d_regs", vc4_v3d_debugfs_regs, 0}, + }; + + #define VC4_DEBUGFS_ENTRIES ARRAY_SIZE(vc4_debugfs_list) +--- a/drivers/gpu/drm/vc4/vc4_drv.c ++++ b/drivers/gpu/drm/vc4/vc4_drv.c +@@ -14,8 +14,10 @@ + #include + #include + #include ++#include + #include "drm_fb_cma_helper.h" + ++#include "uapi/drm/vc4_drm.h" + #include "vc4_drv.h" + #include "vc4_regs.h" + +@@ -63,7 +65,7 @@ static const struct file_operations vc4_ + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, +- .mmap = drm_gem_cma_mmap, ++ .mmap = vc4_mmap, + .poll = drm_poll, + .read = drm_read, + #ifdef CONFIG_COMPAT +@@ -73,16 +75,28 @@ static const struct file_operations vc4_ + }; + + static const struct drm_ioctl_desc vc4_drm_ioctls[] = { ++ DRM_IOCTL_DEF_DRV(VC4_SUBMIT_CL, vc4_submit_cl_ioctl, 0), ++ DRM_IOCTL_DEF_DRV(VC4_WAIT_SEQNO, vc4_wait_seqno_ioctl, 0), ++ DRM_IOCTL_DEF_DRV(VC4_WAIT_BO, vc4_wait_bo_ioctl, 0), ++ DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, 0), ++ DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, 0), ++ DRM_IOCTL_DEF_DRV(VC4_CREATE_SHADER_BO, vc4_create_shader_bo_ioctl, 0), + }; + + static struct drm_driver vc4_drm_driver = { + .driver_features = (DRIVER_MODESET | + DRIVER_ATOMIC | + DRIVER_GEM | ++ DRIVER_HAVE_IRQ | + DRIVER_PRIME), + .lastclose = vc4_lastclose, + .preclose = vc4_drm_preclose, + ++ .irq_handler = vc4_irq, ++ .irq_preinstall = vc4_irq_preinstall, ++ .irq_postinstall = vc4_irq_postinstall, ++ .irq_uninstall = vc4_irq_uninstall, ++ + .enable_vblank = vc4_enable_vblank, + .disable_vblank = vc4_disable_vblank, + .get_vblank_counter = drm_vblank_count, +@@ -92,18 +106,18 @@ static struct drm_driver vc4_drm_driver + .debugfs_cleanup = vc4_debugfs_cleanup, + #endif + +- .gem_free_object = drm_gem_cma_free_object, ++ .gem_free_object = vc4_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_import = drm_gem_prime_import, +- .gem_prime_export = drm_gem_prime_export, ++ .gem_prime_export = vc4_prime_export, + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, +- .gem_prime_vmap = drm_gem_cma_prime_vmap, ++ .gem_prime_vmap = vc4_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, +- .gem_prime_mmap = drm_gem_cma_prime_mmap, ++ .gem_prime_mmap = vc4_prime_mmap, + + .dumb_create = vc4_dumb_create, + .dumb_map_offset = drm_gem_cma_dumb_map_offset, +@@ -113,6 +127,8 @@ static struct drm_driver vc4_drm_driver + .num_ioctls = ARRAY_SIZE(vc4_drm_ioctls), + .fops = &vc4_drm_fops, + ++ .gem_obj_size = sizeof(struct vc4_bo), ++ + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, +@@ -153,6 +169,7 @@ static int vc4_drm_bind(struct device *d + struct drm_device *drm; + struct drm_connector *connector; + struct vc4_dev *vc4; ++ struct device_node *firmware_node; + int ret = 0; + + dev->coherent_dma_mask = DMA_BIT_MASK(32); +@@ -161,6 +178,14 @@ static int vc4_drm_bind(struct device *d + if (!vc4) + return -ENOMEM; + ++ firmware_node = of_parse_phandle(dev->of_node, "firmware", 0); ++ vc4->firmware = rpi_firmware_get(firmware_node); ++ if (!vc4->firmware) { ++ DRM_DEBUG("Failed to get Raspberry Pi firmware reference.\n"); ++ return -EPROBE_DEFER; ++ } ++ of_node_put(firmware_node); ++ + drm = drm_dev_alloc(&vc4_drm_driver, dev); + if (!drm) + return -ENOMEM; +@@ -170,13 +195,17 @@ static int vc4_drm_bind(struct device *d + + drm_dev_set_unique(drm, dev_name(dev)); + ++ vc4_bo_cache_init(drm); ++ + drm_mode_config_init(drm); + if (ret) + goto unref; + ++ vc4_gem_init(drm); ++ + ret = component_bind_all(dev, drm); + if (ret) +- goto unref; ++ goto gem_destroy; + + ret = drm_dev_register(drm, 0); + if (ret < 0) +@@ -200,8 +229,11 @@ unregister: + drm_dev_unregister(drm); + unbind_all: + component_unbind_all(dev, drm); ++gem_destroy: ++ vc4_gem_destroy(drm); + unref: + drm_dev_unref(drm); ++ vc4_bo_cache_destroy(drm); + return ret; + } + +@@ -228,6 +260,7 @@ static struct platform_driver *const com + &vc4_hdmi_driver, + &vc4_crtc_driver, + &vc4_hvs_driver, ++ &vc4_v3d_driver, + }; + + static int vc4_platform_drm_probe(struct platform_device *pdev) +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -15,8 +15,85 @@ struct vc4_dev { + struct vc4_hdmi *hdmi; + struct vc4_hvs *hvs; + struct vc4_crtc *crtc[3]; ++ struct vc4_v3d *v3d; + + struct drm_fbdev_cma *fbdev; ++ struct rpi_firmware *firmware; ++ ++ /* The kernel-space BO cache. Tracks buffers that have been ++ * unreferenced by all other users (refcounts of 0!) but not ++ * yet freed, so we can do cheap allocations. ++ */ ++ struct vc4_bo_cache { ++ /* Array of list heads for entries in the BO cache, ++ * based on number of pages, so we can do O(1) lookups ++ * in the cache when allocating. ++ */ ++ struct list_head *size_list; ++ uint32_t size_list_size; ++ ++ /* List of all BOs in the cache, ordered by age, so we ++ * can do O(1) lookups when trying to free old ++ * buffers. ++ */ ++ struct list_head time_list; ++ struct work_struct time_work; ++ struct timer_list time_timer; ++ } bo_cache; ++ ++ struct vc4_bo_stats { ++ u32 num_allocated; ++ u32 size_allocated; ++ u32 num_cached; ++ u32 size_cached; ++ } bo_stats; ++ ++ /* Protects bo_cache and the BO stats. */ ++ spinlock_t bo_lock; ++ ++ /* Sequence number for the last job queued in job_list. ++ * Starts at 0 (no jobs emitted). ++ */ ++ uint64_t emit_seqno; ++ ++ /* Sequence number for the last completed job on the GPU. ++ * Starts at 0 (no jobs completed). ++ */ ++ uint64_t finished_seqno; ++ ++ /* List of all struct vc4_exec_info for jobs to be executed. ++ * The first job in the list is the one currently programmed ++ * into ct0ca/ct1ca for execution. ++ */ ++ struct list_head job_list; ++ /* List of the finished vc4_exec_infos waiting to be freed by ++ * job_done_work. ++ */ ++ struct list_head job_done_list; ++ spinlock_t job_lock; ++ wait_queue_head_t job_wait_queue; ++ struct work_struct job_done_work; ++ ++ /* List of struct vc4_seqno_cb for callbacks to be made from a ++ * workqueue when the given seqno is passed. ++ */ ++ struct list_head seqno_cb_list; ++ ++ /* The binner overflow memory that's currently set up in ++ * BPOA/BPOS registers. When overflow occurs and a new one is ++ * allocated, the previous one will be moved to ++ * vc4->current_exec's free list. ++ */ ++ struct vc4_bo *overflow_mem; ++ struct work_struct overflow_mem_work; ++ ++ struct { ++ uint32_t last_ct0ca, last_ct1ca; ++ struct timer_list timer; ++ struct work_struct reset_work; ++ } hangcheck; ++ ++ struct semaphore async_modeset; + }; + + static inline struct vc4_dev * +@@ -27,6 +104,25 @@ to_vc4_dev(struct drm_device *dev) + + struct vc4_bo { + struct drm_gem_cma_object base; ++ ++ /* seqno of the last job to render to this BO. */ ++ uint64_t seqno; ++ ++ /* List entry for the BO's position in either ++ * vc4_exec_info->unref_list or vc4_dev->bo_cache.time_list ++ */ ++ struct list_head unref_head; ++ ++ /* Time in jiffies when the BO was put in vc4->bo_cache. */ ++ unsigned long free_time; ++ ++ /* List entry for the BO's position in vc4_dev->bo_cache.size_list */ ++ struct list_head size_head; ++ ++ /* Struct for shader validation state, if created by ++ * DRM_IOCTL_VC4_CREATE_SHADER_BO. ++ */ ++ struct vc4_validated_shader_info *validated_shader; + }; + + static inline struct vc4_bo * +@@ -35,6 +131,17 @@ to_vc4_bo(struct drm_gem_object *bo) + return (struct vc4_bo *)bo; + } + ++struct vc4_seqno_cb { ++ struct work_struct work; ++ uint64_t seqno; ++ void (*func)(struct vc4_seqno_cb *cb); ++}; ++ ++struct vc4_v3d { ++ struct platform_device *pdev; ++ void __iomem *regs; ++}; ++ + struct vc4_hvs { + struct platform_device *pdev; + void __iomem *regs; +@@ -72,9 +179,151 @@ to_vc4_encoder(struct drm_encoder *encod + return container_of(encoder, struct vc4_encoder, base); + } + ++#define V3D_READ(offset) readl(vc4->v3d->regs + offset) ++#define V3D_WRITE(offset, val) writel(val, vc4->v3d->regs + offset) + #define HVS_READ(offset) readl(vc4->hvs->regs + offset) + #define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset) + ++enum vc4_bo_mode { ++ VC4_MODE_UNDECIDED, ++ VC4_MODE_RENDER, ++ VC4_MODE_SHADER, ++}; ++ ++struct vc4_bo_exec_state { ++ struct drm_gem_cma_object *bo; ++ enum vc4_bo_mode mode; ++}; ++ ++struct vc4_exec_info { ++ /* Sequence number for this bin/render job. */ ++ uint64_t seqno; ++ ++ /* Kernel-space copy of the ioctl arguments */ ++ struct drm_vc4_submit_cl *args; ++ ++ /* This is the array of BOs that were looked up at the start of exec. ++ * Command validation will use indices into this array. ++ */ ++ struct vc4_bo_exec_state *bo; ++ uint32_t bo_count; ++ ++ /* Pointers for our position in vc4->job_list */ ++ struct list_head head; ++ ++ /* List of other BOs used in the job that need to be released ++ * once the job is complete. ++ */ ++ struct list_head unref_list; ++ ++ /* Current unvalidated indices into @bo loaded by the non-hardware ++ * VC4_PACKET_GEM_HANDLES. ++ */ ++ uint32_t bo_index[2]; ++ ++ /* This is the BO where we store the validated command lists, shader ++ * records, and uniforms. ++ */ ++ struct drm_gem_cma_object *exec_bo; ++ ++ /** ++ * This tracks the per-shader-record state (packet 64) that ++ * determines the length of the shader record and the offset ++ * it's expected to be found at. It gets read in from the ++ * command lists. ++ */ ++ struct vc4_shader_state { ++ uint8_t packet; ++ uint32_t addr; ++ /* Maximum vertex index referenced by any primitive using this ++ * shader state. ++ */ ++ uint32_t max_index; ++ } *shader_state; ++ ++ /** How many shader states the user declared they were using. */ ++ uint32_t shader_state_size; ++ /** How many shader state records the validator has seen. */ ++ uint32_t shader_state_count; ++ ++ bool found_tile_binning_mode_config_packet; ++ bool found_start_tile_binning_packet; ++ bool found_increment_semaphore_packet; ++ uint8_t bin_tiles_x, bin_tiles_y; ++ struct drm_gem_cma_object *tile_bo; ++ uint32_t tile_alloc_offset; ++ ++ /** ++ * Computed addresses pointing into exec_bo where we start the ++ * bin thread (ct0) and render thread (ct1). ++ */ ++ uint32_t ct0ca, ct0ea; ++ uint32_t ct1ca, ct1ea; ++ ++ /* Pointers to the shader recs. These paddr gets incremented as CL ++ * packets are relocated in validate_gl_shader_state, and the vaddrs ++ * (u and v) get incremented and size decremented as the shader recs ++ * themselves are validated. ++ */ ++ void *shader_rec_u; ++ void *shader_rec_v; ++ uint32_t shader_rec_p; ++ uint32_t shader_rec_size; ++ ++ /* Pointers to the uniform data. These pointers are incremented, and ++ * size decremented, as each batch of uniforms is uploaded. ++ */ ++ void *uniforms_u; ++ void *uniforms_v; ++ uint32_t uniforms_p; ++ uint32_t uniforms_size; ++}; ++ ++static inline struct vc4_exec_info * ++vc4_first_job(struct vc4_dev *vc4) ++{ ++ if (list_empty(&vc4->job_list)) ++ return NULL; ++ return list_first_entry(&vc4->job_list, struct vc4_exec_info, head); ++} ++ ++/** ++ * struct vc4_texture_sample_info - saves the offsets into the UBO for texture ++ * setup parameters. ++ * ++ * This will be used at draw time to relocate the reference to the texture ++ * contents in p0, and validate that the offset combined with ++ * width/height/stride/etc. from p1 and p2/p3 doesn't sample outside the BO. ++ * Note that the hardware treats unprovided config parameters as 0, so not all ++ * of them need to be set up for every texure sample, and we'll store ~0 as ++ * the offset to mark the unused ones. ++ * ++ * See the VC4 3D architecture guide page 41 ("Texture and Memory Lookup Unit ++ * Setup") for definitions of the texture parameters. ++ */ ++struct vc4_texture_sample_info { ++ bool is_direct; ++ uint32_t p_offset[4]; ++}; ++ ++/** ++ * struct vc4_validated_shader_info - information about validated shaders that ++ * needs to be used from command list validation. ++ * ++ * For a given shader, each time a shader state record references it, we need ++ * to verify that the shader doesn't read more uniforms than the shader state ++ * record's uniform BO pointer can provide, and we need to apply relocations ++ * and validate the shader state record's uniforms that define the texture ++ * samples. ++ */ ++struct vc4_validated_shader_info ++{ ++ uint32_t uniforms_size; ++ uint32_t uniforms_src_size; ++ uint32_t num_texture_samples; ++ struct vc4_texture_sample_info *texture_samples; ++}; ++ + /** + * _wait_for - magic (register) wait macro + * +@@ -111,6 +360,18 @@ int vc4_dumb_create(struct drm_file *fil + struct drm_mode_create_dumb *args); + struct dma_buf *vc4_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, int flags); ++int vc4_create_bo_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++int vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++int vc4_mmap(struct file *filp, struct vm_area_struct *vma); ++int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); ++void *vc4_prime_vmap(struct drm_gem_object *obj); ++void vc4_bo_cache_init(struct drm_device *dev); ++void vc4_bo_cache_destroy(struct drm_device *dev); ++int vc4_bo_stats_debugfs(struct seq_file *m, void *arg); + + /* vc4_crtc.c */ + extern struct platform_driver vc4_crtc_driver; +@@ -126,10 +387,34 @@ void vc4_debugfs_cleanup(struct drm_mino + /* vc4_drv.c */ + void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index); + ++/* vc4_gem.c */ ++void vc4_gem_init(struct drm_device *dev); ++void vc4_gem_destroy(struct drm_device *dev); ++int vc4_submit_cl_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++int vc4_wait_seqno_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++int vc4_wait_bo_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++void vc4_submit_next_job(struct drm_device *dev); ++int vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, ++ uint64_t timeout_ns, bool interruptible); ++void vc4_job_handle_completed(struct vc4_dev *vc4); ++int vc4_queue_seqno_cb(struct drm_device *dev, ++ struct vc4_seqno_cb *cb, uint64_t seqno, ++ void (*func)(struct vc4_seqno_cb *cb)); ++ + /* vc4_hdmi.c */ + extern struct platform_driver vc4_hdmi_driver; + int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused); + ++/* vc4_irq.c */ ++irqreturn_t vc4_irq(int irq, void *arg); ++void vc4_irq_preinstall(struct drm_device *dev); ++int vc4_irq_postinstall(struct drm_device *dev); ++void vc4_irq_uninstall(struct drm_device *dev); ++void vc4_irq_reset(struct drm_device *dev); ++ + /* vc4_hvs.c */ + extern struct platform_driver vc4_hvs_driver; + void vc4_hvs_dump_state(struct drm_device *dev); +@@ -143,3 +428,35 @@ struct drm_plane *vc4_plane_init(struct + enum drm_plane_type type); + u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); + u32 vc4_plane_dlist_size(struct drm_plane_state *state); ++void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb); ++ ++/* vc4_v3d.c */ ++extern struct platform_driver vc4_v3d_driver; ++int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused); ++int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused); ++int vc4_v3d_set_power(struct vc4_dev *vc4, bool on); ++ ++/* vc4_validate.c */ ++int ++vc4_validate_bin_cl(struct drm_device *dev, ++ void *validated, ++ void *unvalidated, ++ struct vc4_exec_info *exec); ++ ++int ++vc4_validate_shader_recs(struct drm_device *dev, struct vc4_exec_info *exec); ++ ++struct vc4_validated_shader_info * ++vc4_validate_shader(struct drm_gem_cma_object *shader_obj); ++ ++bool vc4_use_bo(struct vc4_exec_info *exec, ++ uint32_t hindex, ++ enum vc4_bo_mode mode, ++ struct drm_gem_cma_object **obj); ++ ++int vc4_get_rcl(struct drm_device *dev, struct vc4_exec_info *exec); ++ ++bool vc4_check_tex_size(struct vc4_exec_info *exec, ++ struct drm_gem_cma_object *fbo, ++ uint32_t offset, uint8_t tiling_format, ++ uint32_t width, uint32_t height, uint8_t cpp); +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_gem.c +@@ -0,0 +1,686 @@ ++/* ++ * Copyright © 2014 Broadcom ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "uapi/drm/vc4_drm.h" ++#include "vc4_drv.h" ++#include "vc4_regs.h" ++#include "vc4_trace.h" ++ ++static void ++vc4_queue_hangcheck(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ mod_timer(&vc4->hangcheck.timer, ++ round_jiffies_up(jiffies + msecs_to_jiffies(100))); ++} ++ ++static void ++vc4_reset(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ DRM_INFO("Resetting GPU.\n"); ++ vc4_v3d_set_power(vc4, false); ++ vc4_v3d_set_power(vc4, true); ++ ++ vc4_irq_reset(dev); ++ ++ /* Rearm the hangcheck -- another job might have been waiting ++ * for our hung one to get kicked off, and vc4_irq_reset() ++ * would have started it. ++ */ ++ vc4_queue_hangcheck(dev); ++} ++ ++static void ++vc4_reset_work(struct work_struct *work) ++{ ++ struct vc4_dev *vc4 = ++ container_of(work, struct vc4_dev, hangcheck.reset_work); ++ ++ vc4_reset(vc4->dev); ++} ++ ++static void ++vc4_hangcheck_elapsed(unsigned long data) ++{ ++ struct drm_device *dev = (struct drm_device *)data; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ uint32_t ct0ca, ct1ca; ++ ++ /* If idle, we can stop watching for hangs. */ ++ if (list_empty(&vc4->job_list)) ++ return; ++ ++ ct0ca = V3D_READ(V3D_CTNCA(0)); ++ ct1ca = V3D_READ(V3D_CTNCA(1)); ++ ++ /* If we've made any progress in execution, rearm the timer ++ * and wait. ++ */ ++ if (ct0ca != vc4->hangcheck.last_ct0ca || ++ ct1ca != vc4->hangcheck.last_ct1ca) { ++ vc4->hangcheck.last_ct0ca = ct0ca; ++ vc4->hangcheck.last_ct1ca = ct1ca; ++ vc4_queue_hangcheck(dev); ++ return; ++ } ++ ++ /* We've gone too long with no progress, reset. This has to ++ * be done from a work struct, since resetting can sleep and ++ * this timer hook isn't allowed to. ++ */ ++ schedule_work(&vc4->hangcheck.reset_work); ++} ++ ++static void ++submit_cl(struct drm_device *dev, uint32_t thread, uint32_t start, uint32_t end) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ /* Stop any existing thread and set state to "stopped at halt" */ ++ V3D_WRITE(V3D_CTNCS(thread), V3D_CTRUN); ++ barrier(); ++ ++ V3D_WRITE(V3D_CTNCA(thread), start); ++ barrier(); ++ ++ /* Set the end address of the control list. Writing this ++ * register is what starts the job. ++ */ ++ V3D_WRITE(V3D_CTNEA(thread), end); ++ barrier(); ++} ++ ++int ++vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, uint64_t timeout_ns, ++ bool interruptible) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ int ret = 0; ++ unsigned long timeout_expire; ++ DEFINE_WAIT(wait); ++ ++ if (vc4->finished_seqno >= seqno) ++ return 0; ++ ++ if (timeout_ns == 0) ++ return -ETIME; ++ ++ timeout_expire = jiffies + nsecs_to_jiffies(timeout_ns); ++ ++ trace_vc4_wait_for_seqno_begin(dev, seqno, timeout_ns); ++ for (;;) { ++ prepare_to_wait(&vc4->job_wait_queue, &wait, ++ interruptible ? TASK_INTERRUPTIBLE : ++ TASK_UNINTERRUPTIBLE); ++ ++ if (interruptible && signal_pending(current)) { ++ ret = -ERESTARTSYS; ++ break; ++ } ++ ++ if (vc4->finished_seqno >= seqno) ++ break; ++ ++ if (timeout_ns != ~0ull) { ++ if (time_after_eq(jiffies, timeout_expire)) { ++ ret = -ETIME; ++ break; ++ } ++ schedule_timeout(timeout_expire - jiffies); ++ } else { ++ schedule(); ++ } ++ } ++ ++ finish_wait(&vc4->job_wait_queue, &wait); ++ trace_vc4_wait_for_seqno_end(dev, seqno); ++ ++ if (ret && ret != -ERESTARTSYS) { ++ DRM_ERROR("timeout waiting for render thread idle\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void ++vc4_flush_caches(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ /* Flush the GPU L2 caches. These caches sit on top of system ++ * L3 (the 128kb or so shared with the CPU), and are ++ * non-allocating in the L3. ++ */ ++ V3D_WRITE(V3D_L2CACTL, ++ V3D_L2CACTL_L2CCLR); ++ ++ V3D_WRITE(V3D_SLCACTL, ++ VC4_SET_FIELD(0xf, V3D_SLCACTL_T1CC) | ++ VC4_SET_FIELD(0xf, V3D_SLCACTL_T0CC) | ++ VC4_SET_FIELD(0xf, V3D_SLCACTL_UCC) | ++ VC4_SET_FIELD(0xf, V3D_SLCACTL_ICC)); ++} ++ ++/* Sets the registers for the next job to be actually be executed in ++ * the hardware. ++ * ++ * The job_lock should be held during this. ++ */ ++void ++vc4_submit_next_job(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct vc4_exec_info *exec = vc4_first_job(vc4); ++ ++ if (!exec) ++ return; ++ ++ vc4_flush_caches(dev); ++ ++ /* Disable the binner's pre-loaded overflow memory address */ ++ V3D_WRITE(V3D_BPOA, 0); ++ V3D_WRITE(V3D_BPOS, 0); ++ ++ if (exec->ct0ca != exec->ct0ea) ++ submit_cl(dev, 0, exec->ct0ca, exec->ct0ea); ++ submit_cl(dev, 1, exec->ct1ca, exec->ct1ea); ++} ++ ++static void ++vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno) ++{ ++ struct vc4_bo *bo; ++ unsigned i; ++ ++ for (i = 0; i < exec->bo_count; i++) { ++ bo = to_vc4_bo(&exec->bo[i].bo->base); ++ bo->seqno = seqno; ++ } ++ ++ list_for_each_entry(bo, &exec->unref_list, unref_head) { ++ bo->seqno = seqno; ++ } ++} ++ ++/* Queues a struct vc4_exec_info for execution. If no job is ++ * currently executing, then submits it. ++ * ++ * Unlike most GPUs, our hardware only handles one command list at a ++ * time. To queue multiple jobs at once, we'd need to edit the ++ * previous command list to have a jump to the new one at the end, and ++ * then bump the end address. That's a change for a later date, ++ * though. ++ */ ++static void ++vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ uint64_t seqno = ++vc4->emit_seqno; ++ unsigned long irqflags; ++ ++ exec->seqno = seqno; ++ vc4_update_bo_seqnos(exec, seqno); ++ ++ spin_lock_irqsave(&vc4->job_lock, irqflags); ++ list_add_tail(&exec->head, &vc4->job_list); ++ ++ /* If no job was executing, kick ours off. Otherwise, it'll ++ * get started when the previous job's frame done interrupt ++ * occurs. ++ */ ++ if (vc4_first_job(vc4) == exec) { ++ vc4_submit_next_job(dev); ++ vc4_queue_hangcheck(dev); ++ } ++ ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++} ++ ++/** ++ * Looks up a bunch of GEM handles for BOs and stores the array for ++ * use in the command validator that actually writes relocated ++ * addresses pointing to them. ++ */ ++static int ++vc4_cl_lookup_bos(struct drm_device *dev, ++ struct drm_file *file_priv, ++ struct vc4_exec_info *exec) ++{ ++ struct drm_vc4_submit_cl *args = exec->args; ++ uint32_t *handles; ++ int ret = 0; ++ int i; ++ ++ exec->bo_count = args->bo_handle_count; ++ ++ if (!exec->bo_count) { ++ /* See comment on bo_index for why we have to check ++ * this. ++ */ ++ DRM_ERROR("Rendering requires BOs to validate\n"); ++ return -EINVAL; ++ } ++ ++ exec->bo = kcalloc(exec->bo_count, sizeof(struct vc4_bo_exec_state), ++ GFP_KERNEL); ++ if (!exec->bo) { ++ DRM_ERROR("Failed to allocate validated BO pointers\n"); ++ return -ENOMEM; ++ } ++ ++ handles = drm_malloc_ab(exec->bo_count, sizeof(uint32_t)); ++ if (!handles) { ++ DRM_ERROR("Failed to allocate incoming GEM handles\n"); ++ goto fail; ++ } ++ ++ ret = copy_from_user(handles, ++ (void __user *)(uintptr_t)args->bo_handles, ++ exec->bo_count * sizeof(uint32_t)); ++ if (ret) { ++ DRM_ERROR("Failed to copy in GEM handles\n"); ++ goto fail; ++ } ++ ++ spin_lock(&file_priv->table_lock); ++ for (i = 0; i < exec->bo_count; i++) { ++ struct drm_gem_object *bo = idr_find(&file_priv->object_idr, ++ handles[i]); ++ if (!bo) { ++ DRM_ERROR("Failed to look up GEM BO %d: %d\n", ++ i, handles[i]); ++ ret = -EINVAL; ++ spin_unlock(&file_priv->table_lock); ++ goto fail; ++ } ++ drm_gem_object_reference(bo); ++ exec->bo[i].bo = (struct drm_gem_cma_object *)bo; ++ } ++ spin_unlock(&file_priv->table_lock); ++ ++fail: ++ kfree(handles); ++ return 0; ++} ++ ++static int ++vc4_get_bcl(struct drm_device *dev, struct vc4_exec_info *exec) ++{ ++ struct drm_vc4_submit_cl *args = exec->args; ++ void *temp = NULL; ++ void *bin; ++ int ret = 0; ++ uint32_t bin_offset = 0; ++ uint32_t shader_rec_offset = roundup(bin_offset + args->bin_cl_size, ++ 16); ++ uint32_t uniforms_offset = shader_rec_offset + args->shader_rec_size; ++ uint32_t exec_size = uniforms_offset + args->uniforms_size; ++ uint32_t temp_size = exec_size + (sizeof(struct vc4_shader_state) * ++ args->shader_rec_count); ++ struct vc4_bo *bo; ++ ++ if (uniforms_offset < shader_rec_offset || ++ exec_size < uniforms_offset || ++ args->shader_rec_count >= (UINT_MAX / ++ sizeof(struct vc4_shader_state)) || ++ temp_size < exec_size) { ++ DRM_ERROR("overflow in exec arguments\n"); ++ goto fail; ++ } ++ ++ /* Allocate space where we'll store the copied in user command lists ++ * and shader records. ++ * ++ * We don't just copy directly into the BOs because we need to ++ * read the contents back for validation, and I think the ++ * bo->vaddr is uncached access. ++ */ ++ temp = kmalloc(temp_size, GFP_KERNEL); ++ if (!temp) { ++ DRM_ERROR("Failed to allocate storage for copying " ++ "in bin/render CLs.\n"); ++ ret = -ENOMEM; ++ goto fail; ++ } ++ bin = temp + bin_offset; ++ exec->shader_rec_u = temp + shader_rec_offset; ++ exec->uniforms_u = temp + uniforms_offset; ++ exec->shader_state = temp + exec_size; ++ exec->shader_state_size = args->shader_rec_count; ++ ++ ret = copy_from_user(bin, ++ (void __user *)(uintptr_t)args->bin_cl, ++ args->bin_cl_size); ++ if (ret) { ++ DRM_ERROR("Failed to copy in bin cl\n"); ++ goto fail; ++ } ++ ++ ret = copy_from_user(exec->shader_rec_u, ++ (void __user *)(uintptr_t)args->shader_rec, ++ args->shader_rec_size); ++ if (ret) { ++ DRM_ERROR("Failed to copy in shader recs\n"); ++ goto fail; ++ } ++ ++ ret = copy_from_user(exec->uniforms_u, ++ (void __user *)(uintptr_t)args->uniforms, ++ args->uniforms_size); ++ if (ret) { ++ DRM_ERROR("Failed to copy in uniforms cl\n"); ++ goto fail; ++ } ++ ++ bo = vc4_bo_create(dev, exec_size); ++ if (!bo) { ++ DRM_ERROR("Couldn't allocate BO for binning\n"); ++ ret = PTR_ERR(exec->exec_bo); ++ goto fail; ++ } ++ exec->exec_bo = &bo->base; ++ ++ list_add_tail(&to_vc4_bo(&exec->exec_bo->base)->unref_head, ++ &exec->unref_list); ++ ++ exec->ct0ca = exec->exec_bo->paddr + bin_offset; ++ ++ exec->shader_rec_v = exec->exec_bo->vaddr + shader_rec_offset; ++ exec->shader_rec_p = exec->exec_bo->paddr + shader_rec_offset; ++ exec->shader_rec_size = args->shader_rec_size; ++ ++ exec->uniforms_v = exec->exec_bo->vaddr + uniforms_offset; ++ exec->uniforms_p = exec->exec_bo->paddr + uniforms_offset; ++ exec->uniforms_size = args->uniforms_size; ++ ++ ret = vc4_validate_bin_cl(dev, ++ exec->exec_bo->vaddr + bin_offset, ++ bin, ++ exec); ++ if (ret) ++ goto fail; ++ ++ ret = vc4_validate_shader_recs(dev, exec); ++ ++fail: ++ kfree(temp); ++ return ret; ++} ++ ++static void ++vc4_complete_exec(struct vc4_exec_info *exec) ++{ ++ unsigned i; ++ ++ if (exec->bo) { ++ for (i = 0; i < exec->bo_count; i++) ++ drm_gem_object_unreference(&exec->bo[i].bo->base); ++ kfree(exec->bo); ++ } ++ ++ while (!list_empty(&exec->unref_list)) { ++ struct vc4_bo *bo = list_first_entry(&exec->unref_list, ++ struct vc4_bo, unref_head); ++ list_del(&bo->unref_head); ++ drm_gem_object_unreference(&bo->base.base); ++ } ++ ++ kfree(exec); ++} ++ ++void ++vc4_job_handle_completed(struct vc4_dev *vc4) ++{ ++ unsigned long irqflags; ++ struct vc4_seqno_cb *cb, *cb_temp; ++ ++ spin_lock_irqsave(&vc4->job_lock, irqflags); ++ while (!list_empty(&vc4->job_done_list)) { ++ struct vc4_exec_info *exec = ++ list_first_entry(&vc4->job_done_list, ++ struct vc4_exec_info, head); ++ list_del(&exec->head); ++ ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ vc4_complete_exec(exec); ++ spin_lock_irqsave(&vc4->job_lock, irqflags); ++ } ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ ++ list_for_each_entry_safe(cb, cb_temp, &vc4->seqno_cb_list, work.entry) { ++ if (cb->seqno <= vc4->finished_seqno) { ++ list_del_init(&cb->work.entry); ++ schedule_work(&cb->work); ++ } ++ } ++} ++ ++static void vc4_seqno_cb_work(struct work_struct *work) ++{ ++ struct vc4_seqno_cb *cb = container_of(work, struct vc4_seqno_cb, work); ++ cb->func(cb); ++} ++ ++int vc4_queue_seqno_cb(struct drm_device *dev, ++ struct vc4_seqno_cb *cb, uint64_t seqno, ++ void (*func)(struct vc4_seqno_cb *cb)) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ int ret = 0; ++ ++ cb->func = func; ++ INIT_WORK(&cb->work, vc4_seqno_cb_work); ++ ++ mutex_lock(&dev->struct_mutex); ++ if (seqno > vc4->finished_seqno) { ++ cb->seqno = seqno; ++ list_add_tail(&cb->work.entry, &vc4->seqno_cb_list); ++ } else { ++ schedule_work(&cb->work); ++ } ++ mutex_unlock(&dev->struct_mutex); ++ ++ return ret; ++} ++ ++/* Scheduled when any job has been completed, this walks the list of ++ * jobs that had completed and unrefs their BOs and frees their exec ++ * structs. ++ */ ++static void ++vc4_job_done_work(struct work_struct *work) ++{ ++ struct vc4_dev *vc4 = ++ container_of(work, struct vc4_dev, job_done_work); ++ struct drm_device *dev = vc4->dev; ++ ++ /* Need the struct lock for drm_gem_object_unreference(). */ ++ mutex_lock(&dev->struct_mutex); ++ vc4_job_handle_completed(vc4); ++ mutex_unlock(&dev->struct_mutex); ++} ++ ++static int ++vc4_wait_for_seqno_ioctl_helper(struct drm_device *dev, ++ uint64_t seqno, ++ uint64_t *timeout_ns) ++{ ++ unsigned long start = jiffies; ++ int ret = vc4_wait_for_seqno(dev, seqno, *timeout_ns, true); ++ ++ if ((ret == -EINTR || ret == -ERESTARTSYS) && *timeout_ns != ~0ull) { ++ uint64_t delta = jiffies_to_nsecs(jiffies - start); ++ if (*timeout_ns >= delta) ++ *timeout_ns -= delta; ++ } ++ ++ return ret; ++} ++ ++int ++vc4_wait_seqno_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ struct drm_vc4_wait_seqno *args = data; ++ ++ return vc4_wait_for_seqno_ioctl_helper(dev, args->seqno, ++ &args->timeout_ns); ++} ++ ++int ++vc4_wait_bo_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ int ret; ++ struct drm_vc4_wait_bo *args = data; ++ struct drm_gem_object *gem_obj; ++ struct vc4_bo *bo; ++ ++ gem_obj = drm_gem_object_lookup(dev, file_priv, args->handle); ++ if (!gem_obj) { ++ DRM_ERROR("Failed to look up GEM BO %d\n", args->handle); ++ return -EINVAL; ++ } ++ bo = to_vc4_bo(gem_obj); ++ ++ ret = vc4_wait_for_seqno_ioctl_helper(dev, bo->seqno, &args->timeout_ns); ++ ++ drm_gem_object_unreference(gem_obj); ++ return ret; ++} ++ ++/** ++ * Submits a command list to the VC4. ++ * ++ * This is what is called batchbuffer emitting on other hardware. ++ */ ++int ++vc4_submit_cl_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct drm_vc4_submit_cl *args = data; ++ struct vc4_exec_info *exec; ++ int ret; ++ ++ if ((args->flags & ~VC4_SUBMIT_CL_USE_CLEAR_COLOR) != 0) { ++ DRM_ERROR("Unknown flags: 0x%02x\n", args->flags); ++ return -EINVAL; ++ } ++ ++ exec = kcalloc(1, sizeof(*exec), GFP_KERNEL); ++ if (!exec) { ++ DRM_ERROR("malloc failure on exec struct\n"); ++ return -ENOMEM; ++ } ++ ++ exec->args = args; ++ INIT_LIST_HEAD(&exec->unref_list); ++ ++ mutex_lock(&dev->struct_mutex); ++ ++ ret = vc4_cl_lookup_bos(dev, file_priv, exec); ++ if (ret) ++ goto fail; ++ ++ if (exec->args->bin_cl_size != 0) { ++ ret = vc4_get_bcl(dev, exec); ++ if (ret) ++ goto fail; ++ } else { ++ exec->ct0ca = exec->ct0ea = 0; ++ } ++ ++ ret = vc4_get_rcl(dev, exec); ++ if (ret) ++ goto fail; ++ ++ /* Clear this out of the struct we'll be putting in the queue, ++ * since it's part of our stack. ++ */ ++ exec->args = NULL; ++ ++ vc4_queue_submit(dev, exec); ++ ++ /* Return the seqno for our job. */ ++ args->seqno = vc4->emit_seqno; ++ ++ mutex_unlock(&dev->struct_mutex); ++ ++ return 0; ++ ++fail: ++ vc4_complete_exec(exec); ++ ++ mutex_unlock(&dev->struct_mutex); ++ ++ return ret; ++} ++ ++void ++vc4_gem_init(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ INIT_LIST_HEAD(&vc4->job_list); ++ INIT_LIST_HEAD(&vc4->job_done_list); ++ INIT_LIST_HEAD(&vc4->seqno_cb_list); ++ spin_lock_init(&vc4->job_lock); ++ ++ INIT_WORK(&vc4->hangcheck.reset_work, vc4_reset_work); ++ setup_timer(&vc4->hangcheck.timer, ++ vc4_hangcheck_elapsed, ++ (unsigned long) dev); ++ ++ INIT_WORK(&vc4->job_done_work, vc4_job_done_work); ++} ++ ++void ++vc4_gem_destroy(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ /* Waiting for exec to finish would need to be done before ++ * unregistering V3D. ++ */ ++ WARN_ON(vc4->emit_seqno != vc4->finished_seqno); ++ ++ /* V3D should already have disabled its interrupt and cleared ++ * the overflow allocation registers. Now free the object. ++ */ ++ if (vc4->overflow_mem) { ++ drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base); ++ vc4->overflow_mem = NULL; ++ } ++ ++ vc4_bo_cache_destroy(dev); ++} +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_irq.c +@@ -0,0 +1,211 @@ ++/* ++ * Copyright © 2014 Broadcom ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++ ++/** DOC: Interrupt management for the V3D engine. ++ * ++ * We have an interrupt status register (V3D_INTCTL) which reports ++ * interrupts, and where writing 1 bits clears those interrupts. ++ * There are also a pair of interrupt registers ++ * (V3D_INTENA/V3D_INTDIS) where writing a 1 to their bits enables or ++ * disables that specific interrupt, and 0s written are ignored ++ * (reading either one returns the set of enabled interrupts). ++ * ++ * When we take a render frame interrupt, we need to wake the ++ * processes waiting for some frame to be done, and get the next frame ++ * submitted ASAP (so the hardware doesn't sit idle when there's work ++ * to do). ++ * ++ * When we take the binner out of memory interrupt, we need to ++ * allocate some new memory and pass it to the binner so that the ++ * current job can make progress. ++ */ ++ ++#include "vc4_drv.h" ++#include "vc4_regs.h" ++ ++#define V3D_DRIVER_IRQS (V3D_INT_OUTOMEM | \ ++ V3D_INT_FRDONE) ++ ++DECLARE_WAIT_QUEUE_HEAD(render_wait); ++ ++static void ++vc4_overflow_mem_work(struct work_struct *work) ++{ ++ struct vc4_dev *vc4 = ++ container_of(work, struct vc4_dev, overflow_mem_work); ++ struct drm_device *dev = vc4->dev; ++ struct vc4_bo *bo; ++ ++ bo = vc4_bo_create(dev, 256 * 1024); ++ if (!bo) { ++ DRM_ERROR("Couldn't allocate binner overflow mem\n"); ++ return; ++ } ++ ++ /* If there's a job executing currently, then our previous ++ * overflow allocation is getting used in that job and we need ++ * to queue it to be released when the job is done. But if no ++ * job is executing at all, then we can free the old overflow ++ * object direcctly. ++ * ++ * No lock necessary for this pointer since we're the only ++ * ones that update the pointer, and our workqueue won't ++ * reenter. ++ */ ++ if (vc4->overflow_mem) { ++ struct vc4_exec_info *current_exec; ++ unsigned long irqflags; ++ ++ spin_lock_irqsave(&vc4->job_lock, irqflags); ++ current_exec = vc4_first_job(vc4); ++ if (current_exec) { ++ vc4->overflow_mem->seqno = vc4->finished_seqno + 1; ++ list_add_tail(&vc4->overflow_mem->unref_head, ++ ¤t_exec->unref_list); ++ vc4->overflow_mem = NULL; ++ } ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ } ++ ++ if (vc4->overflow_mem) { ++ drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base); ++ } ++ vc4->overflow_mem = bo; ++ ++ V3D_WRITE(V3D_BPOA, bo->base.paddr); ++ V3D_WRITE(V3D_BPOS, bo->base.base.size); ++ V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM); ++ V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM); ++} ++ ++static void ++vc4_irq_finish_job(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct vc4_exec_info *exec = vc4_first_job(vc4); ++ ++ if (!exec) ++ return; ++ ++ vc4->finished_seqno++; ++ list_move_tail(&exec->head, &vc4->job_done_list); ++ vc4_submit_next_job(dev); ++ ++ wake_up_all(&vc4->job_wait_queue); ++ schedule_work(&vc4->job_done_work); ++} ++ ++irqreturn_t ++vc4_irq(int irq, void *arg) ++{ ++ struct drm_device *dev = arg; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ uint32_t intctl; ++ irqreturn_t status = IRQ_NONE; ++ ++ barrier(); ++ intctl = V3D_READ(V3D_INTCTL); ++ ++ /* Acknowledge the interrupts we're handling here. The render ++ * frame done interrupt will be cleared, while OUTOMEM will ++ * stay high until the underlying cause is cleared. ++ */ ++ V3D_WRITE(V3D_INTCTL, intctl); ++ ++ if (intctl & V3D_INT_OUTOMEM) { ++ /* Disable OUTOMEM until the work is done. */ ++ V3D_WRITE(V3D_INTDIS, V3D_INT_OUTOMEM); ++ schedule_work(&vc4->overflow_mem_work); ++ status = IRQ_HANDLED; ++ } ++ ++ if (intctl & V3D_INT_FRDONE) { ++ spin_lock(&vc4->job_lock); ++ vc4_irq_finish_job(dev); ++ spin_unlock(&vc4->job_lock); ++ status = IRQ_HANDLED; ++ } ++ ++ return status; ++} ++ ++void ++vc4_irq_preinstall(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ init_waitqueue_head(&vc4->job_wait_queue); ++ INIT_WORK(&vc4->overflow_mem_work, vc4_overflow_mem_work); ++ ++ /* Clear any pending interrupts someone might have left around ++ * for us. ++ */ ++ V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); ++} ++ ++int ++vc4_irq_postinstall(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ /* Enable both the render done and out of memory interrupts. */ ++ V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); ++ ++ return 0; ++} ++ ++void ++vc4_irq_uninstall(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ /* Disable sending interrupts for our driver's IRQs. */ ++ V3D_WRITE(V3D_INTDIS, V3D_DRIVER_IRQS); ++ ++ /* Clear any pending interrupts we might have left. */ ++ V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); ++ ++ cancel_work_sync(&vc4->overflow_mem_work); ++} ++ ++/** Reinitializes interrupt registers when a GPU reset is performed. */ ++void vc4_irq_reset(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ unsigned long irqflags; ++ ++ /* Acknowledge any stale IRQs. */ ++ V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); ++ ++ /* ++ * Turn all our interrupts on. Binner out of memory is the ++ * only one we expect to trigger at this point, since we've ++ * just come from poweron and haven't supplied any overflow ++ * memory yet. ++ */ ++ V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); ++ ++ spin_lock_irqsave(&vc4->job_lock, irqflags); ++ vc4_irq_finish_job(dev); ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++} +--- a/drivers/gpu/drm/vc4/vc4_kms.c ++++ b/drivers/gpu/drm/vc4/vc4_kms.c +@@ -15,6 +15,7 @@ + */ + + #include "drm_crtc.h" ++#include "drm_atomic.h" + #include "drm_atomic_helper.h" + #include "drm_crtc_helper.h" + #include "drm_plane_helper.h" +@@ -29,10 +30,151 @@ static void vc4_output_poll_changed(stru + drm_fbdev_cma_hotplug_event(vc4->fbdev); + } + ++struct vc4_commit { ++ struct drm_device *dev; ++ struct drm_atomic_state *state; ++ struct vc4_seqno_cb cb; ++}; ++ ++static void ++vc4_atomic_complete_commit(struct vc4_commit *c) ++{ ++ struct drm_atomic_state *state = c->state; ++ struct drm_device *dev = state->dev; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ drm_atomic_helper_commit_modeset_disables(dev, state); ++ ++ drm_atomic_helper_commit_planes(dev, state); ++ ++ drm_atomic_helper_commit_modeset_enables(dev, state); ++ ++ drm_atomic_helper_wait_for_vblanks(dev, state); ++ ++ drm_atomic_helper_cleanup_planes(dev, state); ++ ++ drm_atomic_state_free(state); ++ ++ up(&vc4->async_modeset); ++ ++ kfree(c); ++} ++ ++static void ++vc4_atomic_complete_commit_seqno_cb(struct vc4_seqno_cb *cb) ++{ ++ struct vc4_commit *c = container_of(cb, struct vc4_commit, cb); ++ ++ vc4_atomic_complete_commit(c); ++} ++ ++static struct vc4_commit *commit_init(struct drm_atomic_state *state) ++{ ++ struct vc4_commit *c = kzalloc(sizeof(*c), GFP_KERNEL); ++ ++ if (!c) ++ return NULL; ++ c->dev = state->dev; ++ c->state = state; ++ ++ return c; ++} ++ ++/** ++ * vc4_atomic_commit - commit validated state object ++ * @dev: DRM device ++ * @state: the driver state object ++ * @async: asynchronous commit ++ * ++ * This function commits a with drm_atomic_helper_check() pre-validated state ++ * object. This can still fail when e.g. the framebuffer reservation fails. For ++ * now this doesn't implement asynchronous commits. ++ * ++ * RETURNS ++ * Zero for success or -errno. ++ */ ++static int vc4_atomic_commit(struct drm_device *dev, ++ struct drm_atomic_state *state, ++ bool async) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ int ret; ++ int i; ++ uint64_t wait_seqno = 0; ++ struct vc4_commit *c; ++ ++ c = commit_init(state); ++ if (!c) ++ return -ENOMEM; ++ ++ /* Make sure that any outstanding modesets have finished. */ ++ ret = down_interruptible(&vc4->async_modeset); ++ if (ret) { ++ kfree(c); ++ return ret; ++ } ++ ++ ret = drm_atomic_helper_prepare_planes(dev, state); ++ if (ret) { ++ kfree(c); ++ up(&vc4->async_modeset); ++ return ret; ++ } ++ ++ for (i = 0; i < dev->mode_config.num_total_plane; i++) { ++ struct drm_plane *plane = state->planes[i]; ++ struct drm_plane_state *new_state = state->plane_states[i]; ++ ++ if (!plane) ++ continue; ++ ++ if ((plane->state->fb != new_state->fb) && new_state->fb) { ++ struct drm_gem_cma_object *cma_bo = ++ drm_fb_cma_get_gem_obj(new_state->fb, 0); ++ struct vc4_bo *bo = to_vc4_bo(&cma_bo->base); ++ wait_seqno = max(bo->seqno, wait_seqno); ++ } ++ } ++ ++ /* ++ * This is the point of no return - everything below never fails except ++ * when the hw goes bonghits. Which means we can commit the new state on ++ * the software side now. ++ */ ++ ++ drm_atomic_helper_swap_state(dev, state); ++ ++ /* ++ * Everything below can be run asynchronously without the need to grab ++ * any modeset locks at all under one condition: It must be guaranteed ++ * that the asynchronous work has either been cancelled (if the driver ++ * supports it, which at least requires that the framebuffers get ++ * cleaned up with drm_atomic_helper_cleanup_planes()) or completed ++ * before the new state gets committed on the software side with ++ * drm_atomic_helper_swap_state(). ++ * ++ * This scheme allows new atomic state updates to be prepared and ++ * checked in parallel to the asynchronous completion of the previous ++ * update. Which is important since compositors need to figure out the ++ * composition of the next frame right after having submitted the ++ * current layout. ++ */ ++ ++ if (async) { ++ vc4_queue_seqno_cb(dev, &c->cb, wait_seqno, ++ vc4_atomic_complete_commit_seqno_cb); ++ } else { ++ vc4_wait_for_seqno(dev, wait_seqno, ~0ull, false); ++ vc4_atomic_complete_commit(c); ++ } ++ ++ return 0; ++} ++ + static const struct drm_mode_config_funcs vc4_mode_funcs = { + .output_poll_changed = vc4_output_poll_changed, + .atomic_check = drm_atomic_helper_check, +- .atomic_commit = drm_atomic_helper_commit, ++ .atomic_commit = vc4_atomic_commit, + .fb_create = drm_fb_cma_create, + }; + +@@ -41,6 +183,8 @@ int vc4_kms_load(struct drm_device *dev) + struct vc4_dev *vc4 = to_vc4_dev(dev); + int ret; + ++ sema_init(&vc4->async_modeset, 1); ++ + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); + if (ret < 0) { + dev_err(dev->dev, "failed to initialize vblank\n"); +@@ -51,6 +195,8 @@ int vc4_kms_load(struct drm_device *dev) + dev->mode_config.max_height = 2048; + dev->mode_config.funcs = &vc4_mode_funcs; + dev->mode_config.preferred_depth = 24; ++ dev->mode_config.async_page_flip = true; ++ + dev->vblank_disable_allowed = true; + + drm_mode_config_reset(dev); +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_packet.h +@@ -0,0 +1,384 @@ ++/* ++ * Copyright © 2014 Broadcom ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++ ++#ifndef VC4_PACKET_H ++#define VC4_PACKET_H ++ ++#include "vc4_regs.h" /* for VC4_MASK, VC4_GET_FIELD, VC4_SET_FIELD */ ++ ++enum vc4_packet { ++ VC4_PACKET_HALT = 0, ++ VC4_PACKET_NOP = 1, ++ ++ VC4_PACKET_FLUSH = 4, ++ VC4_PACKET_FLUSH_ALL = 5, ++ VC4_PACKET_START_TILE_BINNING = 6, ++ VC4_PACKET_INCREMENT_SEMAPHORE = 7, ++ VC4_PACKET_WAIT_ON_SEMAPHORE = 8, ++ ++ VC4_PACKET_BRANCH = 16, ++ VC4_PACKET_BRANCH_TO_SUB_LIST = 17, ++ ++ VC4_PACKET_STORE_MS_TILE_BUFFER = 24, ++ VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF = 25, ++ VC4_PACKET_STORE_FULL_RES_TILE_BUFFER = 26, ++ VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER = 27, ++ VC4_PACKET_STORE_TILE_BUFFER_GENERAL = 28, ++ VC4_PACKET_LOAD_TILE_BUFFER_GENERAL = 29, ++ ++ VC4_PACKET_GL_INDEXED_PRIMITIVE = 32, ++ VC4_PACKET_GL_ARRAY_PRIMITIVE = 33, ++ ++ VC4_PACKET_COMPRESSED_PRIMITIVE = 48, ++ VC4_PACKET_CLIPPED_COMPRESSED_PRIMITIVE = 49, ++ ++ VC4_PACKET_PRIMITIVE_LIST_FORMAT = 56, ++ ++ VC4_PACKET_GL_SHADER_STATE = 64, ++ VC4_PACKET_NV_SHADER_STATE = 65, ++ VC4_PACKET_VG_SHADER_STATE = 66, ++ ++ VC4_PACKET_CONFIGURATION_BITS = 96, ++ VC4_PACKET_FLAT_SHADE_FLAGS = 97, ++ VC4_PACKET_POINT_SIZE = 98, ++ VC4_PACKET_LINE_WIDTH = 99, ++ VC4_PACKET_RHT_X_BOUNDARY = 100, ++ VC4_PACKET_DEPTH_OFFSET = 101, ++ VC4_PACKET_CLIP_WINDOW = 102, ++ VC4_PACKET_VIEWPORT_OFFSET = 103, ++ VC4_PACKET_Z_CLIPPING = 104, ++ VC4_PACKET_CLIPPER_XY_SCALING = 105, ++ VC4_PACKET_CLIPPER_Z_SCALING = 106, ++ ++ VC4_PACKET_TILE_BINNING_MODE_CONFIG = 112, ++ VC4_PACKET_TILE_RENDERING_MODE_CONFIG = 113, ++ VC4_PACKET_CLEAR_COLORS = 114, ++ VC4_PACKET_TILE_COORDINATES = 115, ++ ++ /* Not an actual hardware packet -- this is what we use to put ++ * references to GEM bos in the command stream, since we need the u32 ++ * int the actual address packet in order to store the offset from the ++ * start of the BO. ++ */ ++ VC4_PACKET_GEM_HANDLES = 254, ++} __attribute__ ((__packed__)); ++ ++#define VC4_PACKET_HALT_SIZE 1 ++#define VC4_PACKET_NOP_SIZE 1 ++#define VC4_PACKET_FLUSH_SIZE 1 ++#define VC4_PACKET_FLUSH_ALL_SIZE 1 ++#define VC4_PACKET_START_TILE_BINNING_SIZE 1 ++#define VC4_PACKET_INCREMENT_SEMAPHORE_SIZE 1 ++#define VC4_PACKET_WAIT_ON_SEMAPHORE_SIZE 1 ++#define VC4_PACKET_BRANCH_SIZE 5 ++#define VC4_PACKET_BRANCH_TO_SUB_LIST_SIZE 5 ++#define VC4_PACKET_STORE_MS_TILE_BUFFER_SIZE 1 ++#define VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF_SIZE 1 ++#define VC4_PACKET_STORE_FULL_RES_TILE_BUFFER_SIZE 5 ++#define VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER_SIZE 5 ++#define VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE 7 ++#define VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE 7 ++#define VC4_PACKET_GL_INDEXED_PRIMITIVE_SIZE 14 ++#define VC4_PACKET_GL_ARRAY_PRIMITIVE_SIZE 10 ++#define VC4_PACKET_COMPRESSED_PRIMITIVE_SIZE 1 ++#define VC4_PACKET_CLIPPED_COMPRESSED_PRIMITIVE_SIZE 1 ++#define VC4_PACKET_PRIMITIVE_LIST_FORMAT_SIZE 2 ++#define VC4_PACKET_GL_SHADER_STATE_SIZE 5 ++#define VC4_PACKET_NV_SHADER_STATE_SIZE 5 ++#define VC4_PACKET_VG_SHADER_STATE_SIZE 5 ++#define VC4_PACKET_CONFIGURATION_BITS_SIZE 4 ++#define VC4_PACKET_FLAT_SHADE_FLAGS_SIZE 5 ++#define VC4_PACKET_POINT_SIZE_SIZE 5 ++#define VC4_PACKET_LINE_WIDTH_SIZE 5 ++#define VC4_PACKET_RHT_X_BOUNDARY_SIZE 3 ++#define VC4_PACKET_DEPTH_OFFSET_SIZE 5 ++#define VC4_PACKET_CLIP_WINDOW_SIZE 9 ++#define VC4_PACKET_VIEWPORT_OFFSET_SIZE 5 ++#define VC4_PACKET_Z_CLIPPING_SIZE 9 ++#define VC4_PACKET_CLIPPER_XY_SCALING_SIZE 9 ++#define VC4_PACKET_CLIPPER_Z_SCALING_SIZE 9 ++#define VC4_PACKET_TILE_BINNING_MODE_CONFIG_SIZE 16 ++#define VC4_PACKET_TILE_RENDERING_MODE_CONFIG_SIZE 11 ++#define VC4_PACKET_CLEAR_COLORS_SIZE 14 ++#define VC4_PACKET_TILE_COORDINATES_SIZE 3 ++#define VC4_PACKET_GEM_HANDLES_SIZE 9 ++ ++/** @{ ++ * Bits used by packets like VC4_PACKET_STORE_TILE_BUFFER_GENERAL and ++ * VC4_PACKET_TILE_RENDERING_MODE_CONFIG. ++*/ ++#define VC4_TILING_FORMAT_LINEAR 0 ++#define VC4_TILING_FORMAT_T 1 ++#define VC4_TILING_FORMAT_LT 2 ++/** @} */ ++ ++/** @{ ++ * ++ * low bits of VC4_PACKET_STORE_FULL_RES_TILE_BUFFER and ++ * VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER. ++ */ ++#define VC4_LOADSTORE_FULL_RES_EOF (1 << 3) ++#define VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL (1 << 2) ++#define VC4_LOADSTORE_FULL_RES_DISABLE_ZS (1 << 1) ++#define VC4_LOADSTORE_FULL_RES_DISABLE_COLOR (1 << 0) ++ ++/** @{ ++ * ++ * byte 2 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and ++ * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL (low bits of the address) ++ */ ++ ++#define VC4_LOADSTORE_TILE_BUFFER_EOF (1 << 3) ++#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_VG_MASK (1 << 2) ++#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_ZS (1 << 1) ++#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_COLOR (1 << 0) ++ ++/** @} */ ++ ++/** @{ ++ * ++ * byte 0-1 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and ++ * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL ++ */ ++#define VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR (1 << 15) ++#define VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR (1 << 14) ++#define VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR (1 << 13) ++#define VC4_STORE_TILE_BUFFER_DISABLE_SWAP (1 << 12) ++ ++#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK VC4_MASK(9, 8) ++#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_SHIFT 8 ++#define VC4_LOADSTORE_TILE_BUFFER_RGBA8888 0 ++#define VC4_LOADSTORE_TILE_BUFFER_BGR565_DITHER 1 ++#define VC4_LOADSTORE_TILE_BUFFER_BGR565 2 ++/** @} */ ++ ++/** @{ ++ * ++ * byte 0 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and ++ * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL ++ */ ++#define VC4_STORE_TILE_BUFFER_MODE_MASK VC4_MASK(7, 6) ++#define VC4_STORE_TILE_BUFFER_MODE_SHIFT 6 ++#define VC4_STORE_TILE_BUFFER_MODE_SAMPLE0 (0 << 6) ++#define VC4_STORE_TILE_BUFFER_MODE_DECIMATE_X4 (1 << 6) ++#define VC4_STORE_TILE_BUFFER_MODE_DECIMATE_X16 (2 << 6) ++ ++/** The values of the field are VC4_TILING_FORMAT_* */ ++#define VC4_LOADSTORE_TILE_BUFFER_TILING_MASK VC4_MASK(5, 4) ++#define VC4_LOADSTORE_TILE_BUFFER_TILING_SHIFT 4 ++ ++#define VC4_LOADSTORE_TILE_BUFFER_BUFFER_MASK VC4_MASK(2, 0) ++#define VC4_LOADSTORE_TILE_BUFFER_BUFFER_SHIFT 0 ++#define VC4_LOADSTORE_TILE_BUFFER_NONE 0 ++#define VC4_LOADSTORE_TILE_BUFFER_COLOR 1 ++#define VC4_LOADSTORE_TILE_BUFFER_ZS 2 ++#define VC4_LOADSTORE_TILE_BUFFER_Z 3 ++#define VC4_LOADSTORE_TILE_BUFFER_VG_MASK 4 ++#define VC4_LOADSTORE_TILE_BUFFER_FULL 5 ++/** @} */ ++ ++#define VC4_INDEX_BUFFER_U8 (0 << 4) ++#define VC4_INDEX_BUFFER_U16 (1 << 4) ++ ++/* This flag is only present in NV shader state. */ ++#define VC4_SHADER_FLAG_SHADED_CLIP_COORDS (1 << 3) ++#define VC4_SHADER_FLAG_ENABLE_CLIPPING (1 << 2) ++#define VC4_SHADER_FLAG_VS_POINT_SIZE (1 << 1) ++#define VC4_SHADER_FLAG_FS_SINGLE_THREAD (1 << 0) ++ ++/** @{ byte 2 of config bits. */ ++#define VC4_CONFIG_BITS_EARLY_Z_UPDATE (1 << 1) ++#define VC4_CONFIG_BITS_EARLY_Z (1 << 0) ++/** @} */ ++ ++/** @{ byte 1 of config bits. */ ++#define VC4_CONFIG_BITS_Z_UPDATE (1 << 7) ++/** same values in this 3-bit field as PIPE_FUNC_* */ ++#define VC4_CONFIG_BITS_DEPTH_FUNC_SHIFT 4 ++#define VC4_CONFIG_BITS_COVERAGE_READ_LEAVE (1 << 3) ++ ++#define VC4_CONFIG_BITS_COVERAGE_UPDATE_NONZERO (0 << 1) ++#define VC4_CONFIG_BITS_COVERAGE_UPDATE_ODD (1 << 1) ++#define VC4_CONFIG_BITS_COVERAGE_UPDATE_OR (2 << 1) ++#define VC4_CONFIG_BITS_COVERAGE_UPDATE_ZERO (3 << 1) ++ ++#define VC4_CONFIG_BITS_COVERAGE_PIPE_SELECT (1 << 0) ++/** @} */ ++ ++/** @{ byte 0 of config bits. */ ++#define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_NONE (0 << 6) ++#define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_4X (1 << 6) ++#define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_16X (2 << 6) ++ ++#define VC4_CONFIG_BITS_AA_POINTS_AND_LINES (1 << 4) ++#define VC4_CONFIG_BITS_ENABLE_DEPTH_OFFSET (1 << 3) ++#define VC4_CONFIG_BITS_CW_PRIMITIVES (1 << 2) ++#define VC4_CONFIG_BITS_ENABLE_PRIM_BACK (1 << 1) ++#define VC4_CONFIG_BITS_ENABLE_PRIM_FRONT (1 << 0) ++/** @} */ ++ ++/** @{ bits in the last u8 of VC4_PACKET_TILE_BINNING_MODE_CONFIG */ ++#define VC4_BIN_CONFIG_DB_NON_MS (1 << 7) ++ ++#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_MASK VC4_MASK(6, 5) ++#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_SHIFT 5 ++#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_32 0 ++#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_64 1 ++#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128 2 ++#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_256 3 ++ ++#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK VC4_MASK(4, 3) ++#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_SHIFT 3 ++#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_32 0 ++#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_64 1 ++#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_128 2 ++#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_256 3 ++ ++#define VC4_BIN_CONFIG_AUTO_INIT_TSDA (1 << 2) ++#define VC4_BIN_CONFIG_TILE_BUFFER_64BIT (1 << 1) ++#define VC4_BIN_CONFIG_MS_MODE_4X (1 << 0) ++/** @} */ ++ ++/** @{ bits in the last u16 of VC4_PACKET_TILE_RENDERING_MODE_CONFIG */ ++#define VC4_RENDER_CONFIG_DB_NON_MS (1 << 12) ++#define VC4_RENDER_CONFIG_EARLY_Z_COVERAGE_DISABLE (1 << 11) ++#define VC4_RENDER_CONFIG_EARLY_Z_DIRECTION_G (1 << 10) ++#define VC4_RENDER_CONFIG_COVERAGE_MODE (1 << 9) ++#define VC4_RENDER_CONFIG_ENABLE_VG_MASK (1 << 8) ++ ++/** The values of the field are VC4_TILING_FORMAT_* */ ++#define VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK VC4_MASK(7, 6) ++#define VC4_RENDER_CONFIG_MEMORY_FORMAT_SHIFT 6 ++ ++#define VC4_RENDER_CONFIG_DECIMATE_MODE_1X (0 << 4) ++#define VC4_RENDER_CONFIG_DECIMATE_MODE_4X (1 << 4) ++#define VC4_RENDER_CONFIG_DECIMATE_MODE_16X (2 << 4) ++ ++#define VC4_RENDER_CONFIG_FORMAT_MASK VC4_MASK(3, 2) ++#define VC4_RENDER_CONFIG_FORMAT_SHIFT 2 ++#define VC4_RENDER_CONFIG_FORMAT_BGR565_DITHERED 0 ++#define VC4_RENDER_CONFIG_FORMAT_RGBA8888 1 ++#define VC4_RENDER_CONFIG_FORMAT_BGR565 2 ++ ++#define VC4_RENDER_CONFIG_TILE_BUFFER_64BIT (1 << 1) ++#define VC4_RENDER_CONFIG_MS_MODE_4X (1 << 0) ++ ++#define VC4_PRIMITIVE_LIST_FORMAT_16_INDEX (1 << 4) ++#define VC4_PRIMITIVE_LIST_FORMAT_32_XY (3 << 4) ++#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_POINTS (0 << 0) ++#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_LINES (1 << 0) ++#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_TRIANGLES (2 << 0) ++#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_RHT (3 << 0) ++ ++enum vc4_texture_data_type { ++ VC4_TEXTURE_TYPE_RGBA8888 = 0, ++ VC4_TEXTURE_TYPE_RGBX8888 = 1, ++ VC4_TEXTURE_TYPE_RGBA4444 = 2, ++ VC4_TEXTURE_TYPE_RGBA5551 = 3, ++ VC4_TEXTURE_TYPE_RGB565 = 4, ++ VC4_TEXTURE_TYPE_LUMINANCE = 5, ++ VC4_TEXTURE_TYPE_ALPHA = 6, ++ VC4_TEXTURE_TYPE_LUMALPHA = 7, ++ VC4_TEXTURE_TYPE_ETC1 = 8, ++ VC4_TEXTURE_TYPE_S16F = 9, ++ VC4_TEXTURE_TYPE_S8 = 10, ++ VC4_TEXTURE_TYPE_S16 = 11, ++ VC4_TEXTURE_TYPE_BW1 = 12, ++ VC4_TEXTURE_TYPE_A4 = 13, ++ VC4_TEXTURE_TYPE_A1 = 14, ++ VC4_TEXTURE_TYPE_RGBA64 = 15, ++ VC4_TEXTURE_TYPE_RGBA32R = 16, ++ VC4_TEXTURE_TYPE_YUV422R = 17, ++}; ++ ++#define VC4_TEX_P0_OFFSET_MASK VC4_MASK(31, 12) ++#define VC4_TEX_P0_OFFSET_SHIFT 12 ++#define VC4_TEX_P0_CSWIZ_MASK VC4_MASK(11, 10) ++#define VC4_TEX_P0_CSWIZ_SHIFT 10 ++#define VC4_TEX_P0_CMMODE_MASK VC4_MASK(9, 9) ++#define VC4_TEX_P0_CMMODE_SHIFT 9 ++#define VC4_TEX_P0_FLIPY_MASK VC4_MASK(8, 8) ++#define VC4_TEX_P0_FLIPY_SHIFT 8 ++#define VC4_TEX_P0_TYPE_MASK VC4_MASK(7, 4) ++#define VC4_TEX_P0_TYPE_SHIFT 4 ++#define VC4_TEX_P0_MIPLVLS_MASK VC4_MASK(3, 0) ++#define VC4_TEX_P0_MIPLVLS_SHIFT 0 ++ ++#define VC4_TEX_P1_TYPE4_MASK VC4_MASK(31, 31) ++#define VC4_TEX_P1_TYPE4_SHIFT 31 ++#define VC4_TEX_P1_HEIGHT_MASK VC4_MASK(30, 20) ++#define VC4_TEX_P1_HEIGHT_SHIFT 20 ++#define VC4_TEX_P1_ETCFLIP_MASK VC4_MASK(19, 19) ++#define VC4_TEX_P1_ETCFLIP_SHIFT 19 ++#define VC4_TEX_P1_WIDTH_MASK VC4_MASK(18, 8) ++#define VC4_TEX_P1_WIDTH_SHIFT 8 ++ ++#define VC4_TEX_P1_MAGFILT_MASK VC4_MASK(7, 7) ++#define VC4_TEX_P1_MAGFILT_SHIFT 7 ++# define VC4_TEX_P1_MAGFILT_LINEAR 0 ++# define VC4_TEX_P1_MAGFILT_NEAREST 1 ++ ++#define VC4_TEX_P1_MINFILT_MASK VC4_MASK(6, 4) ++#define VC4_TEX_P1_MINFILT_SHIFT 4 ++# define VC4_TEX_P1_MINFILT_LINEAR 0 ++# define VC4_TEX_P1_MINFILT_NEAREST 1 ++# define VC4_TEX_P1_MINFILT_NEAR_MIP_NEAR 2 ++# define VC4_TEX_P1_MINFILT_NEAR_MIP_LIN 3 ++# define VC4_TEX_P1_MINFILT_LIN_MIP_NEAR 4 ++# define VC4_TEX_P1_MINFILT_LIN_MIP_LIN 5 ++ ++#define VC4_TEX_P1_WRAP_T_MASK VC4_MASK(3, 2) ++#define VC4_TEX_P1_WRAP_T_SHIFT 2 ++#define VC4_TEX_P1_WRAP_S_MASK VC4_MASK(1, 0) ++#define VC4_TEX_P1_WRAP_S_SHIFT 0 ++# define VC4_TEX_P1_WRAP_REPEAT 0 ++# define VC4_TEX_P1_WRAP_CLAMP 1 ++# define VC4_TEX_P1_WRAP_MIRROR 2 ++# define VC4_TEX_P1_WRAP_BORDER 3 ++ ++#define VC4_TEX_P2_PTYPE_MASK VC4_MASK(31, 30) ++#define VC4_TEX_P2_PTYPE_SHIFT 30 ++# define VC4_TEX_P2_PTYPE_IGNORED 0 ++# define VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE 1 ++# define VC4_TEX_P2_PTYPE_CHILD_IMAGE_DIMENSIONS 2 ++# define VC4_TEX_P2_PTYPE_CHILD_IMAGE_OFFSETS 3 ++ ++/* VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE bits */ ++#define VC4_TEX_P2_CMST_MASK VC4_MASK(29, 12) ++#define VC4_TEX_P2_CMST_SHIFT 12 ++#define VC4_TEX_P2_BSLOD_MASK VC4_MASK(0, 0) ++#define VC4_TEX_P2_BSLOD_SHIFT 0 ++ ++/* VC4_TEX_P2_PTYPE_CHILD_IMAGE_DIMENSIONS */ ++#define VC4_TEX_P2_CHEIGHT_MASK VC4_MASK(22, 12) ++#define VC4_TEX_P2_CHEIGHT_SHIFT 12 ++#define VC4_TEX_P2_CWIDTH_MASK VC4_MASK(10, 0) ++#define VC4_TEX_P2_CWIDTH_SHIFT 0 ++ ++/* VC4_TEX_P2_PTYPE_CHILD_IMAGE_OFFSETS */ ++#define VC4_TEX_P2_CYOFF_MASK VC4_MASK(22, 12) ++#define VC4_TEX_P2_CYOFF_SHIFT 12 ++#define VC4_TEX_P2_CXOFF_MASK VC4_MASK(10, 0) ++#define VC4_TEX_P2_CXOFF_SHIFT 0 ++ ++#endif /* VC4_PACKET_H */ +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -29,6 +29,14 @@ struct vc4_plane_state { + u32 *dlist; + u32 dlist_size; /* Number of dwords in allocated for the display list */ + u32 dlist_count; /* Number of used dwords in the display list. */ ++ ++ /* Offset in the dlist to pointer word 0. */ ++ u32 pw0_offset; ++ ++ /* Offset where the plane's dlist was last stored in the ++ hardware at vc4_crtc_atomic_flush() time. ++ */ ++ u32 *hw_dlist; + }; + + static inline struct vc4_plane_state * +@@ -207,6 +215,8 @@ static int vc4_plane_mode_set(struct drm + /* Position Word 3: Context. Written by the HVS. */ + vc4_dlist_write(vc4_state, 0xc0c0c0c0); + ++ vc4_state->pw0_offset = vc4_state->dlist_count; ++ + /* Pointer Word 0: RGB / Y Pointer */ + vc4_dlist_write(vc4_state, bo->paddr + offset); + +@@ -258,6 +268,8 @@ u32 vc4_plane_write_dlist(struct drm_pla + struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); + int i; + ++ vc4_state->hw_dlist = dlist; ++ + /* Can't memcpy_toio() because it needs to be 32-bit writes. */ + for (i = 0; i < vc4_state->dlist_count; i++) + writel(vc4_state->dlist[i], &dlist[i]); +@@ -272,6 +284,34 @@ u32 vc4_plane_dlist_size(struct drm_plan + return vc4_state->dlist_count; + } + ++/* Updates the plane to immediately (well, once the FIFO needs ++ * refilling) scan out from at a new framebuffer. ++ */ ++void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb) ++{ ++ struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); ++ struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); ++ uint32_t addr; ++ ++ /* We're skipping the address adjustment for negative origin, ++ * because this is only called on the primary plane. ++ */ ++ WARN_ON_ONCE(plane->state->crtc_x < 0 || plane->state->crtc_y < 0); ++ addr = bo->paddr + fb->offsets[0]; ++ ++ /* Write the new address into the hardware immediately. The ++ * scanout will start from this address as soon as the FIFO ++ * needs to refill with pixels. ++ */ ++ writel(addr, &vc4_state->hw_dlist[vc4_state->pw0_offset]); ++ ++ /* Also update the CPU-side dlist copy, so that any later ++ * atomic updates that don't do a new modeset on our plane ++ * also use our updated address. ++ */ ++ vc4_state->dlist[vc4_state->pw0_offset] = addr; ++} ++ + static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = { + .prepare_fb = NULL, + .cleanup_fb = NULL, +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_qpu_defines.h +@@ -0,0 +1,268 @@ ++/* ++ * Copyright © 2014 Broadcom ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++ ++#ifndef VC4_QPU_DEFINES_H ++#define VC4_QPU_DEFINES_H ++ ++enum qpu_op_add { ++ QPU_A_NOP, ++ QPU_A_FADD, ++ QPU_A_FSUB, ++ QPU_A_FMIN, ++ QPU_A_FMAX, ++ QPU_A_FMINABS, ++ QPU_A_FMAXABS, ++ QPU_A_FTOI, ++ QPU_A_ITOF, ++ QPU_A_ADD = 12, ++ QPU_A_SUB, ++ QPU_A_SHR, ++ QPU_A_ASR, ++ QPU_A_ROR, ++ QPU_A_SHL, ++ QPU_A_MIN, ++ QPU_A_MAX, ++ QPU_A_AND, ++ QPU_A_OR, ++ QPU_A_XOR, ++ QPU_A_NOT, ++ QPU_A_CLZ, ++ QPU_A_V8ADDS = 30, ++ QPU_A_V8SUBS = 31, ++}; ++ ++enum qpu_op_mul { ++ QPU_M_NOP, ++ QPU_M_FMUL, ++ QPU_M_MUL24, ++ QPU_M_V8MULD, ++ QPU_M_V8MIN, ++ QPU_M_V8MAX, ++ QPU_M_V8ADDS, ++ QPU_M_V8SUBS, ++}; ++ ++enum qpu_raddr { ++ QPU_R_FRAG_PAYLOAD_ZW = 15, /* W for A file, Z for B file */ ++ /* 0-31 are the plain regfile a or b fields */ ++ QPU_R_UNIF = 32, ++ QPU_R_VARY = 35, ++ QPU_R_ELEM_QPU = 38, ++ QPU_R_NOP, ++ QPU_R_XY_PIXEL_COORD = 41, ++ QPU_R_MS_REV_FLAGS = 41, ++ QPU_R_VPM = 48, ++ QPU_R_VPM_LD_BUSY, ++ QPU_R_VPM_LD_WAIT, ++ QPU_R_MUTEX_ACQUIRE, ++}; ++ ++enum qpu_waddr { ++ /* 0-31 are the plain regfile a or b fields */ ++ QPU_W_ACC0 = 32, /* aka r0 */ ++ QPU_W_ACC1, ++ QPU_W_ACC2, ++ QPU_W_ACC3, ++ QPU_W_TMU_NOSWAP, ++ QPU_W_ACC5, ++ QPU_W_HOST_INT, ++ QPU_W_NOP, ++ QPU_W_UNIFORMS_ADDRESS, ++ QPU_W_QUAD_XY, /* X for regfile a, Y for regfile b */ ++ QPU_W_MS_FLAGS = 42, ++ QPU_W_REV_FLAG = 42, ++ QPU_W_TLB_STENCIL_SETUP = 43, ++ QPU_W_TLB_Z, ++ QPU_W_TLB_COLOR_MS, ++ QPU_W_TLB_COLOR_ALL, ++ QPU_W_TLB_ALPHA_MASK, ++ QPU_W_VPM, ++ QPU_W_VPMVCD_SETUP, /* LD for regfile a, ST for regfile b */ ++ QPU_W_VPM_ADDR, /* LD for regfile a, ST for regfile b */ ++ QPU_W_MUTEX_RELEASE, ++ QPU_W_SFU_RECIP, ++ QPU_W_SFU_RECIPSQRT, ++ QPU_W_SFU_EXP, ++ QPU_W_SFU_LOG, ++ QPU_W_TMU0_S, ++ QPU_W_TMU0_T, ++ QPU_W_TMU0_R, ++ QPU_W_TMU0_B, ++ QPU_W_TMU1_S, ++ QPU_W_TMU1_T, ++ QPU_W_TMU1_R, ++ QPU_W_TMU1_B, ++}; ++ ++enum qpu_sig_bits { ++ QPU_SIG_SW_BREAKPOINT, ++ QPU_SIG_NONE, ++ QPU_SIG_THREAD_SWITCH, ++ QPU_SIG_PROG_END, ++ QPU_SIG_WAIT_FOR_SCOREBOARD, ++ QPU_SIG_SCOREBOARD_UNLOCK, ++ QPU_SIG_LAST_THREAD_SWITCH, ++ QPU_SIG_COVERAGE_LOAD, ++ QPU_SIG_COLOR_LOAD, ++ QPU_SIG_COLOR_LOAD_END, ++ QPU_SIG_LOAD_TMU0, ++ QPU_SIG_LOAD_TMU1, ++ QPU_SIG_ALPHA_MASK_LOAD, ++ QPU_SIG_SMALL_IMM, ++ QPU_SIG_LOAD_IMM, ++ QPU_SIG_BRANCH ++}; ++ ++enum qpu_mux { ++ /* hardware mux values */ ++ QPU_MUX_R0, ++ QPU_MUX_R1, ++ QPU_MUX_R2, ++ QPU_MUX_R3, ++ QPU_MUX_R4, ++ QPU_MUX_R5, ++ QPU_MUX_A, ++ QPU_MUX_B, ++ ++ /* non-hardware mux values */ ++ QPU_MUX_IMM, ++}; ++ ++enum qpu_cond { ++ QPU_COND_NEVER, ++ QPU_COND_ALWAYS, ++ QPU_COND_ZS, ++ QPU_COND_ZC, ++ QPU_COND_NS, ++ QPU_COND_NC, ++ QPU_COND_CS, ++ QPU_COND_CC, ++}; ++ ++enum qpu_pack_mul { ++ QPU_PACK_MUL_NOP, ++ QPU_PACK_MUL_8888 = 3, /* replicated to each 8 bits of the 32-bit dst. */ ++ QPU_PACK_MUL_8A, ++ QPU_PACK_MUL_8B, ++ QPU_PACK_MUL_8C, ++ QPU_PACK_MUL_8D, ++}; ++ ++enum qpu_pack_a { ++ QPU_PACK_A_NOP, ++ /* convert to 16 bit float if float input, or to int16. */ ++ QPU_PACK_A_16A, ++ QPU_PACK_A_16B, ++ /* replicated to each 8 bits of the 32-bit dst. */ ++ QPU_PACK_A_8888, ++ /* Convert to 8-bit unsigned int. */ ++ QPU_PACK_A_8A, ++ QPU_PACK_A_8B, ++ QPU_PACK_A_8C, ++ QPU_PACK_A_8D, ++ ++ /* Saturating variants of the previous instructions. */ ++ QPU_PACK_A_32_SAT, /* int-only */ ++ QPU_PACK_A_16A_SAT, /* int or float */ ++ QPU_PACK_A_16B_SAT, ++ QPU_PACK_A_8888_SAT, ++ QPU_PACK_A_8A_SAT, ++ QPU_PACK_A_8B_SAT, ++ QPU_PACK_A_8C_SAT, ++ QPU_PACK_A_8D_SAT, ++}; ++ ++enum qpu_unpack_r4 { ++ QPU_UNPACK_R4_NOP, ++ QPU_UNPACK_R4_F16A_TO_F32, ++ QPU_UNPACK_R4_F16B_TO_F32, ++ QPU_UNPACK_R4_8D_REP, ++ QPU_UNPACK_R4_8A, ++ QPU_UNPACK_R4_8B, ++ QPU_UNPACK_R4_8C, ++ QPU_UNPACK_R4_8D, ++}; ++ ++#define QPU_MASK(high, low) ((((uint64_t)1<<((high)-(low)+1))-1)<<(low)) ++/* Using the GNU statement expression extension */ ++#define QPU_SET_FIELD(value, field) \ ++ ({ \ ++ uint64_t fieldval = (uint64_t)(value) << field ## _SHIFT; \ ++ assert((fieldval & ~ field ## _MASK) == 0); \ ++ fieldval & field ## _MASK; \ ++ }) ++ ++#define QPU_GET_FIELD(word, field) ((uint32_t)(((word) & field ## _MASK) >> field ## _SHIFT)) ++ ++#define QPU_SIG_SHIFT 60 ++#define QPU_SIG_MASK QPU_MASK(63, 60) ++ ++#define QPU_UNPACK_SHIFT 57 ++#define QPU_UNPACK_MASK QPU_MASK(59, 57) ++ ++/** ++ * If set, the pack field means PACK_MUL or R4 packing, instead of normal ++ * regfile a packing. ++ */ ++#define QPU_PM ((uint64_t)1 << 56) ++ ++#define QPU_PACK_SHIFT 52 ++#define QPU_PACK_MASK QPU_MASK(55, 52) ++ ++#define QPU_COND_ADD_SHIFT 49 ++#define QPU_COND_ADD_MASK QPU_MASK(51, 49) ++#define QPU_COND_MUL_SHIFT 46 ++#define QPU_COND_MUL_MASK QPU_MASK(48, 46) ++ ++#define QPU_SF ((uint64_t)1 << 45) ++ ++#define QPU_WADDR_ADD_SHIFT 38 ++#define QPU_WADDR_ADD_MASK QPU_MASK(43, 38) ++#define QPU_WADDR_MUL_SHIFT 32 ++#define QPU_WADDR_MUL_MASK QPU_MASK(37, 32) ++ ++#define QPU_OP_MUL_SHIFT 29 ++#define QPU_OP_MUL_MASK QPU_MASK(31, 29) ++ ++#define QPU_RADDR_A_SHIFT 18 ++#define QPU_RADDR_A_MASK QPU_MASK(23, 18) ++#define QPU_RADDR_B_SHIFT 12 ++#define QPU_RADDR_B_MASK QPU_MASK(17, 12) ++#define QPU_SMALL_IMM_SHIFT 12 ++#define QPU_SMALL_IMM_MASK QPU_MASK(17, 12) ++ ++#define QPU_ADD_A_SHIFT 9 ++#define QPU_ADD_A_MASK QPU_MASK(11, 9) ++#define QPU_ADD_B_SHIFT 6 ++#define QPU_ADD_B_MASK QPU_MASK(8, 6) ++#define QPU_MUL_A_SHIFT 3 ++#define QPU_MUL_A_MASK QPU_MASK(5, 3) ++#define QPU_MUL_B_SHIFT 0 ++#define QPU_MUL_B_MASK QPU_MASK(2, 0) ++ ++#define QPU_WS ((uint64_t)1 << 44) ++ ++#define QPU_OP_ADD_SHIFT 24 ++#define QPU_OP_ADD_MASK QPU_MASK(28, 24) ++ ++#endif /* VC4_QPU_DEFINES_H */ +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_render_cl.c +@@ -0,0 +1,448 @@ ++/* ++ * Copyright © 2014-2015 Broadcom ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++ ++/** ++ * DOC: Render command list generation ++ * ++ * In the VC4 driver, render command list generation is performed by the ++ * kernel instead of userspace. We do this because validating a ++ * user-submitted command list is hard to get right and has high CPU overhead, ++ * while the number of valid configurations for render command lists is ++ * actually fairly low. ++ */ ++ ++#include "uapi/drm/vc4_drm.h" ++#include "vc4_drv.h" ++#include "vc4_packet.h" ++ ++struct vc4_rcl_setup { ++ struct drm_gem_cma_object *color_read; ++ struct drm_gem_cma_object *color_ms_write; ++ struct drm_gem_cma_object *zs_read; ++ struct drm_gem_cma_object *zs_write; ++ ++ struct drm_gem_cma_object *rcl; ++ u32 next_offset; ++}; ++ ++static inline void rcl_u8(struct vc4_rcl_setup *setup, u8 val) ++{ ++ *(u8 *)(setup->rcl->vaddr + setup->next_offset) = val; ++ setup->next_offset += 1; ++} ++ ++static inline void rcl_u16(struct vc4_rcl_setup *setup, u16 val) ++{ ++ *(u16 *)(setup->rcl->vaddr + setup->next_offset) = val; ++ setup->next_offset += 2; ++} ++ ++static inline void rcl_u32(struct vc4_rcl_setup *setup, u32 val) ++{ ++ *(u32 *)(setup->rcl->vaddr + setup->next_offset) = val; ++ setup->next_offset += 4; ++} ++ ++ ++/* ++ * Emits a no-op STORE_TILE_BUFFER_GENERAL. ++ * ++ * If we emit a PACKET_TILE_COORDINATES, it must be followed by a store of ++ * some sort before another load is triggered. ++ */ ++static void vc4_store_before_load(struct vc4_rcl_setup *setup) ++{ ++ rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL); ++ rcl_u16(setup, ++ VC4_SET_FIELD(VC4_LOADSTORE_TILE_BUFFER_NONE, ++ VC4_LOADSTORE_TILE_BUFFER_BUFFER) | ++ VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR | ++ VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR | ++ VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR); ++ rcl_u32(setup, 0); /* no address, since we're in None mode */ ++} ++ ++/* ++ * Emits a PACKET_TILE_COORDINATES if one isn't already pending. ++ * ++ * The tile coordinates packet triggers a pending load if there is one, are ++ * used for clipping during rendering, and determine where loads/stores happen ++ * relative to their base address. ++ */ ++static void vc4_tile_coordinates(struct vc4_rcl_setup *setup, ++ uint32_t x, uint32_t y) ++{ ++ rcl_u8(setup, VC4_PACKET_TILE_COORDINATES); ++ rcl_u8(setup, x); ++ rcl_u8(setup, y); ++} ++ ++static void emit_tile(struct vc4_exec_info *exec, ++ struct vc4_rcl_setup *setup, ++ uint8_t x, uint8_t y, bool first, bool last) ++{ ++ struct drm_vc4_submit_cl *args = exec->args; ++ bool has_bin = args->bin_cl_size != 0; ++ ++ /* Note that the load doesn't actually occur until the ++ * tile coords packet is processed, and only one load ++ * may be outstanding at a time. ++ */ ++ if (setup->color_read) { ++ rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); ++ rcl_u16(setup, args->color_read.bits); ++ rcl_u32(setup, ++ setup->color_read->paddr + args->color_read.offset); ++ } ++ ++ if (setup->zs_read) { ++ if (setup->color_read) { ++ /* Exec previous load. */ ++ vc4_tile_coordinates(setup, x, y); ++ vc4_store_before_load(setup); ++ } ++ ++ rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); ++ rcl_u16(setup, args->zs_read.bits); ++ rcl_u32(setup, setup->zs_read->paddr + args->zs_read.offset); ++ } ++ ++ /* Clipping depends on tile coordinates having been ++ * emitted, so we always need one here. ++ */ ++ vc4_tile_coordinates(setup, x, y); ++ ++ /* Wait for the binner before jumping to the first ++ * tile's lists. ++ */ ++ if (first && has_bin) ++ rcl_u8(setup, VC4_PACKET_WAIT_ON_SEMAPHORE); ++ ++ if (has_bin) { ++ rcl_u8(setup, VC4_PACKET_BRANCH_TO_SUB_LIST); ++ rcl_u32(setup, (exec->tile_bo->paddr + ++ exec->tile_alloc_offset + ++ (y * exec->bin_tiles_x + x) * 32)); ++ } ++ ++ if (setup->zs_write) { ++ rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL); ++ rcl_u16(setup, args->zs_write.bits | ++ (setup->color_ms_write ? ++ VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR : 0)); ++ rcl_u32(setup, ++ (setup->zs_write->paddr + args->zs_write.offset) | ++ ((last && !setup->color_ms_write) ? ++ VC4_LOADSTORE_TILE_BUFFER_EOF : 0)); ++ } ++ ++ if (setup->color_ms_write) { ++ if (setup->zs_write) { ++ /* Reset after previous store */ ++ vc4_tile_coordinates(setup, x, y); ++ } ++ ++ if (last) ++ rcl_u8(setup, VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF); ++ else ++ rcl_u8(setup, VC4_PACKET_STORE_MS_TILE_BUFFER); ++ } ++} ++ ++static int vc4_create_rcl_bo(struct drm_device *dev, struct vc4_exec_info *exec, ++ struct vc4_rcl_setup *setup) ++{ ++ struct drm_vc4_submit_cl *args = exec->args; ++ bool has_bin = args->bin_cl_size != 0; ++ uint8_t min_x_tile = args->min_x_tile; ++ uint8_t min_y_tile = args->min_y_tile; ++ uint8_t max_x_tile = args->max_x_tile; ++ uint8_t max_y_tile = args->max_y_tile; ++ uint8_t xtiles = max_x_tile - min_x_tile + 1; ++ uint8_t ytiles = max_y_tile - min_y_tile + 1; ++ uint8_t x, y; ++ uint32_t size, loop_body_size; ++ ++ size = VC4_PACKET_TILE_RENDERING_MODE_CONFIG_SIZE; ++ loop_body_size = VC4_PACKET_TILE_COORDINATES_SIZE; ++ ++ if (args->flags & VC4_SUBMIT_CL_USE_CLEAR_COLOR) { ++ size += VC4_PACKET_CLEAR_COLORS_SIZE + ++ VC4_PACKET_TILE_COORDINATES_SIZE + ++ VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; ++ } ++ ++ if (setup->color_read) { ++ loop_body_size += (VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE); ++ } ++ if (setup->zs_read) { ++ if (setup->color_read) { ++ loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE; ++ loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; ++ } ++ loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE; ++ } ++ ++ if (has_bin) { ++ size += VC4_PACKET_WAIT_ON_SEMAPHORE_SIZE; ++ loop_body_size += VC4_PACKET_BRANCH_TO_SUB_LIST_SIZE; ++ } ++ ++ if (setup->zs_write) ++ loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; ++ if (setup->color_ms_write) { ++ if (setup->zs_write) ++ loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE; ++ loop_body_size += VC4_PACKET_STORE_MS_TILE_BUFFER_SIZE; ++ } ++ size += xtiles * ytiles * loop_body_size; ++ ++ setup->rcl = &vc4_bo_create(dev, size)->base; ++ if (!setup->rcl) ++ return -ENOMEM; ++ list_add_tail(&to_vc4_bo(&setup->rcl->base)->unref_head, ++ &exec->unref_list); ++ ++ rcl_u8(setup, VC4_PACKET_TILE_RENDERING_MODE_CONFIG); ++ rcl_u32(setup, ++ (setup->color_ms_write ? ++ (setup->color_ms_write->paddr + ++ args->color_ms_write.offset) : ++ 0)); ++ rcl_u16(setup, args->width); ++ rcl_u16(setup, args->height); ++ rcl_u16(setup, args->color_ms_write.bits); ++ ++ /* The tile buffer gets cleared when the previous tile is stored. If ++ * the clear values changed between frames, then the tile buffer has ++ * stale clear values in it, so we have to do a store in None mode (no ++ * writes) so that we trigger the tile buffer clear. ++ */ ++ if (args->flags & VC4_SUBMIT_CL_USE_CLEAR_COLOR) { ++ rcl_u8(setup, VC4_PACKET_CLEAR_COLORS); ++ rcl_u32(setup, args->clear_color[0]); ++ rcl_u32(setup, args->clear_color[1]); ++ rcl_u32(setup, args->clear_z); ++ rcl_u8(setup, args->clear_s); ++ ++ vc4_tile_coordinates(setup, 0, 0); ++ ++ rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL); ++ rcl_u16(setup, VC4_LOADSTORE_TILE_BUFFER_NONE); ++ rcl_u32(setup, 0); /* no address, since we're in None mode */ ++ } ++ ++ for (y = min_y_tile; y <= max_y_tile; y++) { ++ for (x = min_x_tile; x <= max_x_tile; x++) { ++ bool first = (x == min_x_tile && y == min_y_tile); ++ bool last = (x == max_x_tile && y == max_y_tile); ++ emit_tile(exec, setup, x, y, first, last); ++ } ++ } ++ ++ BUG_ON(setup->next_offset != size); ++ exec->ct1ca = setup->rcl->paddr; ++ exec->ct1ea = setup->rcl->paddr + setup->next_offset; ++ ++ return 0; ++} ++ ++static int vc4_rcl_surface_setup(struct vc4_exec_info *exec, ++ struct drm_gem_cma_object **obj, ++ struct drm_vc4_submit_rcl_surface *surf) ++{ ++ uint8_t tiling = VC4_GET_FIELD(surf->bits, ++ VC4_LOADSTORE_TILE_BUFFER_TILING); ++ uint8_t buffer = VC4_GET_FIELD(surf->bits, ++ VC4_LOADSTORE_TILE_BUFFER_BUFFER); ++ uint8_t format = VC4_GET_FIELD(surf->bits, ++ VC4_LOADSTORE_TILE_BUFFER_FORMAT); ++ int cpp; ++ ++ if (surf->pad != 0) { ++ DRM_ERROR("Padding unset\n"); ++ return -EINVAL; ++ } ++ ++ if (surf->hindex == ~0) ++ return 0; ++ ++ if (!vc4_use_bo(exec, surf->hindex, VC4_MODE_RENDER, obj)) ++ return -EINVAL; ++ ++ if (surf->bits & ~(VC4_LOADSTORE_TILE_BUFFER_TILING_MASK | ++ VC4_LOADSTORE_TILE_BUFFER_BUFFER_MASK | ++ VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK)) { ++ DRM_ERROR("Unknown bits in load/store: 0x%04x\n", ++ surf->bits); ++ return -EINVAL; ++ } ++ ++ if (tiling > VC4_TILING_FORMAT_LT) { ++ DRM_ERROR("Bad tiling format\n"); ++ return -EINVAL; ++ } ++ ++ if (buffer == VC4_LOADSTORE_TILE_BUFFER_ZS) { ++ if (format != 0) { ++ DRM_ERROR("No color format should be set for ZS\n"); ++ return -EINVAL; ++ } ++ cpp = 4; ++ } else if (buffer == VC4_LOADSTORE_TILE_BUFFER_COLOR) { ++ switch (format) { ++ case VC4_LOADSTORE_TILE_BUFFER_BGR565: ++ case VC4_LOADSTORE_TILE_BUFFER_BGR565_DITHER: ++ cpp = 2; ++ break; ++ case VC4_LOADSTORE_TILE_BUFFER_RGBA8888: ++ cpp = 4; ++ break; ++ default: ++ DRM_ERROR("Bad tile buffer format\n"); ++ return -EINVAL; ++ } ++ } else { ++ DRM_ERROR("Bad load/store buffer %d.\n", buffer); ++ return -EINVAL; ++ } ++ ++ if (surf->offset & 0xf) { ++ DRM_ERROR("load/store buffer must be 16b aligned.\n"); ++ return -EINVAL; ++ } ++ ++ if (!vc4_check_tex_size(exec, *obj, surf->offset, tiling, ++ exec->args->width, exec->args->height, cpp)) { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int ++vc4_rcl_ms_surface_setup(struct vc4_exec_info *exec, ++ struct drm_gem_cma_object **obj, ++ struct drm_vc4_submit_rcl_surface *surf) ++{ ++ uint8_t tiling = VC4_GET_FIELD(surf->bits, ++ VC4_RENDER_CONFIG_MEMORY_FORMAT); ++ uint8_t format = VC4_GET_FIELD(surf->bits, ++ VC4_RENDER_CONFIG_FORMAT); ++ int cpp; ++ ++ if (surf->pad != 0) { ++ DRM_ERROR("Padding unset\n"); ++ return -EINVAL; ++ } ++ ++ if (surf->bits & ~(VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK | ++ VC4_RENDER_CONFIG_FORMAT_MASK)) { ++ DRM_ERROR("Unknown bits in render config: 0x%04x\n", ++ surf->bits); ++ return -EINVAL; ++ } ++ ++ if (surf->hindex == ~0) ++ return 0; ++ ++ if (!vc4_use_bo(exec, surf->hindex, VC4_MODE_RENDER, obj)) ++ return -EINVAL; ++ ++ if (tiling > VC4_TILING_FORMAT_LT) { ++ DRM_ERROR("Bad tiling format\n"); ++ return -EINVAL; ++ } ++ ++ switch (format) { ++ case VC4_RENDER_CONFIG_FORMAT_BGR565_DITHERED: ++ case VC4_RENDER_CONFIG_FORMAT_BGR565: ++ cpp = 2; ++ break; ++ case VC4_RENDER_CONFIG_FORMAT_RGBA8888: ++ cpp = 4; ++ break; ++ default: ++ DRM_ERROR("Bad tile buffer format\n"); ++ return -EINVAL; ++ } ++ ++ if (!vc4_check_tex_size(exec, *obj, surf->offset, tiling, ++ exec->args->width, exec->args->height, cpp)) { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++int vc4_get_rcl(struct drm_device *dev, struct vc4_exec_info *exec) ++{ ++ struct vc4_rcl_setup setup = {0}; ++ struct drm_vc4_submit_cl *args = exec->args; ++ bool has_bin = args->bin_cl_size != 0; ++ int ret; ++ ++ if (args->min_x_tile > args->max_x_tile || ++ args->min_y_tile > args->max_y_tile) { ++ DRM_ERROR("Bad render tile set (%d,%d)-(%d,%d)\n", ++ args->min_x_tile, args->min_y_tile, ++ args->max_x_tile, args->max_y_tile); ++ return -EINVAL; ++ } ++ ++ if (has_bin && ++ (args->max_x_tile > exec->bin_tiles_x || ++ args->max_y_tile > exec->bin_tiles_y)) { ++ DRM_ERROR("Render tiles (%d,%d) outside of bin config (%d,%d)\n", ++ args->max_x_tile, args->max_y_tile, ++ exec->bin_tiles_x, exec->bin_tiles_y); ++ return -EINVAL; ++ } ++ ++ ret = vc4_rcl_surface_setup(exec, &setup.color_read, &args->color_read); ++ if (ret) ++ return ret; ++ ++ ret = vc4_rcl_ms_surface_setup(exec, &setup.color_ms_write, ++ &args->color_ms_write); ++ if (ret) ++ return ret; ++ ++ ret = vc4_rcl_surface_setup(exec, &setup.zs_read, &args->zs_read); ++ if (ret) ++ return ret; ++ ++ ret = vc4_rcl_surface_setup(exec, &setup.zs_write, &args->zs_write); ++ if (ret) ++ return ret; ++ ++ /* We shouldn't even have the job submitted to us if there's no ++ * surface to write out. ++ */ ++ if (!setup.color_ms_write && !setup.zs_write) { ++ DRM_ERROR("RCL requires color or Z/S write\n"); ++ return -EINVAL; ++ } ++ ++ return vc4_create_rcl_bo(dev, exec, &setup); ++} +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_trace.h +@@ -0,0 +1,63 @@ ++/* ++ * Copyright (C) 2015 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. ++ */ ++ ++#if !defined(_VC4_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) ++#define _VC4_TRACE_H_ ++ ++#include ++#include ++#include ++ ++#undef TRACE_SYSTEM ++#define TRACE_SYSTEM vc4 ++#define TRACE_INCLUDE_FILE vc4_trace ++ ++TRACE_EVENT(vc4_wait_for_seqno_begin, ++ TP_PROTO(struct drm_device *dev, uint64_t seqno, uint64_t timeout), ++ TP_ARGS(dev, seqno, timeout), ++ ++ TP_STRUCT__entry( ++ __field(u32, dev) ++ __field(u64, seqno) ++ __field(u64, timeout) ++ ), ++ ++ TP_fast_assign( ++ __entry->dev = dev->primary->index; ++ __entry->seqno = seqno; ++ __entry->timeout = timeout; ++ ), ++ ++ TP_printk("dev=%u, seqno=%llu, timeout=%llu", ++ __entry->dev, __entry->seqno, __entry->timeout) ++); ++ ++TRACE_EVENT(vc4_wait_for_seqno_end, ++ TP_PROTO(struct drm_device *dev, uint64_t seqno), ++ TP_ARGS(dev, seqno), ++ ++ TP_STRUCT__entry( ++ __field(u32, dev) ++ __field(u64, seqno) ++ ), ++ ++ TP_fast_assign( ++ __entry->dev = dev->primary->index; ++ __entry->seqno = seqno; ++ ), ++ ++ TP_printk("dev=%u, seqno=%llu", ++ __entry->dev, __entry->seqno) ++); ++ ++#endif /* _VC4_TRACE_H_ */ ++ ++/* This part must be outside protection */ ++#undef TRACE_INCLUDE_PATH ++#define TRACE_INCLUDE_PATH . ++#include +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_trace_points.c +@@ -0,0 +1,14 @@ ++/* ++ * Copyright (C) 2015 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 "vc4_drv.h" ++ ++#ifndef __CHECKER__ ++#define CREATE_TRACE_POINTS ++#include "vc4_trace.h" ++#endif +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_v3d.c +@@ -0,0 +1,268 @@ ++/* ++ * Copyright (c) 2014 The Linux Foundation. All rights reserved. ++ * Copyright (C) 2013 Red Hat ++ * Author: Rob Clark ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include "linux/component.h" ++#include "soc/bcm2835/raspberrypi-firmware.h" ++#include "vc4_drv.h" ++#include "vc4_regs.h" ++ ++#ifdef CONFIG_DEBUG_FS ++#define REGDEF(reg) { reg, #reg } ++static const struct { ++ uint32_t reg; ++ const char *name; ++} vc4_reg_defs[] = { ++ REGDEF(V3D_IDENT0), ++ REGDEF(V3D_IDENT1), ++ REGDEF(V3D_IDENT2), ++ REGDEF(V3D_SCRATCH), ++ REGDEF(V3D_L2CACTL), ++ REGDEF(V3D_SLCACTL), ++ REGDEF(V3D_INTCTL), ++ REGDEF(V3D_INTENA), ++ REGDEF(V3D_INTDIS), ++ REGDEF(V3D_CT0CS), ++ REGDEF(V3D_CT1CS), ++ REGDEF(V3D_CT0EA), ++ REGDEF(V3D_CT1EA), ++ REGDEF(V3D_CT0CA), ++ REGDEF(V3D_CT1CA), ++ REGDEF(V3D_CT00RA0), ++ REGDEF(V3D_CT01RA0), ++ REGDEF(V3D_CT0LC), ++ REGDEF(V3D_CT1LC), ++ REGDEF(V3D_CT0PC), ++ REGDEF(V3D_CT1PC), ++ REGDEF(V3D_PCS), ++ REGDEF(V3D_BFC), ++ REGDEF(V3D_RFC), ++ REGDEF(V3D_BPCA), ++ REGDEF(V3D_BPCS), ++ REGDEF(V3D_BPOA), ++ REGDEF(V3D_BPOS), ++ REGDEF(V3D_BXCF), ++ REGDEF(V3D_SQRSV0), ++ REGDEF(V3D_SQRSV1), ++ REGDEF(V3D_SQCNTL), ++ REGDEF(V3D_SRQPC), ++ REGDEF(V3D_SRQUA), ++ REGDEF(V3D_SRQUL), ++ REGDEF(V3D_SRQCS), ++ REGDEF(V3D_VPACNTL), ++ REGDEF(V3D_VPMBASE), ++ REGDEF(V3D_PCTRC), ++ REGDEF(V3D_PCTRE), ++ REGDEF(V3D_PCTR0), ++ REGDEF(V3D_PCTRS0), ++ REGDEF(V3D_PCTR1), ++ REGDEF(V3D_PCTRS1), ++ REGDEF(V3D_PCTR2), ++ REGDEF(V3D_PCTRS2), ++ REGDEF(V3D_PCTR3), ++ REGDEF(V3D_PCTRS3), ++ REGDEF(V3D_PCTR4), ++ REGDEF(V3D_PCTRS4), ++ REGDEF(V3D_PCTR5), ++ REGDEF(V3D_PCTRS5), ++ REGDEF(V3D_PCTR6), ++ REGDEF(V3D_PCTRS6), ++ REGDEF(V3D_PCTR7), ++ REGDEF(V3D_PCTRS7), ++ REGDEF(V3D_PCTR8), ++ REGDEF(V3D_PCTRS8), ++ REGDEF(V3D_PCTR9), ++ REGDEF(V3D_PCTRS9), ++ REGDEF(V3D_PCTR10), ++ REGDEF(V3D_PCTRS10), ++ REGDEF(V3D_PCTR11), ++ REGDEF(V3D_PCTRS11), ++ REGDEF(V3D_PCTR12), ++ REGDEF(V3D_PCTRS12), ++ REGDEF(V3D_PCTR13), ++ REGDEF(V3D_PCTRS13), ++ REGDEF(V3D_PCTR14), ++ REGDEF(V3D_PCTRS14), ++ REGDEF(V3D_PCTR15), ++ REGDEF(V3D_PCTRS15), ++ REGDEF(V3D_BGE), ++ REGDEF(V3D_FDBGO), ++ REGDEF(V3D_FDBGB), ++ REGDEF(V3D_FDBGR), ++ REGDEF(V3D_FDBGS), ++ REGDEF(V3D_ERRSTAT), ++}; ++ ++int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused) ++{ ++ struct drm_info_node *node = (struct drm_info_node *) m->private; ++ struct drm_device *dev = node->minor->dev; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(vc4_reg_defs); i++) { ++ seq_printf(m, "%s (0x%04x): 0x%08x\n", ++ vc4_reg_defs[i].name, vc4_reg_defs[i].reg, ++ V3D_READ(vc4_reg_defs[i].reg)); ++ } ++ ++ return 0; ++} ++ ++int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused) ++{ ++ struct drm_info_node *node = (struct drm_info_node *) m->private; ++ struct drm_device *dev = node->minor->dev; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ uint32_t ident1 = V3D_READ(V3D_IDENT1); ++ uint32_t nslc = VC4_GET_FIELD(ident1, V3D_IDENT1_NSLC); ++ uint32_t tups = VC4_GET_FIELD(ident1, V3D_IDENT1_TUPS); ++ uint32_t qups = VC4_GET_FIELD(ident1, V3D_IDENT1_QUPS); ++ ++ seq_printf(m, "Revision: %d\n", VC4_GET_FIELD(ident1, V3D_IDENT1_REV)); ++ seq_printf(m, "Slices: %d\n", nslc); ++ seq_printf(m, "TMUs: %d\n", nslc * tups); ++ seq_printf(m, "QPUs: %d\n", nslc * qups); ++ seq_printf(m, "Semaphores: %d\n", VC4_GET_FIELD(ident1, V3D_IDENT1_NSEM)); ++ ++ return 0; ++} ++#endif /* CONFIG_DEBUG_FS */ ++ ++/* ++ * Asks the firmware to turn on power to the V3D engine. ++ * ++ * This may be doable with just the clocks interface, though this ++ * packet does some other register setup from the firmware, too. ++ */ ++int ++vc4_v3d_set_power(struct vc4_dev *vc4, bool on) ++{ ++ u32 packet = on; ++ ++ return rpi_firmware_property(vc4->firmware, ++ RPI_FIRMWARE_SET_ENABLE_QPU, ++ &packet, sizeof(packet)); ++} ++ ++static void vc4_v3d_init_hw(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ /* Take all the memory that would have been reserved for user ++ * QPU programs, since we don't have an interface for running ++ * them, anyway. ++ */ ++ V3D_WRITE(V3D_VPMBASE, 0); ++} ++ ++static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct drm_device *drm = dev_get_drvdata(master); ++ struct vc4_dev *vc4 = to_vc4_dev(drm); ++ struct vc4_v3d *v3d = NULL; ++ int ret; ++ ++ v3d = devm_kzalloc(&pdev->dev, sizeof(*v3d), GFP_KERNEL); ++ if (!v3d) ++ return -ENOMEM; ++ ++ v3d->pdev = pdev; ++ ++ v3d->regs = vc4_ioremap_regs(pdev, 0); ++ if (IS_ERR(v3d->regs)) ++ return PTR_ERR(v3d->regs); ++ ++ vc4->v3d = v3d; ++ ++ ret = vc4_v3d_set_power(vc4, true); ++ if (ret) ++ return ret; ++ ++ if (V3D_READ(V3D_IDENT0) != V3D_EXPECTED_IDENT0) { ++ DRM_ERROR("V3D_IDENT0 read 0x%08x instead of 0x%08x\n", ++ V3D_READ(V3D_IDENT0), V3D_EXPECTED_IDENT0); ++ return -EINVAL; ++ } ++ ++ /* Reset the binner overflow address/size at setup, to be sure ++ * we don't reuse an old one. ++ */ ++ V3D_WRITE(V3D_BPOA, 0); ++ V3D_WRITE(V3D_BPOS, 0); ++ ++ vc4_v3d_init_hw(drm); ++ ++ ret = drm_irq_install(drm, platform_get_irq(pdev, 0)); ++ if (ret) { ++ DRM_ERROR("Failed to install IRQ handler\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void vc4_v3d_unbind(struct device *dev, struct device *master, ++ void *data) ++{ ++ struct drm_device *drm = dev_get_drvdata(master); ++ struct vc4_dev *vc4 = to_vc4_dev(drm); ++ ++ drm_irq_uninstall(drm); ++ ++ /* Disable the binner's overflow memory address, so the next ++ * driver probe (if any) doesn't try to reuse our old ++ * allocation. ++ */ ++ V3D_WRITE(V3D_BPOA, 0); ++ V3D_WRITE(V3D_BPOS, 0); ++ ++ vc4_v3d_set_power(vc4, false); ++ ++ vc4->v3d = NULL; ++} ++ ++static const struct component_ops vc4_v3d_ops = { ++ .bind = vc4_v3d_bind, ++ .unbind = vc4_v3d_unbind, ++}; ++ ++static int vc4_v3d_dev_probe(struct platform_device *pdev) ++{ ++ return component_add(&pdev->dev, &vc4_v3d_ops); ++} ++ ++static int vc4_v3d_dev_remove(struct platform_device *pdev) ++{ ++ component_del(&pdev->dev, &vc4_v3d_ops); ++ return 0; ++} ++ ++static const struct of_device_id vc4_v3d_dt_match[] = { ++ { .compatible = "brcm,vc4-v3d" }, ++ {} ++}; ++ ++struct platform_driver vc4_v3d_driver = { ++ .probe = vc4_v3d_dev_probe, ++ .remove = vc4_v3d_dev_remove, ++ .driver = { ++ .name = "vc4_v3d", ++ .of_match_table = vc4_v3d_dt_match, ++ }, ++}; +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_validate.c +@@ -0,0 +1,958 @@ ++/* ++ * Copyright © 2014 Broadcom ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++ ++/** ++ * Command list validator for VC4. ++ * ++ * The VC4 has no IOMMU between it and system memory. So, a user with ++ * access to execute command lists could escalate privilege by ++ * overwriting system memory (drawing to it as a framebuffer) or ++ * reading system memory it shouldn't (reading it as a texture, or ++ * uniform data, or vertex data). ++ * ++ * This validates command lists to ensure that all accesses are within ++ * the bounds of the GEM objects referenced. It explicitly whitelists ++ * packets, and looks at the offsets in any address fields to make ++ * sure they're constrained within the BOs they reference. ++ * ++ * Note that because of the validation that's happening anyway, this ++ * is where GEM relocation processing happens. ++ */ ++ ++#include "uapi/drm/vc4_drm.h" ++#include "vc4_drv.h" ++#include "vc4_packet.h" ++ ++#define VALIDATE_ARGS \ ++ struct vc4_exec_info *exec, \ ++ void *validated, \ ++ void *untrusted ++ ++ ++/** Return the width in pixels of a 64-byte microtile. */ ++static uint32_t ++utile_width(int cpp) ++{ ++ switch (cpp) { ++ case 1: ++ case 2: ++ return 8; ++ case 4: ++ return 4; ++ case 8: ++ return 2; ++ default: ++ DRM_ERROR("unknown cpp: %d\n", cpp); ++ return 1; ++ } ++} ++ ++/** Return the height in pixels of a 64-byte microtile. */ ++static uint32_t ++utile_height(int cpp) ++{ ++ switch (cpp) { ++ case 1: ++ return 8; ++ case 2: ++ case 4: ++ case 8: ++ return 4; ++ default: ++ DRM_ERROR("unknown cpp: %d\n", cpp); ++ return 1; ++ } ++} ++ ++/** ++ * The texture unit decides what tiling format a particular miplevel is using ++ * this function, so we lay out our miptrees accordingly. ++ */ ++static bool ++size_is_lt(uint32_t width, uint32_t height, int cpp) ++{ ++ return (width <= 4 * utile_width(cpp) || ++ height <= 4 * utile_height(cpp)); ++} ++ ++bool ++vc4_use_bo(struct vc4_exec_info *exec, ++ uint32_t hindex, ++ enum vc4_bo_mode mode, ++ struct drm_gem_cma_object **obj) ++{ ++ *obj = NULL; ++ ++ if (hindex >= exec->bo_count) { ++ DRM_ERROR("BO index %d greater than BO count %d\n", ++ hindex, exec->bo_count); ++ return false; ++ } ++ ++ if (exec->bo[hindex].mode != mode) { ++ if (exec->bo[hindex].mode == VC4_MODE_UNDECIDED) { ++ exec->bo[hindex].mode = mode; ++ } else { ++ DRM_ERROR("BO index %d reused with mode %d vs %d\n", ++ hindex, exec->bo[hindex].mode, mode); ++ return false; ++ } ++ } ++ ++ *obj = exec->bo[hindex].bo; ++ return true; ++} ++ ++static bool ++vc4_use_handle(struct vc4_exec_info *exec, ++ uint32_t gem_handles_packet_index, ++ enum vc4_bo_mode mode, ++ struct drm_gem_cma_object **obj) ++{ ++ return vc4_use_bo(exec, exec->bo_index[gem_handles_packet_index], ++ mode, obj); ++} ++ ++static uint32_t ++gl_shader_rec_size(uint32_t pointer_bits) ++{ ++ uint32_t attribute_count = pointer_bits & 7; ++ bool extended = pointer_bits & 8; ++ ++ if (attribute_count == 0) ++ attribute_count = 8; ++ ++ if (extended) ++ return 100 + attribute_count * 4; ++ else ++ return 36 + attribute_count * 8; ++} ++ ++bool ++vc4_check_tex_size(struct vc4_exec_info *exec, struct drm_gem_cma_object *fbo, ++ uint32_t offset, uint8_t tiling_format, ++ uint32_t width, uint32_t height, uint8_t cpp) ++{ ++ uint32_t aligned_width, aligned_height, stride, size; ++ uint32_t utile_w = utile_width(cpp); ++ uint32_t utile_h = utile_height(cpp); ++ ++ /* The shaded vertex format stores signed 12.4 fixed point ++ * (-2048,2047) offsets from the viewport center, so we should ++ * never have a render target larger than 4096. The texture ++ * unit can only sample from 2048x2048, so it's even more ++ * restricted. This lets us avoid worrying about overflow in ++ * our math. ++ */ ++ if (width > 4096 || height > 4096) { ++ DRM_ERROR("Surface dimesions (%d,%d) too large", width, height); ++ return false; ++ } ++ ++ switch (tiling_format) { ++ case VC4_TILING_FORMAT_LINEAR: ++ aligned_width = round_up(width, utile_w); ++ aligned_height = height; ++ break; ++ case VC4_TILING_FORMAT_T: ++ aligned_width = round_up(width, utile_w * 8); ++ aligned_height = round_up(height, utile_h * 8); ++ break; ++ case VC4_TILING_FORMAT_LT: ++ aligned_width = round_up(width, utile_w); ++ aligned_height = round_up(height, utile_h); ++ break; ++ default: ++ DRM_ERROR("buffer tiling %d unsupported\n", tiling_format); ++ return false; ++ } ++ ++ stride = aligned_width * cpp; ++ size = stride * aligned_height; ++ ++ if (size + offset < size || ++ size + offset > fbo->base.size) { ++ DRM_ERROR("Overflow in %dx%d (%dx%d) fbo size (%d + %d > %d)\n", ++ width, height, ++ aligned_width, aligned_height, ++ size, offset, fbo->base.size); ++ return false; ++ } ++ ++ return true; ++} ++ ++static int ++validate_flush_all(VALIDATE_ARGS) ++{ ++ if (exec->found_increment_semaphore_packet) { ++ DRM_ERROR("VC4_PACKET_FLUSH_ALL after " ++ "VC4_PACKET_INCREMENT_SEMAPHORE\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int ++validate_start_tile_binning(VALIDATE_ARGS) ++{ ++ if (exec->found_start_tile_binning_packet) { ++ DRM_ERROR("Duplicate VC4_PACKET_START_TILE_BINNING\n"); ++ return -EINVAL; ++ } ++ exec->found_start_tile_binning_packet = true; ++ ++ if (!exec->found_tile_binning_mode_config_packet) { ++ DRM_ERROR("missing VC4_PACKET_TILE_BINNING_MODE_CONFIG\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int ++validate_increment_semaphore(VALIDATE_ARGS) ++{ ++ if (exec->found_increment_semaphore_packet) { ++ DRM_ERROR("Duplicate VC4_PACKET_INCREMENT_SEMAPHORE\n"); ++ return -EINVAL; ++ } ++ exec->found_increment_semaphore_packet = true; ++ ++ /* Once we've found the semaphore increment, there should be one FLUSH ++ * then the end of the command list. The FLUSH actually triggers the ++ * increment, so we only need to make sure there ++ */ ++ ++ return 0; ++} ++ ++static int ++validate_indexed_prim_list(VALIDATE_ARGS) ++{ ++ struct drm_gem_cma_object *ib; ++ uint32_t length = *(uint32_t *)(untrusted + 1); ++ uint32_t offset = *(uint32_t *)(untrusted + 5); ++ uint32_t max_index = *(uint32_t *)(untrusted + 9); ++ uint32_t index_size = (*(uint8_t *)(untrusted + 0) >> 4) ? 2 : 1; ++ struct vc4_shader_state *shader_state; ++ ++ if (exec->found_increment_semaphore_packet) { ++ DRM_ERROR("Drawing after VC4_PACKET_INCREMENT_SEMAPHORE\n"); ++ return -EINVAL; ++ } ++ ++ /* Check overflow condition */ ++ if (exec->shader_state_count == 0) { ++ DRM_ERROR("shader state must precede primitives\n"); ++ return -EINVAL; ++ } ++ shader_state = &exec->shader_state[exec->shader_state_count - 1]; ++ ++ if (max_index > shader_state->max_index) ++ shader_state->max_index = max_index; ++ ++ if (!vc4_use_handle(exec, 0, VC4_MODE_RENDER, &ib)) ++ return -EINVAL; ++ ++ if (offset > ib->base.size || ++ (ib->base.size - offset) / index_size < length) { ++ DRM_ERROR("IB access overflow (%d + %d*%d > %d)\n", ++ offset, length, index_size, ib->base.size); ++ return -EINVAL; ++ } ++ ++ *(uint32_t *)(validated + 5) = ib->paddr + offset; ++ ++ return 0; ++} ++ ++static int ++validate_gl_array_primitive(VALIDATE_ARGS) ++{ ++ uint32_t length = *(uint32_t *)(untrusted + 1); ++ uint32_t base_index = *(uint32_t *)(untrusted + 5); ++ uint32_t max_index; ++ struct vc4_shader_state *shader_state; ++ ++ if (exec->found_increment_semaphore_packet) { ++ DRM_ERROR("Drawing after VC4_PACKET_INCREMENT_SEMAPHORE\n"); ++ return -EINVAL; ++ } ++ ++ /* Check overflow condition */ ++ if (exec->shader_state_count == 0) { ++ DRM_ERROR("shader state must precede primitives\n"); ++ return -EINVAL; ++ } ++ shader_state = &exec->shader_state[exec->shader_state_count - 1]; ++ ++ if (length + base_index < length) { ++ DRM_ERROR("primitive vertex count overflow\n"); ++ return -EINVAL; ++ } ++ max_index = length + base_index - 1; ++ ++ if (max_index > shader_state->max_index) ++ shader_state->max_index = max_index; ++ ++ return 0; ++} ++ ++static int ++validate_gl_shader_state(VALIDATE_ARGS) ++{ ++ uint32_t i = exec->shader_state_count++; ++ ++ if (i >= exec->shader_state_size) { ++ DRM_ERROR("More requests for shader states than declared\n"); ++ return -EINVAL; ++ } ++ ++ exec->shader_state[i].packet = VC4_PACKET_GL_SHADER_STATE; ++ exec->shader_state[i].addr = *(uint32_t *)untrusted; ++ exec->shader_state[i].max_index = 0; ++ ++ if (exec->shader_state[i].addr & ~0xf) { ++ DRM_ERROR("high bits set in GL shader rec reference\n"); ++ return -EINVAL; ++ } ++ ++ *(uint32_t *)validated = (exec->shader_rec_p + ++ exec->shader_state[i].addr); ++ ++ exec->shader_rec_p += ++ roundup(gl_shader_rec_size(exec->shader_state[i].addr), 16); ++ ++ return 0; ++} ++ ++static int ++validate_nv_shader_state(VALIDATE_ARGS) ++{ ++ uint32_t i = exec->shader_state_count++; ++ ++ if (i >= exec->shader_state_size) { ++ DRM_ERROR("More requests for shader states than declared\n"); ++ return -EINVAL; ++ } ++ ++ exec->shader_state[i].packet = VC4_PACKET_NV_SHADER_STATE; ++ exec->shader_state[i].addr = *(uint32_t *)untrusted; ++ ++ if (exec->shader_state[i].addr & 15) { ++ DRM_ERROR("NV shader state address 0x%08x misaligned\n", ++ exec->shader_state[i].addr); ++ return -EINVAL; ++ } ++ ++ *(uint32_t *)validated = (exec->shader_state[i].addr + ++ exec->shader_rec_p); ++ ++ return 0; ++} ++ ++static int ++validate_tile_binning_config(VALIDATE_ARGS) ++{ ++ struct drm_device *dev = exec->exec_bo->base.dev; ++ uint8_t flags; ++ uint32_t tile_state_size, tile_alloc_size; ++ uint32_t tile_count; ++ ++ if (exec->found_tile_binning_mode_config_packet) { ++ DRM_ERROR("Duplicate VC4_PACKET_TILE_BINNING_MODE_CONFIG\n"); ++ return -EINVAL; ++ } ++ exec->found_tile_binning_mode_config_packet = true; ++ ++ exec->bin_tiles_x = *(uint8_t *)(untrusted + 12); ++ exec->bin_tiles_y = *(uint8_t *)(untrusted + 13); ++ tile_count = exec->bin_tiles_x * exec->bin_tiles_y; ++ flags = *(uint8_t *)(untrusted + 14); ++ ++ if (exec->bin_tiles_x == 0 || ++ exec->bin_tiles_y == 0) { ++ DRM_ERROR("Tile binning config of %dx%d too small\n", ++ exec->bin_tiles_x, exec->bin_tiles_y); ++ return -EINVAL; ++ } ++ ++ if (flags & (VC4_BIN_CONFIG_DB_NON_MS | ++ VC4_BIN_CONFIG_TILE_BUFFER_64BIT | ++ VC4_BIN_CONFIG_MS_MODE_4X)) { ++ DRM_ERROR("unsupported bining config flags 0x%02x\n", flags); ++ return -EINVAL; ++ } ++ ++ /* The tile state data array is 48 bytes per tile, and we put it at ++ * the start of a BO containing both it and the tile alloc. ++ */ ++ tile_state_size = 48 * tile_count; ++ ++ /* Since the tile alloc array will follow us, align. */ ++ exec->tile_alloc_offset = roundup(tile_state_size, 4096); ++ ++ *(uint8_t *)(validated + 14) = ++ ((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK | ++ VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_MASK)) | ++ VC4_BIN_CONFIG_AUTO_INIT_TSDA | ++ VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_32, ++ VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE) | ++ VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128, ++ VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE)); ++ ++ /* Initial block size. */ ++ tile_alloc_size = 32 * tile_count; ++ ++ /* ++ * The initial allocation gets rounded to the next 256 bytes before ++ * the hardware starts fulfilling further allocations. ++ */ ++ tile_alloc_size = roundup(tile_alloc_size, 256); ++ ++ /* Add space for the extra allocations. This is what gets used first, ++ * before overflow memory. It must have at least 4096 bytes, but we ++ * want to avoid overflow memory usage if possible. ++ */ ++ tile_alloc_size += 1024 * 1024; ++ ++ exec->tile_bo = &vc4_bo_create(dev, exec->tile_alloc_offset + ++ tile_alloc_size)->base; ++ if (!exec->tile_bo) ++ return -ENOMEM; ++ list_add_tail(&to_vc4_bo(&exec->tile_bo->base)->unref_head, ++ &exec->unref_list); ++ ++ /* tile alloc address. */ ++ *(uint32_t *)(validated + 0) = (exec->tile_bo->paddr + ++ exec->tile_alloc_offset); ++ /* tile alloc size. */ ++ *(uint32_t *)(validated + 4) = tile_alloc_size; ++ /* tile state address. */ ++ *(uint32_t *)(validated + 8) = exec->tile_bo->paddr; ++ ++ return 0; ++} ++ ++static int ++validate_gem_handles(VALIDATE_ARGS) ++{ ++ memcpy(exec->bo_index, untrusted, sizeof(exec->bo_index)); ++ return 0; ++} ++ ++#define VC4_DEFINE_PACKET(packet, name, func) \ ++ [packet] = { packet ## _SIZE, name, func } ++ ++static const struct cmd_info { ++ uint16_t len; ++ const char *name; ++ int (*func)(struct vc4_exec_info *exec, void *validated, ++ void *untrusted); ++} cmd_info[] = { ++ VC4_DEFINE_PACKET(VC4_PACKET_HALT, "halt", NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_NOP, "nop", NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_FLUSH, "flush", NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_FLUSH_ALL, "flush all state", validate_flush_all), ++ VC4_DEFINE_PACKET(VC4_PACKET_START_TILE_BINNING, "start tile binning", validate_start_tile_binning), ++ VC4_DEFINE_PACKET(VC4_PACKET_INCREMENT_SEMAPHORE, "increment semaphore", validate_increment_semaphore), ++ ++ VC4_DEFINE_PACKET(VC4_PACKET_GL_INDEXED_PRIMITIVE, "Indexed Primitive List", validate_indexed_prim_list), ++ ++ VC4_DEFINE_PACKET(VC4_PACKET_GL_ARRAY_PRIMITIVE, "Vertex Array Primitives", validate_gl_array_primitive), ++ ++ /* This is only used by clipped primitives (packets 48 and 49), which ++ * we don't support parsing yet. ++ */ ++ VC4_DEFINE_PACKET(VC4_PACKET_PRIMITIVE_LIST_FORMAT, "primitive list format", NULL), ++ ++ VC4_DEFINE_PACKET(VC4_PACKET_GL_SHADER_STATE, "GL Shader State", validate_gl_shader_state), ++ VC4_DEFINE_PACKET(VC4_PACKET_NV_SHADER_STATE, "NV Shader State", validate_nv_shader_state), ++ ++ VC4_DEFINE_PACKET(VC4_PACKET_CONFIGURATION_BITS, "configuration bits", NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_FLAT_SHADE_FLAGS, "flat shade flags", NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_POINT_SIZE, "point size", NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_LINE_WIDTH, "line width", NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_RHT_X_BOUNDARY, "RHT X boundary", NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_DEPTH_OFFSET, "Depth Offset", NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_CLIP_WINDOW, "Clip Window", NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_VIEWPORT_OFFSET, "Viewport Offset", NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_XY_SCALING, "Clipper XY Scaling", NULL), ++ /* Note: The docs say this was also 105, but it was 106 in the ++ * initial userland code drop. ++ */ ++ VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_Z_SCALING, "Clipper Z Scale and Offset", NULL), ++ ++ VC4_DEFINE_PACKET(VC4_PACKET_TILE_BINNING_MODE_CONFIG, "tile binning configuration", validate_tile_binning_config), ++ ++ VC4_DEFINE_PACKET(VC4_PACKET_GEM_HANDLES, "GEM handles", validate_gem_handles), ++}; ++ ++int ++vc4_validate_bin_cl(struct drm_device *dev, ++ void *validated, ++ void *unvalidated, ++ struct vc4_exec_info *exec) ++{ ++ uint32_t len = exec->args->bin_cl_size; ++ uint32_t dst_offset = 0; ++ uint32_t src_offset = 0; ++ ++ while (src_offset < len) { ++ void *dst_pkt = validated + dst_offset; ++ void *src_pkt = unvalidated + src_offset; ++ u8 cmd = *(uint8_t *)src_pkt; ++ const struct cmd_info *info; ++ ++ if (cmd > ARRAY_SIZE(cmd_info)) { ++ DRM_ERROR("0x%08x: packet %d out of bounds\n", ++ src_offset, cmd); ++ return -EINVAL; ++ } ++ ++ info = &cmd_info[cmd]; ++ if (!info->name) { ++ DRM_ERROR("0x%08x: packet %d invalid\n", ++ src_offset, cmd); ++ return -EINVAL; ++ } ++ ++#if 0 ++ DRM_INFO("0x%08x: packet %d (%s) size %d processing...\n", ++ src_offset, cmd, info->name, info->len); ++#endif ++ ++ if (src_offset + info->len > len) { ++ DRM_ERROR("0x%08x: packet %d (%s) length 0x%08x " ++ "exceeds bounds (0x%08x)\n", ++ src_offset, cmd, info->name, info->len, ++ src_offset + len); ++ return -EINVAL; ++ } ++ ++ if (cmd != VC4_PACKET_GEM_HANDLES) ++ memcpy(dst_pkt, src_pkt, info->len); ++ ++ if (info->func && info->func(exec, ++ dst_pkt + 1, ++ src_pkt + 1)) { ++ DRM_ERROR("0x%08x: packet %d (%s) failed to " ++ "validate\n", ++ src_offset, cmd, info->name); ++ return -EINVAL; ++ } ++ ++ src_offset += info->len; ++ /* GEM handle loading doesn't produce HW packets. */ ++ if (cmd != VC4_PACKET_GEM_HANDLES) ++ dst_offset += info->len; ++ ++ /* When the CL hits halt, it'll stop reading anything else. */ ++ if (cmd == VC4_PACKET_HALT) ++ break; ++ } ++ ++ exec->ct0ea = exec->ct0ca + dst_offset; ++ ++ if (!exec->found_start_tile_binning_packet) { ++ DRM_ERROR("Bin CL missing VC4_PACKET_START_TILE_BINNING\n"); ++ return -EINVAL; ++ } ++ ++ if (!exec->found_increment_semaphore_packet) { ++ DRM_ERROR("Bin CL missing VC4_PACKET_INCREMENT_SEMAPHORE\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static bool ++reloc_tex(struct vc4_exec_info *exec, ++ void *uniform_data_u, ++ struct vc4_texture_sample_info *sample, ++ uint32_t texture_handle_index) ++ ++{ ++ struct drm_gem_cma_object *tex; ++ uint32_t p0 = *(uint32_t *)(uniform_data_u + sample->p_offset[0]); ++ uint32_t p1 = *(uint32_t *)(uniform_data_u + sample->p_offset[1]); ++ uint32_t p2 = (sample->p_offset[2] != ~0 ? ++ *(uint32_t *)(uniform_data_u + sample->p_offset[2]) : 0); ++ uint32_t p3 = (sample->p_offset[3] != ~0 ? ++ *(uint32_t *)(uniform_data_u + sample->p_offset[3]) : 0); ++ uint32_t *validated_p0 = exec->uniforms_v + sample->p_offset[0]; ++ uint32_t offset = p0 & VC4_TEX_P0_OFFSET_MASK; ++ uint32_t miplevels = VC4_GET_FIELD(p0, VC4_TEX_P0_MIPLVLS); ++ uint32_t width = VC4_GET_FIELD(p1, VC4_TEX_P1_WIDTH); ++ uint32_t height = VC4_GET_FIELD(p1, VC4_TEX_P1_HEIGHT); ++ uint32_t cpp, tiling_format, utile_w, utile_h; ++ uint32_t i; ++ uint32_t cube_map_stride = 0; ++ enum vc4_texture_data_type type; ++ ++ if (!vc4_use_bo(exec, texture_handle_index, VC4_MODE_RENDER, &tex)) ++ return false; ++ ++ if (sample->is_direct) { ++ uint32_t remaining_size = tex->base.size - p0; ++ if (p0 > tex->base.size - 4) { ++ DRM_ERROR("UBO offset greater than UBO size\n"); ++ goto fail; ++ } ++ if (p1 > remaining_size - 4) { ++ DRM_ERROR("UBO clamp would allow reads outside of UBO\n"); ++ goto fail; ++ } ++ *validated_p0 = tex->paddr + p0; ++ return true; ++ } ++ ++ if (width == 0) ++ width = 2048; ++ if (height == 0) ++ height = 2048; ++ ++ if (p0 & VC4_TEX_P0_CMMODE_MASK) { ++ if (VC4_GET_FIELD(p2, VC4_TEX_P2_PTYPE) == ++ VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE) ++ cube_map_stride = p2 & VC4_TEX_P2_CMST_MASK; ++ if (VC4_GET_FIELD(p3, VC4_TEX_P2_PTYPE) == ++ VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE) { ++ if (cube_map_stride) { ++ DRM_ERROR("Cube map stride set twice\n"); ++ goto fail; ++ } ++ ++ cube_map_stride = p3 & VC4_TEX_P2_CMST_MASK; ++ } ++ if (!cube_map_stride) { ++ DRM_ERROR("Cube map stride not set\n"); ++ goto fail; ++ } ++ } ++ ++ type = (VC4_GET_FIELD(p0, VC4_TEX_P0_TYPE) | ++ (VC4_GET_FIELD(p1, VC4_TEX_P1_TYPE4) << 4)); ++ ++ switch (type) { ++ case VC4_TEXTURE_TYPE_RGBA8888: ++ case VC4_TEXTURE_TYPE_RGBX8888: ++ case VC4_TEXTURE_TYPE_RGBA32R: ++ cpp = 4; ++ break; ++ case VC4_TEXTURE_TYPE_RGBA4444: ++ case VC4_TEXTURE_TYPE_RGBA5551: ++ case VC4_TEXTURE_TYPE_RGB565: ++ case VC4_TEXTURE_TYPE_LUMALPHA: ++ case VC4_TEXTURE_TYPE_S16F: ++ case VC4_TEXTURE_TYPE_S16: ++ cpp = 2; ++ break; ++ case VC4_TEXTURE_TYPE_LUMINANCE: ++ case VC4_TEXTURE_TYPE_ALPHA: ++ case VC4_TEXTURE_TYPE_S8: ++ cpp = 1; ++ break; ++ case VC4_TEXTURE_TYPE_ETC1: ++ case VC4_TEXTURE_TYPE_BW1: ++ case VC4_TEXTURE_TYPE_A4: ++ case VC4_TEXTURE_TYPE_A1: ++ case VC4_TEXTURE_TYPE_RGBA64: ++ case VC4_TEXTURE_TYPE_YUV422R: ++ default: ++ DRM_ERROR("Texture format %d unsupported\n", type); ++ goto fail; ++ } ++ utile_w = utile_width(cpp); ++ utile_h = utile_height(cpp); ++ ++ if (type == VC4_TEXTURE_TYPE_RGBA32R) { ++ tiling_format = VC4_TILING_FORMAT_LINEAR; ++ } else { ++ if (size_is_lt(width, height, cpp)) ++ tiling_format = VC4_TILING_FORMAT_LT; ++ else ++ tiling_format = VC4_TILING_FORMAT_T; ++ } ++ ++ if (!vc4_check_tex_size(exec, tex, offset + cube_map_stride * 5, ++ tiling_format, width, height, cpp)) { ++ goto fail; ++ } ++ ++ /* The mipmap levels are stored before the base of the texture. Make ++ * sure there is actually space in the BO. ++ */ ++ for (i = 1; i <= miplevels; i++) { ++ uint32_t level_width = max(width >> i, 1u); ++ uint32_t level_height = max(height >> i, 1u); ++ uint32_t aligned_width, aligned_height; ++ uint32_t level_size; ++ ++ /* Once the levels get small enough, they drop from T to LT. */ ++ if (tiling_format == VC4_TILING_FORMAT_T && ++ size_is_lt(level_width, level_height, cpp)) { ++ tiling_format = VC4_TILING_FORMAT_LT; ++ } ++ ++ switch (tiling_format) { ++ case VC4_TILING_FORMAT_T: ++ aligned_width = round_up(level_width, utile_w * 8); ++ aligned_height = round_up(level_height, utile_h * 8); ++ break; ++ case VC4_TILING_FORMAT_LT: ++ aligned_width = round_up(level_width, utile_w); ++ aligned_height = round_up(level_height, utile_h); ++ break; ++ default: ++ aligned_width = round_up(level_width, utile_w); ++ aligned_height = level_height; ++ break; ++ } ++ ++ level_size = aligned_width * cpp * aligned_height; ++ ++ if (offset < level_size) { ++ DRM_ERROR("Level %d (%dx%d -> %dx%d) size %db " ++ "overflowed buffer bounds (offset %d)\n", ++ i, level_width, level_height, ++ aligned_width, aligned_height, ++ level_size, offset); ++ goto fail; ++ } ++ ++ offset -= level_size; ++ } ++ ++ *validated_p0 = tex->paddr + p0; ++ ++ return true; ++ fail: ++ DRM_INFO("Texture p0 at %d: 0x%08x\n", sample->p_offset[0], p0); ++ DRM_INFO("Texture p1 at %d: 0x%08x\n", sample->p_offset[1], p1); ++ DRM_INFO("Texture p2 at %d: 0x%08x\n", sample->p_offset[2], p2); ++ DRM_INFO("Texture p3 at %d: 0x%08x\n", sample->p_offset[3], p3); ++ return false; ++} ++ ++static int ++validate_shader_rec(struct drm_device *dev, ++ struct vc4_exec_info *exec, ++ struct vc4_shader_state *state) ++{ ++ uint32_t *src_handles; ++ void *pkt_u, *pkt_v; ++ enum shader_rec_reloc_type { ++ RELOC_CODE, ++ RELOC_VBO, ++ }; ++ struct shader_rec_reloc { ++ enum shader_rec_reloc_type type; ++ uint32_t offset; ++ }; ++ static const struct shader_rec_reloc gl_relocs[] = { ++ { RELOC_CODE, 4 }, /* fs */ ++ { RELOC_CODE, 16 }, /* vs */ ++ { RELOC_CODE, 28 }, /* cs */ ++ }; ++ static const struct shader_rec_reloc nv_relocs[] = { ++ { RELOC_CODE, 4 }, /* fs */ ++ { RELOC_VBO, 12 } ++ }; ++ const struct shader_rec_reloc *relocs; ++ struct drm_gem_cma_object *bo[ARRAY_SIZE(gl_relocs) + 8]; ++ uint32_t nr_attributes = 0, nr_fixed_relocs, nr_relocs, packet_size; ++ int i; ++ struct vc4_validated_shader_info *validated_shader; ++ ++ if (state->packet == VC4_PACKET_NV_SHADER_STATE) { ++ relocs = nv_relocs; ++ nr_fixed_relocs = ARRAY_SIZE(nv_relocs); ++ ++ packet_size = 16; ++ } else { ++ relocs = gl_relocs; ++ nr_fixed_relocs = ARRAY_SIZE(gl_relocs); ++ ++ nr_attributes = state->addr & 0x7; ++ if (nr_attributes == 0) ++ nr_attributes = 8; ++ packet_size = gl_shader_rec_size(state->addr); ++ } ++ nr_relocs = nr_fixed_relocs + nr_attributes; ++ ++ if (nr_relocs * 4 > exec->shader_rec_size) { ++ DRM_ERROR("overflowed shader recs reading %d handles " ++ "from %d bytes left\n", ++ nr_relocs, exec->shader_rec_size); ++ return -EINVAL; ++ } ++ src_handles = exec->shader_rec_u; ++ exec->shader_rec_u += nr_relocs * 4; ++ exec->shader_rec_size -= nr_relocs * 4; ++ ++ if (packet_size > exec->shader_rec_size) { ++ DRM_ERROR("overflowed shader recs copying %db packet " ++ "from %d bytes left\n", ++ packet_size, exec->shader_rec_size); ++ return -EINVAL; ++ } ++ pkt_u = exec->shader_rec_u; ++ pkt_v = exec->shader_rec_v; ++ memcpy(pkt_v, pkt_u, packet_size); ++ exec->shader_rec_u += packet_size; ++ /* Shader recs have to be aligned to 16 bytes (due to the attribute ++ * flags being in the low bytes), so round the next validated shader ++ * rec address up. This should be safe, since we've got so many ++ * relocations in a shader rec packet. ++ */ ++ BUG_ON(roundup(packet_size, 16) - packet_size > nr_relocs * 4); ++ exec->shader_rec_v += roundup(packet_size, 16); ++ exec->shader_rec_size -= packet_size; ++ ++ for (i = 0; i < nr_relocs; i++) { ++ enum vc4_bo_mode mode; ++ ++ if (i < nr_fixed_relocs && relocs[i].type == RELOC_CODE) ++ mode = VC4_MODE_SHADER; ++ else ++ mode = VC4_MODE_RENDER; ++ ++ if (!vc4_use_bo(exec, src_handles[i], mode, &bo[i])) { ++ return false; ++ } ++ } ++ ++ for (i = 0; i < nr_fixed_relocs; i++) { ++ uint32_t o = relocs[i].offset; ++ uint32_t src_offset = *(uint32_t *)(pkt_u + o); ++ uint32_t *texture_handles_u; ++ void *uniform_data_u; ++ uint32_t tex; ++ ++ *(uint32_t *)(pkt_v + o) = bo[i]->paddr + src_offset; ++ ++ switch (relocs[i].type) { ++ case RELOC_CODE: ++ if (src_offset != 0) { ++ DRM_ERROR("Shaders must be at offset 0 of " ++ "the BO.\n"); ++ goto fail; ++ } ++ ++ validated_shader = to_vc4_bo(&bo[i]->base)->validated_shader; ++ if (!validated_shader) ++ goto fail; ++ ++ if (validated_shader->uniforms_src_size > ++ exec->uniforms_size) { ++ DRM_ERROR("Uniforms src buffer overflow\n"); ++ goto fail; ++ } ++ ++ texture_handles_u = exec->uniforms_u; ++ uniform_data_u = (texture_handles_u + ++ validated_shader->num_texture_samples); ++ ++ memcpy(exec->uniforms_v, uniform_data_u, ++ validated_shader->uniforms_size); ++ ++ for (tex = 0; ++ tex < validated_shader->num_texture_samples; ++ tex++) { ++ if (!reloc_tex(exec, ++ uniform_data_u, ++ &validated_shader->texture_samples[tex], ++ texture_handles_u[tex])) { ++ goto fail; ++ } ++ } ++ ++ *(uint32_t *)(pkt_v + o + 4) = exec->uniforms_p; ++ ++ exec->uniforms_u += validated_shader->uniforms_src_size; ++ exec->uniforms_v += validated_shader->uniforms_size; ++ exec->uniforms_p += validated_shader->uniforms_size; ++ ++ break; ++ ++ case RELOC_VBO: ++ break; ++ } ++ } ++ ++ for (i = 0; i < nr_attributes; i++) { ++ struct drm_gem_cma_object *vbo = bo[nr_fixed_relocs + i]; ++ uint32_t o = 36 + i * 8; ++ uint32_t offset = *(uint32_t *)(pkt_u + o + 0); ++ uint32_t attr_size = *(uint8_t *)(pkt_u + o + 4) + 1; ++ uint32_t stride = *(uint8_t *)(pkt_u + o + 5); ++ uint32_t max_index; ++ ++ if (state->addr & 0x8) ++ stride |= (*(uint32_t *)(pkt_u + 100 + i * 4)) & ~0xff; ++ ++ if (vbo->base.size < offset || ++ vbo->base.size - offset < attr_size) { ++ DRM_ERROR("BO offset overflow (%d + %d > %d)\n", ++ offset, attr_size, vbo->base.size); ++ return -EINVAL; ++ } ++ ++ if (stride != 0) { ++ max_index = ((vbo->base.size - offset - attr_size) / ++ stride); ++ if (state->max_index > max_index) { ++ DRM_ERROR("primitives use index %d out of supplied %d\n", ++ state->max_index, max_index); ++ return -EINVAL; ++ } ++ } ++ ++ *(uint32_t *)(pkt_v + o) = vbo->paddr + offset; ++ } ++ ++ return 0; ++ ++fail: ++ return -EINVAL; ++} ++ ++int ++vc4_validate_shader_recs(struct drm_device *dev, ++ struct vc4_exec_info *exec) ++{ ++ uint32_t i; ++ int ret = 0; ++ ++ for (i = 0; i < exec->shader_state_count; i++) { ++ ret = validate_shader_rec(dev, exec, &exec->shader_state[i]); ++ if (ret) ++ return ret; ++ } ++ ++ return ret; ++} +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c +@@ -0,0 +1,521 @@ ++/* ++ * Copyright © 2014 Broadcom ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++ ++/** ++ * DOC: Shader validator for VC4. ++ * ++ * The VC4 has no IOMMU between it and system memory. So, a user with access ++ * to execute shaders could escalate privilege by overwriting system memory ++ * (using the VPM write address register in the general-purpose DMA mode) or ++ * reading system memory it shouldn't (reading it as a texture, or uniform ++ * data, or vertex data). ++ * ++ * This walks over a shader starting from some offset within a BO, ensuring ++ * that its accesses are appropriately bounded, and recording how many texture ++ * accesses are made and where so that we can do relocations for them in the ++ * uniform stream. ++ * ++ * The kernel API has shaders stored in user-mapped BOs. The BOs will be ++ * forcibly unmapped from the process before validation, and any cache of ++ * validated state will be flushed if the mapping is faulted back in. ++ * ++ * Storing the shaders in BOs means that the validation process will be slow ++ * due to uncached reads, but since shaders are long-lived and shader BOs are ++ * never actually modified, this shouldn't be a problem. ++ */ ++ ++#include "vc4_drv.h" ++#include "vc4_qpu_defines.h" ++ ++struct vc4_shader_validation_state { ++ struct vc4_texture_sample_info tmu_setup[2]; ++ int tmu_write_count[2]; ++ ++ /* For registers that were last written to by a MIN instruction with ++ * one argument being a uniform, the address of the uniform. ++ * Otherwise, ~0. ++ * ++ * This is used for the validation of direct address memory reads. ++ */ ++ uint32_t live_min_clamp_offsets[32 + 32 + 4]; ++ bool live_max_clamp_regs[32 + 32 + 4]; ++}; ++ ++static uint32_t ++waddr_to_live_reg_index(uint32_t waddr, bool is_b) ++{ ++ if (waddr < 32) { ++ if (is_b) ++ return 32 + waddr; ++ else ++ return waddr; ++ } else if (waddr <= QPU_W_ACC3) { ++ ++ return 64 + waddr - QPU_W_ACC0; ++ } else { ++ return ~0; ++ } ++} ++ ++static uint32_t ++raddr_add_a_to_live_reg_index(uint64_t inst) ++{ ++ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); ++ uint32_t add_a = QPU_GET_FIELD(inst, QPU_ADD_A); ++ uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); ++ uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); ++ ++ if (add_a == QPU_MUX_A) { ++ return raddr_a; ++ } else if (add_a == QPU_MUX_B && sig != QPU_SIG_SMALL_IMM) { ++ return 32 + raddr_b; ++ } else if (add_a <= QPU_MUX_R3) { ++ return 64 + add_a; ++ } else { ++ return ~0; ++ } ++} ++ ++static bool ++is_tmu_submit(uint32_t waddr) ++{ ++ return (waddr == QPU_W_TMU0_S || ++ waddr == QPU_W_TMU1_S); ++} ++ ++static bool ++is_tmu_write(uint32_t waddr) ++{ ++ return (waddr >= QPU_W_TMU0_S && ++ waddr <= QPU_W_TMU1_B); ++} ++ ++static bool ++record_validated_texture_sample(struct vc4_validated_shader_info *validated_shader, ++ struct vc4_shader_validation_state *validation_state, ++ int tmu) ++{ ++ uint32_t s = validated_shader->num_texture_samples; ++ int i; ++ struct vc4_texture_sample_info *temp_samples; ++ ++ temp_samples = krealloc(validated_shader->texture_samples, ++ (s + 1) * sizeof(*temp_samples), ++ GFP_KERNEL); ++ if (!temp_samples) ++ return false; ++ ++ memcpy(&temp_samples[s], ++ &validation_state->tmu_setup[tmu], ++ sizeof(*temp_samples)); ++ ++ validated_shader->num_texture_samples = s + 1; ++ validated_shader->texture_samples = temp_samples; ++ ++ for (i = 0; i < 4; i++) ++ validation_state->tmu_setup[tmu].p_offset[i] = ~0; ++ ++ return true; ++} ++ ++static bool ++check_tmu_write(uint64_t inst, ++ struct vc4_validated_shader_info *validated_shader, ++ struct vc4_shader_validation_state *validation_state, ++ bool is_mul) ++{ ++ uint32_t waddr = (is_mul ? ++ QPU_GET_FIELD(inst, QPU_WADDR_MUL) : ++ QPU_GET_FIELD(inst, QPU_WADDR_ADD)); ++ uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); ++ uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); ++ int tmu = waddr > QPU_W_TMU0_B; ++ bool submit = is_tmu_submit(waddr); ++ bool is_direct = submit && validation_state->tmu_write_count[tmu] == 0; ++ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); ++ ++ if (is_direct) { ++ uint32_t add_b = QPU_GET_FIELD(inst, QPU_ADD_B); ++ uint32_t clamp_reg, clamp_offset; ++ ++ if (sig == QPU_SIG_SMALL_IMM) { ++ DRM_ERROR("direct TMU read used small immediate\n"); ++ return false; ++ } ++ ++ /* Make sure that this texture load is an add of the base ++ * address of the UBO to a clamped offset within the UBO. ++ */ ++ if (is_mul || ++ QPU_GET_FIELD(inst, QPU_OP_ADD) != QPU_A_ADD) { ++ DRM_ERROR("direct TMU load wasn't an add\n"); ++ return false; ++ } ++ ++ /* We assert that the the clamped address is the first ++ * argument, and the UBO base address is the second argument. ++ * This is arbitrary, but simpler than supporting flipping the ++ * two either way. ++ */ ++ clamp_reg = raddr_add_a_to_live_reg_index(inst); ++ if (clamp_reg == ~0) { ++ DRM_ERROR("direct TMU load wasn't clamped\n"); ++ return false; ++ } ++ ++ clamp_offset = validation_state->live_min_clamp_offsets[clamp_reg]; ++ if (clamp_offset == ~0) { ++ DRM_ERROR("direct TMU load wasn't clamped\n"); ++ return false; ++ } ++ ++ /* Store the clamp value's offset in p1 (see reloc_tex() in ++ * vc4_validate.c). ++ */ ++ validation_state->tmu_setup[tmu].p_offset[1] = ++ clamp_offset; ++ ++ if (!(add_b == QPU_MUX_A && raddr_a == QPU_R_UNIF) && ++ !(add_b == QPU_MUX_B && raddr_b == QPU_R_UNIF)) { ++ DRM_ERROR("direct TMU load didn't add to a uniform\n"); ++ return false; ++ } ++ ++ validation_state->tmu_setup[tmu].is_direct = true; ++ } else { ++ if (raddr_a == QPU_R_UNIF || (sig != QPU_SIG_SMALL_IMM && ++ raddr_b == QPU_R_UNIF)) { ++ DRM_ERROR("uniform read in the same instruction as " ++ "texture setup.\n"); ++ return false; ++ } ++ } ++ ++ if (validation_state->tmu_write_count[tmu] >= 4) { ++ DRM_ERROR("TMU%d got too many parameters before dispatch\n", ++ tmu); ++ return false; ++ } ++ validation_state->tmu_setup[tmu].p_offset[validation_state->tmu_write_count[tmu]] = ++ validated_shader->uniforms_size; ++ validation_state->tmu_write_count[tmu]++; ++ /* Since direct uses a RADDR uniform reference, it will get counted in ++ * check_instruction_reads() ++ */ ++ if (!is_direct) ++ validated_shader->uniforms_size += 4; ++ ++ if (submit) { ++ if (!record_validated_texture_sample(validated_shader, ++ validation_state, tmu)) { ++ return false; ++ } ++ ++ validation_state->tmu_write_count[tmu] = 0; ++ } ++ ++ return true; ++} ++ ++static bool ++check_register_write(uint64_t inst, ++ struct vc4_validated_shader_info *validated_shader, ++ struct vc4_shader_validation_state *validation_state, ++ bool is_mul) ++{ ++ uint32_t waddr = (is_mul ? ++ QPU_GET_FIELD(inst, QPU_WADDR_MUL) : ++ QPU_GET_FIELD(inst, QPU_WADDR_ADD)); ++ ++ switch (waddr) { ++ case QPU_W_UNIFORMS_ADDRESS: ++ /* XXX: We'll probably need to support this for reladdr, but ++ * it's definitely a security-related one. ++ */ ++ DRM_ERROR("uniforms address load unsupported\n"); ++ return false; ++ ++ case QPU_W_TLB_COLOR_MS: ++ case QPU_W_TLB_COLOR_ALL: ++ case QPU_W_TLB_Z: ++ /* These only interact with the tile buffer, not main memory, ++ * so they're safe. ++ */ ++ return true; ++ ++ case QPU_W_TMU0_S: ++ case QPU_W_TMU0_T: ++ case QPU_W_TMU0_R: ++ case QPU_W_TMU0_B: ++ case QPU_W_TMU1_S: ++ case QPU_W_TMU1_T: ++ case QPU_W_TMU1_R: ++ case QPU_W_TMU1_B: ++ return check_tmu_write(inst, validated_shader, validation_state, ++ is_mul); ++ ++ case QPU_W_HOST_INT: ++ case QPU_W_TMU_NOSWAP: ++ case QPU_W_TLB_ALPHA_MASK: ++ case QPU_W_MUTEX_RELEASE: ++ /* XXX: I haven't thought about these, so don't support them ++ * for now. ++ */ ++ DRM_ERROR("Unsupported waddr %d\n", waddr); ++ return false; ++ ++ case QPU_W_VPM_ADDR: ++ DRM_ERROR("General VPM DMA unsupported\n"); ++ return false; ++ ++ case QPU_W_VPM: ++ case QPU_W_VPMVCD_SETUP: ++ /* We allow VPM setup in general, even including VPM DMA ++ * configuration setup, because the (unsafe) DMA can only be ++ * triggered by QPU_W_VPM_ADDR writes. ++ */ ++ return true; ++ ++ case QPU_W_TLB_STENCIL_SETUP: ++ return true; ++ } ++ ++ return true; ++} ++ ++static void ++track_live_clamps(uint64_t inst, ++ struct vc4_validated_shader_info *validated_shader, ++ struct vc4_shader_validation_state *validation_state) ++{ ++ uint32_t op_add = QPU_GET_FIELD(inst, QPU_OP_ADD); ++ uint32_t waddr_add = QPU_GET_FIELD(inst, QPU_WADDR_ADD); ++ uint32_t waddr_mul = QPU_GET_FIELD(inst, QPU_WADDR_MUL); ++ uint32_t cond_add = QPU_GET_FIELD(inst, QPU_COND_ADD); ++ uint32_t add_a = QPU_GET_FIELD(inst, QPU_ADD_A); ++ uint32_t add_b = QPU_GET_FIELD(inst, QPU_ADD_B); ++ uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); ++ uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); ++ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); ++ bool ws = inst & QPU_WS; ++ uint32_t lri_add_a, lri_add, lri_mul; ++ bool add_a_is_min_0; ++ ++ /* Check whether OP_ADD's A argumennt comes from a live MAX(x, 0), ++ * before we clear previous live state. ++ */ ++ lri_add_a = raddr_add_a_to_live_reg_index(inst); ++ add_a_is_min_0 = (lri_add_a != ~0 && ++ validation_state->live_max_clamp_regs[lri_add_a]); ++ ++ /* Clear live state for registers written by our instruction. */ ++ lri_add = waddr_to_live_reg_index(waddr_add, ws); ++ lri_mul = waddr_to_live_reg_index(waddr_mul, !ws); ++ if (lri_mul != ~0) { ++ validation_state->live_max_clamp_regs[lri_mul] = false; ++ validation_state->live_min_clamp_offsets[lri_mul] = ~0; ++ } ++ if (lri_add != ~0) { ++ validation_state->live_max_clamp_regs[lri_add] = false; ++ validation_state->live_min_clamp_offsets[lri_add] = ~0; ++ } else { ++ /* Nothing further to do for live tracking, since only ADDs ++ * generate new live clamp registers. ++ */ ++ return; ++ } ++ ++ /* Now, handle remaining live clamp tracking for the ADD operation. */ ++ ++ if (cond_add != QPU_COND_ALWAYS) ++ return; ++ ++ if (op_add == QPU_A_MAX) { ++ /* Track live clamps of a value to a minimum of 0 (in either ++ * arg). ++ */ ++ if (sig != QPU_SIG_SMALL_IMM || raddr_b != 0 || ++ (add_a != QPU_MUX_B && add_b != QPU_MUX_B)) { ++ return; ++ } ++ ++ validation_state->live_max_clamp_regs[lri_add] = true; ++ } if (op_add == QPU_A_MIN) { ++ /* Track live clamps of a value clamped to a minimum of 0 and ++ * a maximum of some uniform's offset. ++ */ ++ if (!add_a_is_min_0) ++ return; ++ ++ if (!(add_b == QPU_MUX_A && raddr_a == QPU_R_UNIF) && ++ !(add_b == QPU_MUX_B && raddr_b == QPU_R_UNIF && ++ sig != QPU_SIG_SMALL_IMM)) { ++ return; ++ } ++ ++ validation_state->live_min_clamp_offsets[lri_add] = ++ validated_shader->uniforms_size; ++ } ++} ++ ++static bool ++check_instruction_writes(uint64_t inst, ++ struct vc4_validated_shader_info *validated_shader, ++ struct vc4_shader_validation_state *validation_state) ++{ ++ uint32_t waddr_add = QPU_GET_FIELD(inst, QPU_WADDR_ADD); ++ uint32_t waddr_mul = QPU_GET_FIELD(inst, QPU_WADDR_MUL); ++ bool ok; ++ ++ if (is_tmu_write(waddr_add) && is_tmu_write(waddr_mul)) { ++ DRM_ERROR("ADD and MUL both set up textures\n"); ++ return false; ++ } ++ ++ ok = (check_register_write(inst, validated_shader, validation_state, false) && ++ check_register_write(inst, validated_shader, validation_state, true)); ++ ++ track_live_clamps(inst, validated_shader, validation_state); ++ ++ return ok; ++} ++ ++static bool ++check_instruction_reads(uint64_t inst, ++ struct vc4_validated_shader_info *validated_shader) ++{ ++ uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); ++ uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); ++ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); ++ ++ if (raddr_a == QPU_R_UNIF || ++ (raddr_b == QPU_R_UNIF && sig != QPU_SIG_SMALL_IMM)) { ++ /* This can't overflow the uint32_t, because we're reading 8 ++ * bytes of instruction to increment by 4 here, so we'd ++ * already be OOM. ++ */ ++ validated_shader->uniforms_size += 4; ++ } ++ ++ return true; ++} ++ ++struct vc4_validated_shader_info * ++vc4_validate_shader(struct drm_gem_cma_object *shader_obj) ++{ ++ bool found_shader_end = false; ++ int shader_end_ip = 0; ++ uint32_t ip, max_ip; ++ uint64_t *shader; ++ struct vc4_validated_shader_info *validated_shader; ++ struct vc4_shader_validation_state validation_state; ++ int i; ++ ++ memset(&validation_state, 0, sizeof(validation_state)); ++ ++ for (i = 0; i < 8; i++) ++ validation_state.tmu_setup[i / 4].p_offset[i % 4] = ~0; ++ for (i = 0; i < ARRAY_SIZE(validation_state.live_min_clamp_offsets); i++) ++ validation_state.live_min_clamp_offsets[i] = ~0; ++ ++ shader = shader_obj->vaddr; ++ max_ip = shader_obj->base.size / sizeof(uint64_t); ++ ++ validated_shader = kcalloc(sizeof(*validated_shader), 1, GFP_KERNEL); ++ if (!validated_shader) ++ return NULL; ++ ++ for (ip = 0; ip < max_ip; ip++) { ++ uint64_t inst = shader[ip]; ++ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); ++ ++ switch (sig) { ++ case QPU_SIG_NONE: ++ case QPU_SIG_WAIT_FOR_SCOREBOARD: ++ case QPU_SIG_SCOREBOARD_UNLOCK: ++ case QPU_SIG_COLOR_LOAD: ++ case QPU_SIG_LOAD_TMU0: ++ case QPU_SIG_LOAD_TMU1: ++ case QPU_SIG_PROG_END: ++ case QPU_SIG_SMALL_IMM: ++ if (!check_instruction_writes(inst, validated_shader, ++ &validation_state)) { ++ DRM_ERROR("Bad write at ip %d\n", ip); ++ goto fail; ++ } ++ ++ if (!check_instruction_reads(inst, validated_shader)) ++ goto fail; ++ ++ if (sig == QPU_SIG_PROG_END) { ++ found_shader_end = true; ++ shader_end_ip = ip; ++ } ++ ++ break; ++ ++ case QPU_SIG_LOAD_IMM: ++ if (!check_instruction_writes(inst, validated_shader, ++ &validation_state)) { ++ DRM_ERROR("Bad LOAD_IMM write at ip %d\n", ip); ++ goto fail; ++ } ++ break; ++ ++ default: ++ DRM_ERROR("Unsupported QPU signal %d at " ++ "instruction %d\n", sig, ip); ++ goto fail; ++ } ++ ++ /* There are two delay slots after program end is signaled ++ * that are still executed, then we're finished. ++ */ ++ if (found_shader_end && ip == shader_end_ip + 2) ++ break; ++ } ++ ++ if (ip == max_ip) { ++ DRM_ERROR("shader failed to terminate before " ++ "shader BO end at %d\n", ++ shader_obj->base.size); ++ goto fail; ++ } ++ ++ /* Again, no chance of integer overflow here because the worst case ++ * scenario is 8 bytes of uniforms plus handles per 8-byte ++ * instruction. ++ */ ++ validated_shader->uniforms_src_size = ++ (validated_shader->uniforms_size + ++ 4 * validated_shader->num_texture_samples); ++ ++ return validated_shader; ++ ++fail: ++ if (validated_shader) { ++ kfree(validated_shader->texture_samples); ++ kfree(validated_shader); ++ } ++ return NULL; ++} +--- /dev/null ++++ b/include/uapi/drm/vc4_drm.h +@@ -0,0 +1,229 @@ ++/* ++ * Copyright © 2014-2015 Broadcom ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++ ++#ifndef _UAPI_VC4_DRM_H_ ++#define _UAPI_VC4_DRM_H_ ++ ++#include ++ ++#define DRM_VC4_SUBMIT_CL 0x00 ++#define DRM_VC4_WAIT_SEQNO 0x01 ++#define DRM_VC4_WAIT_BO 0x02 ++#define DRM_VC4_CREATE_BO 0x03 ++#define DRM_VC4_MMAP_BO 0x04 ++#define DRM_VC4_CREATE_SHADER_BO 0x05 ++ ++#define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) ++#define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) ++#define DRM_IOCTL_VC4_WAIT_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_WAIT_BO, struct drm_vc4_wait_bo) ++#define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) ++#define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) ++#define DRM_IOCTL_VC4_CREATE_SHADER_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo) ++ ++struct drm_vc4_submit_rcl_surface { ++ uint32_t hindex; /* Handle index, or ~0 if not present. */ ++ uint32_t offset; /* Offset to start of buffer. */ ++ /* ++ * Bits for either render config (color_ms_write) or load/store packet. ++ */ ++ uint16_t bits; ++ uint16_t pad; ++}; ++ ++/** ++ * struct drm_vc4_submit_cl - ioctl argument for submitting commands to the 3D ++ * engine. ++ * ++ * Drivers typically use GPU BOs to store batchbuffers / command lists and ++ * their associated state. However, because the VC4 lacks an MMU, we have to ++ * do validation of memory accesses by the GPU commands. If we were to store ++ * our commands in BOs, we'd need to do uncached readback from them to do the ++ * validation process, which is too expensive. Instead, userspace accumulates ++ * commands and associated state in plain memory, then the kernel copies the ++ * data to its own address space, and then validates and stores it in a GPU ++ * BO. ++ */ ++struct drm_vc4_submit_cl { ++ /* Pointer to the binner command list. ++ * ++ * This is the first set of commands executed, which runs the ++ * coordinate shader to determine where primitives land on the screen, ++ * then writes out the state updates and draw calls necessary per tile ++ * to the tile allocation BO. ++ */ ++ uint64_t bin_cl; ++ ++ /* Pointer to the shader records. ++ * ++ * Shader records are the structures read by the hardware that contain ++ * pointers to uniforms, shaders, and vertex attributes. The ++ * reference to the shader record has enough information to determine ++ * how many pointers are necessary (fixed number for shaders/uniforms, ++ * and an attribute count), so those BO indices into bo_handles are ++ * just stored as uint32_ts before each shader record passed in. ++ */ ++ uint64_t shader_rec; ++ ++ /* Pointer to uniform data and texture handles for the textures ++ * referenced by the shader. ++ * ++ * For each shader state record, there is a set of uniform data in the ++ * order referenced by the record (FS, VS, then CS). Each set of ++ * uniform data has a uint32_t index into bo_handles per texture ++ * sample operation, in the order the QPU_W_TMUn_S writes appear in ++ * the program. Following the texture BO handle indices is the actual ++ * uniform data. ++ * ++ * The individual uniform state blocks don't have sizes passed in, ++ * because the kernel has to determine the sizes anyway during shader ++ * code validation. ++ */ ++ uint64_t uniforms; ++ uint64_t bo_handles; ++ ++ /* Size in bytes of the binner command list. */ ++ uint32_t bin_cl_size; ++ /* Size in bytes of the set of shader records. */ ++ uint32_t shader_rec_size; ++ /* Number of shader records. ++ * ++ * This could just be computed from the contents of shader_records and ++ * the address bits of references to them from the bin CL, but it ++ * keeps the kernel from having to resize some allocations it makes. ++ */ ++ uint32_t shader_rec_count; ++ /* Size in bytes of the uniform state. */ ++ uint32_t uniforms_size; ++ ++ /* Number of BO handles passed in (size is that times 4). */ ++ uint32_t bo_handle_count; ++ ++ /* RCL setup: */ ++ uint16_t width; ++ uint16_t height; ++ uint8_t min_x_tile; ++ uint8_t min_y_tile; ++ uint8_t max_x_tile; ++ uint8_t max_y_tile; ++ struct drm_vc4_submit_rcl_surface color_read; ++ struct drm_vc4_submit_rcl_surface color_ms_write; ++ struct drm_vc4_submit_rcl_surface zs_read; ++ struct drm_vc4_submit_rcl_surface zs_write; ++ uint32_t clear_color[2]; ++ uint32_t clear_z; ++ uint8_t clear_s; ++ ++ uint32_t pad:24; ++ ++#define VC4_SUBMIT_CL_USE_CLEAR_COLOR (1 << 0) ++ uint32_t flags; ++ ++ /* Returned value of the seqno of this render job (for the ++ * wait ioctl). ++ */ ++ uint64_t seqno; ++}; ++ ++/** ++ * struct drm_vc4_wait_seqno - ioctl argument for waiting for ++ * DRM_VC4_SUBMIT_CL completion using its returned seqno. ++ * ++ * timeout_ns is the timeout in nanoseconds, where "0" means "don't ++ * block, just return the status." ++ */ ++struct drm_vc4_wait_seqno { ++ uint64_t seqno; ++ uint64_t timeout_ns; ++}; ++ ++/** ++ * struct drm_vc4_wait_bo - ioctl argument for waiting for ++ * completion of the last DRM_VC4_SUBMIT_CL on a BO. ++ * ++ * This is useful for cases where multiple processes might be ++ * rendering to a BO and you want to wait for all rendering to be ++ * completed. ++ */ ++struct drm_vc4_wait_bo { ++ uint32_t handle; ++ uint32_t pad; ++ uint64_t timeout_ns; ++}; ++ ++/** ++ * struct drm_vc4_create_bo - ioctl argument for creating VC4 BOs. ++ * ++ * There are currently no values for the flags argument, but it may be ++ * used in a future extension. ++ */ ++struct drm_vc4_create_bo { ++ uint32_t size; ++ uint32_t flags; ++ /** Returned GEM handle for the BO. */ ++ uint32_t handle; ++ uint32_t pad; ++}; ++ ++/** ++ * struct drm_vc4_create_shader_bo - ioctl argument for creating VC4 ++ * shader BOs. ++ * ++ * Since allowing a shader to be overwritten while it's also being ++ * executed from would allow privlege escalation, shaders must be ++ * created using this ioctl, and they can't be mmapped later. ++ */ ++struct drm_vc4_create_shader_bo { ++ /* Size of the data argument. */ ++ uint32_t size; ++ /* Flags, currently must be 0. */ ++ uint32_t flags; ++ ++ /* Pointer to the data. */ ++ uint64_t data; ++ ++ /** Returned GEM handle for the BO. */ ++ uint32_t handle; ++ /* Pad, must be 0. */ ++ uint32_t pad; ++}; ++ ++/** ++ * struct drm_vc4_mmap_bo - ioctl argument for mapping VC4 BOs. ++ * ++ * This doesn't actually perform an mmap. Instead, it returns the ++ * offset you need to use in an mmap on the DRM device node. This ++ * means that tools like valgrind end up knowing about the mapped ++ * memory. ++ * ++ * There are currently no values for the flags argument, but it may be ++ * used in a future extension. ++ */ ++struct drm_vc4_mmap_bo { ++ /** Handle for the object being mapped. */ ++ uint32_t handle; ++ uint32_t flags; ++ /** offset into the drm node to use for subsequent mmap call. */ ++ uint64_t offset; ++}; ++ ++#endif /* _UAPI_VC4_DRM_H_ */ diff --git a/target/linux/brcm2708/patches-4.4/0090-scripts-Multi-platform-support-for-mkknlimg-and-knli.patch b/target/linux/brcm2708/patches-4.4/0090-scripts-Multi-platform-support-for-mkknlimg-and-knli.patch deleted file mode 100644 index 3651d18121..0000000000 --- a/target/linux/brcm2708/patches-4.4/0090-scripts-Multi-platform-support-for-mkknlimg-and-knli.patch +++ /dev/null @@ -1,247 +0,0 @@ -From f6dd60426dc7970ef4e9e3d9e00d374f80a6abd2 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Wed, 11 Nov 2015 11:38:59 +0000 -Subject: [PATCH 090/170] scripts: Multi-platform support for mkknlimg and - knlinfo - -The firmware uses tags in the kernel trailer to choose which dtb file -to load. Current firmware loads bcm2835-*.dtb if the '283x' tag is true, -otherwise it loads bcm270*.dtb. This scheme breaks if an image supports -multiple platforms. - -This patch adds '270X' and '283X' tags to indicate support for RPi and -upstream platforms, respectively. '283x' (note lower case 'x') is left -for old firmware, and is only set if the image only supports upstream -builds. ---- - scripts/knlinfo | 2 + - scripts/mkknlimg | 136 +++++++++++++++++++++++++++++++------------------------ - 2 files changed, 80 insertions(+), 58 deletions(-) - ---- a/scripts/knlinfo -+++ b/scripts/knlinfo -@@ -18,6 +18,8 @@ my %atom_formats = - ( - 'DTOK' => \&format_bool, - 'KVer' => \&format_string, -+ '270X' => \&format_bool, -+ '283X' => \&format_bool, - '283x' => \&format_bool, - ); - ---- a/scripts/mkknlimg -+++ b/scripts/mkknlimg -@@ -13,12 +13,20 @@ use strict; - use warnings; - use integer; - -+use constant FLAG_PI => 0x01; -+use constant FLAG_DTOK => 0x02; -+use constant FLAG_DDTK => 0x04; -+use constant FLAG_270X => 0x08; -+use constant FLAG_283X => 0x10; -+ - my $trailer_magic = 'RPTL'; - - my $tmpfile1 = "/tmp/mkknlimg_$$.1"; - my $tmpfile2 = "/tmp/mkknlimg_$$.2"; - - my $dtok = 0; -+my $ddtk = 0; -+my $is_270x = 0; - my $is_283x = 0; - - while (@ARGV && ($ARGV[0] =~ /^-/)) -@@ -28,6 +36,14 @@ while (@ARGV && ($ARGV[0] =~ /^-/)) - { - $dtok = 1; - } -+ elsif ($arg eq '--ddtk') -+ { -+ $ddtk = 1; -+ } -+ elsif ($arg eq '--270x') -+ { -+ $is_270x = 1; -+ } - elsif ($arg eq '--283x') - { - $is_283x = 1; -@@ -50,30 +66,33 @@ if (! -r $kernel_file) - usage(); - } - --my @wanted_strings = --( -- 'bcm2708_fb', -- 'brcm,bcm2835-mmc', -- 'brcm,bcm2835-sdhost', -- 'brcm,bcm2708-pinctrl', -- 'brcm,bcm2835-gpio', -- 'brcm,bcm2835', -- 'brcm,bcm2836' --); -+my $wanted_strings = -+{ -+ 'bcm2708_fb' => FLAG_PI, -+ 'brcm,bcm2835-mmc' => FLAG_PI, -+ 'brcm,bcm2835-sdhost' => FLAG_PI, -+ 'brcm,bcm2708-pinctrl' => FLAG_PI | FLAG_DTOK, -+ 'brcm,bcm2835-gpio' => FLAG_PI | FLAG_DTOK, -+ 'brcm,bcm2708' => FLAG_PI | FLAG_DTOK | FLAG_270X, -+ 'brcm,bcm2709' => FLAG_PI | FLAG_DTOK | FLAG_270X, -+ 'brcm,bcm2835' => FLAG_PI | FLAG_DTOK | FLAG_283X, -+ 'brcm,bcm2836' => FLAG_PI | FLAG_DTOK | FLAG_283X, -+ 'of_overlay_apply' => FLAG_DTOK | FLAG_DDTK, -+}; - - my $res = try_extract($kernel_file, $tmpfile1); --$res = try_decompress('\037\213\010', 'xy', 'gunzip', 0, -- $kernel_file, $tmpfile1, $tmpfile2) if (!$res); --$res = try_decompress('\3757zXZ\000', 'abcde', 'unxz --single-stream', -1, -- $kernel_file, $tmpfile1, $tmpfile2) if (!$res); --$res = try_decompress('BZh', 'xy', 'bunzip2', 0, -- $kernel_file, $tmpfile1, $tmpfile2) if (!$res); --$res = try_decompress('\135\0\0\0', 'xxx', 'unlzma', 0, -- $kernel_file, $tmpfile1, $tmpfile2) if (!$res); --$res = try_decompress('\211\114\132', 'xy', 'lzop -d', 0, -- $kernel_file, $tmpfile1, $tmpfile2) if (!$res); --$res = try_decompress('\002\041\114\030', 'xy', 'lz4 -d', 1, -- $kernel_file, $tmpfile1, $tmpfile2) if (!$res); -+$res ||= try_decompress('\037\213\010', 'xy', 'gunzip', 0, -+ $kernel_file, $tmpfile1, $tmpfile2); -+$res ||= try_decompress('\3757zXZ\000', 'abcde', 'unxz --single-stream', -1, -+ $kernel_file, $tmpfile1, $tmpfile2); -+$res ||= try_decompress('BZh', 'xy', 'bunzip2', 0, -+ $kernel_file, $tmpfile1, $tmpfile2); -+$res ||= try_decompress('\135\0\0\0', 'xxx', 'unlzma', 0, -+ $kernel_file, $tmpfile1, $tmpfile2); -+$res ||= try_decompress('\211\114\132', 'xy', 'lzop -d', 0, -+ $kernel_file, $tmpfile1, $tmpfile2); -+$res ||= try_decompress('\002\041\114\030', 'xy', 'lz4 -d', 1, -+ $kernel_file, $tmpfile1, $tmpfile2); - - my $append_trailer; - my $trailer; -@@ -83,27 +102,21 @@ $append_trailer = $dtok; - - if ($res) - { -- $kver = $res->{''} || '?'; -+ $kver = $res->{'kver'} || '?'; -+ my $flags = $res->{'flags'}; - print("Version: $kver\n"); - -- $append_trailer = $dtok; -- if (!$dtok) -+ if ($flags & FLAG_PI) - { -- if (config_bool($res, 'bcm2708_fb') || -- config_bool($res, 'brcm,bcm2835-mmc') || -- config_bool($res, 'brcm,bcm2835-sdhost')) -- { -- $dtok ||= config_bool($res, 'brcm,bcm2708-pinctrl'); -- $dtok ||= config_bool($res, 'brcm,bcm2835-gpio'); -- $is_283x ||= config_bool($res, 'brcm,bcm2835'); -- $is_283x ||= config_bool($res, 'brcm,bcm2836'); -- $dtok ||= $is_283x; -- $append_trailer = 1; -- } -- else -- { -- print ("* This doesn't look like a Raspberry Pi kernel. In pass-through mode.\n"); -- } -+ $append_trailer = 1; -+ $dtok ||= ($flags & FLAG_DTOK) != 0; -+ $is_270x ||= ($flags & FLAG_270X) != 0; -+ $is_283x ||= ($flags & FLAG_283X) != 0; -+ $ddtk ||= ($flags & FLAG_DDTK) != 0; -+ } -+ else -+ { -+ print ("* This doesn't look like a Raspberry Pi kernel. In pass-through mode.\n"); - } - } - elsif (!$dtok) -@@ -114,6 +127,8 @@ elsif (!$dtok) - if ($append_trailer) - { - printf("DT: %s\n", $dtok ? "y" : "n"); -+ printf("DDT: %s\n", $ddtk ? "y" : "n") if ($ddtk); -+ printf("270x: %s\n", $is_270x ? "y" : "n"); - printf("283x: %s\n", $is_283x ? "y" : "n"); - - my @atoms; -@@ -121,7 +136,10 @@ if ($append_trailer) - push @atoms, [ $trailer_magic, pack('V', 0) ]; - push @atoms, [ 'KVer', $kver ]; - push @atoms, [ 'DTOK', pack('V', $dtok) ]; -- push @atoms, [ '283x', pack('V', $is_283x) ]; -+ push @atoms, [ 'DDTK', pack('V', $ddtk) ] if ($ddtk); -+ push @atoms, [ '270X', pack('V', $is_270x) ]; -+ push @atoms, [ '283X', pack('V', $is_283x) ]; -+ push @atoms, [ '283x', pack('V', $is_283x && !$is_270x) ]; - - $trailer = pack_trailer(\@atoms); - $atoms[0]->[1] = pack('V', length($trailer)); -@@ -175,7 +193,7 @@ END { - - sub usage - { -- print ("Usage: mkknlimg [--dtok] [--283x] \n"); -+ print ("Usage: mkknlimg [--dtok] [--270x] [--283x] \n"); - exit(1); - } - -@@ -189,15 +207,8 @@ sub try_extract - - chomp($ver); - -- my $res = { ''=>$ver }; -- my $string_pattern = '^('.join('|', @wanted_strings).')$'; -- -- my @matches = `strings \"$knl\" | grep -E \"$string_pattern\"`; -- foreach my $match (@matches) -- { -- chomp($match); -- $res->{$match} = 1; -- } -+ my $res = { 'kver'=>$ver }; -+ $res->{'flags'} = strings_to_flags($knl, $wanted_strings); - - return $res; - } -@@ -224,6 +235,22 @@ sub try_decompress - return undef; - } - -+sub strings_to_flags -+{ -+ my ($knl, $strings) = @_; -+ my $string_pattern = '^('.join('|', keys(%$strings)).')$'; -+ my $flags = 0; -+ -+ my @matches = `strings \"$knl\" | grep -E \"$string_pattern\"`; -+ foreach my $match (@matches) -+ { -+ chomp($match); -+ $flags |= $strings->{$match}; -+ } -+ -+ return $flags; -+} -+ - sub pack_trailer - { - my ($atoms) = @_; -@@ -235,10 +262,3 @@ sub pack_trailer - } - return $trailer; - } -- --sub config_bool --{ -- my ($configs, $wanted) = @_; -- my $val = $configs->{$wanted} || 'n'; -- return (($val eq 'y') || ($val eq '1')); --} diff --git a/target/linux/brcm2708/patches-4.4/0091-drm-vc4-Add-suport-for-3D-rendering-using-the-V3D-en.patch b/target/linux/brcm2708/patches-4.4/0091-drm-vc4-Add-suport-for-3D-rendering-using-the-V3D-en.patch deleted file mode 100644 index df0296d233..0000000000 --- a/target/linux/brcm2708/patches-4.4/0091-drm-vc4-Add-suport-for-3D-rendering-using-the-V3D-en.patch +++ /dev/null @@ -1,5558 +0,0 @@ -From 6d38553e5ce7fb70dd1f89665caf58064a7e97d7 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Mon, 2 Mar 2015 13:01:12 -0800 -Subject: [PATCH 091/170] drm/vc4: Add suport for 3D rendering using the V3D - engine. - -This is a squash of the out-of-tree development series. Since that -series contained code from the first "get a demo triangle rendered -using a hacked up driver using binary shader code" to "plug the last -known security hole", it's hard to reconstruct a different series of -incremental development that's mergeable without security holes -throughout it. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/Makefile | 11 +- - drivers/gpu/drm/vc4/vc4_bo.c | 476 +++++++++++++- - drivers/gpu/drm/vc4/vc4_crtc.c | 98 ++- - drivers/gpu/drm/vc4/vc4_debugfs.c | 3 + - drivers/gpu/drm/vc4/vc4_drv.c | 45 +- - drivers/gpu/drm/vc4/vc4_drv.h | 317 ++++++++++ - drivers/gpu/drm/vc4/vc4_gem.c | 686 +++++++++++++++++++++ - drivers/gpu/drm/vc4/vc4_irq.c | 211 +++++++ - drivers/gpu/drm/vc4/vc4_kms.c | 148 ++++- - drivers/gpu/drm/vc4/vc4_packet.h | 384 ++++++++++++ - drivers/gpu/drm/vc4/vc4_plane.c | 40 ++ - drivers/gpu/drm/vc4/vc4_qpu_defines.h | 268 ++++++++ - drivers/gpu/drm/vc4/vc4_render_cl.c | 448 ++++++++++++++ - drivers/gpu/drm/vc4/vc4_trace.h | 63 ++ - drivers/gpu/drm/vc4/vc4_trace_points.c | 14 + - drivers/gpu/drm/vc4/vc4_v3d.c | 268 ++++++++ - drivers/gpu/drm/vc4/vc4_validate.c | 958 +++++++++++++++++++++++++++++ - drivers/gpu/drm/vc4/vc4_validate_shaders.c | 521 ++++++++++++++++ - include/uapi/drm/vc4_drm.h | 229 +++++++ - 19 files changed, 5173 insertions(+), 15 deletions(-) - create mode 100644 drivers/gpu/drm/vc4/vc4_gem.c - create mode 100644 drivers/gpu/drm/vc4/vc4_irq.c - create mode 100644 drivers/gpu/drm/vc4/vc4_packet.h - create mode 100644 drivers/gpu/drm/vc4/vc4_qpu_defines.h - create mode 100644 drivers/gpu/drm/vc4/vc4_render_cl.c - create mode 100644 drivers/gpu/drm/vc4/vc4_trace.h - create mode 100644 drivers/gpu/drm/vc4/vc4_trace_points.c - create mode 100644 drivers/gpu/drm/vc4/vc4_v3d.c - create mode 100644 drivers/gpu/drm/vc4/vc4_validate.c - create mode 100644 drivers/gpu/drm/vc4/vc4_validate_shaders.c - create mode 100644 include/uapi/drm/vc4_drm.h - ---- a/drivers/gpu/drm/vc4/Makefile -+++ b/drivers/gpu/drm/vc4/Makefile -@@ -8,10 +8,19 @@ vc4-y := \ - vc4_crtc.o \ - vc4_drv.o \ - vc4_kms.o \ -+ vc4_gem.o \ - vc4_hdmi.o \ - vc4_hvs.o \ -- vc4_plane.o -+ vc4_irq.o \ -+ vc4_plane.o \ -+ vc4_render_cl.o \ -+ vc4_trace_points.o \ -+ vc4_v3d.o \ -+ vc4_validate.o \ -+ vc4_validate_shaders.o - - vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o - - obj-$(CONFIG_DRM_VC4) += vc4.o -+ -+CFLAGS_vc4_trace_points.o := -I$(src) ---- a/drivers/gpu/drm/vc4/vc4_bo.c -+++ b/drivers/gpu/drm/vc4/vc4_bo.c -@@ -15,16 +15,174 @@ - */ - - #include "vc4_drv.h" -+#include "uapi/drm/vc4_drm.h" - --struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size) -+static void vc4_bo_stats_dump(struct vc4_dev *vc4) - { -+ DRM_INFO("num bos allocated: %d\n", -+ vc4->bo_stats.num_allocated); -+ DRM_INFO("size bos allocated: %dkb\n", -+ vc4->bo_stats.size_allocated / 1024); -+ DRM_INFO("num bos used: %d\n", -+ vc4->bo_stats.num_allocated - vc4->bo_stats.num_cached); -+ DRM_INFO("size bos used: %dkb\n", -+ (vc4->bo_stats.size_allocated - -+ vc4->bo_stats.size_cached) / 1024); -+ DRM_INFO("num bos cached: %d\n", -+ vc4->bo_stats.num_cached); -+ DRM_INFO("size bos cached: %dkb\n", -+ vc4->bo_stats.size_cached / 1024); -+} -+ -+static uint32_t bo_page_index(size_t size) -+{ -+ return (size / PAGE_SIZE) - 1; -+} -+ -+/* Must be called with bo_lock held. */ -+static void vc4_bo_destroy(struct vc4_bo *bo) -+{ -+ struct drm_gem_object *obj = &bo->base.base; -+ struct vc4_dev *vc4 = to_vc4_dev(obj->dev); -+ -+ if (bo->validated_shader) { -+ kfree(bo->validated_shader->texture_samples); -+ kfree(bo->validated_shader); -+ bo->validated_shader = NULL; -+ } -+ -+ vc4->bo_stats.num_allocated--; -+ vc4->bo_stats.size_allocated -= obj->size; -+ drm_gem_cma_free_object(obj); -+} -+ -+/* Must be called with bo_lock held. */ -+static void vc4_bo_remove_from_cache(struct vc4_bo *bo) -+{ -+ struct drm_gem_object *obj = &bo->base.base; -+ struct vc4_dev *vc4 = to_vc4_dev(obj->dev); -+ -+ vc4->bo_stats.num_cached--; -+ vc4->bo_stats.size_cached -= obj->size; -+ -+ list_del(&bo->unref_head); -+ list_del(&bo->size_head); -+} -+ -+static struct list_head *vc4_get_cache_list_for_size(struct drm_device *dev, -+ size_t size) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ uint32_t page_index = bo_page_index(size); -+ -+ if (vc4->bo_cache.size_list_size <= page_index) { -+ uint32_t new_size = max(vc4->bo_cache.size_list_size * 2, -+ page_index + 1); -+ struct list_head *new_list; -+ uint32_t i; -+ -+ new_list = kmalloc(new_size * sizeof(struct list_head), -+ GFP_KERNEL); -+ if (!new_list) -+ return NULL; -+ -+ /* Rebase the old cached BO lists to their new list -+ * head locations. -+ */ -+ for (i = 0; i < vc4->bo_cache.size_list_size; i++) { -+ struct list_head *old_list = &vc4->bo_cache.size_list[i]; -+ if (list_empty(old_list)) -+ INIT_LIST_HEAD(&new_list[i]); -+ else -+ list_replace(old_list, &new_list[i]); -+ } -+ /* And initialize the brand new BO list heads. */ -+ for (i = vc4->bo_cache.size_list_size; i < new_size; i++) -+ INIT_LIST_HEAD(&new_list[i]); -+ -+ kfree(vc4->bo_cache.size_list); -+ vc4->bo_cache.size_list = new_list; -+ vc4->bo_cache.size_list_size = new_size; -+ } -+ -+ return &vc4->bo_cache.size_list[page_index]; -+} -+ -+void vc4_bo_cache_purge(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ spin_lock(&vc4->bo_lock); -+ while (!list_empty(&vc4->bo_cache.time_list)) { -+ struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, -+ struct vc4_bo, unref_head); -+ vc4_bo_remove_from_cache(bo); -+ vc4_bo_destroy(bo); -+ } -+ spin_unlock(&vc4->bo_lock); -+} -+ -+struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ uint32_t size = roundup(unaligned_size, PAGE_SIZE); -+ uint32_t page_index = bo_page_index(size); - struct drm_gem_cma_object *cma_obj; -+ int pass; - -- cma_obj = drm_gem_cma_create(dev, size); -- if (IS_ERR(cma_obj)) -+ if (size == 0) - return NULL; -- else -- return to_vc4_bo(&cma_obj->base); -+ -+ /* First, try to get a vc4_bo from the kernel BO cache. */ -+ spin_lock(&vc4->bo_lock); -+ if (page_index < vc4->bo_cache.size_list_size && -+ !list_empty(&vc4->bo_cache.size_list[page_index])) { -+ struct vc4_bo *bo = -+ list_first_entry(&vc4->bo_cache.size_list[page_index], -+ struct vc4_bo, size_head); -+ vc4_bo_remove_from_cache(bo); -+ spin_unlock(&vc4->bo_lock); -+ kref_init(&bo->base.base.refcount); -+ return bo; -+ } -+ spin_unlock(&vc4->bo_lock); -+ -+ /* Otherwise, make a new BO. */ -+ for (pass = 0; ; pass++) { -+ cma_obj = drm_gem_cma_create(dev, size); -+ if (!IS_ERR(cma_obj)) -+ break; -+ -+ switch (pass) { -+ case 0: -+ /* -+ * If we've run out of CMA memory, kill the cache of -+ * CMA allocations we've got laying around and try again. -+ */ -+ vc4_bo_cache_purge(dev); -+ break; -+ case 1: -+ /* -+ * Getting desperate, so try to wait for any -+ * previous rendering to finish, free its -+ * unreferenced BOs to the cache, and then -+ * free the cache. -+ */ -+ vc4_wait_for_seqno(dev, vc4->emit_seqno, ~0ull, true); -+ vc4_job_handle_completed(vc4); -+ vc4_bo_cache_purge(dev); -+ break; -+ case 3: -+ DRM_ERROR("Failed to allocate from CMA:\n"); -+ vc4_bo_stats_dump(vc4); -+ return NULL; -+ } -+ } -+ -+ vc4->bo_stats.num_allocated++; -+ vc4->bo_stats.size_allocated += size; -+ -+ return to_vc4_bo(&cma_obj->base); - } - - int vc4_dumb_create(struct drm_file *file_priv, -@@ -41,7 +199,129 @@ int vc4_dumb_create(struct drm_file *fil - if (args->size < args->pitch * args->height) - args->size = args->pitch * args->height; - -- bo = vc4_bo_create(dev, roundup(args->size, PAGE_SIZE)); -+ bo = vc4_bo_create(dev, args->size); -+ if (!bo) -+ return -ENOMEM; -+ -+ ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); -+ drm_gem_object_unreference_unlocked(&bo->base.base); -+ -+ return ret; -+} -+ -+static void -+vc4_bo_cache_free_old(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ unsigned long expire_time = jiffies - msecs_to_jiffies(1000); -+ -+ spin_lock(&vc4->bo_lock); -+ while (!list_empty(&vc4->bo_cache.time_list)) { -+ struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, -+ struct vc4_bo, unref_head); -+ if (time_before(expire_time, bo->free_time)) { -+ mod_timer(&vc4->bo_cache.time_timer, -+ round_jiffies_up(jiffies + -+ msecs_to_jiffies(1000))); -+ spin_unlock(&vc4->bo_lock); -+ return; -+ } -+ -+ vc4_bo_remove_from_cache(bo); -+ vc4_bo_destroy(bo); -+ } -+ spin_unlock(&vc4->bo_lock); -+} -+ -+/* Called on the last userspace/kernel unreference of the BO. Returns -+ * it to the BO cache if possible, otherwise frees it. -+ * -+ * Note that this is called with the struct_mutex held. -+ */ -+void vc4_free_object(struct drm_gem_object *gem_bo) -+{ -+ struct drm_device *dev = gem_bo->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct vc4_bo *bo = to_vc4_bo(gem_bo); -+ struct list_head *cache_list; -+ -+ /* If the object references someone else's memory, we can't cache it. -+ */ -+ if (gem_bo->import_attach) { -+ vc4_bo_destroy(bo); -+ return; -+ } -+ -+ /* Don't cache if it was publicly named. */ -+ if (gem_bo->name) { -+ vc4_bo_destroy(bo); -+ return; -+ } -+ -+ spin_lock(&vc4->bo_lock); -+ cache_list = vc4_get_cache_list_for_size(dev, gem_bo->size); -+ if (!cache_list) { -+ vc4_bo_destroy(bo); -+ spin_unlock(&vc4->bo_lock); -+ return; -+ } -+ -+ if (bo->validated_shader) { -+ kfree(bo->validated_shader->texture_samples); -+ kfree(bo->validated_shader); -+ bo->validated_shader = NULL; -+ } -+ -+ bo->free_time = jiffies; -+ list_add(&bo->size_head, cache_list); -+ list_add(&bo->unref_head, &vc4->bo_cache.time_list); -+ -+ vc4->bo_stats.num_cached++; -+ vc4->bo_stats.size_cached += gem_bo->size; -+ spin_unlock(&vc4->bo_lock); -+ -+ vc4_bo_cache_free_old(dev); -+} -+ -+static void vc4_bo_cache_time_work(struct work_struct *work) -+{ -+ struct vc4_dev *vc4 = -+ container_of(work, struct vc4_dev, bo_cache.time_work); -+ struct drm_device *dev = vc4->dev; -+ -+ vc4_bo_cache_free_old(dev); -+} -+ -+static void vc4_bo_cache_time_timer(unsigned long data) -+{ -+ struct drm_device *dev = (struct drm_device *)data; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ schedule_work(&vc4->bo_cache.time_work); -+} -+ -+struct dma_buf * -+vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) -+{ -+ struct vc4_bo *bo = to_vc4_bo(obj); -+ -+ if (bo->validated_shader) { -+ DRM_ERROR("Attempting to export shader BO\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ return drm_gem_prime_export(dev, obj, flags); -+} -+ -+int -+vc4_create_bo_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv) -+{ -+ struct drm_vc4_create_bo *args = data; -+ struct vc4_bo *bo = NULL; -+ int ret; -+ -+ bo = vc4_bo_create(dev, args->size); - if (!bo) - return -ENOMEM; - -@@ -50,3 +330,187 @@ int vc4_dumb_create(struct drm_file *fil - - return ret; - } -+ -+int -+vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv) -+{ -+ struct drm_vc4_create_shader_bo *args = data; -+ struct vc4_bo *bo = NULL; -+ int ret; -+ -+ if (args->size == 0) -+ return -EINVAL; -+ -+ if (args->size % sizeof(u64) != 0) -+ return -EINVAL; -+ -+ if (args->flags != 0) { -+ DRM_INFO("Unknown flags set: 0x%08x\n", args->flags); -+ return -EINVAL; -+ } -+ -+ if (args->pad != 0) { -+ DRM_INFO("Pad set: 0x%08x\n", args->pad); -+ return -EINVAL; -+ } -+ -+ bo = vc4_bo_create(dev, args->size); -+ if (!bo) -+ return -ENOMEM; -+ -+ ret = copy_from_user(bo->base.vaddr, -+ (void __user *)(uintptr_t)args->data, -+ args->size); -+ if (ret != 0) -+ goto fail; -+ -+ bo->validated_shader = vc4_validate_shader(&bo->base); -+ if (!bo->validated_shader) { -+ ret = -EINVAL; -+ goto fail; -+ } -+ -+ /* We have to create the handle after validation, to avoid -+ * races for users to do doing things like mmap the shader BO. -+ */ -+ ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); -+ -+ fail: -+ drm_gem_object_unreference_unlocked(&bo->base.base); -+ -+ return ret; -+} -+ -+int -+vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv) -+{ -+ struct drm_vc4_mmap_bo *args = data; -+ struct drm_gem_object *gem_obj; -+ -+ gem_obj = drm_gem_object_lookup(dev, file_priv, args->handle); -+ if (!gem_obj) { -+ DRM_ERROR("Failed to look up GEM BO %d\n", args->handle); -+ return -EINVAL; -+ } -+ -+ /* The mmap offset was set up at BO allocation time. */ -+ args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); -+ -+ drm_gem_object_unreference(gem_obj); -+ return 0; -+} -+ -+int vc4_mmap(struct file *filp, struct vm_area_struct *vma) -+{ -+ struct drm_gem_object *gem_obj; -+ struct vc4_bo *bo; -+ int ret; -+ -+ ret = drm_gem_mmap(filp, vma); -+ if (ret) -+ return ret; -+ -+ gem_obj = vma->vm_private_data; -+ bo = to_vc4_bo(gem_obj); -+ -+ if (bo->validated_shader) { -+ DRM_ERROR("mmaping of shader BOs not allowed.\n"); -+ return -EINVAL; -+ } -+ -+ /* -+ * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the -+ * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map -+ * the whole buffer. -+ */ -+ vma->vm_flags &= ~VM_PFNMAP; -+ vma->vm_pgoff = 0; -+ -+ ret = dma_mmap_writecombine(bo->base.base.dev->dev, vma, -+ bo->base.vaddr, bo->base.paddr, -+ vma->vm_end - vma->vm_start); -+ if (ret) -+ drm_gem_vm_close(vma); -+ -+ return ret; -+} -+ -+int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) -+{ -+ struct vc4_bo *bo = to_vc4_bo(obj); -+ -+ if (bo->validated_shader) { -+ DRM_ERROR("mmaping of shader BOs not allowed.\n"); -+ return -EINVAL; -+ } -+ -+ return drm_gem_cma_prime_mmap(obj, vma); -+} -+ -+void *vc4_prime_vmap(struct drm_gem_object *obj) -+{ -+ struct vc4_bo *bo = to_vc4_bo(obj); -+ -+ if (bo->validated_shader) { -+ DRM_ERROR("mmaping of shader BOs not allowed.\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ return drm_gem_cma_prime_vmap(obj); -+} -+ -+void vc4_bo_cache_init(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ spin_lock_init(&vc4->bo_lock); -+ -+ INIT_LIST_HEAD(&vc4->bo_cache.time_list); -+ -+ INIT_WORK(&vc4->bo_cache.time_work, vc4_bo_cache_time_work); -+ setup_timer(&vc4->bo_cache.time_timer, -+ vc4_bo_cache_time_timer, -+ (unsigned long) dev); -+} -+ -+void vc4_bo_cache_destroy(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ del_timer(&vc4->bo_cache.time_timer); -+ cancel_work_sync(&vc4->bo_cache.time_work); -+ -+ vc4_bo_cache_purge(dev); -+ -+ if (vc4->bo_stats.num_allocated) { -+ DRM_ERROR("Destroying BO cache while BOs still allocated:\n"); -+ vc4_bo_stats_dump(vc4); -+ } -+} -+ -+#ifdef CONFIG_DEBUG_FS -+int vc4_bo_stats_debugfs(struct seq_file *m, void *unused) -+{ -+ struct drm_info_node *node = (struct drm_info_node *) m->private; -+ struct drm_device *dev = node->minor->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct vc4_bo_stats stats; -+ -+ spin_lock(&vc4->bo_lock); -+ stats = vc4->bo_stats; -+ spin_unlock(&vc4->bo_lock); -+ -+ seq_printf(m, "num bos allocated: %d\n", stats.num_allocated); -+ seq_printf(m, "size bos allocated: %dkb\n", stats.size_allocated / 1024); -+ seq_printf(m, "num bos used: %d\n", (stats.num_allocated - -+ stats.num_cached)); -+ seq_printf(m, "size bos used: %dkb\n", (stats.size_allocated - -+ stats.size_cached) / 1024); -+ seq_printf(m, "num bos cached: %d\n", stats.num_cached); -+ seq_printf(m, "size bos cached: %dkb\n", stats.size_cached / 1024); -+ -+ return 0; -+} -+#endif ---- a/drivers/gpu/drm/vc4/vc4_crtc.c -+++ b/drivers/gpu/drm/vc4/vc4_crtc.c -@@ -35,6 +35,7 @@ - #include "drm_atomic_helper.h" - #include "drm_crtc_helper.h" - #include "linux/clk.h" -+#include "drm_fb_cma_helper.h" - #include "linux/component.h" - #include "linux/of_device.h" - #include "vc4_drv.h" -@@ -476,10 +477,105 @@ static irqreturn_t vc4_crtc_irq_handler( - return ret; - } - -+struct vc4_async_flip_state { -+ struct drm_crtc *crtc; -+ struct drm_framebuffer *fb; -+ struct drm_pending_vblank_event *event; -+ -+ struct vc4_seqno_cb cb; -+}; -+ -+/* Called when the V3D execution for the BO being flipped to is done, so that -+ * we can actually update the plane's address to point to it. -+ */ -+static void -+vc4_async_page_flip_complete(struct vc4_seqno_cb *cb) -+{ -+ struct vc4_async_flip_state *flip_state = -+ container_of(cb, struct vc4_async_flip_state, cb); -+ struct drm_crtc *crtc = flip_state->crtc; -+ struct drm_device *dev = crtc->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct drm_plane *plane = crtc->primary; -+ -+ vc4_plane_async_set_fb(plane, flip_state->fb); -+ if (flip_state->event) { -+ unsigned long flags; -+ spin_lock_irqsave(&dev->event_lock, flags); -+ drm_crtc_send_vblank_event(crtc, flip_state->event); -+ spin_unlock_irqrestore(&dev->event_lock, flags); -+ } -+ -+ drm_framebuffer_unreference(flip_state->fb); -+ kfree(flip_state); -+ -+ up(&vc4->async_modeset); -+} -+ -+/* Implements async (non-vblank-synced) page flips. -+ * -+ * The page flip ioctl needs to return immediately, so we grab the -+ * modeset semaphore on the pipe, and queue the address update for -+ * when V3D is done with the BO being flipped to. -+ */ -+static int vc4_async_page_flip(struct drm_crtc *crtc, -+ struct drm_framebuffer *fb, -+ struct drm_pending_vblank_event *event, -+ uint32_t flags) -+{ -+ struct drm_device *dev = crtc->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct drm_plane *plane = crtc->primary; -+ int ret = 0; -+ struct vc4_async_flip_state *flip_state; -+ struct drm_gem_cma_object *cma_bo = drm_fb_cma_get_gem_obj(fb, 0); -+ struct vc4_bo *bo = to_vc4_bo(&cma_bo->base); -+ -+ flip_state = kzalloc(sizeof(*flip_state), GFP_KERNEL); -+ if (!flip_state) -+ return -ENOMEM; -+ -+ drm_framebuffer_reference(fb); -+ flip_state->fb = fb; -+ flip_state->crtc = crtc; -+ flip_state->event = event; -+ -+ /* Make sure all other async modesetes have landed. */ -+ ret = down_interruptible(&vc4->async_modeset); -+ if (ret) { -+ kfree(flip_state); -+ return ret; -+ } -+ -+ /* Immediately update the plane's legacy fb pointer, so that later -+ * modeset prep sees the state that will be present when the semaphore -+ * is released. -+ */ -+ drm_atomic_set_fb_for_plane(plane->state, fb); -+ plane->fb = fb; -+ -+ vc4_queue_seqno_cb(dev, &flip_state->cb, bo->seqno, -+ vc4_async_page_flip_complete); -+ -+ /* Driver takes ownership of state on successful async commit. */ -+ return 0; -+} -+ -+static int vc4_page_flip(struct drm_crtc *crtc, -+ struct drm_framebuffer *fb, -+ struct drm_pending_vblank_event *event, -+ uint32_t flags) -+{ -+ if (flags & DRM_MODE_PAGE_FLIP_ASYNC) -+ return vc4_async_page_flip(crtc, fb, event, flags); -+ else -+ return drm_atomic_helper_page_flip(crtc, fb, event, flags); -+} -+ - static const struct drm_crtc_funcs vc4_crtc_funcs = { - .set_config = drm_atomic_helper_set_config, - .destroy = vc4_crtc_destroy, -- .page_flip = drm_atomic_helper_page_flip, -+ .page_flip = vc4_page_flip, - .set_property = NULL, - .cursor_set = NULL, /* handled by drm_mode_cursor_universal */ - .cursor_move = NULL, /* handled by drm_mode_cursor_universal */ ---- a/drivers/gpu/drm/vc4/vc4_debugfs.c -+++ b/drivers/gpu/drm/vc4/vc4_debugfs.c -@@ -16,11 +16,14 @@ - #include "vc4_regs.h" - - static const struct drm_info_list vc4_debugfs_list[] = { -+ {"bo_stats", vc4_bo_stats_debugfs, 0}, - {"hdmi_regs", vc4_hdmi_debugfs_regs, 0}, - {"hvs_regs", vc4_hvs_debugfs_regs, 0}, - {"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0}, - {"crtc1_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)1}, - {"crtc2_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)2}, -+ {"v3d_ident", vc4_v3d_debugfs_ident, 0}, -+ {"v3d_regs", vc4_v3d_debugfs_regs, 0}, - }; - - #define VC4_DEBUGFS_ENTRIES ARRAY_SIZE(vc4_debugfs_list) ---- a/drivers/gpu/drm/vc4/vc4_drv.c -+++ b/drivers/gpu/drm/vc4/vc4_drv.c -@@ -14,8 +14,10 @@ - #include - #include - #include -+#include - #include "drm_fb_cma_helper.h" - -+#include "uapi/drm/vc4_drm.h" - #include "vc4_drv.h" - #include "vc4_regs.h" - -@@ -63,7 +65,7 @@ static const struct file_operations vc4_ - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, -- .mmap = drm_gem_cma_mmap, -+ .mmap = vc4_mmap, - .poll = drm_poll, - .read = drm_read, - #ifdef CONFIG_COMPAT -@@ -73,16 +75,28 @@ static const struct file_operations vc4_ - }; - - static const struct drm_ioctl_desc vc4_drm_ioctls[] = { -+ DRM_IOCTL_DEF_DRV(VC4_SUBMIT_CL, vc4_submit_cl_ioctl, 0), -+ DRM_IOCTL_DEF_DRV(VC4_WAIT_SEQNO, vc4_wait_seqno_ioctl, 0), -+ DRM_IOCTL_DEF_DRV(VC4_WAIT_BO, vc4_wait_bo_ioctl, 0), -+ DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, 0), -+ DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, 0), -+ DRM_IOCTL_DEF_DRV(VC4_CREATE_SHADER_BO, vc4_create_shader_bo_ioctl, 0), - }; - - static struct drm_driver vc4_drm_driver = { - .driver_features = (DRIVER_MODESET | - DRIVER_ATOMIC | - DRIVER_GEM | -+ DRIVER_HAVE_IRQ | - DRIVER_PRIME), - .lastclose = vc4_lastclose, - .preclose = vc4_drm_preclose, - -+ .irq_handler = vc4_irq, -+ .irq_preinstall = vc4_irq_preinstall, -+ .irq_postinstall = vc4_irq_postinstall, -+ .irq_uninstall = vc4_irq_uninstall, -+ - .enable_vblank = vc4_enable_vblank, - .disable_vblank = vc4_disable_vblank, - .get_vblank_counter = drm_vblank_count, -@@ -92,18 +106,18 @@ static struct drm_driver vc4_drm_driver - .debugfs_cleanup = vc4_debugfs_cleanup, - #endif - -- .gem_free_object = drm_gem_cma_free_object, -+ .gem_free_object = vc4_free_object, - .gem_vm_ops = &drm_gem_cma_vm_ops, - - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - .gem_prime_import = drm_gem_prime_import, -- .gem_prime_export = drm_gem_prime_export, -+ .gem_prime_export = vc4_prime_export, - .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, - .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, -- .gem_prime_vmap = drm_gem_cma_prime_vmap, -+ .gem_prime_vmap = vc4_prime_vmap, - .gem_prime_vunmap = drm_gem_cma_prime_vunmap, -- .gem_prime_mmap = drm_gem_cma_prime_mmap, -+ .gem_prime_mmap = vc4_prime_mmap, - - .dumb_create = vc4_dumb_create, - .dumb_map_offset = drm_gem_cma_dumb_map_offset, -@@ -113,6 +127,8 @@ static struct drm_driver vc4_drm_driver - .num_ioctls = ARRAY_SIZE(vc4_drm_ioctls), - .fops = &vc4_drm_fops, - -+ .gem_obj_size = sizeof(struct vc4_bo), -+ - .name = DRIVER_NAME, - .desc = DRIVER_DESC, - .date = DRIVER_DATE, -@@ -153,6 +169,7 @@ static int vc4_drm_bind(struct device *d - struct drm_device *drm; - struct drm_connector *connector; - struct vc4_dev *vc4; -+ struct device_node *firmware_node; - int ret = 0; - - dev->coherent_dma_mask = DMA_BIT_MASK(32); -@@ -161,6 +178,14 @@ static int vc4_drm_bind(struct device *d - if (!vc4) - return -ENOMEM; - -+ firmware_node = of_parse_phandle(dev->of_node, "firmware", 0); -+ vc4->firmware = rpi_firmware_get(firmware_node); -+ if (!vc4->firmware) { -+ DRM_DEBUG("Failed to get Raspberry Pi firmware reference.\n"); -+ return -EPROBE_DEFER; -+ } -+ of_node_put(firmware_node); -+ - drm = drm_dev_alloc(&vc4_drm_driver, dev); - if (!drm) - return -ENOMEM; -@@ -170,13 +195,17 @@ static int vc4_drm_bind(struct device *d - - drm_dev_set_unique(drm, dev_name(dev)); - -+ vc4_bo_cache_init(drm); -+ - drm_mode_config_init(drm); - if (ret) - goto unref; - -+ vc4_gem_init(drm); -+ - ret = component_bind_all(dev, drm); - if (ret) -- goto unref; -+ goto gem_destroy; - - ret = drm_dev_register(drm, 0); - if (ret < 0) -@@ -200,8 +229,11 @@ unregister: - drm_dev_unregister(drm); - unbind_all: - component_unbind_all(dev, drm); -+gem_destroy: -+ vc4_gem_destroy(drm); - unref: - drm_dev_unref(drm); -+ vc4_bo_cache_destroy(drm); - return ret; - } - -@@ -228,6 +260,7 @@ static struct platform_driver *const com - &vc4_hdmi_driver, - &vc4_crtc_driver, - &vc4_hvs_driver, -+ &vc4_v3d_driver, - }; - - static int vc4_platform_drm_probe(struct platform_device *pdev) ---- a/drivers/gpu/drm/vc4/vc4_drv.h -+++ b/drivers/gpu/drm/vc4/vc4_drv.h -@@ -15,8 +15,85 @@ struct vc4_dev { - struct vc4_hdmi *hdmi; - struct vc4_hvs *hvs; - struct vc4_crtc *crtc[3]; -+ struct vc4_v3d *v3d; - - struct drm_fbdev_cma *fbdev; -+ struct rpi_firmware *firmware; -+ -+ /* The kernel-space BO cache. Tracks buffers that have been -+ * unreferenced by all other users (refcounts of 0!) but not -+ * yet freed, so we can do cheap allocations. -+ */ -+ struct vc4_bo_cache { -+ /* Array of list heads for entries in the BO cache, -+ * based on number of pages, so we can do O(1) lookups -+ * in the cache when allocating. -+ */ -+ struct list_head *size_list; -+ uint32_t size_list_size; -+ -+ /* List of all BOs in the cache, ordered by age, so we -+ * can do O(1) lookups when trying to free old -+ * buffers. -+ */ -+ struct list_head time_list; -+ struct work_struct time_work; -+ struct timer_list time_timer; -+ } bo_cache; -+ -+ struct vc4_bo_stats { -+ u32 num_allocated; -+ u32 size_allocated; -+ u32 num_cached; -+ u32 size_cached; -+ } bo_stats; -+ -+ /* Protects bo_cache and the BO stats. */ -+ spinlock_t bo_lock; -+ -+ /* Sequence number for the last job queued in job_list. -+ * Starts at 0 (no jobs emitted). -+ */ -+ uint64_t emit_seqno; -+ -+ /* Sequence number for the last completed job on the GPU. -+ * Starts at 0 (no jobs completed). -+ */ -+ uint64_t finished_seqno; -+ -+ /* List of all struct vc4_exec_info for jobs to be executed. -+ * The first job in the list is the one currently programmed -+ * into ct0ca/ct1ca for execution. -+ */ -+ struct list_head job_list; -+ /* List of the finished vc4_exec_infos waiting to be freed by -+ * job_done_work. -+ */ -+ struct list_head job_done_list; -+ spinlock_t job_lock; -+ wait_queue_head_t job_wait_queue; -+ struct work_struct job_done_work; -+ -+ /* List of struct vc4_seqno_cb for callbacks to be made from a -+ * workqueue when the given seqno is passed. -+ */ -+ struct list_head seqno_cb_list; -+ -+ /* The binner overflow memory that's currently set up in -+ * BPOA/BPOS registers. When overflow occurs and a new one is -+ * allocated, the previous one will be moved to -+ * vc4->current_exec's free list. -+ */ -+ struct vc4_bo *overflow_mem; -+ struct work_struct overflow_mem_work; -+ -+ struct { -+ uint32_t last_ct0ca, last_ct1ca; -+ struct timer_list timer; -+ struct work_struct reset_work; -+ } hangcheck; -+ -+ struct semaphore async_modeset; - }; - - static inline struct vc4_dev * -@@ -27,6 +104,25 @@ to_vc4_dev(struct drm_device *dev) - - struct vc4_bo { - struct drm_gem_cma_object base; -+ -+ /* seqno of the last job to render to this BO. */ -+ uint64_t seqno; -+ -+ /* List entry for the BO's position in either -+ * vc4_exec_info->unref_list or vc4_dev->bo_cache.time_list -+ */ -+ struct list_head unref_head; -+ -+ /* Time in jiffies when the BO was put in vc4->bo_cache. */ -+ unsigned long free_time; -+ -+ /* List entry for the BO's position in vc4_dev->bo_cache.size_list */ -+ struct list_head size_head; -+ -+ /* Struct for shader validation state, if created by -+ * DRM_IOCTL_VC4_CREATE_SHADER_BO. -+ */ -+ struct vc4_validated_shader_info *validated_shader; - }; - - static inline struct vc4_bo * -@@ -35,6 +131,17 @@ to_vc4_bo(struct drm_gem_object *bo) - return (struct vc4_bo *)bo; - } - -+struct vc4_seqno_cb { -+ struct work_struct work; -+ uint64_t seqno; -+ void (*func)(struct vc4_seqno_cb *cb); -+}; -+ -+struct vc4_v3d { -+ struct platform_device *pdev; -+ void __iomem *regs; -+}; -+ - struct vc4_hvs { - struct platform_device *pdev; - void __iomem *regs; -@@ -72,9 +179,151 @@ to_vc4_encoder(struct drm_encoder *encod - return container_of(encoder, struct vc4_encoder, base); - } - -+#define V3D_READ(offset) readl(vc4->v3d->regs + offset) -+#define V3D_WRITE(offset, val) writel(val, vc4->v3d->regs + offset) - #define HVS_READ(offset) readl(vc4->hvs->regs + offset) - #define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset) - -+enum vc4_bo_mode { -+ VC4_MODE_UNDECIDED, -+ VC4_MODE_RENDER, -+ VC4_MODE_SHADER, -+}; -+ -+struct vc4_bo_exec_state { -+ struct drm_gem_cma_object *bo; -+ enum vc4_bo_mode mode; -+}; -+ -+struct vc4_exec_info { -+ /* Sequence number for this bin/render job. */ -+ uint64_t seqno; -+ -+ /* Kernel-space copy of the ioctl arguments */ -+ struct drm_vc4_submit_cl *args; -+ -+ /* This is the array of BOs that were looked up at the start of exec. -+ * Command validation will use indices into this array. -+ */ -+ struct vc4_bo_exec_state *bo; -+ uint32_t bo_count; -+ -+ /* Pointers for our position in vc4->job_list */ -+ struct list_head head; -+ -+ /* List of other BOs used in the job that need to be released -+ * once the job is complete. -+ */ -+ struct list_head unref_list; -+ -+ /* Current unvalidated indices into @bo loaded by the non-hardware -+ * VC4_PACKET_GEM_HANDLES. -+ */ -+ uint32_t bo_index[2]; -+ -+ /* This is the BO where we store the validated command lists, shader -+ * records, and uniforms. -+ */ -+ struct drm_gem_cma_object *exec_bo; -+ -+ /** -+ * This tracks the per-shader-record state (packet 64) that -+ * determines the length of the shader record and the offset -+ * it's expected to be found at. It gets read in from the -+ * command lists. -+ */ -+ struct vc4_shader_state { -+ uint8_t packet; -+ uint32_t addr; -+ /* Maximum vertex index referenced by any primitive using this -+ * shader state. -+ */ -+ uint32_t max_index; -+ } *shader_state; -+ -+ /** How many shader states the user declared they were using. */ -+ uint32_t shader_state_size; -+ /** How many shader state records the validator has seen. */ -+ uint32_t shader_state_count; -+ -+ bool found_tile_binning_mode_config_packet; -+ bool found_start_tile_binning_packet; -+ bool found_increment_semaphore_packet; -+ uint8_t bin_tiles_x, bin_tiles_y; -+ struct drm_gem_cma_object *tile_bo; -+ uint32_t tile_alloc_offset; -+ -+ /** -+ * Computed addresses pointing into exec_bo where we start the -+ * bin thread (ct0) and render thread (ct1). -+ */ -+ uint32_t ct0ca, ct0ea; -+ uint32_t ct1ca, ct1ea; -+ -+ /* Pointers to the shader recs. These paddr gets incremented as CL -+ * packets are relocated in validate_gl_shader_state, and the vaddrs -+ * (u and v) get incremented and size decremented as the shader recs -+ * themselves are validated. -+ */ -+ void *shader_rec_u; -+ void *shader_rec_v; -+ uint32_t shader_rec_p; -+ uint32_t shader_rec_size; -+ -+ /* Pointers to the uniform data. These pointers are incremented, and -+ * size decremented, as each batch of uniforms is uploaded. -+ */ -+ void *uniforms_u; -+ void *uniforms_v; -+ uint32_t uniforms_p; -+ uint32_t uniforms_size; -+}; -+ -+static inline struct vc4_exec_info * -+vc4_first_job(struct vc4_dev *vc4) -+{ -+ if (list_empty(&vc4->job_list)) -+ return NULL; -+ return list_first_entry(&vc4->job_list, struct vc4_exec_info, head); -+} -+ -+/** -+ * struct vc4_texture_sample_info - saves the offsets into the UBO for texture -+ * setup parameters. -+ * -+ * This will be used at draw time to relocate the reference to the texture -+ * contents in p0, and validate that the offset combined with -+ * width/height/stride/etc. from p1 and p2/p3 doesn't sample outside the BO. -+ * Note that the hardware treats unprovided config parameters as 0, so not all -+ * of them need to be set up for every texure sample, and we'll store ~0 as -+ * the offset to mark the unused ones. -+ * -+ * See the VC4 3D architecture guide page 41 ("Texture and Memory Lookup Unit -+ * Setup") for definitions of the texture parameters. -+ */ -+struct vc4_texture_sample_info { -+ bool is_direct; -+ uint32_t p_offset[4]; -+}; -+ -+/** -+ * struct vc4_validated_shader_info - information about validated shaders that -+ * needs to be used from command list validation. -+ * -+ * For a given shader, each time a shader state record references it, we need -+ * to verify that the shader doesn't read more uniforms than the shader state -+ * record's uniform BO pointer can provide, and we need to apply relocations -+ * and validate the shader state record's uniforms that define the texture -+ * samples. -+ */ -+struct vc4_validated_shader_info -+{ -+ uint32_t uniforms_size; -+ uint32_t uniforms_src_size; -+ uint32_t num_texture_samples; -+ struct vc4_texture_sample_info *texture_samples; -+}; -+ - /** - * _wait_for - magic (register) wait macro - * -@@ -111,6 +360,18 @@ int vc4_dumb_create(struct drm_file *fil - struct drm_mode_create_dumb *args); - struct dma_buf *vc4_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, int flags); -+int vc4_create_bo_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv); -+int vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv); -+int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv); -+int vc4_mmap(struct file *filp, struct vm_area_struct *vma); -+int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); -+void *vc4_prime_vmap(struct drm_gem_object *obj); -+void vc4_bo_cache_init(struct drm_device *dev); -+void vc4_bo_cache_destroy(struct drm_device *dev); -+int vc4_bo_stats_debugfs(struct seq_file *m, void *arg); - - /* vc4_crtc.c */ - extern struct platform_driver vc4_crtc_driver; -@@ -126,10 +387,34 @@ void vc4_debugfs_cleanup(struct drm_mino - /* vc4_drv.c */ - void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index); - -+/* vc4_gem.c */ -+void vc4_gem_init(struct drm_device *dev); -+void vc4_gem_destroy(struct drm_device *dev); -+int vc4_submit_cl_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv); -+int vc4_wait_seqno_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv); -+int vc4_wait_bo_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv); -+void vc4_submit_next_job(struct drm_device *dev); -+int vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, -+ uint64_t timeout_ns, bool interruptible); -+void vc4_job_handle_completed(struct vc4_dev *vc4); -+int vc4_queue_seqno_cb(struct drm_device *dev, -+ struct vc4_seqno_cb *cb, uint64_t seqno, -+ void (*func)(struct vc4_seqno_cb *cb)); -+ - /* vc4_hdmi.c */ - extern struct platform_driver vc4_hdmi_driver; - int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused); - -+/* vc4_irq.c */ -+irqreturn_t vc4_irq(int irq, void *arg); -+void vc4_irq_preinstall(struct drm_device *dev); -+int vc4_irq_postinstall(struct drm_device *dev); -+void vc4_irq_uninstall(struct drm_device *dev); -+void vc4_irq_reset(struct drm_device *dev); -+ - /* vc4_hvs.c */ - extern struct platform_driver vc4_hvs_driver; - void vc4_hvs_dump_state(struct drm_device *dev); -@@ -143,3 +428,35 @@ struct drm_plane *vc4_plane_init(struct - enum drm_plane_type type); - u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); - u32 vc4_plane_dlist_size(struct drm_plane_state *state); -+void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb); -+ -+/* vc4_v3d.c */ -+extern struct platform_driver vc4_v3d_driver; -+int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused); -+int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused); -+int vc4_v3d_set_power(struct vc4_dev *vc4, bool on); -+ -+/* vc4_validate.c */ -+int -+vc4_validate_bin_cl(struct drm_device *dev, -+ void *validated, -+ void *unvalidated, -+ struct vc4_exec_info *exec); -+ -+int -+vc4_validate_shader_recs(struct drm_device *dev, struct vc4_exec_info *exec); -+ -+struct vc4_validated_shader_info * -+vc4_validate_shader(struct drm_gem_cma_object *shader_obj); -+ -+bool vc4_use_bo(struct vc4_exec_info *exec, -+ uint32_t hindex, -+ enum vc4_bo_mode mode, -+ struct drm_gem_cma_object **obj); -+ -+int vc4_get_rcl(struct drm_device *dev, struct vc4_exec_info *exec); -+ -+bool vc4_check_tex_size(struct vc4_exec_info *exec, -+ struct drm_gem_cma_object *fbo, -+ uint32_t offset, uint8_t tiling_format, -+ uint32_t width, uint32_t height, uint8_t cpp); ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_gem.c -@@ -0,0 +1,686 @@ -+/* -+ * Copyright © 2014 Broadcom -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#include "uapi/drm/vc4_drm.h" -+#include "vc4_drv.h" -+#include "vc4_regs.h" -+#include "vc4_trace.h" -+ -+static void -+vc4_queue_hangcheck(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ mod_timer(&vc4->hangcheck.timer, -+ round_jiffies_up(jiffies + msecs_to_jiffies(100))); -+} -+ -+static void -+vc4_reset(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ DRM_INFO("Resetting GPU.\n"); -+ vc4_v3d_set_power(vc4, false); -+ vc4_v3d_set_power(vc4, true); -+ -+ vc4_irq_reset(dev); -+ -+ /* Rearm the hangcheck -- another job might have been waiting -+ * for our hung one to get kicked off, and vc4_irq_reset() -+ * would have started it. -+ */ -+ vc4_queue_hangcheck(dev); -+} -+ -+static void -+vc4_reset_work(struct work_struct *work) -+{ -+ struct vc4_dev *vc4 = -+ container_of(work, struct vc4_dev, hangcheck.reset_work); -+ -+ vc4_reset(vc4->dev); -+} -+ -+static void -+vc4_hangcheck_elapsed(unsigned long data) -+{ -+ struct drm_device *dev = (struct drm_device *)data; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ uint32_t ct0ca, ct1ca; -+ -+ /* If idle, we can stop watching for hangs. */ -+ if (list_empty(&vc4->job_list)) -+ return; -+ -+ ct0ca = V3D_READ(V3D_CTNCA(0)); -+ ct1ca = V3D_READ(V3D_CTNCA(1)); -+ -+ /* If we've made any progress in execution, rearm the timer -+ * and wait. -+ */ -+ if (ct0ca != vc4->hangcheck.last_ct0ca || -+ ct1ca != vc4->hangcheck.last_ct1ca) { -+ vc4->hangcheck.last_ct0ca = ct0ca; -+ vc4->hangcheck.last_ct1ca = ct1ca; -+ vc4_queue_hangcheck(dev); -+ return; -+ } -+ -+ /* We've gone too long with no progress, reset. This has to -+ * be done from a work struct, since resetting can sleep and -+ * this timer hook isn't allowed to. -+ */ -+ schedule_work(&vc4->hangcheck.reset_work); -+} -+ -+static void -+submit_cl(struct drm_device *dev, uint32_t thread, uint32_t start, uint32_t end) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ /* Stop any existing thread and set state to "stopped at halt" */ -+ V3D_WRITE(V3D_CTNCS(thread), V3D_CTRUN); -+ barrier(); -+ -+ V3D_WRITE(V3D_CTNCA(thread), start); -+ barrier(); -+ -+ /* Set the end address of the control list. Writing this -+ * register is what starts the job. -+ */ -+ V3D_WRITE(V3D_CTNEA(thread), end); -+ barrier(); -+} -+ -+int -+vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, uint64_t timeout_ns, -+ bool interruptible) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ int ret = 0; -+ unsigned long timeout_expire; -+ DEFINE_WAIT(wait); -+ -+ if (vc4->finished_seqno >= seqno) -+ return 0; -+ -+ if (timeout_ns == 0) -+ return -ETIME; -+ -+ timeout_expire = jiffies + nsecs_to_jiffies(timeout_ns); -+ -+ trace_vc4_wait_for_seqno_begin(dev, seqno, timeout_ns); -+ for (;;) { -+ prepare_to_wait(&vc4->job_wait_queue, &wait, -+ interruptible ? TASK_INTERRUPTIBLE : -+ TASK_UNINTERRUPTIBLE); -+ -+ if (interruptible && signal_pending(current)) { -+ ret = -ERESTARTSYS; -+ break; -+ } -+ -+ if (vc4->finished_seqno >= seqno) -+ break; -+ -+ if (timeout_ns != ~0ull) { -+ if (time_after_eq(jiffies, timeout_expire)) { -+ ret = -ETIME; -+ break; -+ } -+ schedule_timeout(timeout_expire - jiffies); -+ } else { -+ schedule(); -+ } -+ } -+ -+ finish_wait(&vc4->job_wait_queue, &wait); -+ trace_vc4_wait_for_seqno_end(dev, seqno); -+ -+ if (ret && ret != -ERESTARTSYS) { -+ DRM_ERROR("timeout waiting for render thread idle\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static void -+vc4_flush_caches(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ /* Flush the GPU L2 caches. These caches sit on top of system -+ * L3 (the 128kb or so shared with the CPU), and are -+ * non-allocating in the L3. -+ */ -+ V3D_WRITE(V3D_L2CACTL, -+ V3D_L2CACTL_L2CCLR); -+ -+ V3D_WRITE(V3D_SLCACTL, -+ VC4_SET_FIELD(0xf, V3D_SLCACTL_T1CC) | -+ VC4_SET_FIELD(0xf, V3D_SLCACTL_T0CC) | -+ VC4_SET_FIELD(0xf, V3D_SLCACTL_UCC) | -+ VC4_SET_FIELD(0xf, V3D_SLCACTL_ICC)); -+} -+ -+/* Sets the registers for the next job to be actually be executed in -+ * the hardware. -+ * -+ * The job_lock should be held during this. -+ */ -+void -+vc4_submit_next_job(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct vc4_exec_info *exec = vc4_first_job(vc4); -+ -+ if (!exec) -+ return; -+ -+ vc4_flush_caches(dev); -+ -+ /* Disable the binner's pre-loaded overflow memory address */ -+ V3D_WRITE(V3D_BPOA, 0); -+ V3D_WRITE(V3D_BPOS, 0); -+ -+ if (exec->ct0ca != exec->ct0ea) -+ submit_cl(dev, 0, exec->ct0ca, exec->ct0ea); -+ submit_cl(dev, 1, exec->ct1ca, exec->ct1ea); -+} -+ -+static void -+vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno) -+{ -+ struct vc4_bo *bo; -+ unsigned i; -+ -+ for (i = 0; i < exec->bo_count; i++) { -+ bo = to_vc4_bo(&exec->bo[i].bo->base); -+ bo->seqno = seqno; -+ } -+ -+ list_for_each_entry(bo, &exec->unref_list, unref_head) { -+ bo->seqno = seqno; -+ } -+} -+ -+/* Queues a struct vc4_exec_info for execution. If no job is -+ * currently executing, then submits it. -+ * -+ * Unlike most GPUs, our hardware only handles one command list at a -+ * time. To queue multiple jobs at once, we'd need to edit the -+ * previous command list to have a jump to the new one at the end, and -+ * then bump the end address. That's a change for a later date, -+ * though. -+ */ -+static void -+vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ uint64_t seqno = ++vc4->emit_seqno; -+ unsigned long irqflags; -+ -+ exec->seqno = seqno; -+ vc4_update_bo_seqnos(exec, seqno); -+ -+ spin_lock_irqsave(&vc4->job_lock, irqflags); -+ list_add_tail(&exec->head, &vc4->job_list); -+ -+ /* If no job was executing, kick ours off. Otherwise, it'll -+ * get started when the previous job's frame done interrupt -+ * occurs. -+ */ -+ if (vc4_first_job(vc4) == exec) { -+ vc4_submit_next_job(dev); -+ vc4_queue_hangcheck(dev); -+ } -+ -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+} -+ -+/** -+ * Looks up a bunch of GEM handles for BOs and stores the array for -+ * use in the command validator that actually writes relocated -+ * addresses pointing to them. -+ */ -+static int -+vc4_cl_lookup_bos(struct drm_device *dev, -+ struct drm_file *file_priv, -+ struct vc4_exec_info *exec) -+{ -+ struct drm_vc4_submit_cl *args = exec->args; -+ uint32_t *handles; -+ int ret = 0; -+ int i; -+ -+ exec->bo_count = args->bo_handle_count; -+ -+ if (!exec->bo_count) { -+ /* See comment on bo_index for why we have to check -+ * this. -+ */ -+ DRM_ERROR("Rendering requires BOs to validate\n"); -+ return -EINVAL; -+ } -+ -+ exec->bo = kcalloc(exec->bo_count, sizeof(struct vc4_bo_exec_state), -+ GFP_KERNEL); -+ if (!exec->bo) { -+ DRM_ERROR("Failed to allocate validated BO pointers\n"); -+ return -ENOMEM; -+ } -+ -+ handles = drm_malloc_ab(exec->bo_count, sizeof(uint32_t)); -+ if (!handles) { -+ DRM_ERROR("Failed to allocate incoming GEM handles\n"); -+ goto fail; -+ } -+ -+ ret = copy_from_user(handles, -+ (void __user *)(uintptr_t)args->bo_handles, -+ exec->bo_count * sizeof(uint32_t)); -+ if (ret) { -+ DRM_ERROR("Failed to copy in GEM handles\n"); -+ goto fail; -+ } -+ -+ spin_lock(&file_priv->table_lock); -+ for (i = 0; i < exec->bo_count; i++) { -+ struct drm_gem_object *bo = idr_find(&file_priv->object_idr, -+ handles[i]); -+ if (!bo) { -+ DRM_ERROR("Failed to look up GEM BO %d: %d\n", -+ i, handles[i]); -+ ret = -EINVAL; -+ spin_unlock(&file_priv->table_lock); -+ goto fail; -+ } -+ drm_gem_object_reference(bo); -+ exec->bo[i].bo = (struct drm_gem_cma_object *)bo; -+ } -+ spin_unlock(&file_priv->table_lock); -+ -+fail: -+ kfree(handles); -+ return 0; -+} -+ -+static int -+vc4_get_bcl(struct drm_device *dev, struct vc4_exec_info *exec) -+{ -+ struct drm_vc4_submit_cl *args = exec->args; -+ void *temp = NULL; -+ void *bin; -+ int ret = 0; -+ uint32_t bin_offset = 0; -+ uint32_t shader_rec_offset = roundup(bin_offset + args->bin_cl_size, -+ 16); -+ uint32_t uniforms_offset = shader_rec_offset + args->shader_rec_size; -+ uint32_t exec_size = uniforms_offset + args->uniforms_size; -+ uint32_t temp_size = exec_size + (sizeof(struct vc4_shader_state) * -+ args->shader_rec_count); -+ struct vc4_bo *bo; -+ -+ if (uniforms_offset < shader_rec_offset || -+ exec_size < uniforms_offset || -+ args->shader_rec_count >= (UINT_MAX / -+ sizeof(struct vc4_shader_state)) || -+ temp_size < exec_size) { -+ DRM_ERROR("overflow in exec arguments\n"); -+ goto fail; -+ } -+ -+ /* Allocate space where we'll store the copied in user command lists -+ * and shader records. -+ * -+ * We don't just copy directly into the BOs because we need to -+ * read the contents back for validation, and I think the -+ * bo->vaddr is uncached access. -+ */ -+ temp = kmalloc(temp_size, GFP_KERNEL); -+ if (!temp) { -+ DRM_ERROR("Failed to allocate storage for copying " -+ "in bin/render CLs.\n"); -+ ret = -ENOMEM; -+ goto fail; -+ } -+ bin = temp + bin_offset; -+ exec->shader_rec_u = temp + shader_rec_offset; -+ exec->uniforms_u = temp + uniforms_offset; -+ exec->shader_state = temp + exec_size; -+ exec->shader_state_size = args->shader_rec_count; -+ -+ ret = copy_from_user(bin, -+ (void __user *)(uintptr_t)args->bin_cl, -+ args->bin_cl_size); -+ if (ret) { -+ DRM_ERROR("Failed to copy in bin cl\n"); -+ goto fail; -+ } -+ -+ ret = copy_from_user(exec->shader_rec_u, -+ (void __user *)(uintptr_t)args->shader_rec, -+ args->shader_rec_size); -+ if (ret) { -+ DRM_ERROR("Failed to copy in shader recs\n"); -+ goto fail; -+ } -+ -+ ret = copy_from_user(exec->uniforms_u, -+ (void __user *)(uintptr_t)args->uniforms, -+ args->uniforms_size); -+ if (ret) { -+ DRM_ERROR("Failed to copy in uniforms cl\n"); -+ goto fail; -+ } -+ -+ bo = vc4_bo_create(dev, exec_size); -+ if (!bo) { -+ DRM_ERROR("Couldn't allocate BO for binning\n"); -+ ret = PTR_ERR(exec->exec_bo); -+ goto fail; -+ } -+ exec->exec_bo = &bo->base; -+ -+ list_add_tail(&to_vc4_bo(&exec->exec_bo->base)->unref_head, -+ &exec->unref_list); -+ -+ exec->ct0ca = exec->exec_bo->paddr + bin_offset; -+ -+ exec->shader_rec_v = exec->exec_bo->vaddr + shader_rec_offset; -+ exec->shader_rec_p = exec->exec_bo->paddr + shader_rec_offset; -+ exec->shader_rec_size = args->shader_rec_size; -+ -+ exec->uniforms_v = exec->exec_bo->vaddr + uniforms_offset; -+ exec->uniforms_p = exec->exec_bo->paddr + uniforms_offset; -+ exec->uniforms_size = args->uniforms_size; -+ -+ ret = vc4_validate_bin_cl(dev, -+ exec->exec_bo->vaddr + bin_offset, -+ bin, -+ exec); -+ if (ret) -+ goto fail; -+ -+ ret = vc4_validate_shader_recs(dev, exec); -+ -+fail: -+ kfree(temp); -+ return ret; -+} -+ -+static void -+vc4_complete_exec(struct vc4_exec_info *exec) -+{ -+ unsigned i; -+ -+ if (exec->bo) { -+ for (i = 0; i < exec->bo_count; i++) -+ drm_gem_object_unreference(&exec->bo[i].bo->base); -+ kfree(exec->bo); -+ } -+ -+ while (!list_empty(&exec->unref_list)) { -+ struct vc4_bo *bo = list_first_entry(&exec->unref_list, -+ struct vc4_bo, unref_head); -+ list_del(&bo->unref_head); -+ drm_gem_object_unreference(&bo->base.base); -+ } -+ -+ kfree(exec); -+} -+ -+void -+vc4_job_handle_completed(struct vc4_dev *vc4) -+{ -+ unsigned long irqflags; -+ struct vc4_seqno_cb *cb, *cb_temp; -+ -+ spin_lock_irqsave(&vc4->job_lock, irqflags); -+ while (!list_empty(&vc4->job_done_list)) { -+ struct vc4_exec_info *exec = -+ list_first_entry(&vc4->job_done_list, -+ struct vc4_exec_info, head); -+ list_del(&exec->head); -+ -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ vc4_complete_exec(exec); -+ spin_lock_irqsave(&vc4->job_lock, irqflags); -+ } -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ -+ list_for_each_entry_safe(cb, cb_temp, &vc4->seqno_cb_list, work.entry) { -+ if (cb->seqno <= vc4->finished_seqno) { -+ list_del_init(&cb->work.entry); -+ schedule_work(&cb->work); -+ } -+ } -+} -+ -+static void vc4_seqno_cb_work(struct work_struct *work) -+{ -+ struct vc4_seqno_cb *cb = container_of(work, struct vc4_seqno_cb, work); -+ cb->func(cb); -+} -+ -+int vc4_queue_seqno_cb(struct drm_device *dev, -+ struct vc4_seqno_cb *cb, uint64_t seqno, -+ void (*func)(struct vc4_seqno_cb *cb)) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ int ret = 0; -+ -+ cb->func = func; -+ INIT_WORK(&cb->work, vc4_seqno_cb_work); -+ -+ mutex_lock(&dev->struct_mutex); -+ if (seqno > vc4->finished_seqno) { -+ cb->seqno = seqno; -+ list_add_tail(&cb->work.entry, &vc4->seqno_cb_list); -+ } else { -+ schedule_work(&cb->work); -+ } -+ mutex_unlock(&dev->struct_mutex); -+ -+ return ret; -+} -+ -+/* Scheduled when any job has been completed, this walks the list of -+ * jobs that had completed and unrefs their BOs and frees their exec -+ * structs. -+ */ -+static void -+vc4_job_done_work(struct work_struct *work) -+{ -+ struct vc4_dev *vc4 = -+ container_of(work, struct vc4_dev, job_done_work); -+ struct drm_device *dev = vc4->dev; -+ -+ /* Need the struct lock for drm_gem_object_unreference(). */ -+ mutex_lock(&dev->struct_mutex); -+ vc4_job_handle_completed(vc4); -+ mutex_unlock(&dev->struct_mutex); -+} -+ -+static int -+vc4_wait_for_seqno_ioctl_helper(struct drm_device *dev, -+ uint64_t seqno, -+ uint64_t *timeout_ns) -+{ -+ unsigned long start = jiffies; -+ int ret = vc4_wait_for_seqno(dev, seqno, *timeout_ns, true); -+ -+ if ((ret == -EINTR || ret == -ERESTARTSYS) && *timeout_ns != ~0ull) { -+ uint64_t delta = jiffies_to_nsecs(jiffies - start); -+ if (*timeout_ns >= delta) -+ *timeout_ns -= delta; -+ } -+ -+ return ret; -+} -+ -+int -+vc4_wait_seqno_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv) -+{ -+ struct drm_vc4_wait_seqno *args = data; -+ -+ return vc4_wait_for_seqno_ioctl_helper(dev, args->seqno, -+ &args->timeout_ns); -+} -+ -+int -+vc4_wait_bo_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv) -+{ -+ int ret; -+ struct drm_vc4_wait_bo *args = data; -+ struct drm_gem_object *gem_obj; -+ struct vc4_bo *bo; -+ -+ gem_obj = drm_gem_object_lookup(dev, file_priv, args->handle); -+ if (!gem_obj) { -+ DRM_ERROR("Failed to look up GEM BO %d\n", args->handle); -+ return -EINVAL; -+ } -+ bo = to_vc4_bo(gem_obj); -+ -+ ret = vc4_wait_for_seqno_ioctl_helper(dev, bo->seqno, &args->timeout_ns); -+ -+ drm_gem_object_unreference(gem_obj); -+ return ret; -+} -+ -+/** -+ * Submits a command list to the VC4. -+ * -+ * This is what is called batchbuffer emitting on other hardware. -+ */ -+int -+vc4_submit_cl_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct drm_vc4_submit_cl *args = data; -+ struct vc4_exec_info *exec; -+ int ret; -+ -+ if ((args->flags & ~VC4_SUBMIT_CL_USE_CLEAR_COLOR) != 0) { -+ DRM_ERROR("Unknown flags: 0x%02x\n", args->flags); -+ return -EINVAL; -+ } -+ -+ exec = kcalloc(1, sizeof(*exec), GFP_KERNEL); -+ if (!exec) { -+ DRM_ERROR("malloc failure on exec struct\n"); -+ return -ENOMEM; -+ } -+ -+ exec->args = args; -+ INIT_LIST_HEAD(&exec->unref_list); -+ -+ mutex_lock(&dev->struct_mutex); -+ -+ ret = vc4_cl_lookup_bos(dev, file_priv, exec); -+ if (ret) -+ goto fail; -+ -+ if (exec->args->bin_cl_size != 0) { -+ ret = vc4_get_bcl(dev, exec); -+ if (ret) -+ goto fail; -+ } else { -+ exec->ct0ca = exec->ct0ea = 0; -+ } -+ -+ ret = vc4_get_rcl(dev, exec); -+ if (ret) -+ goto fail; -+ -+ /* Clear this out of the struct we'll be putting in the queue, -+ * since it's part of our stack. -+ */ -+ exec->args = NULL; -+ -+ vc4_queue_submit(dev, exec); -+ -+ /* Return the seqno for our job. */ -+ args->seqno = vc4->emit_seqno; -+ -+ mutex_unlock(&dev->struct_mutex); -+ -+ return 0; -+ -+fail: -+ vc4_complete_exec(exec); -+ -+ mutex_unlock(&dev->struct_mutex); -+ -+ return ret; -+} -+ -+void -+vc4_gem_init(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ INIT_LIST_HEAD(&vc4->job_list); -+ INIT_LIST_HEAD(&vc4->job_done_list); -+ INIT_LIST_HEAD(&vc4->seqno_cb_list); -+ spin_lock_init(&vc4->job_lock); -+ -+ INIT_WORK(&vc4->hangcheck.reset_work, vc4_reset_work); -+ setup_timer(&vc4->hangcheck.timer, -+ vc4_hangcheck_elapsed, -+ (unsigned long) dev); -+ -+ INIT_WORK(&vc4->job_done_work, vc4_job_done_work); -+} -+ -+void -+vc4_gem_destroy(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ /* Waiting for exec to finish would need to be done before -+ * unregistering V3D. -+ */ -+ WARN_ON(vc4->emit_seqno != vc4->finished_seqno); -+ -+ /* V3D should already have disabled its interrupt and cleared -+ * the overflow allocation registers. Now free the object. -+ */ -+ if (vc4->overflow_mem) { -+ drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base); -+ vc4->overflow_mem = NULL; -+ } -+ -+ vc4_bo_cache_destroy(dev); -+} ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_irq.c -@@ -0,0 +1,211 @@ -+/* -+ * Copyright © 2014 Broadcom -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ */ -+ -+/** DOC: Interrupt management for the V3D engine. -+ * -+ * We have an interrupt status register (V3D_INTCTL) which reports -+ * interrupts, and where writing 1 bits clears those interrupts. -+ * There are also a pair of interrupt registers -+ * (V3D_INTENA/V3D_INTDIS) where writing a 1 to their bits enables or -+ * disables that specific interrupt, and 0s written are ignored -+ * (reading either one returns the set of enabled interrupts). -+ * -+ * When we take a render frame interrupt, we need to wake the -+ * processes waiting for some frame to be done, and get the next frame -+ * submitted ASAP (so the hardware doesn't sit idle when there's work -+ * to do). -+ * -+ * When we take the binner out of memory interrupt, we need to -+ * allocate some new memory and pass it to the binner so that the -+ * current job can make progress. -+ */ -+ -+#include "vc4_drv.h" -+#include "vc4_regs.h" -+ -+#define V3D_DRIVER_IRQS (V3D_INT_OUTOMEM | \ -+ V3D_INT_FRDONE) -+ -+DECLARE_WAIT_QUEUE_HEAD(render_wait); -+ -+static void -+vc4_overflow_mem_work(struct work_struct *work) -+{ -+ struct vc4_dev *vc4 = -+ container_of(work, struct vc4_dev, overflow_mem_work); -+ struct drm_device *dev = vc4->dev; -+ struct vc4_bo *bo; -+ -+ bo = vc4_bo_create(dev, 256 * 1024); -+ if (!bo) { -+ DRM_ERROR("Couldn't allocate binner overflow mem\n"); -+ return; -+ } -+ -+ /* If there's a job executing currently, then our previous -+ * overflow allocation is getting used in that job and we need -+ * to queue it to be released when the job is done. But if no -+ * job is executing at all, then we can free the old overflow -+ * object direcctly. -+ * -+ * No lock necessary for this pointer since we're the only -+ * ones that update the pointer, and our workqueue won't -+ * reenter. -+ */ -+ if (vc4->overflow_mem) { -+ struct vc4_exec_info *current_exec; -+ unsigned long irqflags; -+ -+ spin_lock_irqsave(&vc4->job_lock, irqflags); -+ current_exec = vc4_first_job(vc4); -+ if (current_exec) { -+ vc4->overflow_mem->seqno = vc4->finished_seqno + 1; -+ list_add_tail(&vc4->overflow_mem->unref_head, -+ ¤t_exec->unref_list); -+ vc4->overflow_mem = NULL; -+ } -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ } -+ -+ if (vc4->overflow_mem) { -+ drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base); -+ } -+ vc4->overflow_mem = bo; -+ -+ V3D_WRITE(V3D_BPOA, bo->base.paddr); -+ V3D_WRITE(V3D_BPOS, bo->base.base.size); -+ V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM); -+ V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM); -+} -+ -+static void -+vc4_irq_finish_job(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct vc4_exec_info *exec = vc4_first_job(vc4); -+ -+ if (!exec) -+ return; -+ -+ vc4->finished_seqno++; -+ list_move_tail(&exec->head, &vc4->job_done_list); -+ vc4_submit_next_job(dev); -+ -+ wake_up_all(&vc4->job_wait_queue); -+ schedule_work(&vc4->job_done_work); -+} -+ -+irqreturn_t -+vc4_irq(int irq, void *arg) -+{ -+ struct drm_device *dev = arg; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ uint32_t intctl; -+ irqreturn_t status = IRQ_NONE; -+ -+ barrier(); -+ intctl = V3D_READ(V3D_INTCTL); -+ -+ /* Acknowledge the interrupts we're handling here. The render -+ * frame done interrupt will be cleared, while OUTOMEM will -+ * stay high until the underlying cause is cleared. -+ */ -+ V3D_WRITE(V3D_INTCTL, intctl); -+ -+ if (intctl & V3D_INT_OUTOMEM) { -+ /* Disable OUTOMEM until the work is done. */ -+ V3D_WRITE(V3D_INTDIS, V3D_INT_OUTOMEM); -+ schedule_work(&vc4->overflow_mem_work); -+ status = IRQ_HANDLED; -+ } -+ -+ if (intctl & V3D_INT_FRDONE) { -+ spin_lock(&vc4->job_lock); -+ vc4_irq_finish_job(dev); -+ spin_unlock(&vc4->job_lock); -+ status = IRQ_HANDLED; -+ } -+ -+ return status; -+} -+ -+void -+vc4_irq_preinstall(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ init_waitqueue_head(&vc4->job_wait_queue); -+ INIT_WORK(&vc4->overflow_mem_work, vc4_overflow_mem_work); -+ -+ /* Clear any pending interrupts someone might have left around -+ * for us. -+ */ -+ V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); -+} -+ -+int -+vc4_irq_postinstall(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ /* Enable both the render done and out of memory interrupts. */ -+ V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); -+ -+ return 0; -+} -+ -+void -+vc4_irq_uninstall(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ /* Disable sending interrupts for our driver's IRQs. */ -+ V3D_WRITE(V3D_INTDIS, V3D_DRIVER_IRQS); -+ -+ /* Clear any pending interrupts we might have left. */ -+ V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); -+ -+ cancel_work_sync(&vc4->overflow_mem_work); -+} -+ -+/** Reinitializes interrupt registers when a GPU reset is performed. */ -+void vc4_irq_reset(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ unsigned long irqflags; -+ -+ /* Acknowledge any stale IRQs. */ -+ V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); -+ -+ /* -+ * Turn all our interrupts on. Binner out of memory is the -+ * only one we expect to trigger at this point, since we've -+ * just come from poweron and haven't supplied any overflow -+ * memory yet. -+ */ -+ V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); -+ -+ spin_lock_irqsave(&vc4->job_lock, irqflags); -+ vc4_irq_finish_job(dev); -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+} ---- a/drivers/gpu/drm/vc4/vc4_kms.c -+++ b/drivers/gpu/drm/vc4/vc4_kms.c -@@ -15,6 +15,7 @@ - */ - - #include "drm_crtc.h" -+#include "drm_atomic.h" - #include "drm_atomic_helper.h" - #include "drm_crtc_helper.h" - #include "drm_plane_helper.h" -@@ -29,10 +30,151 @@ static void vc4_output_poll_changed(stru - drm_fbdev_cma_hotplug_event(vc4->fbdev); - } - -+struct vc4_commit { -+ struct drm_device *dev; -+ struct drm_atomic_state *state; -+ struct vc4_seqno_cb cb; -+}; -+ -+static void -+vc4_atomic_complete_commit(struct vc4_commit *c) -+{ -+ struct drm_atomic_state *state = c->state; -+ struct drm_device *dev = state->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ drm_atomic_helper_commit_modeset_disables(dev, state); -+ -+ drm_atomic_helper_commit_planes(dev, state); -+ -+ drm_atomic_helper_commit_modeset_enables(dev, state); -+ -+ drm_atomic_helper_wait_for_vblanks(dev, state); -+ -+ drm_atomic_helper_cleanup_planes(dev, state); -+ -+ drm_atomic_state_free(state); -+ -+ up(&vc4->async_modeset); -+ -+ kfree(c); -+} -+ -+static void -+vc4_atomic_complete_commit_seqno_cb(struct vc4_seqno_cb *cb) -+{ -+ struct vc4_commit *c = container_of(cb, struct vc4_commit, cb); -+ -+ vc4_atomic_complete_commit(c); -+} -+ -+static struct vc4_commit *commit_init(struct drm_atomic_state *state) -+{ -+ struct vc4_commit *c = kzalloc(sizeof(*c), GFP_KERNEL); -+ -+ if (!c) -+ return NULL; -+ c->dev = state->dev; -+ c->state = state; -+ -+ return c; -+} -+ -+/** -+ * vc4_atomic_commit - commit validated state object -+ * @dev: DRM device -+ * @state: the driver state object -+ * @async: asynchronous commit -+ * -+ * This function commits a with drm_atomic_helper_check() pre-validated state -+ * object. This can still fail when e.g. the framebuffer reservation fails. For -+ * now this doesn't implement asynchronous commits. -+ * -+ * RETURNS -+ * Zero for success or -errno. -+ */ -+static int vc4_atomic_commit(struct drm_device *dev, -+ struct drm_atomic_state *state, -+ bool async) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ int ret; -+ int i; -+ uint64_t wait_seqno = 0; -+ struct vc4_commit *c; -+ -+ c = commit_init(state); -+ if (!c) -+ return -ENOMEM; -+ -+ /* Make sure that any outstanding modesets have finished. */ -+ ret = down_interruptible(&vc4->async_modeset); -+ if (ret) { -+ kfree(c); -+ return ret; -+ } -+ -+ ret = drm_atomic_helper_prepare_planes(dev, state); -+ if (ret) { -+ kfree(c); -+ up(&vc4->async_modeset); -+ return ret; -+ } -+ -+ for (i = 0; i < dev->mode_config.num_total_plane; i++) { -+ struct drm_plane *plane = state->planes[i]; -+ struct drm_plane_state *new_state = state->plane_states[i]; -+ -+ if (!plane) -+ continue; -+ -+ if ((plane->state->fb != new_state->fb) && new_state->fb) { -+ struct drm_gem_cma_object *cma_bo = -+ drm_fb_cma_get_gem_obj(new_state->fb, 0); -+ struct vc4_bo *bo = to_vc4_bo(&cma_bo->base); -+ wait_seqno = max(bo->seqno, wait_seqno); -+ } -+ } -+ -+ /* -+ * This is the point of no return - everything below never fails except -+ * when the hw goes bonghits. Which means we can commit the new state on -+ * the software side now. -+ */ -+ -+ drm_atomic_helper_swap_state(dev, state); -+ -+ /* -+ * Everything below can be run asynchronously without the need to grab -+ * any modeset locks at all under one condition: It must be guaranteed -+ * that the asynchronous work has either been cancelled (if the driver -+ * supports it, which at least requires that the framebuffers get -+ * cleaned up with drm_atomic_helper_cleanup_planes()) or completed -+ * before the new state gets committed on the software side with -+ * drm_atomic_helper_swap_state(). -+ * -+ * This scheme allows new atomic state updates to be prepared and -+ * checked in parallel to the asynchronous completion of the previous -+ * update. Which is important since compositors need to figure out the -+ * composition of the next frame right after having submitted the -+ * current layout. -+ */ -+ -+ if (async) { -+ vc4_queue_seqno_cb(dev, &c->cb, wait_seqno, -+ vc4_atomic_complete_commit_seqno_cb); -+ } else { -+ vc4_wait_for_seqno(dev, wait_seqno, ~0ull, false); -+ vc4_atomic_complete_commit(c); -+ } -+ -+ return 0; -+} -+ - static const struct drm_mode_config_funcs vc4_mode_funcs = { - .output_poll_changed = vc4_output_poll_changed, - .atomic_check = drm_atomic_helper_check, -- .atomic_commit = drm_atomic_helper_commit, -+ .atomic_commit = vc4_atomic_commit, - .fb_create = drm_fb_cma_create, - }; - -@@ -41,6 +183,8 @@ int vc4_kms_load(struct drm_device *dev) - struct vc4_dev *vc4 = to_vc4_dev(dev); - int ret; - -+ sema_init(&vc4->async_modeset, 1); -+ - ret = drm_vblank_init(dev, dev->mode_config.num_crtc); - if (ret < 0) { - dev_err(dev->dev, "failed to initialize vblank\n"); -@@ -51,6 +195,8 @@ int vc4_kms_load(struct drm_device *dev) - dev->mode_config.max_height = 2048; - dev->mode_config.funcs = &vc4_mode_funcs; - dev->mode_config.preferred_depth = 24; -+ dev->mode_config.async_page_flip = true; -+ - dev->vblank_disable_allowed = true; - - drm_mode_config_reset(dev); ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_packet.h -@@ -0,0 +1,384 @@ -+/* -+ * Copyright © 2014 Broadcom -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ */ -+ -+#ifndef VC4_PACKET_H -+#define VC4_PACKET_H -+ -+#include "vc4_regs.h" /* for VC4_MASK, VC4_GET_FIELD, VC4_SET_FIELD */ -+ -+enum vc4_packet { -+ VC4_PACKET_HALT = 0, -+ VC4_PACKET_NOP = 1, -+ -+ VC4_PACKET_FLUSH = 4, -+ VC4_PACKET_FLUSH_ALL = 5, -+ VC4_PACKET_START_TILE_BINNING = 6, -+ VC4_PACKET_INCREMENT_SEMAPHORE = 7, -+ VC4_PACKET_WAIT_ON_SEMAPHORE = 8, -+ -+ VC4_PACKET_BRANCH = 16, -+ VC4_PACKET_BRANCH_TO_SUB_LIST = 17, -+ -+ VC4_PACKET_STORE_MS_TILE_BUFFER = 24, -+ VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF = 25, -+ VC4_PACKET_STORE_FULL_RES_TILE_BUFFER = 26, -+ VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER = 27, -+ VC4_PACKET_STORE_TILE_BUFFER_GENERAL = 28, -+ VC4_PACKET_LOAD_TILE_BUFFER_GENERAL = 29, -+ -+ VC4_PACKET_GL_INDEXED_PRIMITIVE = 32, -+ VC4_PACKET_GL_ARRAY_PRIMITIVE = 33, -+ -+ VC4_PACKET_COMPRESSED_PRIMITIVE = 48, -+ VC4_PACKET_CLIPPED_COMPRESSED_PRIMITIVE = 49, -+ -+ VC4_PACKET_PRIMITIVE_LIST_FORMAT = 56, -+ -+ VC4_PACKET_GL_SHADER_STATE = 64, -+ VC4_PACKET_NV_SHADER_STATE = 65, -+ VC4_PACKET_VG_SHADER_STATE = 66, -+ -+ VC4_PACKET_CONFIGURATION_BITS = 96, -+ VC4_PACKET_FLAT_SHADE_FLAGS = 97, -+ VC4_PACKET_POINT_SIZE = 98, -+ VC4_PACKET_LINE_WIDTH = 99, -+ VC4_PACKET_RHT_X_BOUNDARY = 100, -+ VC4_PACKET_DEPTH_OFFSET = 101, -+ VC4_PACKET_CLIP_WINDOW = 102, -+ VC4_PACKET_VIEWPORT_OFFSET = 103, -+ VC4_PACKET_Z_CLIPPING = 104, -+ VC4_PACKET_CLIPPER_XY_SCALING = 105, -+ VC4_PACKET_CLIPPER_Z_SCALING = 106, -+ -+ VC4_PACKET_TILE_BINNING_MODE_CONFIG = 112, -+ VC4_PACKET_TILE_RENDERING_MODE_CONFIG = 113, -+ VC4_PACKET_CLEAR_COLORS = 114, -+ VC4_PACKET_TILE_COORDINATES = 115, -+ -+ /* Not an actual hardware packet -- this is what we use to put -+ * references to GEM bos in the command stream, since we need the u32 -+ * int the actual address packet in order to store the offset from the -+ * start of the BO. -+ */ -+ VC4_PACKET_GEM_HANDLES = 254, -+} __attribute__ ((__packed__)); -+ -+#define VC4_PACKET_HALT_SIZE 1 -+#define VC4_PACKET_NOP_SIZE 1 -+#define VC4_PACKET_FLUSH_SIZE 1 -+#define VC4_PACKET_FLUSH_ALL_SIZE 1 -+#define VC4_PACKET_START_TILE_BINNING_SIZE 1 -+#define VC4_PACKET_INCREMENT_SEMAPHORE_SIZE 1 -+#define VC4_PACKET_WAIT_ON_SEMAPHORE_SIZE 1 -+#define VC4_PACKET_BRANCH_SIZE 5 -+#define VC4_PACKET_BRANCH_TO_SUB_LIST_SIZE 5 -+#define VC4_PACKET_STORE_MS_TILE_BUFFER_SIZE 1 -+#define VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF_SIZE 1 -+#define VC4_PACKET_STORE_FULL_RES_TILE_BUFFER_SIZE 5 -+#define VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER_SIZE 5 -+#define VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE 7 -+#define VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE 7 -+#define VC4_PACKET_GL_INDEXED_PRIMITIVE_SIZE 14 -+#define VC4_PACKET_GL_ARRAY_PRIMITIVE_SIZE 10 -+#define VC4_PACKET_COMPRESSED_PRIMITIVE_SIZE 1 -+#define VC4_PACKET_CLIPPED_COMPRESSED_PRIMITIVE_SIZE 1 -+#define VC4_PACKET_PRIMITIVE_LIST_FORMAT_SIZE 2 -+#define VC4_PACKET_GL_SHADER_STATE_SIZE 5 -+#define VC4_PACKET_NV_SHADER_STATE_SIZE 5 -+#define VC4_PACKET_VG_SHADER_STATE_SIZE 5 -+#define VC4_PACKET_CONFIGURATION_BITS_SIZE 4 -+#define VC4_PACKET_FLAT_SHADE_FLAGS_SIZE 5 -+#define VC4_PACKET_POINT_SIZE_SIZE 5 -+#define VC4_PACKET_LINE_WIDTH_SIZE 5 -+#define VC4_PACKET_RHT_X_BOUNDARY_SIZE 3 -+#define VC4_PACKET_DEPTH_OFFSET_SIZE 5 -+#define VC4_PACKET_CLIP_WINDOW_SIZE 9 -+#define VC4_PACKET_VIEWPORT_OFFSET_SIZE 5 -+#define VC4_PACKET_Z_CLIPPING_SIZE 9 -+#define VC4_PACKET_CLIPPER_XY_SCALING_SIZE 9 -+#define VC4_PACKET_CLIPPER_Z_SCALING_SIZE 9 -+#define VC4_PACKET_TILE_BINNING_MODE_CONFIG_SIZE 16 -+#define VC4_PACKET_TILE_RENDERING_MODE_CONFIG_SIZE 11 -+#define VC4_PACKET_CLEAR_COLORS_SIZE 14 -+#define VC4_PACKET_TILE_COORDINATES_SIZE 3 -+#define VC4_PACKET_GEM_HANDLES_SIZE 9 -+ -+/** @{ -+ * Bits used by packets like VC4_PACKET_STORE_TILE_BUFFER_GENERAL and -+ * VC4_PACKET_TILE_RENDERING_MODE_CONFIG. -+*/ -+#define VC4_TILING_FORMAT_LINEAR 0 -+#define VC4_TILING_FORMAT_T 1 -+#define VC4_TILING_FORMAT_LT 2 -+/** @} */ -+ -+/** @{ -+ * -+ * low bits of VC4_PACKET_STORE_FULL_RES_TILE_BUFFER and -+ * VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER. -+ */ -+#define VC4_LOADSTORE_FULL_RES_EOF (1 << 3) -+#define VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL (1 << 2) -+#define VC4_LOADSTORE_FULL_RES_DISABLE_ZS (1 << 1) -+#define VC4_LOADSTORE_FULL_RES_DISABLE_COLOR (1 << 0) -+ -+/** @{ -+ * -+ * byte 2 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and -+ * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL (low bits of the address) -+ */ -+ -+#define VC4_LOADSTORE_TILE_BUFFER_EOF (1 << 3) -+#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_VG_MASK (1 << 2) -+#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_ZS (1 << 1) -+#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_COLOR (1 << 0) -+ -+/** @} */ -+ -+/** @{ -+ * -+ * byte 0-1 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and -+ * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL -+ */ -+#define VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR (1 << 15) -+#define VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR (1 << 14) -+#define VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR (1 << 13) -+#define VC4_STORE_TILE_BUFFER_DISABLE_SWAP (1 << 12) -+ -+#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK VC4_MASK(9, 8) -+#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_SHIFT 8 -+#define VC4_LOADSTORE_TILE_BUFFER_RGBA8888 0 -+#define VC4_LOADSTORE_TILE_BUFFER_BGR565_DITHER 1 -+#define VC4_LOADSTORE_TILE_BUFFER_BGR565 2 -+/** @} */ -+ -+/** @{ -+ * -+ * byte 0 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and -+ * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL -+ */ -+#define VC4_STORE_TILE_BUFFER_MODE_MASK VC4_MASK(7, 6) -+#define VC4_STORE_TILE_BUFFER_MODE_SHIFT 6 -+#define VC4_STORE_TILE_BUFFER_MODE_SAMPLE0 (0 << 6) -+#define VC4_STORE_TILE_BUFFER_MODE_DECIMATE_X4 (1 << 6) -+#define VC4_STORE_TILE_BUFFER_MODE_DECIMATE_X16 (2 << 6) -+ -+/** The values of the field are VC4_TILING_FORMAT_* */ -+#define VC4_LOADSTORE_TILE_BUFFER_TILING_MASK VC4_MASK(5, 4) -+#define VC4_LOADSTORE_TILE_BUFFER_TILING_SHIFT 4 -+ -+#define VC4_LOADSTORE_TILE_BUFFER_BUFFER_MASK VC4_MASK(2, 0) -+#define VC4_LOADSTORE_TILE_BUFFER_BUFFER_SHIFT 0 -+#define VC4_LOADSTORE_TILE_BUFFER_NONE 0 -+#define VC4_LOADSTORE_TILE_BUFFER_COLOR 1 -+#define VC4_LOADSTORE_TILE_BUFFER_ZS 2 -+#define VC4_LOADSTORE_TILE_BUFFER_Z 3 -+#define VC4_LOADSTORE_TILE_BUFFER_VG_MASK 4 -+#define VC4_LOADSTORE_TILE_BUFFER_FULL 5 -+/** @} */ -+ -+#define VC4_INDEX_BUFFER_U8 (0 << 4) -+#define VC4_INDEX_BUFFER_U16 (1 << 4) -+ -+/* This flag is only present in NV shader state. */ -+#define VC4_SHADER_FLAG_SHADED_CLIP_COORDS (1 << 3) -+#define VC4_SHADER_FLAG_ENABLE_CLIPPING (1 << 2) -+#define VC4_SHADER_FLAG_VS_POINT_SIZE (1 << 1) -+#define VC4_SHADER_FLAG_FS_SINGLE_THREAD (1 << 0) -+ -+/** @{ byte 2 of config bits. */ -+#define VC4_CONFIG_BITS_EARLY_Z_UPDATE (1 << 1) -+#define VC4_CONFIG_BITS_EARLY_Z (1 << 0) -+/** @} */ -+ -+/** @{ byte 1 of config bits. */ -+#define VC4_CONFIG_BITS_Z_UPDATE (1 << 7) -+/** same values in this 3-bit field as PIPE_FUNC_* */ -+#define VC4_CONFIG_BITS_DEPTH_FUNC_SHIFT 4 -+#define VC4_CONFIG_BITS_COVERAGE_READ_LEAVE (1 << 3) -+ -+#define VC4_CONFIG_BITS_COVERAGE_UPDATE_NONZERO (0 << 1) -+#define VC4_CONFIG_BITS_COVERAGE_UPDATE_ODD (1 << 1) -+#define VC4_CONFIG_BITS_COVERAGE_UPDATE_OR (2 << 1) -+#define VC4_CONFIG_BITS_COVERAGE_UPDATE_ZERO (3 << 1) -+ -+#define VC4_CONFIG_BITS_COVERAGE_PIPE_SELECT (1 << 0) -+/** @} */ -+ -+/** @{ byte 0 of config bits. */ -+#define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_NONE (0 << 6) -+#define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_4X (1 << 6) -+#define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_16X (2 << 6) -+ -+#define VC4_CONFIG_BITS_AA_POINTS_AND_LINES (1 << 4) -+#define VC4_CONFIG_BITS_ENABLE_DEPTH_OFFSET (1 << 3) -+#define VC4_CONFIG_BITS_CW_PRIMITIVES (1 << 2) -+#define VC4_CONFIG_BITS_ENABLE_PRIM_BACK (1 << 1) -+#define VC4_CONFIG_BITS_ENABLE_PRIM_FRONT (1 << 0) -+/** @} */ -+ -+/** @{ bits in the last u8 of VC4_PACKET_TILE_BINNING_MODE_CONFIG */ -+#define VC4_BIN_CONFIG_DB_NON_MS (1 << 7) -+ -+#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_MASK VC4_MASK(6, 5) -+#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_SHIFT 5 -+#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_32 0 -+#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_64 1 -+#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128 2 -+#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_256 3 -+ -+#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK VC4_MASK(4, 3) -+#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_SHIFT 3 -+#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_32 0 -+#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_64 1 -+#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_128 2 -+#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_256 3 -+ -+#define VC4_BIN_CONFIG_AUTO_INIT_TSDA (1 << 2) -+#define VC4_BIN_CONFIG_TILE_BUFFER_64BIT (1 << 1) -+#define VC4_BIN_CONFIG_MS_MODE_4X (1 << 0) -+/** @} */ -+ -+/** @{ bits in the last u16 of VC4_PACKET_TILE_RENDERING_MODE_CONFIG */ -+#define VC4_RENDER_CONFIG_DB_NON_MS (1 << 12) -+#define VC4_RENDER_CONFIG_EARLY_Z_COVERAGE_DISABLE (1 << 11) -+#define VC4_RENDER_CONFIG_EARLY_Z_DIRECTION_G (1 << 10) -+#define VC4_RENDER_CONFIG_COVERAGE_MODE (1 << 9) -+#define VC4_RENDER_CONFIG_ENABLE_VG_MASK (1 << 8) -+ -+/** The values of the field are VC4_TILING_FORMAT_* */ -+#define VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK VC4_MASK(7, 6) -+#define VC4_RENDER_CONFIG_MEMORY_FORMAT_SHIFT 6 -+ -+#define VC4_RENDER_CONFIG_DECIMATE_MODE_1X (0 << 4) -+#define VC4_RENDER_CONFIG_DECIMATE_MODE_4X (1 << 4) -+#define VC4_RENDER_CONFIG_DECIMATE_MODE_16X (2 << 4) -+ -+#define VC4_RENDER_CONFIG_FORMAT_MASK VC4_MASK(3, 2) -+#define VC4_RENDER_CONFIG_FORMAT_SHIFT 2 -+#define VC4_RENDER_CONFIG_FORMAT_BGR565_DITHERED 0 -+#define VC4_RENDER_CONFIG_FORMAT_RGBA8888 1 -+#define VC4_RENDER_CONFIG_FORMAT_BGR565 2 -+ -+#define VC4_RENDER_CONFIG_TILE_BUFFER_64BIT (1 << 1) -+#define VC4_RENDER_CONFIG_MS_MODE_4X (1 << 0) -+ -+#define VC4_PRIMITIVE_LIST_FORMAT_16_INDEX (1 << 4) -+#define VC4_PRIMITIVE_LIST_FORMAT_32_XY (3 << 4) -+#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_POINTS (0 << 0) -+#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_LINES (1 << 0) -+#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_TRIANGLES (2 << 0) -+#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_RHT (3 << 0) -+ -+enum vc4_texture_data_type { -+ VC4_TEXTURE_TYPE_RGBA8888 = 0, -+ VC4_TEXTURE_TYPE_RGBX8888 = 1, -+ VC4_TEXTURE_TYPE_RGBA4444 = 2, -+ VC4_TEXTURE_TYPE_RGBA5551 = 3, -+ VC4_TEXTURE_TYPE_RGB565 = 4, -+ VC4_TEXTURE_TYPE_LUMINANCE = 5, -+ VC4_TEXTURE_TYPE_ALPHA = 6, -+ VC4_TEXTURE_TYPE_LUMALPHA = 7, -+ VC4_TEXTURE_TYPE_ETC1 = 8, -+ VC4_TEXTURE_TYPE_S16F = 9, -+ VC4_TEXTURE_TYPE_S8 = 10, -+ VC4_TEXTURE_TYPE_S16 = 11, -+ VC4_TEXTURE_TYPE_BW1 = 12, -+ VC4_TEXTURE_TYPE_A4 = 13, -+ VC4_TEXTURE_TYPE_A1 = 14, -+ VC4_TEXTURE_TYPE_RGBA64 = 15, -+ VC4_TEXTURE_TYPE_RGBA32R = 16, -+ VC4_TEXTURE_TYPE_YUV422R = 17, -+}; -+ -+#define VC4_TEX_P0_OFFSET_MASK VC4_MASK(31, 12) -+#define VC4_TEX_P0_OFFSET_SHIFT 12 -+#define VC4_TEX_P0_CSWIZ_MASK VC4_MASK(11, 10) -+#define VC4_TEX_P0_CSWIZ_SHIFT 10 -+#define VC4_TEX_P0_CMMODE_MASK VC4_MASK(9, 9) -+#define VC4_TEX_P0_CMMODE_SHIFT 9 -+#define VC4_TEX_P0_FLIPY_MASK VC4_MASK(8, 8) -+#define VC4_TEX_P0_FLIPY_SHIFT 8 -+#define VC4_TEX_P0_TYPE_MASK VC4_MASK(7, 4) -+#define VC4_TEX_P0_TYPE_SHIFT 4 -+#define VC4_TEX_P0_MIPLVLS_MASK VC4_MASK(3, 0) -+#define VC4_TEX_P0_MIPLVLS_SHIFT 0 -+ -+#define VC4_TEX_P1_TYPE4_MASK VC4_MASK(31, 31) -+#define VC4_TEX_P1_TYPE4_SHIFT 31 -+#define VC4_TEX_P1_HEIGHT_MASK VC4_MASK(30, 20) -+#define VC4_TEX_P1_HEIGHT_SHIFT 20 -+#define VC4_TEX_P1_ETCFLIP_MASK VC4_MASK(19, 19) -+#define VC4_TEX_P1_ETCFLIP_SHIFT 19 -+#define VC4_TEX_P1_WIDTH_MASK VC4_MASK(18, 8) -+#define VC4_TEX_P1_WIDTH_SHIFT 8 -+ -+#define VC4_TEX_P1_MAGFILT_MASK VC4_MASK(7, 7) -+#define VC4_TEX_P1_MAGFILT_SHIFT 7 -+# define VC4_TEX_P1_MAGFILT_LINEAR 0 -+# define VC4_TEX_P1_MAGFILT_NEAREST 1 -+ -+#define VC4_TEX_P1_MINFILT_MASK VC4_MASK(6, 4) -+#define VC4_TEX_P1_MINFILT_SHIFT 4 -+# define VC4_TEX_P1_MINFILT_LINEAR 0 -+# define VC4_TEX_P1_MINFILT_NEAREST 1 -+# define VC4_TEX_P1_MINFILT_NEAR_MIP_NEAR 2 -+# define VC4_TEX_P1_MINFILT_NEAR_MIP_LIN 3 -+# define VC4_TEX_P1_MINFILT_LIN_MIP_NEAR 4 -+# define VC4_TEX_P1_MINFILT_LIN_MIP_LIN 5 -+ -+#define VC4_TEX_P1_WRAP_T_MASK VC4_MASK(3, 2) -+#define VC4_TEX_P1_WRAP_T_SHIFT 2 -+#define VC4_TEX_P1_WRAP_S_MASK VC4_MASK(1, 0) -+#define VC4_TEX_P1_WRAP_S_SHIFT 0 -+# define VC4_TEX_P1_WRAP_REPEAT 0 -+# define VC4_TEX_P1_WRAP_CLAMP 1 -+# define VC4_TEX_P1_WRAP_MIRROR 2 -+# define VC4_TEX_P1_WRAP_BORDER 3 -+ -+#define VC4_TEX_P2_PTYPE_MASK VC4_MASK(31, 30) -+#define VC4_TEX_P2_PTYPE_SHIFT 30 -+# define VC4_TEX_P2_PTYPE_IGNORED 0 -+# define VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE 1 -+# define VC4_TEX_P2_PTYPE_CHILD_IMAGE_DIMENSIONS 2 -+# define VC4_TEX_P2_PTYPE_CHILD_IMAGE_OFFSETS 3 -+ -+/* VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE bits */ -+#define VC4_TEX_P2_CMST_MASK VC4_MASK(29, 12) -+#define VC4_TEX_P2_CMST_SHIFT 12 -+#define VC4_TEX_P2_BSLOD_MASK VC4_MASK(0, 0) -+#define VC4_TEX_P2_BSLOD_SHIFT 0 -+ -+/* VC4_TEX_P2_PTYPE_CHILD_IMAGE_DIMENSIONS */ -+#define VC4_TEX_P2_CHEIGHT_MASK VC4_MASK(22, 12) -+#define VC4_TEX_P2_CHEIGHT_SHIFT 12 -+#define VC4_TEX_P2_CWIDTH_MASK VC4_MASK(10, 0) -+#define VC4_TEX_P2_CWIDTH_SHIFT 0 -+ -+/* VC4_TEX_P2_PTYPE_CHILD_IMAGE_OFFSETS */ -+#define VC4_TEX_P2_CYOFF_MASK VC4_MASK(22, 12) -+#define VC4_TEX_P2_CYOFF_SHIFT 12 -+#define VC4_TEX_P2_CXOFF_MASK VC4_MASK(10, 0) -+#define VC4_TEX_P2_CXOFF_SHIFT 0 -+ -+#endif /* VC4_PACKET_H */ ---- a/drivers/gpu/drm/vc4/vc4_plane.c -+++ b/drivers/gpu/drm/vc4/vc4_plane.c -@@ -29,6 +29,14 @@ struct vc4_plane_state { - u32 *dlist; - u32 dlist_size; /* Number of dwords in allocated for the display list */ - u32 dlist_count; /* Number of used dwords in the display list. */ -+ -+ /* Offset in the dlist to pointer word 0. */ -+ u32 pw0_offset; -+ -+ /* Offset where the plane's dlist was last stored in the -+ hardware at vc4_crtc_atomic_flush() time. -+ */ -+ u32 *hw_dlist; - }; - - static inline struct vc4_plane_state * -@@ -207,6 +215,8 @@ static int vc4_plane_mode_set(struct drm - /* Position Word 3: Context. Written by the HVS. */ - vc4_dlist_write(vc4_state, 0xc0c0c0c0); - -+ vc4_state->pw0_offset = vc4_state->dlist_count; -+ - /* Pointer Word 0: RGB / Y Pointer */ - vc4_dlist_write(vc4_state, bo->paddr + offset); - -@@ -258,6 +268,8 @@ u32 vc4_plane_write_dlist(struct drm_pla - struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); - int i; - -+ vc4_state->hw_dlist = dlist; -+ - /* Can't memcpy_toio() because it needs to be 32-bit writes. */ - for (i = 0; i < vc4_state->dlist_count; i++) - writel(vc4_state->dlist[i], &dlist[i]); -@@ -272,6 +284,34 @@ u32 vc4_plane_dlist_size(struct drm_plan - return vc4_state->dlist_count; - } - -+/* Updates the plane to immediately (well, once the FIFO needs -+ * refilling) scan out from at a new framebuffer. -+ */ -+void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb) -+{ -+ struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); -+ struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); -+ uint32_t addr; -+ -+ /* We're skipping the address adjustment for negative origin, -+ * because this is only called on the primary plane. -+ */ -+ WARN_ON_ONCE(plane->state->crtc_x < 0 || plane->state->crtc_y < 0); -+ addr = bo->paddr + fb->offsets[0]; -+ -+ /* Write the new address into the hardware immediately. The -+ * scanout will start from this address as soon as the FIFO -+ * needs to refill with pixels. -+ */ -+ writel(addr, &vc4_state->hw_dlist[vc4_state->pw0_offset]); -+ -+ /* Also update the CPU-side dlist copy, so that any later -+ * atomic updates that don't do a new modeset on our plane -+ * also use our updated address. -+ */ -+ vc4_state->dlist[vc4_state->pw0_offset] = addr; -+} -+ - static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = { - .prepare_fb = NULL, - .cleanup_fb = NULL, ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_qpu_defines.h -@@ -0,0 +1,268 @@ -+/* -+ * Copyright © 2014 Broadcom -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ */ -+ -+#ifndef VC4_QPU_DEFINES_H -+#define VC4_QPU_DEFINES_H -+ -+enum qpu_op_add { -+ QPU_A_NOP, -+ QPU_A_FADD, -+ QPU_A_FSUB, -+ QPU_A_FMIN, -+ QPU_A_FMAX, -+ QPU_A_FMINABS, -+ QPU_A_FMAXABS, -+ QPU_A_FTOI, -+ QPU_A_ITOF, -+ QPU_A_ADD = 12, -+ QPU_A_SUB, -+ QPU_A_SHR, -+ QPU_A_ASR, -+ QPU_A_ROR, -+ QPU_A_SHL, -+ QPU_A_MIN, -+ QPU_A_MAX, -+ QPU_A_AND, -+ QPU_A_OR, -+ QPU_A_XOR, -+ QPU_A_NOT, -+ QPU_A_CLZ, -+ QPU_A_V8ADDS = 30, -+ QPU_A_V8SUBS = 31, -+}; -+ -+enum qpu_op_mul { -+ QPU_M_NOP, -+ QPU_M_FMUL, -+ QPU_M_MUL24, -+ QPU_M_V8MULD, -+ QPU_M_V8MIN, -+ QPU_M_V8MAX, -+ QPU_M_V8ADDS, -+ QPU_M_V8SUBS, -+}; -+ -+enum qpu_raddr { -+ QPU_R_FRAG_PAYLOAD_ZW = 15, /* W for A file, Z for B file */ -+ /* 0-31 are the plain regfile a or b fields */ -+ QPU_R_UNIF = 32, -+ QPU_R_VARY = 35, -+ QPU_R_ELEM_QPU = 38, -+ QPU_R_NOP, -+ QPU_R_XY_PIXEL_COORD = 41, -+ QPU_R_MS_REV_FLAGS = 41, -+ QPU_R_VPM = 48, -+ QPU_R_VPM_LD_BUSY, -+ QPU_R_VPM_LD_WAIT, -+ QPU_R_MUTEX_ACQUIRE, -+}; -+ -+enum qpu_waddr { -+ /* 0-31 are the plain regfile a or b fields */ -+ QPU_W_ACC0 = 32, /* aka r0 */ -+ QPU_W_ACC1, -+ QPU_W_ACC2, -+ QPU_W_ACC3, -+ QPU_W_TMU_NOSWAP, -+ QPU_W_ACC5, -+ QPU_W_HOST_INT, -+ QPU_W_NOP, -+ QPU_W_UNIFORMS_ADDRESS, -+ QPU_W_QUAD_XY, /* X for regfile a, Y for regfile b */ -+ QPU_W_MS_FLAGS = 42, -+ QPU_W_REV_FLAG = 42, -+ QPU_W_TLB_STENCIL_SETUP = 43, -+ QPU_W_TLB_Z, -+ QPU_W_TLB_COLOR_MS, -+ QPU_W_TLB_COLOR_ALL, -+ QPU_W_TLB_ALPHA_MASK, -+ QPU_W_VPM, -+ QPU_W_VPMVCD_SETUP, /* LD for regfile a, ST for regfile b */ -+ QPU_W_VPM_ADDR, /* LD for regfile a, ST for regfile b */ -+ QPU_W_MUTEX_RELEASE, -+ QPU_W_SFU_RECIP, -+ QPU_W_SFU_RECIPSQRT, -+ QPU_W_SFU_EXP, -+ QPU_W_SFU_LOG, -+ QPU_W_TMU0_S, -+ QPU_W_TMU0_T, -+ QPU_W_TMU0_R, -+ QPU_W_TMU0_B, -+ QPU_W_TMU1_S, -+ QPU_W_TMU1_T, -+ QPU_W_TMU1_R, -+ QPU_W_TMU1_B, -+}; -+ -+enum qpu_sig_bits { -+ QPU_SIG_SW_BREAKPOINT, -+ QPU_SIG_NONE, -+ QPU_SIG_THREAD_SWITCH, -+ QPU_SIG_PROG_END, -+ QPU_SIG_WAIT_FOR_SCOREBOARD, -+ QPU_SIG_SCOREBOARD_UNLOCK, -+ QPU_SIG_LAST_THREAD_SWITCH, -+ QPU_SIG_COVERAGE_LOAD, -+ QPU_SIG_COLOR_LOAD, -+ QPU_SIG_COLOR_LOAD_END, -+ QPU_SIG_LOAD_TMU0, -+ QPU_SIG_LOAD_TMU1, -+ QPU_SIG_ALPHA_MASK_LOAD, -+ QPU_SIG_SMALL_IMM, -+ QPU_SIG_LOAD_IMM, -+ QPU_SIG_BRANCH -+}; -+ -+enum qpu_mux { -+ /* hardware mux values */ -+ QPU_MUX_R0, -+ QPU_MUX_R1, -+ QPU_MUX_R2, -+ QPU_MUX_R3, -+ QPU_MUX_R4, -+ QPU_MUX_R5, -+ QPU_MUX_A, -+ QPU_MUX_B, -+ -+ /* non-hardware mux values */ -+ QPU_MUX_IMM, -+}; -+ -+enum qpu_cond { -+ QPU_COND_NEVER, -+ QPU_COND_ALWAYS, -+ QPU_COND_ZS, -+ QPU_COND_ZC, -+ QPU_COND_NS, -+ QPU_COND_NC, -+ QPU_COND_CS, -+ QPU_COND_CC, -+}; -+ -+enum qpu_pack_mul { -+ QPU_PACK_MUL_NOP, -+ QPU_PACK_MUL_8888 = 3, /* replicated to each 8 bits of the 32-bit dst. */ -+ QPU_PACK_MUL_8A, -+ QPU_PACK_MUL_8B, -+ QPU_PACK_MUL_8C, -+ QPU_PACK_MUL_8D, -+}; -+ -+enum qpu_pack_a { -+ QPU_PACK_A_NOP, -+ /* convert to 16 bit float if float input, or to int16. */ -+ QPU_PACK_A_16A, -+ QPU_PACK_A_16B, -+ /* replicated to each 8 bits of the 32-bit dst. */ -+ QPU_PACK_A_8888, -+ /* Convert to 8-bit unsigned int. */ -+ QPU_PACK_A_8A, -+ QPU_PACK_A_8B, -+ QPU_PACK_A_8C, -+ QPU_PACK_A_8D, -+ -+ /* Saturating variants of the previous instructions. */ -+ QPU_PACK_A_32_SAT, /* int-only */ -+ QPU_PACK_A_16A_SAT, /* int or float */ -+ QPU_PACK_A_16B_SAT, -+ QPU_PACK_A_8888_SAT, -+ QPU_PACK_A_8A_SAT, -+ QPU_PACK_A_8B_SAT, -+ QPU_PACK_A_8C_SAT, -+ QPU_PACK_A_8D_SAT, -+}; -+ -+enum qpu_unpack_r4 { -+ QPU_UNPACK_R4_NOP, -+ QPU_UNPACK_R4_F16A_TO_F32, -+ QPU_UNPACK_R4_F16B_TO_F32, -+ QPU_UNPACK_R4_8D_REP, -+ QPU_UNPACK_R4_8A, -+ QPU_UNPACK_R4_8B, -+ QPU_UNPACK_R4_8C, -+ QPU_UNPACK_R4_8D, -+}; -+ -+#define QPU_MASK(high, low) ((((uint64_t)1<<((high)-(low)+1))-1)<<(low)) -+/* Using the GNU statement expression extension */ -+#define QPU_SET_FIELD(value, field) \ -+ ({ \ -+ uint64_t fieldval = (uint64_t)(value) << field ## _SHIFT; \ -+ assert((fieldval & ~ field ## _MASK) == 0); \ -+ fieldval & field ## _MASK; \ -+ }) -+ -+#define QPU_GET_FIELD(word, field) ((uint32_t)(((word) & field ## _MASK) >> field ## _SHIFT)) -+ -+#define QPU_SIG_SHIFT 60 -+#define QPU_SIG_MASK QPU_MASK(63, 60) -+ -+#define QPU_UNPACK_SHIFT 57 -+#define QPU_UNPACK_MASK QPU_MASK(59, 57) -+ -+/** -+ * If set, the pack field means PACK_MUL or R4 packing, instead of normal -+ * regfile a packing. -+ */ -+#define QPU_PM ((uint64_t)1 << 56) -+ -+#define QPU_PACK_SHIFT 52 -+#define QPU_PACK_MASK QPU_MASK(55, 52) -+ -+#define QPU_COND_ADD_SHIFT 49 -+#define QPU_COND_ADD_MASK QPU_MASK(51, 49) -+#define QPU_COND_MUL_SHIFT 46 -+#define QPU_COND_MUL_MASK QPU_MASK(48, 46) -+ -+#define QPU_SF ((uint64_t)1 << 45) -+ -+#define QPU_WADDR_ADD_SHIFT 38 -+#define QPU_WADDR_ADD_MASK QPU_MASK(43, 38) -+#define QPU_WADDR_MUL_SHIFT 32 -+#define QPU_WADDR_MUL_MASK QPU_MASK(37, 32) -+ -+#define QPU_OP_MUL_SHIFT 29 -+#define QPU_OP_MUL_MASK QPU_MASK(31, 29) -+ -+#define QPU_RADDR_A_SHIFT 18 -+#define QPU_RADDR_A_MASK QPU_MASK(23, 18) -+#define QPU_RADDR_B_SHIFT 12 -+#define QPU_RADDR_B_MASK QPU_MASK(17, 12) -+#define QPU_SMALL_IMM_SHIFT 12 -+#define QPU_SMALL_IMM_MASK QPU_MASK(17, 12) -+ -+#define QPU_ADD_A_SHIFT 9 -+#define QPU_ADD_A_MASK QPU_MASK(11, 9) -+#define QPU_ADD_B_SHIFT 6 -+#define QPU_ADD_B_MASK QPU_MASK(8, 6) -+#define QPU_MUL_A_SHIFT 3 -+#define QPU_MUL_A_MASK QPU_MASK(5, 3) -+#define QPU_MUL_B_SHIFT 0 -+#define QPU_MUL_B_MASK QPU_MASK(2, 0) -+ -+#define QPU_WS ((uint64_t)1 << 44) -+ -+#define QPU_OP_ADD_SHIFT 24 -+#define QPU_OP_ADD_MASK QPU_MASK(28, 24) -+ -+#endif /* VC4_QPU_DEFINES_H */ ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_render_cl.c -@@ -0,0 +1,448 @@ -+/* -+ * Copyright © 2014-2015 Broadcom -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ */ -+ -+/** -+ * DOC: Render command list generation -+ * -+ * In the VC4 driver, render command list generation is performed by the -+ * kernel instead of userspace. We do this because validating a -+ * user-submitted command list is hard to get right and has high CPU overhead, -+ * while the number of valid configurations for render command lists is -+ * actually fairly low. -+ */ -+ -+#include "uapi/drm/vc4_drm.h" -+#include "vc4_drv.h" -+#include "vc4_packet.h" -+ -+struct vc4_rcl_setup { -+ struct drm_gem_cma_object *color_read; -+ struct drm_gem_cma_object *color_ms_write; -+ struct drm_gem_cma_object *zs_read; -+ struct drm_gem_cma_object *zs_write; -+ -+ struct drm_gem_cma_object *rcl; -+ u32 next_offset; -+}; -+ -+static inline void rcl_u8(struct vc4_rcl_setup *setup, u8 val) -+{ -+ *(u8 *)(setup->rcl->vaddr + setup->next_offset) = val; -+ setup->next_offset += 1; -+} -+ -+static inline void rcl_u16(struct vc4_rcl_setup *setup, u16 val) -+{ -+ *(u16 *)(setup->rcl->vaddr + setup->next_offset) = val; -+ setup->next_offset += 2; -+} -+ -+static inline void rcl_u32(struct vc4_rcl_setup *setup, u32 val) -+{ -+ *(u32 *)(setup->rcl->vaddr + setup->next_offset) = val; -+ setup->next_offset += 4; -+} -+ -+ -+/* -+ * Emits a no-op STORE_TILE_BUFFER_GENERAL. -+ * -+ * If we emit a PACKET_TILE_COORDINATES, it must be followed by a store of -+ * some sort before another load is triggered. -+ */ -+static void vc4_store_before_load(struct vc4_rcl_setup *setup) -+{ -+ rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL); -+ rcl_u16(setup, -+ VC4_SET_FIELD(VC4_LOADSTORE_TILE_BUFFER_NONE, -+ VC4_LOADSTORE_TILE_BUFFER_BUFFER) | -+ VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR | -+ VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR | -+ VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR); -+ rcl_u32(setup, 0); /* no address, since we're in None mode */ -+} -+ -+/* -+ * Emits a PACKET_TILE_COORDINATES if one isn't already pending. -+ * -+ * The tile coordinates packet triggers a pending load if there is one, are -+ * used for clipping during rendering, and determine where loads/stores happen -+ * relative to their base address. -+ */ -+static void vc4_tile_coordinates(struct vc4_rcl_setup *setup, -+ uint32_t x, uint32_t y) -+{ -+ rcl_u8(setup, VC4_PACKET_TILE_COORDINATES); -+ rcl_u8(setup, x); -+ rcl_u8(setup, y); -+} -+ -+static void emit_tile(struct vc4_exec_info *exec, -+ struct vc4_rcl_setup *setup, -+ uint8_t x, uint8_t y, bool first, bool last) -+{ -+ struct drm_vc4_submit_cl *args = exec->args; -+ bool has_bin = args->bin_cl_size != 0; -+ -+ /* Note that the load doesn't actually occur until the -+ * tile coords packet is processed, and only one load -+ * may be outstanding at a time. -+ */ -+ if (setup->color_read) { -+ rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); -+ rcl_u16(setup, args->color_read.bits); -+ rcl_u32(setup, -+ setup->color_read->paddr + args->color_read.offset); -+ } -+ -+ if (setup->zs_read) { -+ if (setup->color_read) { -+ /* Exec previous load. */ -+ vc4_tile_coordinates(setup, x, y); -+ vc4_store_before_load(setup); -+ } -+ -+ rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); -+ rcl_u16(setup, args->zs_read.bits); -+ rcl_u32(setup, setup->zs_read->paddr + args->zs_read.offset); -+ } -+ -+ /* Clipping depends on tile coordinates having been -+ * emitted, so we always need one here. -+ */ -+ vc4_tile_coordinates(setup, x, y); -+ -+ /* Wait for the binner before jumping to the first -+ * tile's lists. -+ */ -+ if (first && has_bin) -+ rcl_u8(setup, VC4_PACKET_WAIT_ON_SEMAPHORE); -+ -+ if (has_bin) { -+ rcl_u8(setup, VC4_PACKET_BRANCH_TO_SUB_LIST); -+ rcl_u32(setup, (exec->tile_bo->paddr + -+ exec->tile_alloc_offset + -+ (y * exec->bin_tiles_x + x) * 32)); -+ } -+ -+ if (setup->zs_write) { -+ rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL); -+ rcl_u16(setup, args->zs_write.bits | -+ (setup->color_ms_write ? -+ VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR : 0)); -+ rcl_u32(setup, -+ (setup->zs_write->paddr + args->zs_write.offset) | -+ ((last && !setup->color_ms_write) ? -+ VC4_LOADSTORE_TILE_BUFFER_EOF : 0)); -+ } -+ -+ if (setup->color_ms_write) { -+ if (setup->zs_write) { -+ /* Reset after previous store */ -+ vc4_tile_coordinates(setup, x, y); -+ } -+ -+ if (last) -+ rcl_u8(setup, VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF); -+ else -+ rcl_u8(setup, VC4_PACKET_STORE_MS_TILE_BUFFER); -+ } -+} -+ -+static int vc4_create_rcl_bo(struct drm_device *dev, struct vc4_exec_info *exec, -+ struct vc4_rcl_setup *setup) -+{ -+ struct drm_vc4_submit_cl *args = exec->args; -+ bool has_bin = args->bin_cl_size != 0; -+ uint8_t min_x_tile = args->min_x_tile; -+ uint8_t min_y_tile = args->min_y_tile; -+ uint8_t max_x_tile = args->max_x_tile; -+ uint8_t max_y_tile = args->max_y_tile; -+ uint8_t xtiles = max_x_tile - min_x_tile + 1; -+ uint8_t ytiles = max_y_tile - min_y_tile + 1; -+ uint8_t x, y; -+ uint32_t size, loop_body_size; -+ -+ size = VC4_PACKET_TILE_RENDERING_MODE_CONFIG_SIZE; -+ loop_body_size = VC4_PACKET_TILE_COORDINATES_SIZE; -+ -+ if (args->flags & VC4_SUBMIT_CL_USE_CLEAR_COLOR) { -+ size += VC4_PACKET_CLEAR_COLORS_SIZE + -+ VC4_PACKET_TILE_COORDINATES_SIZE + -+ VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; -+ } -+ -+ if (setup->color_read) { -+ loop_body_size += (VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE); -+ } -+ if (setup->zs_read) { -+ if (setup->color_read) { -+ loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE; -+ loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; -+ } -+ loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE; -+ } -+ -+ if (has_bin) { -+ size += VC4_PACKET_WAIT_ON_SEMAPHORE_SIZE; -+ loop_body_size += VC4_PACKET_BRANCH_TO_SUB_LIST_SIZE; -+ } -+ -+ if (setup->zs_write) -+ loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; -+ if (setup->color_ms_write) { -+ if (setup->zs_write) -+ loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE; -+ loop_body_size += VC4_PACKET_STORE_MS_TILE_BUFFER_SIZE; -+ } -+ size += xtiles * ytiles * loop_body_size; -+ -+ setup->rcl = &vc4_bo_create(dev, size)->base; -+ if (!setup->rcl) -+ return -ENOMEM; -+ list_add_tail(&to_vc4_bo(&setup->rcl->base)->unref_head, -+ &exec->unref_list); -+ -+ rcl_u8(setup, VC4_PACKET_TILE_RENDERING_MODE_CONFIG); -+ rcl_u32(setup, -+ (setup->color_ms_write ? -+ (setup->color_ms_write->paddr + -+ args->color_ms_write.offset) : -+ 0)); -+ rcl_u16(setup, args->width); -+ rcl_u16(setup, args->height); -+ rcl_u16(setup, args->color_ms_write.bits); -+ -+ /* The tile buffer gets cleared when the previous tile is stored. If -+ * the clear values changed between frames, then the tile buffer has -+ * stale clear values in it, so we have to do a store in None mode (no -+ * writes) so that we trigger the tile buffer clear. -+ */ -+ if (args->flags & VC4_SUBMIT_CL_USE_CLEAR_COLOR) { -+ rcl_u8(setup, VC4_PACKET_CLEAR_COLORS); -+ rcl_u32(setup, args->clear_color[0]); -+ rcl_u32(setup, args->clear_color[1]); -+ rcl_u32(setup, args->clear_z); -+ rcl_u8(setup, args->clear_s); -+ -+ vc4_tile_coordinates(setup, 0, 0); -+ -+ rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL); -+ rcl_u16(setup, VC4_LOADSTORE_TILE_BUFFER_NONE); -+ rcl_u32(setup, 0); /* no address, since we're in None mode */ -+ } -+ -+ for (y = min_y_tile; y <= max_y_tile; y++) { -+ for (x = min_x_tile; x <= max_x_tile; x++) { -+ bool first = (x == min_x_tile && y == min_y_tile); -+ bool last = (x == max_x_tile && y == max_y_tile); -+ emit_tile(exec, setup, x, y, first, last); -+ } -+ } -+ -+ BUG_ON(setup->next_offset != size); -+ exec->ct1ca = setup->rcl->paddr; -+ exec->ct1ea = setup->rcl->paddr + setup->next_offset; -+ -+ return 0; -+} -+ -+static int vc4_rcl_surface_setup(struct vc4_exec_info *exec, -+ struct drm_gem_cma_object **obj, -+ struct drm_vc4_submit_rcl_surface *surf) -+{ -+ uint8_t tiling = VC4_GET_FIELD(surf->bits, -+ VC4_LOADSTORE_TILE_BUFFER_TILING); -+ uint8_t buffer = VC4_GET_FIELD(surf->bits, -+ VC4_LOADSTORE_TILE_BUFFER_BUFFER); -+ uint8_t format = VC4_GET_FIELD(surf->bits, -+ VC4_LOADSTORE_TILE_BUFFER_FORMAT); -+ int cpp; -+ -+ if (surf->pad != 0) { -+ DRM_ERROR("Padding unset\n"); -+ return -EINVAL; -+ } -+ -+ if (surf->hindex == ~0) -+ return 0; -+ -+ if (!vc4_use_bo(exec, surf->hindex, VC4_MODE_RENDER, obj)) -+ return -EINVAL; -+ -+ if (surf->bits & ~(VC4_LOADSTORE_TILE_BUFFER_TILING_MASK | -+ VC4_LOADSTORE_TILE_BUFFER_BUFFER_MASK | -+ VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK)) { -+ DRM_ERROR("Unknown bits in load/store: 0x%04x\n", -+ surf->bits); -+ return -EINVAL; -+ } -+ -+ if (tiling > VC4_TILING_FORMAT_LT) { -+ DRM_ERROR("Bad tiling format\n"); -+ return -EINVAL; -+ } -+ -+ if (buffer == VC4_LOADSTORE_TILE_BUFFER_ZS) { -+ if (format != 0) { -+ DRM_ERROR("No color format should be set for ZS\n"); -+ return -EINVAL; -+ } -+ cpp = 4; -+ } else if (buffer == VC4_LOADSTORE_TILE_BUFFER_COLOR) { -+ switch (format) { -+ case VC4_LOADSTORE_TILE_BUFFER_BGR565: -+ case VC4_LOADSTORE_TILE_BUFFER_BGR565_DITHER: -+ cpp = 2; -+ break; -+ case VC4_LOADSTORE_TILE_BUFFER_RGBA8888: -+ cpp = 4; -+ break; -+ default: -+ DRM_ERROR("Bad tile buffer format\n"); -+ return -EINVAL; -+ } -+ } else { -+ DRM_ERROR("Bad load/store buffer %d.\n", buffer); -+ return -EINVAL; -+ } -+ -+ if (surf->offset & 0xf) { -+ DRM_ERROR("load/store buffer must be 16b aligned.\n"); -+ return -EINVAL; -+ } -+ -+ if (!vc4_check_tex_size(exec, *obj, surf->offset, tiling, -+ exec->args->width, exec->args->height, cpp)) { -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int -+vc4_rcl_ms_surface_setup(struct vc4_exec_info *exec, -+ struct drm_gem_cma_object **obj, -+ struct drm_vc4_submit_rcl_surface *surf) -+{ -+ uint8_t tiling = VC4_GET_FIELD(surf->bits, -+ VC4_RENDER_CONFIG_MEMORY_FORMAT); -+ uint8_t format = VC4_GET_FIELD(surf->bits, -+ VC4_RENDER_CONFIG_FORMAT); -+ int cpp; -+ -+ if (surf->pad != 0) { -+ DRM_ERROR("Padding unset\n"); -+ return -EINVAL; -+ } -+ -+ if (surf->bits & ~(VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK | -+ VC4_RENDER_CONFIG_FORMAT_MASK)) { -+ DRM_ERROR("Unknown bits in render config: 0x%04x\n", -+ surf->bits); -+ return -EINVAL; -+ } -+ -+ if (surf->hindex == ~0) -+ return 0; -+ -+ if (!vc4_use_bo(exec, surf->hindex, VC4_MODE_RENDER, obj)) -+ return -EINVAL; -+ -+ if (tiling > VC4_TILING_FORMAT_LT) { -+ DRM_ERROR("Bad tiling format\n"); -+ return -EINVAL; -+ } -+ -+ switch (format) { -+ case VC4_RENDER_CONFIG_FORMAT_BGR565_DITHERED: -+ case VC4_RENDER_CONFIG_FORMAT_BGR565: -+ cpp = 2; -+ break; -+ case VC4_RENDER_CONFIG_FORMAT_RGBA8888: -+ cpp = 4; -+ break; -+ default: -+ DRM_ERROR("Bad tile buffer format\n"); -+ return -EINVAL; -+ } -+ -+ if (!vc4_check_tex_size(exec, *obj, surf->offset, tiling, -+ exec->args->width, exec->args->height, cpp)) { -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+int vc4_get_rcl(struct drm_device *dev, struct vc4_exec_info *exec) -+{ -+ struct vc4_rcl_setup setup = {0}; -+ struct drm_vc4_submit_cl *args = exec->args; -+ bool has_bin = args->bin_cl_size != 0; -+ int ret; -+ -+ if (args->min_x_tile > args->max_x_tile || -+ args->min_y_tile > args->max_y_tile) { -+ DRM_ERROR("Bad render tile set (%d,%d)-(%d,%d)\n", -+ args->min_x_tile, args->min_y_tile, -+ args->max_x_tile, args->max_y_tile); -+ return -EINVAL; -+ } -+ -+ if (has_bin && -+ (args->max_x_tile > exec->bin_tiles_x || -+ args->max_y_tile > exec->bin_tiles_y)) { -+ DRM_ERROR("Render tiles (%d,%d) outside of bin config (%d,%d)\n", -+ args->max_x_tile, args->max_y_tile, -+ exec->bin_tiles_x, exec->bin_tiles_y); -+ return -EINVAL; -+ } -+ -+ ret = vc4_rcl_surface_setup(exec, &setup.color_read, &args->color_read); -+ if (ret) -+ return ret; -+ -+ ret = vc4_rcl_ms_surface_setup(exec, &setup.color_ms_write, -+ &args->color_ms_write); -+ if (ret) -+ return ret; -+ -+ ret = vc4_rcl_surface_setup(exec, &setup.zs_read, &args->zs_read); -+ if (ret) -+ return ret; -+ -+ ret = vc4_rcl_surface_setup(exec, &setup.zs_write, &args->zs_write); -+ if (ret) -+ return ret; -+ -+ /* We shouldn't even have the job submitted to us if there's no -+ * surface to write out. -+ */ -+ if (!setup.color_ms_write && !setup.zs_write) { -+ DRM_ERROR("RCL requires color or Z/S write\n"); -+ return -EINVAL; -+ } -+ -+ return vc4_create_rcl_bo(dev, exec, &setup); -+} ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_trace.h -@@ -0,0 +1,63 @@ -+/* -+ * Copyright (C) 2015 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. -+ */ -+ -+#if !defined(_VC4_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) -+#define _VC4_TRACE_H_ -+ -+#include -+#include -+#include -+ -+#undef TRACE_SYSTEM -+#define TRACE_SYSTEM vc4 -+#define TRACE_INCLUDE_FILE vc4_trace -+ -+TRACE_EVENT(vc4_wait_for_seqno_begin, -+ TP_PROTO(struct drm_device *dev, uint64_t seqno, uint64_t timeout), -+ TP_ARGS(dev, seqno, timeout), -+ -+ TP_STRUCT__entry( -+ __field(u32, dev) -+ __field(u64, seqno) -+ __field(u64, timeout) -+ ), -+ -+ TP_fast_assign( -+ __entry->dev = dev->primary->index; -+ __entry->seqno = seqno; -+ __entry->timeout = timeout; -+ ), -+ -+ TP_printk("dev=%u, seqno=%llu, timeout=%llu", -+ __entry->dev, __entry->seqno, __entry->timeout) -+); -+ -+TRACE_EVENT(vc4_wait_for_seqno_end, -+ TP_PROTO(struct drm_device *dev, uint64_t seqno), -+ TP_ARGS(dev, seqno), -+ -+ TP_STRUCT__entry( -+ __field(u32, dev) -+ __field(u64, seqno) -+ ), -+ -+ TP_fast_assign( -+ __entry->dev = dev->primary->index; -+ __entry->seqno = seqno; -+ ), -+ -+ TP_printk("dev=%u, seqno=%llu", -+ __entry->dev, __entry->seqno) -+); -+ -+#endif /* _VC4_TRACE_H_ */ -+ -+/* This part must be outside protection */ -+#undef TRACE_INCLUDE_PATH -+#define TRACE_INCLUDE_PATH . -+#include ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_trace_points.c -@@ -0,0 +1,14 @@ -+/* -+ * Copyright (C) 2015 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 "vc4_drv.h" -+ -+#ifndef __CHECKER__ -+#define CREATE_TRACE_POINTS -+#include "vc4_trace.h" -+#endif ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_v3d.c -@@ -0,0 +1,268 @@ -+/* -+ * Copyright (c) 2014 The Linux Foundation. All rights reserved. -+ * Copyright (C) 2013 Red Hat -+ * Author: Rob Clark -+ * -+ * 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. -+ * -+ * You should have received a copy of the GNU General Public License along with -+ * this program. If not, see . -+ */ -+ -+#include "linux/component.h" -+#include "soc/bcm2835/raspberrypi-firmware.h" -+#include "vc4_drv.h" -+#include "vc4_regs.h" -+ -+#ifdef CONFIG_DEBUG_FS -+#define REGDEF(reg) { reg, #reg } -+static const struct { -+ uint32_t reg; -+ const char *name; -+} vc4_reg_defs[] = { -+ REGDEF(V3D_IDENT0), -+ REGDEF(V3D_IDENT1), -+ REGDEF(V3D_IDENT2), -+ REGDEF(V3D_SCRATCH), -+ REGDEF(V3D_L2CACTL), -+ REGDEF(V3D_SLCACTL), -+ REGDEF(V3D_INTCTL), -+ REGDEF(V3D_INTENA), -+ REGDEF(V3D_INTDIS), -+ REGDEF(V3D_CT0CS), -+ REGDEF(V3D_CT1CS), -+ REGDEF(V3D_CT0EA), -+ REGDEF(V3D_CT1EA), -+ REGDEF(V3D_CT0CA), -+ REGDEF(V3D_CT1CA), -+ REGDEF(V3D_CT00RA0), -+ REGDEF(V3D_CT01RA0), -+ REGDEF(V3D_CT0LC), -+ REGDEF(V3D_CT1LC), -+ REGDEF(V3D_CT0PC), -+ REGDEF(V3D_CT1PC), -+ REGDEF(V3D_PCS), -+ REGDEF(V3D_BFC), -+ REGDEF(V3D_RFC), -+ REGDEF(V3D_BPCA), -+ REGDEF(V3D_BPCS), -+ REGDEF(V3D_BPOA), -+ REGDEF(V3D_BPOS), -+ REGDEF(V3D_BXCF), -+ REGDEF(V3D_SQRSV0), -+ REGDEF(V3D_SQRSV1), -+ REGDEF(V3D_SQCNTL), -+ REGDEF(V3D_SRQPC), -+ REGDEF(V3D_SRQUA), -+ REGDEF(V3D_SRQUL), -+ REGDEF(V3D_SRQCS), -+ REGDEF(V3D_VPACNTL), -+ REGDEF(V3D_VPMBASE), -+ REGDEF(V3D_PCTRC), -+ REGDEF(V3D_PCTRE), -+ REGDEF(V3D_PCTR0), -+ REGDEF(V3D_PCTRS0), -+ REGDEF(V3D_PCTR1), -+ REGDEF(V3D_PCTRS1), -+ REGDEF(V3D_PCTR2), -+ REGDEF(V3D_PCTRS2), -+ REGDEF(V3D_PCTR3), -+ REGDEF(V3D_PCTRS3), -+ REGDEF(V3D_PCTR4), -+ REGDEF(V3D_PCTRS4), -+ REGDEF(V3D_PCTR5), -+ REGDEF(V3D_PCTRS5), -+ REGDEF(V3D_PCTR6), -+ REGDEF(V3D_PCTRS6), -+ REGDEF(V3D_PCTR7), -+ REGDEF(V3D_PCTRS7), -+ REGDEF(V3D_PCTR8), -+ REGDEF(V3D_PCTRS8), -+ REGDEF(V3D_PCTR9), -+ REGDEF(V3D_PCTRS9), -+ REGDEF(V3D_PCTR10), -+ REGDEF(V3D_PCTRS10), -+ REGDEF(V3D_PCTR11), -+ REGDEF(V3D_PCTRS11), -+ REGDEF(V3D_PCTR12), -+ REGDEF(V3D_PCTRS12), -+ REGDEF(V3D_PCTR13), -+ REGDEF(V3D_PCTRS13), -+ REGDEF(V3D_PCTR14), -+ REGDEF(V3D_PCTRS14), -+ REGDEF(V3D_PCTR15), -+ REGDEF(V3D_PCTRS15), -+ REGDEF(V3D_BGE), -+ REGDEF(V3D_FDBGO), -+ REGDEF(V3D_FDBGB), -+ REGDEF(V3D_FDBGR), -+ REGDEF(V3D_FDBGS), -+ REGDEF(V3D_ERRSTAT), -+}; -+ -+int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused) -+{ -+ struct drm_info_node *node = (struct drm_info_node *) m->private; -+ struct drm_device *dev = node->minor->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(vc4_reg_defs); i++) { -+ seq_printf(m, "%s (0x%04x): 0x%08x\n", -+ vc4_reg_defs[i].name, vc4_reg_defs[i].reg, -+ V3D_READ(vc4_reg_defs[i].reg)); -+ } -+ -+ return 0; -+} -+ -+int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused) -+{ -+ struct drm_info_node *node = (struct drm_info_node *) m->private; -+ struct drm_device *dev = node->minor->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ uint32_t ident1 = V3D_READ(V3D_IDENT1); -+ uint32_t nslc = VC4_GET_FIELD(ident1, V3D_IDENT1_NSLC); -+ uint32_t tups = VC4_GET_FIELD(ident1, V3D_IDENT1_TUPS); -+ uint32_t qups = VC4_GET_FIELD(ident1, V3D_IDENT1_QUPS); -+ -+ seq_printf(m, "Revision: %d\n", VC4_GET_FIELD(ident1, V3D_IDENT1_REV)); -+ seq_printf(m, "Slices: %d\n", nslc); -+ seq_printf(m, "TMUs: %d\n", nslc * tups); -+ seq_printf(m, "QPUs: %d\n", nslc * qups); -+ seq_printf(m, "Semaphores: %d\n", VC4_GET_FIELD(ident1, V3D_IDENT1_NSEM)); -+ -+ return 0; -+} -+#endif /* CONFIG_DEBUG_FS */ -+ -+/* -+ * Asks the firmware to turn on power to the V3D engine. -+ * -+ * This may be doable with just the clocks interface, though this -+ * packet does some other register setup from the firmware, too. -+ */ -+int -+vc4_v3d_set_power(struct vc4_dev *vc4, bool on) -+{ -+ u32 packet = on; -+ -+ return rpi_firmware_property(vc4->firmware, -+ RPI_FIRMWARE_SET_ENABLE_QPU, -+ &packet, sizeof(packet)); -+} -+ -+static void vc4_v3d_init_hw(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ -+ /* Take all the memory that would have been reserved for user -+ * QPU programs, since we don't have an interface for running -+ * them, anyway. -+ */ -+ V3D_WRITE(V3D_VPMBASE, 0); -+} -+ -+static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) -+{ -+ struct platform_device *pdev = to_platform_device(dev); -+ struct drm_device *drm = dev_get_drvdata(master); -+ struct vc4_dev *vc4 = to_vc4_dev(drm); -+ struct vc4_v3d *v3d = NULL; -+ int ret; -+ -+ v3d = devm_kzalloc(&pdev->dev, sizeof(*v3d), GFP_KERNEL); -+ if (!v3d) -+ return -ENOMEM; -+ -+ v3d->pdev = pdev; -+ -+ v3d->regs = vc4_ioremap_regs(pdev, 0); -+ if (IS_ERR(v3d->regs)) -+ return PTR_ERR(v3d->regs); -+ -+ vc4->v3d = v3d; -+ -+ ret = vc4_v3d_set_power(vc4, true); -+ if (ret) -+ return ret; -+ -+ if (V3D_READ(V3D_IDENT0) != V3D_EXPECTED_IDENT0) { -+ DRM_ERROR("V3D_IDENT0 read 0x%08x instead of 0x%08x\n", -+ V3D_READ(V3D_IDENT0), V3D_EXPECTED_IDENT0); -+ return -EINVAL; -+ } -+ -+ /* Reset the binner overflow address/size at setup, to be sure -+ * we don't reuse an old one. -+ */ -+ V3D_WRITE(V3D_BPOA, 0); -+ V3D_WRITE(V3D_BPOS, 0); -+ -+ vc4_v3d_init_hw(drm); -+ -+ ret = drm_irq_install(drm, platform_get_irq(pdev, 0)); -+ if (ret) { -+ DRM_ERROR("Failed to install IRQ handler\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static void vc4_v3d_unbind(struct device *dev, struct device *master, -+ void *data) -+{ -+ struct drm_device *drm = dev_get_drvdata(master); -+ struct vc4_dev *vc4 = to_vc4_dev(drm); -+ -+ drm_irq_uninstall(drm); -+ -+ /* Disable the binner's overflow memory address, so the next -+ * driver probe (if any) doesn't try to reuse our old -+ * allocation. -+ */ -+ V3D_WRITE(V3D_BPOA, 0); -+ V3D_WRITE(V3D_BPOS, 0); -+ -+ vc4_v3d_set_power(vc4, false); -+ -+ vc4->v3d = NULL; -+} -+ -+static const struct component_ops vc4_v3d_ops = { -+ .bind = vc4_v3d_bind, -+ .unbind = vc4_v3d_unbind, -+}; -+ -+static int vc4_v3d_dev_probe(struct platform_device *pdev) -+{ -+ return component_add(&pdev->dev, &vc4_v3d_ops); -+} -+ -+static int vc4_v3d_dev_remove(struct platform_device *pdev) -+{ -+ component_del(&pdev->dev, &vc4_v3d_ops); -+ return 0; -+} -+ -+static const struct of_device_id vc4_v3d_dt_match[] = { -+ { .compatible = "brcm,vc4-v3d" }, -+ {} -+}; -+ -+struct platform_driver vc4_v3d_driver = { -+ .probe = vc4_v3d_dev_probe, -+ .remove = vc4_v3d_dev_remove, -+ .driver = { -+ .name = "vc4_v3d", -+ .of_match_table = vc4_v3d_dt_match, -+ }, -+}; ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_validate.c -@@ -0,0 +1,958 @@ -+/* -+ * Copyright © 2014 Broadcom -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ */ -+ -+/** -+ * Command list validator for VC4. -+ * -+ * The VC4 has no IOMMU between it and system memory. So, a user with -+ * access to execute command lists could escalate privilege by -+ * overwriting system memory (drawing to it as a framebuffer) or -+ * reading system memory it shouldn't (reading it as a texture, or -+ * uniform data, or vertex data). -+ * -+ * This validates command lists to ensure that all accesses are within -+ * the bounds of the GEM objects referenced. It explicitly whitelists -+ * packets, and looks at the offsets in any address fields to make -+ * sure they're constrained within the BOs they reference. -+ * -+ * Note that because of the validation that's happening anyway, this -+ * is where GEM relocation processing happens. -+ */ -+ -+#include "uapi/drm/vc4_drm.h" -+#include "vc4_drv.h" -+#include "vc4_packet.h" -+ -+#define VALIDATE_ARGS \ -+ struct vc4_exec_info *exec, \ -+ void *validated, \ -+ void *untrusted -+ -+ -+/** Return the width in pixels of a 64-byte microtile. */ -+static uint32_t -+utile_width(int cpp) -+{ -+ switch (cpp) { -+ case 1: -+ case 2: -+ return 8; -+ case 4: -+ return 4; -+ case 8: -+ return 2; -+ default: -+ DRM_ERROR("unknown cpp: %d\n", cpp); -+ return 1; -+ } -+} -+ -+/** Return the height in pixels of a 64-byte microtile. */ -+static uint32_t -+utile_height(int cpp) -+{ -+ switch (cpp) { -+ case 1: -+ return 8; -+ case 2: -+ case 4: -+ case 8: -+ return 4; -+ default: -+ DRM_ERROR("unknown cpp: %d\n", cpp); -+ return 1; -+ } -+} -+ -+/** -+ * The texture unit decides what tiling format a particular miplevel is using -+ * this function, so we lay out our miptrees accordingly. -+ */ -+static bool -+size_is_lt(uint32_t width, uint32_t height, int cpp) -+{ -+ return (width <= 4 * utile_width(cpp) || -+ height <= 4 * utile_height(cpp)); -+} -+ -+bool -+vc4_use_bo(struct vc4_exec_info *exec, -+ uint32_t hindex, -+ enum vc4_bo_mode mode, -+ struct drm_gem_cma_object **obj) -+{ -+ *obj = NULL; -+ -+ if (hindex >= exec->bo_count) { -+ DRM_ERROR("BO index %d greater than BO count %d\n", -+ hindex, exec->bo_count); -+ return false; -+ } -+ -+ if (exec->bo[hindex].mode != mode) { -+ if (exec->bo[hindex].mode == VC4_MODE_UNDECIDED) { -+ exec->bo[hindex].mode = mode; -+ } else { -+ DRM_ERROR("BO index %d reused with mode %d vs %d\n", -+ hindex, exec->bo[hindex].mode, mode); -+ return false; -+ } -+ } -+ -+ *obj = exec->bo[hindex].bo; -+ return true; -+} -+ -+static bool -+vc4_use_handle(struct vc4_exec_info *exec, -+ uint32_t gem_handles_packet_index, -+ enum vc4_bo_mode mode, -+ struct drm_gem_cma_object **obj) -+{ -+ return vc4_use_bo(exec, exec->bo_index[gem_handles_packet_index], -+ mode, obj); -+} -+ -+static uint32_t -+gl_shader_rec_size(uint32_t pointer_bits) -+{ -+ uint32_t attribute_count = pointer_bits & 7; -+ bool extended = pointer_bits & 8; -+ -+ if (attribute_count == 0) -+ attribute_count = 8; -+ -+ if (extended) -+ return 100 + attribute_count * 4; -+ else -+ return 36 + attribute_count * 8; -+} -+ -+bool -+vc4_check_tex_size(struct vc4_exec_info *exec, struct drm_gem_cma_object *fbo, -+ uint32_t offset, uint8_t tiling_format, -+ uint32_t width, uint32_t height, uint8_t cpp) -+{ -+ uint32_t aligned_width, aligned_height, stride, size; -+ uint32_t utile_w = utile_width(cpp); -+ uint32_t utile_h = utile_height(cpp); -+ -+ /* The shaded vertex format stores signed 12.4 fixed point -+ * (-2048,2047) offsets from the viewport center, so we should -+ * never have a render target larger than 4096. The texture -+ * unit can only sample from 2048x2048, so it's even more -+ * restricted. This lets us avoid worrying about overflow in -+ * our math. -+ */ -+ if (width > 4096 || height > 4096) { -+ DRM_ERROR("Surface dimesions (%d,%d) too large", width, height); -+ return false; -+ } -+ -+ switch (tiling_format) { -+ case VC4_TILING_FORMAT_LINEAR: -+ aligned_width = round_up(width, utile_w); -+ aligned_height = height; -+ break; -+ case VC4_TILING_FORMAT_T: -+ aligned_width = round_up(width, utile_w * 8); -+ aligned_height = round_up(height, utile_h * 8); -+ break; -+ case VC4_TILING_FORMAT_LT: -+ aligned_width = round_up(width, utile_w); -+ aligned_height = round_up(height, utile_h); -+ break; -+ default: -+ DRM_ERROR("buffer tiling %d unsupported\n", tiling_format); -+ return false; -+ } -+ -+ stride = aligned_width * cpp; -+ size = stride * aligned_height; -+ -+ if (size + offset < size || -+ size + offset > fbo->base.size) { -+ DRM_ERROR("Overflow in %dx%d (%dx%d) fbo size (%d + %d > %d)\n", -+ width, height, -+ aligned_width, aligned_height, -+ size, offset, fbo->base.size); -+ return false; -+ } -+ -+ return true; -+} -+ -+static int -+validate_flush_all(VALIDATE_ARGS) -+{ -+ if (exec->found_increment_semaphore_packet) { -+ DRM_ERROR("VC4_PACKET_FLUSH_ALL after " -+ "VC4_PACKET_INCREMENT_SEMAPHORE\n"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int -+validate_start_tile_binning(VALIDATE_ARGS) -+{ -+ if (exec->found_start_tile_binning_packet) { -+ DRM_ERROR("Duplicate VC4_PACKET_START_TILE_BINNING\n"); -+ return -EINVAL; -+ } -+ exec->found_start_tile_binning_packet = true; -+ -+ if (!exec->found_tile_binning_mode_config_packet) { -+ DRM_ERROR("missing VC4_PACKET_TILE_BINNING_MODE_CONFIG\n"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int -+validate_increment_semaphore(VALIDATE_ARGS) -+{ -+ if (exec->found_increment_semaphore_packet) { -+ DRM_ERROR("Duplicate VC4_PACKET_INCREMENT_SEMAPHORE\n"); -+ return -EINVAL; -+ } -+ exec->found_increment_semaphore_packet = true; -+ -+ /* Once we've found the semaphore increment, there should be one FLUSH -+ * then the end of the command list. The FLUSH actually triggers the -+ * increment, so we only need to make sure there -+ */ -+ -+ return 0; -+} -+ -+static int -+validate_indexed_prim_list(VALIDATE_ARGS) -+{ -+ struct drm_gem_cma_object *ib; -+ uint32_t length = *(uint32_t *)(untrusted + 1); -+ uint32_t offset = *(uint32_t *)(untrusted + 5); -+ uint32_t max_index = *(uint32_t *)(untrusted + 9); -+ uint32_t index_size = (*(uint8_t *)(untrusted + 0) >> 4) ? 2 : 1; -+ struct vc4_shader_state *shader_state; -+ -+ if (exec->found_increment_semaphore_packet) { -+ DRM_ERROR("Drawing after VC4_PACKET_INCREMENT_SEMAPHORE\n"); -+ return -EINVAL; -+ } -+ -+ /* Check overflow condition */ -+ if (exec->shader_state_count == 0) { -+ DRM_ERROR("shader state must precede primitives\n"); -+ return -EINVAL; -+ } -+ shader_state = &exec->shader_state[exec->shader_state_count - 1]; -+ -+ if (max_index > shader_state->max_index) -+ shader_state->max_index = max_index; -+ -+ if (!vc4_use_handle(exec, 0, VC4_MODE_RENDER, &ib)) -+ return -EINVAL; -+ -+ if (offset > ib->base.size || -+ (ib->base.size - offset) / index_size < length) { -+ DRM_ERROR("IB access overflow (%d + %d*%d > %d)\n", -+ offset, length, index_size, ib->base.size); -+ return -EINVAL; -+ } -+ -+ *(uint32_t *)(validated + 5) = ib->paddr + offset; -+ -+ return 0; -+} -+ -+static int -+validate_gl_array_primitive(VALIDATE_ARGS) -+{ -+ uint32_t length = *(uint32_t *)(untrusted + 1); -+ uint32_t base_index = *(uint32_t *)(untrusted + 5); -+ uint32_t max_index; -+ struct vc4_shader_state *shader_state; -+ -+ if (exec->found_increment_semaphore_packet) { -+ DRM_ERROR("Drawing after VC4_PACKET_INCREMENT_SEMAPHORE\n"); -+ return -EINVAL; -+ } -+ -+ /* Check overflow condition */ -+ if (exec->shader_state_count == 0) { -+ DRM_ERROR("shader state must precede primitives\n"); -+ return -EINVAL; -+ } -+ shader_state = &exec->shader_state[exec->shader_state_count - 1]; -+ -+ if (length + base_index < length) { -+ DRM_ERROR("primitive vertex count overflow\n"); -+ return -EINVAL; -+ } -+ max_index = length + base_index - 1; -+ -+ if (max_index > shader_state->max_index) -+ shader_state->max_index = max_index; -+ -+ return 0; -+} -+ -+static int -+validate_gl_shader_state(VALIDATE_ARGS) -+{ -+ uint32_t i = exec->shader_state_count++; -+ -+ if (i >= exec->shader_state_size) { -+ DRM_ERROR("More requests for shader states than declared\n"); -+ return -EINVAL; -+ } -+ -+ exec->shader_state[i].packet = VC4_PACKET_GL_SHADER_STATE; -+ exec->shader_state[i].addr = *(uint32_t *)untrusted; -+ exec->shader_state[i].max_index = 0; -+ -+ if (exec->shader_state[i].addr & ~0xf) { -+ DRM_ERROR("high bits set in GL shader rec reference\n"); -+ return -EINVAL; -+ } -+ -+ *(uint32_t *)validated = (exec->shader_rec_p + -+ exec->shader_state[i].addr); -+ -+ exec->shader_rec_p += -+ roundup(gl_shader_rec_size(exec->shader_state[i].addr), 16); -+ -+ return 0; -+} -+ -+static int -+validate_nv_shader_state(VALIDATE_ARGS) -+{ -+ uint32_t i = exec->shader_state_count++; -+ -+ if (i >= exec->shader_state_size) { -+ DRM_ERROR("More requests for shader states than declared\n"); -+ return -EINVAL; -+ } -+ -+ exec->shader_state[i].packet = VC4_PACKET_NV_SHADER_STATE; -+ exec->shader_state[i].addr = *(uint32_t *)untrusted; -+ -+ if (exec->shader_state[i].addr & 15) { -+ DRM_ERROR("NV shader state address 0x%08x misaligned\n", -+ exec->shader_state[i].addr); -+ return -EINVAL; -+ } -+ -+ *(uint32_t *)validated = (exec->shader_state[i].addr + -+ exec->shader_rec_p); -+ -+ return 0; -+} -+ -+static int -+validate_tile_binning_config(VALIDATE_ARGS) -+{ -+ struct drm_device *dev = exec->exec_bo->base.dev; -+ uint8_t flags; -+ uint32_t tile_state_size, tile_alloc_size; -+ uint32_t tile_count; -+ -+ if (exec->found_tile_binning_mode_config_packet) { -+ DRM_ERROR("Duplicate VC4_PACKET_TILE_BINNING_MODE_CONFIG\n"); -+ return -EINVAL; -+ } -+ exec->found_tile_binning_mode_config_packet = true; -+ -+ exec->bin_tiles_x = *(uint8_t *)(untrusted + 12); -+ exec->bin_tiles_y = *(uint8_t *)(untrusted + 13); -+ tile_count = exec->bin_tiles_x * exec->bin_tiles_y; -+ flags = *(uint8_t *)(untrusted + 14); -+ -+ if (exec->bin_tiles_x == 0 || -+ exec->bin_tiles_y == 0) { -+ DRM_ERROR("Tile binning config of %dx%d too small\n", -+ exec->bin_tiles_x, exec->bin_tiles_y); -+ return -EINVAL; -+ } -+ -+ if (flags & (VC4_BIN_CONFIG_DB_NON_MS | -+ VC4_BIN_CONFIG_TILE_BUFFER_64BIT | -+ VC4_BIN_CONFIG_MS_MODE_4X)) { -+ DRM_ERROR("unsupported bining config flags 0x%02x\n", flags); -+ return -EINVAL; -+ } -+ -+ /* The tile state data array is 48 bytes per tile, and we put it at -+ * the start of a BO containing both it and the tile alloc. -+ */ -+ tile_state_size = 48 * tile_count; -+ -+ /* Since the tile alloc array will follow us, align. */ -+ exec->tile_alloc_offset = roundup(tile_state_size, 4096); -+ -+ *(uint8_t *)(validated + 14) = -+ ((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK | -+ VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_MASK)) | -+ VC4_BIN_CONFIG_AUTO_INIT_TSDA | -+ VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_32, -+ VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE) | -+ VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128, -+ VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE)); -+ -+ /* Initial block size. */ -+ tile_alloc_size = 32 * tile_count; -+ -+ /* -+ * The initial allocation gets rounded to the next 256 bytes before -+ * the hardware starts fulfilling further allocations. -+ */ -+ tile_alloc_size = roundup(tile_alloc_size, 256); -+ -+ /* Add space for the extra allocations. This is what gets used first, -+ * before overflow memory. It must have at least 4096 bytes, but we -+ * want to avoid overflow memory usage if possible. -+ */ -+ tile_alloc_size += 1024 * 1024; -+ -+ exec->tile_bo = &vc4_bo_create(dev, exec->tile_alloc_offset + -+ tile_alloc_size)->base; -+ if (!exec->tile_bo) -+ return -ENOMEM; -+ list_add_tail(&to_vc4_bo(&exec->tile_bo->base)->unref_head, -+ &exec->unref_list); -+ -+ /* tile alloc address. */ -+ *(uint32_t *)(validated + 0) = (exec->tile_bo->paddr + -+ exec->tile_alloc_offset); -+ /* tile alloc size. */ -+ *(uint32_t *)(validated + 4) = tile_alloc_size; -+ /* tile state address. */ -+ *(uint32_t *)(validated + 8) = exec->tile_bo->paddr; -+ -+ return 0; -+} -+ -+static int -+validate_gem_handles(VALIDATE_ARGS) -+{ -+ memcpy(exec->bo_index, untrusted, sizeof(exec->bo_index)); -+ return 0; -+} -+ -+#define VC4_DEFINE_PACKET(packet, name, func) \ -+ [packet] = { packet ## _SIZE, name, func } -+ -+static const struct cmd_info { -+ uint16_t len; -+ const char *name; -+ int (*func)(struct vc4_exec_info *exec, void *validated, -+ void *untrusted); -+} cmd_info[] = { -+ VC4_DEFINE_PACKET(VC4_PACKET_HALT, "halt", NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_NOP, "nop", NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_FLUSH, "flush", NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_FLUSH_ALL, "flush all state", validate_flush_all), -+ VC4_DEFINE_PACKET(VC4_PACKET_START_TILE_BINNING, "start tile binning", validate_start_tile_binning), -+ VC4_DEFINE_PACKET(VC4_PACKET_INCREMENT_SEMAPHORE, "increment semaphore", validate_increment_semaphore), -+ -+ VC4_DEFINE_PACKET(VC4_PACKET_GL_INDEXED_PRIMITIVE, "Indexed Primitive List", validate_indexed_prim_list), -+ -+ VC4_DEFINE_PACKET(VC4_PACKET_GL_ARRAY_PRIMITIVE, "Vertex Array Primitives", validate_gl_array_primitive), -+ -+ /* This is only used by clipped primitives (packets 48 and 49), which -+ * we don't support parsing yet. -+ */ -+ VC4_DEFINE_PACKET(VC4_PACKET_PRIMITIVE_LIST_FORMAT, "primitive list format", NULL), -+ -+ VC4_DEFINE_PACKET(VC4_PACKET_GL_SHADER_STATE, "GL Shader State", validate_gl_shader_state), -+ VC4_DEFINE_PACKET(VC4_PACKET_NV_SHADER_STATE, "NV Shader State", validate_nv_shader_state), -+ -+ VC4_DEFINE_PACKET(VC4_PACKET_CONFIGURATION_BITS, "configuration bits", NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_FLAT_SHADE_FLAGS, "flat shade flags", NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_POINT_SIZE, "point size", NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_LINE_WIDTH, "line width", NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_RHT_X_BOUNDARY, "RHT X boundary", NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_DEPTH_OFFSET, "Depth Offset", NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_CLIP_WINDOW, "Clip Window", NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_VIEWPORT_OFFSET, "Viewport Offset", NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_XY_SCALING, "Clipper XY Scaling", NULL), -+ /* Note: The docs say this was also 105, but it was 106 in the -+ * initial userland code drop. -+ */ -+ VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_Z_SCALING, "Clipper Z Scale and Offset", NULL), -+ -+ VC4_DEFINE_PACKET(VC4_PACKET_TILE_BINNING_MODE_CONFIG, "tile binning configuration", validate_tile_binning_config), -+ -+ VC4_DEFINE_PACKET(VC4_PACKET_GEM_HANDLES, "GEM handles", validate_gem_handles), -+}; -+ -+int -+vc4_validate_bin_cl(struct drm_device *dev, -+ void *validated, -+ void *unvalidated, -+ struct vc4_exec_info *exec) -+{ -+ uint32_t len = exec->args->bin_cl_size; -+ uint32_t dst_offset = 0; -+ uint32_t src_offset = 0; -+ -+ while (src_offset < len) { -+ void *dst_pkt = validated + dst_offset; -+ void *src_pkt = unvalidated + src_offset; -+ u8 cmd = *(uint8_t *)src_pkt; -+ const struct cmd_info *info; -+ -+ if (cmd > ARRAY_SIZE(cmd_info)) { -+ DRM_ERROR("0x%08x: packet %d out of bounds\n", -+ src_offset, cmd); -+ return -EINVAL; -+ } -+ -+ info = &cmd_info[cmd]; -+ if (!info->name) { -+ DRM_ERROR("0x%08x: packet %d invalid\n", -+ src_offset, cmd); -+ return -EINVAL; -+ } -+ -+#if 0 -+ DRM_INFO("0x%08x: packet %d (%s) size %d processing...\n", -+ src_offset, cmd, info->name, info->len); -+#endif -+ -+ if (src_offset + info->len > len) { -+ DRM_ERROR("0x%08x: packet %d (%s) length 0x%08x " -+ "exceeds bounds (0x%08x)\n", -+ src_offset, cmd, info->name, info->len, -+ src_offset + len); -+ return -EINVAL; -+ } -+ -+ if (cmd != VC4_PACKET_GEM_HANDLES) -+ memcpy(dst_pkt, src_pkt, info->len); -+ -+ if (info->func && info->func(exec, -+ dst_pkt + 1, -+ src_pkt + 1)) { -+ DRM_ERROR("0x%08x: packet %d (%s) failed to " -+ "validate\n", -+ src_offset, cmd, info->name); -+ return -EINVAL; -+ } -+ -+ src_offset += info->len; -+ /* GEM handle loading doesn't produce HW packets. */ -+ if (cmd != VC4_PACKET_GEM_HANDLES) -+ dst_offset += info->len; -+ -+ /* When the CL hits halt, it'll stop reading anything else. */ -+ if (cmd == VC4_PACKET_HALT) -+ break; -+ } -+ -+ exec->ct0ea = exec->ct0ca + dst_offset; -+ -+ if (!exec->found_start_tile_binning_packet) { -+ DRM_ERROR("Bin CL missing VC4_PACKET_START_TILE_BINNING\n"); -+ return -EINVAL; -+ } -+ -+ if (!exec->found_increment_semaphore_packet) { -+ DRM_ERROR("Bin CL missing VC4_PACKET_INCREMENT_SEMAPHORE\n"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static bool -+reloc_tex(struct vc4_exec_info *exec, -+ void *uniform_data_u, -+ struct vc4_texture_sample_info *sample, -+ uint32_t texture_handle_index) -+ -+{ -+ struct drm_gem_cma_object *tex; -+ uint32_t p0 = *(uint32_t *)(uniform_data_u + sample->p_offset[0]); -+ uint32_t p1 = *(uint32_t *)(uniform_data_u + sample->p_offset[1]); -+ uint32_t p2 = (sample->p_offset[2] != ~0 ? -+ *(uint32_t *)(uniform_data_u + sample->p_offset[2]) : 0); -+ uint32_t p3 = (sample->p_offset[3] != ~0 ? -+ *(uint32_t *)(uniform_data_u + sample->p_offset[3]) : 0); -+ uint32_t *validated_p0 = exec->uniforms_v + sample->p_offset[0]; -+ uint32_t offset = p0 & VC4_TEX_P0_OFFSET_MASK; -+ uint32_t miplevels = VC4_GET_FIELD(p0, VC4_TEX_P0_MIPLVLS); -+ uint32_t width = VC4_GET_FIELD(p1, VC4_TEX_P1_WIDTH); -+ uint32_t height = VC4_GET_FIELD(p1, VC4_TEX_P1_HEIGHT); -+ uint32_t cpp, tiling_format, utile_w, utile_h; -+ uint32_t i; -+ uint32_t cube_map_stride = 0; -+ enum vc4_texture_data_type type; -+ -+ if (!vc4_use_bo(exec, texture_handle_index, VC4_MODE_RENDER, &tex)) -+ return false; -+ -+ if (sample->is_direct) { -+ uint32_t remaining_size = tex->base.size - p0; -+ if (p0 > tex->base.size - 4) { -+ DRM_ERROR("UBO offset greater than UBO size\n"); -+ goto fail; -+ } -+ if (p1 > remaining_size - 4) { -+ DRM_ERROR("UBO clamp would allow reads outside of UBO\n"); -+ goto fail; -+ } -+ *validated_p0 = tex->paddr + p0; -+ return true; -+ } -+ -+ if (width == 0) -+ width = 2048; -+ if (height == 0) -+ height = 2048; -+ -+ if (p0 & VC4_TEX_P0_CMMODE_MASK) { -+ if (VC4_GET_FIELD(p2, VC4_TEX_P2_PTYPE) == -+ VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE) -+ cube_map_stride = p2 & VC4_TEX_P2_CMST_MASK; -+ if (VC4_GET_FIELD(p3, VC4_TEX_P2_PTYPE) == -+ VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE) { -+ if (cube_map_stride) { -+ DRM_ERROR("Cube map stride set twice\n"); -+ goto fail; -+ } -+ -+ cube_map_stride = p3 & VC4_TEX_P2_CMST_MASK; -+ } -+ if (!cube_map_stride) { -+ DRM_ERROR("Cube map stride not set\n"); -+ goto fail; -+ } -+ } -+ -+ type = (VC4_GET_FIELD(p0, VC4_TEX_P0_TYPE) | -+ (VC4_GET_FIELD(p1, VC4_TEX_P1_TYPE4) << 4)); -+ -+ switch (type) { -+ case VC4_TEXTURE_TYPE_RGBA8888: -+ case VC4_TEXTURE_TYPE_RGBX8888: -+ case VC4_TEXTURE_TYPE_RGBA32R: -+ cpp = 4; -+ break; -+ case VC4_TEXTURE_TYPE_RGBA4444: -+ case VC4_TEXTURE_TYPE_RGBA5551: -+ case VC4_TEXTURE_TYPE_RGB565: -+ case VC4_TEXTURE_TYPE_LUMALPHA: -+ case VC4_TEXTURE_TYPE_S16F: -+ case VC4_TEXTURE_TYPE_S16: -+ cpp = 2; -+ break; -+ case VC4_TEXTURE_TYPE_LUMINANCE: -+ case VC4_TEXTURE_TYPE_ALPHA: -+ case VC4_TEXTURE_TYPE_S8: -+ cpp = 1; -+ break; -+ case VC4_TEXTURE_TYPE_ETC1: -+ case VC4_TEXTURE_TYPE_BW1: -+ case VC4_TEXTURE_TYPE_A4: -+ case VC4_TEXTURE_TYPE_A1: -+ case VC4_TEXTURE_TYPE_RGBA64: -+ case VC4_TEXTURE_TYPE_YUV422R: -+ default: -+ DRM_ERROR("Texture format %d unsupported\n", type); -+ goto fail; -+ } -+ utile_w = utile_width(cpp); -+ utile_h = utile_height(cpp); -+ -+ if (type == VC4_TEXTURE_TYPE_RGBA32R) { -+ tiling_format = VC4_TILING_FORMAT_LINEAR; -+ } else { -+ if (size_is_lt(width, height, cpp)) -+ tiling_format = VC4_TILING_FORMAT_LT; -+ else -+ tiling_format = VC4_TILING_FORMAT_T; -+ } -+ -+ if (!vc4_check_tex_size(exec, tex, offset + cube_map_stride * 5, -+ tiling_format, width, height, cpp)) { -+ goto fail; -+ } -+ -+ /* The mipmap levels are stored before the base of the texture. Make -+ * sure there is actually space in the BO. -+ */ -+ for (i = 1; i <= miplevels; i++) { -+ uint32_t level_width = max(width >> i, 1u); -+ uint32_t level_height = max(height >> i, 1u); -+ uint32_t aligned_width, aligned_height; -+ uint32_t level_size; -+ -+ /* Once the levels get small enough, they drop from T to LT. */ -+ if (tiling_format == VC4_TILING_FORMAT_T && -+ size_is_lt(level_width, level_height, cpp)) { -+ tiling_format = VC4_TILING_FORMAT_LT; -+ } -+ -+ switch (tiling_format) { -+ case VC4_TILING_FORMAT_T: -+ aligned_width = round_up(level_width, utile_w * 8); -+ aligned_height = round_up(level_height, utile_h * 8); -+ break; -+ case VC4_TILING_FORMAT_LT: -+ aligned_width = round_up(level_width, utile_w); -+ aligned_height = round_up(level_height, utile_h); -+ break; -+ default: -+ aligned_width = round_up(level_width, utile_w); -+ aligned_height = level_height; -+ break; -+ } -+ -+ level_size = aligned_width * cpp * aligned_height; -+ -+ if (offset < level_size) { -+ DRM_ERROR("Level %d (%dx%d -> %dx%d) size %db " -+ "overflowed buffer bounds (offset %d)\n", -+ i, level_width, level_height, -+ aligned_width, aligned_height, -+ level_size, offset); -+ goto fail; -+ } -+ -+ offset -= level_size; -+ } -+ -+ *validated_p0 = tex->paddr + p0; -+ -+ return true; -+ fail: -+ DRM_INFO("Texture p0 at %d: 0x%08x\n", sample->p_offset[0], p0); -+ DRM_INFO("Texture p1 at %d: 0x%08x\n", sample->p_offset[1], p1); -+ DRM_INFO("Texture p2 at %d: 0x%08x\n", sample->p_offset[2], p2); -+ DRM_INFO("Texture p3 at %d: 0x%08x\n", sample->p_offset[3], p3); -+ return false; -+} -+ -+static int -+validate_shader_rec(struct drm_device *dev, -+ struct vc4_exec_info *exec, -+ struct vc4_shader_state *state) -+{ -+ uint32_t *src_handles; -+ void *pkt_u, *pkt_v; -+ enum shader_rec_reloc_type { -+ RELOC_CODE, -+ RELOC_VBO, -+ }; -+ struct shader_rec_reloc { -+ enum shader_rec_reloc_type type; -+ uint32_t offset; -+ }; -+ static const struct shader_rec_reloc gl_relocs[] = { -+ { RELOC_CODE, 4 }, /* fs */ -+ { RELOC_CODE, 16 }, /* vs */ -+ { RELOC_CODE, 28 }, /* cs */ -+ }; -+ static const struct shader_rec_reloc nv_relocs[] = { -+ { RELOC_CODE, 4 }, /* fs */ -+ { RELOC_VBO, 12 } -+ }; -+ const struct shader_rec_reloc *relocs; -+ struct drm_gem_cma_object *bo[ARRAY_SIZE(gl_relocs) + 8]; -+ uint32_t nr_attributes = 0, nr_fixed_relocs, nr_relocs, packet_size; -+ int i; -+ struct vc4_validated_shader_info *validated_shader; -+ -+ if (state->packet == VC4_PACKET_NV_SHADER_STATE) { -+ relocs = nv_relocs; -+ nr_fixed_relocs = ARRAY_SIZE(nv_relocs); -+ -+ packet_size = 16; -+ } else { -+ relocs = gl_relocs; -+ nr_fixed_relocs = ARRAY_SIZE(gl_relocs); -+ -+ nr_attributes = state->addr & 0x7; -+ if (nr_attributes == 0) -+ nr_attributes = 8; -+ packet_size = gl_shader_rec_size(state->addr); -+ } -+ nr_relocs = nr_fixed_relocs + nr_attributes; -+ -+ if (nr_relocs * 4 > exec->shader_rec_size) { -+ DRM_ERROR("overflowed shader recs reading %d handles " -+ "from %d bytes left\n", -+ nr_relocs, exec->shader_rec_size); -+ return -EINVAL; -+ } -+ src_handles = exec->shader_rec_u; -+ exec->shader_rec_u += nr_relocs * 4; -+ exec->shader_rec_size -= nr_relocs * 4; -+ -+ if (packet_size > exec->shader_rec_size) { -+ DRM_ERROR("overflowed shader recs copying %db packet " -+ "from %d bytes left\n", -+ packet_size, exec->shader_rec_size); -+ return -EINVAL; -+ } -+ pkt_u = exec->shader_rec_u; -+ pkt_v = exec->shader_rec_v; -+ memcpy(pkt_v, pkt_u, packet_size); -+ exec->shader_rec_u += packet_size; -+ /* Shader recs have to be aligned to 16 bytes (due to the attribute -+ * flags being in the low bytes), so round the next validated shader -+ * rec address up. This should be safe, since we've got so many -+ * relocations in a shader rec packet. -+ */ -+ BUG_ON(roundup(packet_size, 16) - packet_size > nr_relocs * 4); -+ exec->shader_rec_v += roundup(packet_size, 16); -+ exec->shader_rec_size -= packet_size; -+ -+ for (i = 0; i < nr_relocs; i++) { -+ enum vc4_bo_mode mode; -+ -+ if (i < nr_fixed_relocs && relocs[i].type == RELOC_CODE) -+ mode = VC4_MODE_SHADER; -+ else -+ mode = VC4_MODE_RENDER; -+ -+ if (!vc4_use_bo(exec, src_handles[i], mode, &bo[i])) { -+ return false; -+ } -+ } -+ -+ for (i = 0; i < nr_fixed_relocs; i++) { -+ uint32_t o = relocs[i].offset; -+ uint32_t src_offset = *(uint32_t *)(pkt_u + o); -+ uint32_t *texture_handles_u; -+ void *uniform_data_u; -+ uint32_t tex; -+ -+ *(uint32_t *)(pkt_v + o) = bo[i]->paddr + src_offset; -+ -+ switch (relocs[i].type) { -+ case RELOC_CODE: -+ if (src_offset != 0) { -+ DRM_ERROR("Shaders must be at offset 0 of " -+ "the BO.\n"); -+ goto fail; -+ } -+ -+ validated_shader = to_vc4_bo(&bo[i]->base)->validated_shader; -+ if (!validated_shader) -+ goto fail; -+ -+ if (validated_shader->uniforms_src_size > -+ exec->uniforms_size) { -+ DRM_ERROR("Uniforms src buffer overflow\n"); -+ goto fail; -+ } -+ -+ texture_handles_u = exec->uniforms_u; -+ uniform_data_u = (texture_handles_u + -+ validated_shader->num_texture_samples); -+ -+ memcpy(exec->uniforms_v, uniform_data_u, -+ validated_shader->uniforms_size); -+ -+ for (tex = 0; -+ tex < validated_shader->num_texture_samples; -+ tex++) { -+ if (!reloc_tex(exec, -+ uniform_data_u, -+ &validated_shader->texture_samples[tex], -+ texture_handles_u[tex])) { -+ goto fail; -+ } -+ } -+ -+ *(uint32_t *)(pkt_v + o + 4) = exec->uniforms_p; -+ -+ exec->uniforms_u += validated_shader->uniforms_src_size; -+ exec->uniforms_v += validated_shader->uniforms_size; -+ exec->uniforms_p += validated_shader->uniforms_size; -+ -+ break; -+ -+ case RELOC_VBO: -+ break; -+ } -+ } -+ -+ for (i = 0; i < nr_attributes; i++) { -+ struct drm_gem_cma_object *vbo = bo[nr_fixed_relocs + i]; -+ uint32_t o = 36 + i * 8; -+ uint32_t offset = *(uint32_t *)(pkt_u + o + 0); -+ uint32_t attr_size = *(uint8_t *)(pkt_u + o + 4) + 1; -+ uint32_t stride = *(uint8_t *)(pkt_u + o + 5); -+ uint32_t max_index; -+ -+ if (state->addr & 0x8) -+ stride |= (*(uint32_t *)(pkt_u + 100 + i * 4)) & ~0xff; -+ -+ if (vbo->base.size < offset || -+ vbo->base.size - offset < attr_size) { -+ DRM_ERROR("BO offset overflow (%d + %d > %d)\n", -+ offset, attr_size, vbo->base.size); -+ return -EINVAL; -+ } -+ -+ if (stride != 0) { -+ max_index = ((vbo->base.size - offset - attr_size) / -+ stride); -+ if (state->max_index > max_index) { -+ DRM_ERROR("primitives use index %d out of supplied %d\n", -+ state->max_index, max_index); -+ return -EINVAL; -+ } -+ } -+ -+ *(uint32_t *)(pkt_v + o) = vbo->paddr + offset; -+ } -+ -+ return 0; -+ -+fail: -+ return -EINVAL; -+} -+ -+int -+vc4_validate_shader_recs(struct drm_device *dev, -+ struct vc4_exec_info *exec) -+{ -+ uint32_t i; -+ int ret = 0; -+ -+ for (i = 0; i < exec->shader_state_count; i++) { -+ ret = validate_shader_rec(dev, exec, &exec->shader_state[i]); -+ if (ret) -+ return ret; -+ } -+ -+ return ret; -+} ---- /dev/null -+++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c -@@ -0,0 +1,521 @@ -+/* -+ * Copyright © 2014 Broadcom -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ */ -+ -+/** -+ * DOC: Shader validator for VC4. -+ * -+ * The VC4 has no IOMMU between it and system memory. So, a user with access -+ * to execute shaders could escalate privilege by overwriting system memory -+ * (using the VPM write address register in the general-purpose DMA mode) or -+ * reading system memory it shouldn't (reading it as a texture, or uniform -+ * data, or vertex data). -+ * -+ * This walks over a shader starting from some offset within a BO, ensuring -+ * that its accesses are appropriately bounded, and recording how many texture -+ * accesses are made and where so that we can do relocations for them in the -+ * uniform stream. -+ * -+ * The kernel API has shaders stored in user-mapped BOs. The BOs will be -+ * forcibly unmapped from the process before validation, and any cache of -+ * validated state will be flushed if the mapping is faulted back in. -+ * -+ * Storing the shaders in BOs means that the validation process will be slow -+ * due to uncached reads, but since shaders are long-lived and shader BOs are -+ * never actually modified, this shouldn't be a problem. -+ */ -+ -+#include "vc4_drv.h" -+#include "vc4_qpu_defines.h" -+ -+struct vc4_shader_validation_state { -+ struct vc4_texture_sample_info tmu_setup[2]; -+ int tmu_write_count[2]; -+ -+ /* For registers that were last written to by a MIN instruction with -+ * one argument being a uniform, the address of the uniform. -+ * Otherwise, ~0. -+ * -+ * This is used for the validation of direct address memory reads. -+ */ -+ uint32_t live_min_clamp_offsets[32 + 32 + 4]; -+ bool live_max_clamp_regs[32 + 32 + 4]; -+}; -+ -+static uint32_t -+waddr_to_live_reg_index(uint32_t waddr, bool is_b) -+{ -+ if (waddr < 32) { -+ if (is_b) -+ return 32 + waddr; -+ else -+ return waddr; -+ } else if (waddr <= QPU_W_ACC3) { -+ -+ return 64 + waddr - QPU_W_ACC0; -+ } else { -+ return ~0; -+ } -+} -+ -+static uint32_t -+raddr_add_a_to_live_reg_index(uint64_t inst) -+{ -+ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); -+ uint32_t add_a = QPU_GET_FIELD(inst, QPU_ADD_A); -+ uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); -+ uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); -+ -+ if (add_a == QPU_MUX_A) { -+ return raddr_a; -+ } else if (add_a == QPU_MUX_B && sig != QPU_SIG_SMALL_IMM) { -+ return 32 + raddr_b; -+ } else if (add_a <= QPU_MUX_R3) { -+ return 64 + add_a; -+ } else { -+ return ~0; -+ } -+} -+ -+static bool -+is_tmu_submit(uint32_t waddr) -+{ -+ return (waddr == QPU_W_TMU0_S || -+ waddr == QPU_W_TMU1_S); -+} -+ -+static bool -+is_tmu_write(uint32_t waddr) -+{ -+ return (waddr >= QPU_W_TMU0_S && -+ waddr <= QPU_W_TMU1_B); -+} -+ -+static bool -+record_validated_texture_sample(struct vc4_validated_shader_info *validated_shader, -+ struct vc4_shader_validation_state *validation_state, -+ int tmu) -+{ -+ uint32_t s = validated_shader->num_texture_samples; -+ int i; -+ struct vc4_texture_sample_info *temp_samples; -+ -+ temp_samples = krealloc(validated_shader->texture_samples, -+ (s + 1) * sizeof(*temp_samples), -+ GFP_KERNEL); -+ if (!temp_samples) -+ return false; -+ -+ memcpy(&temp_samples[s], -+ &validation_state->tmu_setup[tmu], -+ sizeof(*temp_samples)); -+ -+ validated_shader->num_texture_samples = s + 1; -+ validated_shader->texture_samples = temp_samples; -+ -+ for (i = 0; i < 4; i++) -+ validation_state->tmu_setup[tmu].p_offset[i] = ~0; -+ -+ return true; -+} -+ -+static bool -+check_tmu_write(uint64_t inst, -+ struct vc4_validated_shader_info *validated_shader, -+ struct vc4_shader_validation_state *validation_state, -+ bool is_mul) -+{ -+ uint32_t waddr = (is_mul ? -+ QPU_GET_FIELD(inst, QPU_WADDR_MUL) : -+ QPU_GET_FIELD(inst, QPU_WADDR_ADD)); -+ uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); -+ uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); -+ int tmu = waddr > QPU_W_TMU0_B; -+ bool submit = is_tmu_submit(waddr); -+ bool is_direct = submit && validation_state->tmu_write_count[tmu] == 0; -+ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); -+ -+ if (is_direct) { -+ uint32_t add_b = QPU_GET_FIELD(inst, QPU_ADD_B); -+ uint32_t clamp_reg, clamp_offset; -+ -+ if (sig == QPU_SIG_SMALL_IMM) { -+ DRM_ERROR("direct TMU read used small immediate\n"); -+ return false; -+ } -+ -+ /* Make sure that this texture load is an add of the base -+ * address of the UBO to a clamped offset within the UBO. -+ */ -+ if (is_mul || -+ QPU_GET_FIELD(inst, QPU_OP_ADD) != QPU_A_ADD) { -+ DRM_ERROR("direct TMU load wasn't an add\n"); -+ return false; -+ } -+ -+ /* We assert that the the clamped address is the first -+ * argument, and the UBO base address is the second argument. -+ * This is arbitrary, but simpler than supporting flipping the -+ * two either way. -+ */ -+ clamp_reg = raddr_add_a_to_live_reg_index(inst); -+ if (clamp_reg == ~0) { -+ DRM_ERROR("direct TMU load wasn't clamped\n"); -+ return false; -+ } -+ -+ clamp_offset = validation_state->live_min_clamp_offsets[clamp_reg]; -+ if (clamp_offset == ~0) { -+ DRM_ERROR("direct TMU load wasn't clamped\n"); -+ return false; -+ } -+ -+ /* Store the clamp value's offset in p1 (see reloc_tex() in -+ * vc4_validate.c). -+ */ -+ validation_state->tmu_setup[tmu].p_offset[1] = -+ clamp_offset; -+ -+ if (!(add_b == QPU_MUX_A && raddr_a == QPU_R_UNIF) && -+ !(add_b == QPU_MUX_B && raddr_b == QPU_R_UNIF)) { -+ DRM_ERROR("direct TMU load didn't add to a uniform\n"); -+ return false; -+ } -+ -+ validation_state->tmu_setup[tmu].is_direct = true; -+ } else { -+ if (raddr_a == QPU_R_UNIF || (sig != QPU_SIG_SMALL_IMM && -+ raddr_b == QPU_R_UNIF)) { -+ DRM_ERROR("uniform read in the same instruction as " -+ "texture setup.\n"); -+ return false; -+ } -+ } -+ -+ if (validation_state->tmu_write_count[tmu] >= 4) { -+ DRM_ERROR("TMU%d got too many parameters before dispatch\n", -+ tmu); -+ return false; -+ } -+ validation_state->tmu_setup[tmu].p_offset[validation_state->tmu_write_count[tmu]] = -+ validated_shader->uniforms_size; -+ validation_state->tmu_write_count[tmu]++; -+ /* Since direct uses a RADDR uniform reference, it will get counted in -+ * check_instruction_reads() -+ */ -+ if (!is_direct) -+ validated_shader->uniforms_size += 4; -+ -+ if (submit) { -+ if (!record_validated_texture_sample(validated_shader, -+ validation_state, tmu)) { -+ return false; -+ } -+ -+ validation_state->tmu_write_count[tmu] = 0; -+ } -+ -+ return true; -+} -+ -+static bool -+check_register_write(uint64_t inst, -+ struct vc4_validated_shader_info *validated_shader, -+ struct vc4_shader_validation_state *validation_state, -+ bool is_mul) -+{ -+ uint32_t waddr = (is_mul ? -+ QPU_GET_FIELD(inst, QPU_WADDR_MUL) : -+ QPU_GET_FIELD(inst, QPU_WADDR_ADD)); -+ -+ switch (waddr) { -+ case QPU_W_UNIFORMS_ADDRESS: -+ /* XXX: We'll probably need to support this for reladdr, but -+ * it's definitely a security-related one. -+ */ -+ DRM_ERROR("uniforms address load unsupported\n"); -+ return false; -+ -+ case QPU_W_TLB_COLOR_MS: -+ case QPU_W_TLB_COLOR_ALL: -+ case QPU_W_TLB_Z: -+ /* These only interact with the tile buffer, not main memory, -+ * so they're safe. -+ */ -+ return true; -+ -+ case QPU_W_TMU0_S: -+ case QPU_W_TMU0_T: -+ case QPU_W_TMU0_R: -+ case QPU_W_TMU0_B: -+ case QPU_W_TMU1_S: -+ case QPU_W_TMU1_T: -+ case QPU_W_TMU1_R: -+ case QPU_W_TMU1_B: -+ return check_tmu_write(inst, validated_shader, validation_state, -+ is_mul); -+ -+ case QPU_W_HOST_INT: -+ case QPU_W_TMU_NOSWAP: -+ case QPU_W_TLB_ALPHA_MASK: -+ case QPU_W_MUTEX_RELEASE: -+ /* XXX: I haven't thought about these, so don't support them -+ * for now. -+ */ -+ DRM_ERROR("Unsupported waddr %d\n", waddr); -+ return false; -+ -+ case QPU_W_VPM_ADDR: -+ DRM_ERROR("General VPM DMA unsupported\n"); -+ return false; -+ -+ case QPU_W_VPM: -+ case QPU_W_VPMVCD_SETUP: -+ /* We allow VPM setup in general, even including VPM DMA -+ * configuration setup, because the (unsafe) DMA can only be -+ * triggered by QPU_W_VPM_ADDR writes. -+ */ -+ return true; -+ -+ case QPU_W_TLB_STENCIL_SETUP: -+ return true; -+ } -+ -+ return true; -+} -+ -+static void -+track_live_clamps(uint64_t inst, -+ struct vc4_validated_shader_info *validated_shader, -+ struct vc4_shader_validation_state *validation_state) -+{ -+ uint32_t op_add = QPU_GET_FIELD(inst, QPU_OP_ADD); -+ uint32_t waddr_add = QPU_GET_FIELD(inst, QPU_WADDR_ADD); -+ uint32_t waddr_mul = QPU_GET_FIELD(inst, QPU_WADDR_MUL); -+ uint32_t cond_add = QPU_GET_FIELD(inst, QPU_COND_ADD); -+ uint32_t add_a = QPU_GET_FIELD(inst, QPU_ADD_A); -+ uint32_t add_b = QPU_GET_FIELD(inst, QPU_ADD_B); -+ uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); -+ uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); -+ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); -+ bool ws = inst & QPU_WS; -+ uint32_t lri_add_a, lri_add, lri_mul; -+ bool add_a_is_min_0; -+ -+ /* Check whether OP_ADD's A argumennt comes from a live MAX(x, 0), -+ * before we clear previous live state. -+ */ -+ lri_add_a = raddr_add_a_to_live_reg_index(inst); -+ add_a_is_min_0 = (lri_add_a != ~0 && -+ validation_state->live_max_clamp_regs[lri_add_a]); -+ -+ /* Clear live state for registers written by our instruction. */ -+ lri_add = waddr_to_live_reg_index(waddr_add, ws); -+ lri_mul = waddr_to_live_reg_index(waddr_mul, !ws); -+ if (lri_mul != ~0) { -+ validation_state->live_max_clamp_regs[lri_mul] = false; -+ validation_state->live_min_clamp_offsets[lri_mul] = ~0; -+ } -+ if (lri_add != ~0) { -+ validation_state->live_max_clamp_regs[lri_add] = false; -+ validation_state->live_min_clamp_offsets[lri_add] = ~0; -+ } else { -+ /* Nothing further to do for live tracking, since only ADDs -+ * generate new live clamp registers. -+ */ -+ return; -+ } -+ -+ /* Now, handle remaining live clamp tracking for the ADD operation. */ -+ -+ if (cond_add != QPU_COND_ALWAYS) -+ return; -+ -+ if (op_add == QPU_A_MAX) { -+ /* Track live clamps of a value to a minimum of 0 (in either -+ * arg). -+ */ -+ if (sig != QPU_SIG_SMALL_IMM || raddr_b != 0 || -+ (add_a != QPU_MUX_B && add_b != QPU_MUX_B)) { -+ return; -+ } -+ -+ validation_state->live_max_clamp_regs[lri_add] = true; -+ } if (op_add == QPU_A_MIN) { -+ /* Track live clamps of a value clamped to a minimum of 0 and -+ * a maximum of some uniform's offset. -+ */ -+ if (!add_a_is_min_0) -+ return; -+ -+ if (!(add_b == QPU_MUX_A && raddr_a == QPU_R_UNIF) && -+ !(add_b == QPU_MUX_B && raddr_b == QPU_R_UNIF && -+ sig != QPU_SIG_SMALL_IMM)) { -+ return; -+ } -+ -+ validation_state->live_min_clamp_offsets[lri_add] = -+ validated_shader->uniforms_size; -+ } -+} -+ -+static bool -+check_instruction_writes(uint64_t inst, -+ struct vc4_validated_shader_info *validated_shader, -+ struct vc4_shader_validation_state *validation_state) -+{ -+ uint32_t waddr_add = QPU_GET_FIELD(inst, QPU_WADDR_ADD); -+ uint32_t waddr_mul = QPU_GET_FIELD(inst, QPU_WADDR_MUL); -+ bool ok; -+ -+ if (is_tmu_write(waddr_add) && is_tmu_write(waddr_mul)) { -+ DRM_ERROR("ADD and MUL both set up textures\n"); -+ return false; -+ } -+ -+ ok = (check_register_write(inst, validated_shader, validation_state, false) && -+ check_register_write(inst, validated_shader, validation_state, true)); -+ -+ track_live_clamps(inst, validated_shader, validation_state); -+ -+ return ok; -+} -+ -+static bool -+check_instruction_reads(uint64_t inst, -+ struct vc4_validated_shader_info *validated_shader) -+{ -+ uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); -+ uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); -+ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); -+ -+ if (raddr_a == QPU_R_UNIF || -+ (raddr_b == QPU_R_UNIF && sig != QPU_SIG_SMALL_IMM)) { -+ /* This can't overflow the uint32_t, because we're reading 8 -+ * bytes of instruction to increment by 4 here, so we'd -+ * already be OOM. -+ */ -+ validated_shader->uniforms_size += 4; -+ } -+ -+ return true; -+} -+ -+struct vc4_validated_shader_info * -+vc4_validate_shader(struct drm_gem_cma_object *shader_obj) -+{ -+ bool found_shader_end = false; -+ int shader_end_ip = 0; -+ uint32_t ip, max_ip; -+ uint64_t *shader; -+ struct vc4_validated_shader_info *validated_shader; -+ struct vc4_shader_validation_state validation_state; -+ int i; -+ -+ memset(&validation_state, 0, sizeof(validation_state)); -+ -+ for (i = 0; i < 8; i++) -+ validation_state.tmu_setup[i / 4].p_offset[i % 4] = ~0; -+ for (i = 0; i < ARRAY_SIZE(validation_state.live_min_clamp_offsets); i++) -+ validation_state.live_min_clamp_offsets[i] = ~0; -+ -+ shader = shader_obj->vaddr; -+ max_ip = shader_obj->base.size / sizeof(uint64_t); -+ -+ validated_shader = kcalloc(sizeof(*validated_shader), 1, GFP_KERNEL); -+ if (!validated_shader) -+ return NULL; -+ -+ for (ip = 0; ip < max_ip; ip++) { -+ uint64_t inst = shader[ip]; -+ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); -+ -+ switch (sig) { -+ case QPU_SIG_NONE: -+ case QPU_SIG_WAIT_FOR_SCOREBOARD: -+ case QPU_SIG_SCOREBOARD_UNLOCK: -+ case QPU_SIG_COLOR_LOAD: -+ case QPU_SIG_LOAD_TMU0: -+ case QPU_SIG_LOAD_TMU1: -+ case QPU_SIG_PROG_END: -+ case QPU_SIG_SMALL_IMM: -+ if (!check_instruction_writes(inst, validated_shader, -+ &validation_state)) { -+ DRM_ERROR("Bad write at ip %d\n", ip); -+ goto fail; -+ } -+ -+ if (!check_instruction_reads(inst, validated_shader)) -+ goto fail; -+ -+ if (sig == QPU_SIG_PROG_END) { -+ found_shader_end = true; -+ shader_end_ip = ip; -+ } -+ -+ break; -+ -+ case QPU_SIG_LOAD_IMM: -+ if (!check_instruction_writes(inst, validated_shader, -+ &validation_state)) { -+ DRM_ERROR("Bad LOAD_IMM write at ip %d\n", ip); -+ goto fail; -+ } -+ break; -+ -+ default: -+ DRM_ERROR("Unsupported QPU signal %d at " -+ "instruction %d\n", sig, ip); -+ goto fail; -+ } -+ -+ /* There are two delay slots after program end is signaled -+ * that are still executed, then we're finished. -+ */ -+ if (found_shader_end && ip == shader_end_ip + 2) -+ break; -+ } -+ -+ if (ip == max_ip) { -+ DRM_ERROR("shader failed to terminate before " -+ "shader BO end at %d\n", -+ shader_obj->base.size); -+ goto fail; -+ } -+ -+ /* Again, no chance of integer overflow here because the worst case -+ * scenario is 8 bytes of uniforms plus handles per 8-byte -+ * instruction. -+ */ -+ validated_shader->uniforms_src_size = -+ (validated_shader->uniforms_size + -+ 4 * validated_shader->num_texture_samples); -+ -+ return validated_shader; -+ -+fail: -+ if (validated_shader) { -+ kfree(validated_shader->texture_samples); -+ kfree(validated_shader); -+ } -+ return NULL; -+} ---- /dev/null -+++ b/include/uapi/drm/vc4_drm.h -@@ -0,0 +1,229 @@ -+/* -+ * Copyright © 2014-2015 Broadcom -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+ * IN THE SOFTWARE. -+ */ -+ -+#ifndef _UAPI_VC4_DRM_H_ -+#define _UAPI_VC4_DRM_H_ -+ -+#include -+ -+#define DRM_VC4_SUBMIT_CL 0x00 -+#define DRM_VC4_WAIT_SEQNO 0x01 -+#define DRM_VC4_WAIT_BO 0x02 -+#define DRM_VC4_CREATE_BO 0x03 -+#define DRM_VC4_MMAP_BO 0x04 -+#define DRM_VC4_CREATE_SHADER_BO 0x05 -+ -+#define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) -+#define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) -+#define DRM_IOCTL_VC4_WAIT_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_WAIT_BO, struct drm_vc4_wait_bo) -+#define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) -+#define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) -+#define DRM_IOCTL_VC4_CREATE_SHADER_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo) -+ -+struct drm_vc4_submit_rcl_surface { -+ uint32_t hindex; /* Handle index, or ~0 if not present. */ -+ uint32_t offset; /* Offset to start of buffer. */ -+ /* -+ * Bits for either render config (color_ms_write) or load/store packet. -+ */ -+ uint16_t bits; -+ uint16_t pad; -+}; -+ -+/** -+ * struct drm_vc4_submit_cl - ioctl argument for submitting commands to the 3D -+ * engine. -+ * -+ * Drivers typically use GPU BOs to store batchbuffers / command lists and -+ * their associated state. However, because the VC4 lacks an MMU, we have to -+ * do validation of memory accesses by the GPU commands. If we were to store -+ * our commands in BOs, we'd need to do uncached readback from them to do the -+ * validation process, which is too expensive. Instead, userspace accumulates -+ * commands and associated state in plain memory, then the kernel copies the -+ * data to its own address space, and then validates and stores it in a GPU -+ * BO. -+ */ -+struct drm_vc4_submit_cl { -+ /* Pointer to the binner command list. -+ * -+ * This is the first set of commands executed, which runs the -+ * coordinate shader to determine where primitives land on the screen, -+ * then writes out the state updates and draw calls necessary per tile -+ * to the tile allocation BO. -+ */ -+ uint64_t bin_cl; -+ -+ /* Pointer to the shader records. -+ * -+ * Shader records are the structures read by the hardware that contain -+ * pointers to uniforms, shaders, and vertex attributes. The -+ * reference to the shader record has enough information to determine -+ * how many pointers are necessary (fixed number for shaders/uniforms, -+ * and an attribute count), so those BO indices into bo_handles are -+ * just stored as uint32_ts before each shader record passed in. -+ */ -+ uint64_t shader_rec; -+ -+ /* Pointer to uniform data and texture handles for the textures -+ * referenced by the shader. -+ * -+ * For each shader state record, there is a set of uniform data in the -+ * order referenced by the record (FS, VS, then CS). Each set of -+ * uniform data has a uint32_t index into bo_handles per texture -+ * sample operation, in the order the QPU_W_TMUn_S writes appear in -+ * the program. Following the texture BO handle indices is the actual -+ * uniform data. -+ * -+ * The individual uniform state blocks don't have sizes passed in, -+ * because the kernel has to determine the sizes anyway during shader -+ * code validation. -+ */ -+ uint64_t uniforms; -+ uint64_t bo_handles; -+ -+ /* Size in bytes of the binner command list. */ -+ uint32_t bin_cl_size; -+ /* Size in bytes of the set of shader records. */ -+ uint32_t shader_rec_size; -+ /* Number of shader records. -+ * -+ * This could just be computed from the contents of shader_records and -+ * the address bits of references to them from the bin CL, but it -+ * keeps the kernel from having to resize some allocations it makes. -+ */ -+ uint32_t shader_rec_count; -+ /* Size in bytes of the uniform state. */ -+ uint32_t uniforms_size; -+ -+ /* Number of BO handles passed in (size is that times 4). */ -+ uint32_t bo_handle_count; -+ -+ /* RCL setup: */ -+ uint16_t width; -+ uint16_t height; -+ uint8_t min_x_tile; -+ uint8_t min_y_tile; -+ uint8_t max_x_tile; -+ uint8_t max_y_tile; -+ struct drm_vc4_submit_rcl_surface color_read; -+ struct drm_vc4_submit_rcl_surface color_ms_write; -+ struct drm_vc4_submit_rcl_surface zs_read; -+ struct drm_vc4_submit_rcl_surface zs_write; -+ uint32_t clear_color[2]; -+ uint32_t clear_z; -+ uint8_t clear_s; -+ -+ uint32_t pad:24; -+ -+#define VC4_SUBMIT_CL_USE_CLEAR_COLOR (1 << 0) -+ uint32_t flags; -+ -+ /* Returned value of the seqno of this render job (for the -+ * wait ioctl). -+ */ -+ uint64_t seqno; -+}; -+ -+/** -+ * struct drm_vc4_wait_seqno - ioctl argument for waiting for -+ * DRM_VC4_SUBMIT_CL completion using its returned seqno. -+ * -+ * timeout_ns is the timeout in nanoseconds, where "0" means "don't -+ * block, just return the status." -+ */ -+struct drm_vc4_wait_seqno { -+ uint64_t seqno; -+ uint64_t timeout_ns; -+}; -+ -+/** -+ * struct drm_vc4_wait_bo - ioctl argument for waiting for -+ * completion of the last DRM_VC4_SUBMIT_CL on a BO. -+ * -+ * This is useful for cases where multiple processes might be -+ * rendering to a BO and you want to wait for all rendering to be -+ * completed. -+ */ -+struct drm_vc4_wait_bo { -+ uint32_t handle; -+ uint32_t pad; -+ uint64_t timeout_ns; -+}; -+ -+/** -+ * struct drm_vc4_create_bo - ioctl argument for creating VC4 BOs. -+ * -+ * There are currently no values for the flags argument, but it may be -+ * used in a future extension. -+ */ -+struct drm_vc4_create_bo { -+ uint32_t size; -+ uint32_t flags; -+ /** Returned GEM handle for the BO. */ -+ uint32_t handle; -+ uint32_t pad; -+}; -+ -+/** -+ * struct drm_vc4_create_shader_bo - ioctl argument for creating VC4 -+ * shader BOs. -+ * -+ * Since allowing a shader to be overwritten while it's also being -+ * executed from would allow privlege escalation, shaders must be -+ * created using this ioctl, and they can't be mmapped later. -+ */ -+struct drm_vc4_create_shader_bo { -+ /* Size of the data argument. */ -+ uint32_t size; -+ /* Flags, currently must be 0. */ -+ uint32_t flags; -+ -+ /* Pointer to the data. */ -+ uint64_t data; -+ -+ /** Returned GEM handle for the BO. */ -+ uint32_t handle; -+ /* Pad, must be 0. */ -+ uint32_t pad; -+}; -+ -+/** -+ * struct drm_vc4_mmap_bo - ioctl argument for mapping VC4 BOs. -+ * -+ * This doesn't actually perform an mmap. Instead, it returns the -+ * offset you need to use in an mmap on the DRM device node. This -+ * means that tools like valgrind end up knowing about the mapped -+ * memory. -+ * -+ * There are currently no values for the flags argument, but it may be -+ * used in a future extension. -+ */ -+struct drm_vc4_mmap_bo { -+ /** Handle for the object being mapped. */ -+ uint32_t handle; -+ uint32_t flags; -+ /** offset into the drm node to use for subsequent mmap call. */ -+ uint64_t offset; -+}; -+ -+#endif /* _UAPI_VC4_DRM_H_ */ diff --git a/target/linux/brcm2708/patches-4.4/0091-drm-vc4-Force-HDMI-to-connected.patch b/target/linux/brcm2708/patches-4.4/0091-drm-vc4-Force-HDMI-to-connected.patch new file mode 100644 index 0000000000..f8251ccb8f --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0091-drm-vc4-Force-HDMI-to-connected.patch @@ -0,0 +1,23 @@ +From fbec01e2d17b924d91850e17eeecf975e74c9ebf Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Wed, 14 Oct 2015 11:32:14 -0700 +Subject: [PATCH 091/232] drm/vc4: Force HDMI to connected. + +For some reason on the downstream tree, the HPD GPIO isn't working. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -164,6 +164,8 @@ vc4_hdmi_connector_detect(struct drm_con + struct drm_device *dev = connector->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + ++ return connector_status_connected; ++ + if (vc4->hdmi->hpd_gpio) { + if (gpio_get_value(vc4->hdmi->hpd_gpio)) + return connector_status_connected; diff --git a/target/linux/brcm2708/patches-4.4/0092-drm-vc4-Force-HDMI-to-connected.patch b/target/linux/brcm2708/patches-4.4/0092-drm-vc4-Force-HDMI-to-connected.patch deleted file mode 100644 index 3c3417ca87..0000000000 --- a/target/linux/brcm2708/patches-4.4/0092-drm-vc4-Force-HDMI-to-connected.patch +++ /dev/null @@ -1,23 +0,0 @@ -From aa579c7485e1bae681c50b223e89cd658dd0b2c1 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Wed, 14 Oct 2015 11:32:14 -0700 -Subject: [PATCH 092/170] drm/vc4: Force HDMI to connected. - -For some reason on the downstream tree, the HPD GPIO isn't working. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/vc4_hdmi.c | 2 ++ - 1 file changed, 2 insertions(+) - ---- a/drivers/gpu/drm/vc4/vc4_hdmi.c -+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c -@@ -164,6 +164,8 @@ vc4_hdmi_connector_detect(struct drm_con - struct drm_device *dev = connector->dev; - struct vc4_dev *vc4 = to_vc4_dev(dev); - -+ return connector_status_connected; -+ - if (vc4->hdmi->hpd_gpio) { - if (gpio_get_value(vc4->hdmi->hpd_gpio)) - return connector_status_connected; diff --git a/target/linux/brcm2708/patches-4.4/0092-drm-vc4-bo-cache-locking-fixes.patch b/target/linux/brcm2708/patches-4.4/0092-drm-vc4-bo-cache-locking-fixes.patch new file mode 100644 index 0000000000..3a7bec723d --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0092-drm-vc4-bo-cache-locking-fixes.patch @@ -0,0 +1,147 @@ +From d27b72cbcf572b0e2c18359f494d1f6aa90dd6f3 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 19 Oct 2015 08:23:18 -0700 +Subject: [PATCH 092/232] drm/vc4: bo cache locking fixes. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_bo.c | 32 ++++++++++++++++++-------------- + drivers/gpu/drm/vc4/vc4_drv.h | 2 +- + 2 files changed, 19 insertions(+), 15 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_bo.c ++++ b/drivers/gpu/drm/vc4/vc4_bo.c +@@ -112,14 +112,14 @@ void vc4_bo_cache_purge(struct drm_devic + { + struct vc4_dev *vc4 = to_vc4_dev(dev); + +- spin_lock(&vc4->bo_lock); ++ mutex_lock(&vc4->bo_lock); + while (!list_empty(&vc4->bo_cache.time_list)) { + struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, + struct vc4_bo, unref_head); + vc4_bo_remove_from_cache(bo); + vc4_bo_destroy(bo); + } +- spin_unlock(&vc4->bo_lock); ++ mutex_unlock(&vc4->bo_lock); + } + + struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size) +@@ -134,18 +134,18 @@ struct vc4_bo *vc4_bo_create(struct drm_ + return NULL; + + /* First, try to get a vc4_bo from the kernel BO cache. */ +- spin_lock(&vc4->bo_lock); ++ mutex_lock(&vc4->bo_lock); + if (page_index < vc4->bo_cache.size_list_size && + !list_empty(&vc4->bo_cache.size_list[page_index])) { + struct vc4_bo *bo = + list_first_entry(&vc4->bo_cache.size_list[page_index], + struct vc4_bo, size_head); + vc4_bo_remove_from_cache(bo); +- spin_unlock(&vc4->bo_lock); ++ mutex_unlock(&vc4->bo_lock); + kref_init(&bo->base.base.refcount); + return bo; + } +- spin_unlock(&vc4->bo_lock); ++ mutex_unlock(&vc4->bo_lock); + + /* Otherwise, make a new BO. */ + for (pass = 0; ; pass++) { +@@ -215,7 +215,7 @@ vc4_bo_cache_free_old(struct drm_device + struct vc4_dev *vc4 = to_vc4_dev(dev); + unsigned long expire_time = jiffies - msecs_to_jiffies(1000); + +- spin_lock(&vc4->bo_lock); ++ mutex_lock(&vc4->bo_lock); + while (!list_empty(&vc4->bo_cache.time_list)) { + struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, + struct vc4_bo, unref_head); +@@ -223,14 +223,14 @@ vc4_bo_cache_free_old(struct drm_device + mod_timer(&vc4->bo_cache.time_timer, + round_jiffies_up(jiffies + + msecs_to_jiffies(1000))); +- spin_unlock(&vc4->bo_lock); ++ mutex_unlock(&vc4->bo_lock); + return; + } + + vc4_bo_remove_from_cache(bo); + vc4_bo_destroy(bo); + } +- spin_unlock(&vc4->bo_lock); ++ mutex_unlock(&vc4->bo_lock); + } + + /* Called on the last userspace/kernel unreference of the BO. Returns +@@ -248,21 +248,25 @@ void vc4_free_object(struct drm_gem_obje + /* If the object references someone else's memory, we can't cache it. + */ + if (gem_bo->import_attach) { ++ mutex_lock(&vc4->bo_lock); + vc4_bo_destroy(bo); ++ mutex_unlock(&vc4->bo_lock); + return; + } + + /* Don't cache if it was publicly named. */ + if (gem_bo->name) { ++ mutex_lock(&vc4->bo_lock); + vc4_bo_destroy(bo); ++ mutex_unlock(&vc4->bo_lock); + return; + } + +- spin_lock(&vc4->bo_lock); ++ mutex_lock(&vc4->bo_lock); + cache_list = vc4_get_cache_list_for_size(dev, gem_bo->size); + if (!cache_list) { + vc4_bo_destroy(bo); +- spin_unlock(&vc4->bo_lock); ++ mutex_unlock(&vc4->bo_lock); + return; + } + +@@ -278,7 +282,7 @@ void vc4_free_object(struct drm_gem_obje + + vc4->bo_stats.num_cached++; + vc4->bo_stats.size_cached += gem_bo->size; +- spin_unlock(&vc4->bo_lock); ++ mutex_unlock(&vc4->bo_lock); + + vc4_bo_cache_free_old(dev); + } +@@ -465,7 +469,7 @@ void vc4_bo_cache_init(struct drm_device + { + struct vc4_dev *vc4 = to_vc4_dev(dev); + +- spin_lock_init(&vc4->bo_lock); ++ mutex_init(&vc4->bo_lock); + + INIT_LIST_HEAD(&vc4->bo_cache.time_list); + +@@ -498,9 +502,9 @@ int vc4_bo_stats_debugfs(struct seq_file + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_bo_stats stats; + +- spin_lock(&vc4->bo_lock); ++ mutex_lock(&vc4->bo_lock); + stats = vc4->bo_stats; +- spin_unlock(&vc4->bo_lock); ++ mutex_unlock(&vc4->bo_lock); + + seq_printf(m, "num bos allocated: %d\n", stats.num_allocated); + seq_printf(m, "size bos allocated: %dkb\n", stats.size_allocated / 1024); +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -49,7 +49,7 @@ struct vc4_dev { + } bo_stats; + + /* Protects bo_cache and the BO stats. */ +- spinlock_t bo_lock; ++ struct mutex bo_lock; + + /* Sequence number for the last job queued in job_list. + * Starts at 0 (no jobs emitted). diff --git a/target/linux/brcm2708/patches-4.4/0093-drm-vc4-bo-cache-locking-cleanup.patch b/target/linux/brcm2708/patches-4.4/0093-drm-vc4-bo-cache-locking-cleanup.patch new file mode 100644 index 0000000000..369b1d8dae --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0093-drm-vc4-bo-cache-locking-cleanup.patch @@ -0,0 +1,92 @@ +From 6473779beaeda1017d8487c3f510015f2b9b8f9d Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 19 Oct 2015 08:29:41 -0700 +Subject: [PATCH 093/232] drm/vc4: bo cache locking cleanup. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_bo.c | 22 +++++++++------------- + 1 file changed, 9 insertions(+), 13 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_bo.c ++++ b/drivers/gpu/drm/vc4/vc4_bo.c +@@ -215,7 +215,6 @@ vc4_bo_cache_free_old(struct drm_device + struct vc4_dev *vc4 = to_vc4_dev(dev); + unsigned long expire_time = jiffies - msecs_to_jiffies(1000); + +- mutex_lock(&vc4->bo_lock); + while (!list_empty(&vc4->bo_cache.time_list)) { + struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, + struct vc4_bo, unref_head); +@@ -223,14 +222,12 @@ vc4_bo_cache_free_old(struct drm_device + mod_timer(&vc4->bo_cache.time_timer, + round_jiffies_up(jiffies + + msecs_to_jiffies(1000))); +- mutex_unlock(&vc4->bo_lock); + return; + } + + vc4_bo_remove_from_cache(bo); + vc4_bo_destroy(bo); + } +- mutex_unlock(&vc4->bo_lock); + } + + /* Called on the last userspace/kernel unreference of the BO. Returns +@@ -245,29 +242,24 @@ void vc4_free_object(struct drm_gem_obje + struct vc4_bo *bo = to_vc4_bo(gem_bo); + struct list_head *cache_list; + ++ mutex_lock(&vc4->bo_lock); + /* If the object references someone else's memory, we can't cache it. + */ + if (gem_bo->import_attach) { +- mutex_lock(&vc4->bo_lock); + vc4_bo_destroy(bo); +- mutex_unlock(&vc4->bo_lock); +- return; ++ goto out; + } + + /* Don't cache if it was publicly named. */ + if (gem_bo->name) { +- mutex_lock(&vc4->bo_lock); + vc4_bo_destroy(bo); +- mutex_unlock(&vc4->bo_lock); +- return; ++ goto out; + } + +- mutex_lock(&vc4->bo_lock); + cache_list = vc4_get_cache_list_for_size(dev, gem_bo->size); + if (!cache_list) { + vc4_bo_destroy(bo); +- mutex_unlock(&vc4->bo_lock); +- return; ++ goto out; + } + + if (bo->validated_shader) { +@@ -282,9 +274,11 @@ void vc4_free_object(struct drm_gem_obje + + vc4->bo_stats.num_cached++; + vc4->bo_stats.size_cached += gem_bo->size; +- mutex_unlock(&vc4->bo_lock); + + vc4_bo_cache_free_old(dev); ++ ++out: ++ mutex_unlock(&vc4->bo_lock); + } + + static void vc4_bo_cache_time_work(struct work_struct *work) +@@ -293,7 +287,9 @@ static void vc4_bo_cache_time_work(struc + container_of(work, struct vc4_dev, bo_cache.time_work); + struct drm_device *dev = vc4->dev; + ++ mutex_lock(&vc4->bo_lock); + vc4_bo_cache_free_old(dev); ++ mutex_unlock(&vc4->bo_lock); + } + + static void vc4_bo_cache_time_timer(unsigned long data) diff --git a/target/linux/brcm2708/patches-4.4/0093-drm-vc4-bo-cache-locking-fixes.patch b/target/linux/brcm2708/patches-4.4/0093-drm-vc4-bo-cache-locking-fixes.patch deleted file mode 100644 index 927d0dc782..0000000000 --- a/target/linux/brcm2708/patches-4.4/0093-drm-vc4-bo-cache-locking-fixes.patch +++ /dev/null @@ -1,147 +0,0 @@ -From edb828d91374e8fa4f05d68be008c40d4f9a158d Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Mon, 19 Oct 2015 08:23:18 -0700 -Subject: [PATCH 093/170] drm/vc4: bo cache locking fixes. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/vc4_bo.c | 32 ++++++++++++++++++-------------- - drivers/gpu/drm/vc4/vc4_drv.h | 2 +- - 2 files changed, 19 insertions(+), 15 deletions(-) - ---- a/drivers/gpu/drm/vc4/vc4_bo.c -+++ b/drivers/gpu/drm/vc4/vc4_bo.c -@@ -112,14 +112,14 @@ void vc4_bo_cache_purge(struct drm_devic - { - struct vc4_dev *vc4 = to_vc4_dev(dev); - -- spin_lock(&vc4->bo_lock); -+ mutex_lock(&vc4->bo_lock); - while (!list_empty(&vc4->bo_cache.time_list)) { - struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, - struct vc4_bo, unref_head); - vc4_bo_remove_from_cache(bo); - vc4_bo_destroy(bo); - } -- spin_unlock(&vc4->bo_lock); -+ mutex_unlock(&vc4->bo_lock); - } - - struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size) -@@ -134,18 +134,18 @@ struct vc4_bo *vc4_bo_create(struct drm_ - return NULL; - - /* First, try to get a vc4_bo from the kernel BO cache. */ -- spin_lock(&vc4->bo_lock); -+ mutex_lock(&vc4->bo_lock); - if (page_index < vc4->bo_cache.size_list_size && - !list_empty(&vc4->bo_cache.size_list[page_index])) { - struct vc4_bo *bo = - list_first_entry(&vc4->bo_cache.size_list[page_index], - struct vc4_bo, size_head); - vc4_bo_remove_from_cache(bo); -- spin_unlock(&vc4->bo_lock); -+ mutex_unlock(&vc4->bo_lock); - kref_init(&bo->base.base.refcount); - return bo; - } -- spin_unlock(&vc4->bo_lock); -+ mutex_unlock(&vc4->bo_lock); - - /* Otherwise, make a new BO. */ - for (pass = 0; ; pass++) { -@@ -215,7 +215,7 @@ vc4_bo_cache_free_old(struct drm_device - struct vc4_dev *vc4 = to_vc4_dev(dev); - unsigned long expire_time = jiffies - msecs_to_jiffies(1000); - -- spin_lock(&vc4->bo_lock); -+ mutex_lock(&vc4->bo_lock); - while (!list_empty(&vc4->bo_cache.time_list)) { - struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, - struct vc4_bo, unref_head); -@@ -223,14 +223,14 @@ vc4_bo_cache_free_old(struct drm_device - mod_timer(&vc4->bo_cache.time_timer, - round_jiffies_up(jiffies + - msecs_to_jiffies(1000))); -- spin_unlock(&vc4->bo_lock); -+ mutex_unlock(&vc4->bo_lock); - return; - } - - vc4_bo_remove_from_cache(bo); - vc4_bo_destroy(bo); - } -- spin_unlock(&vc4->bo_lock); -+ mutex_unlock(&vc4->bo_lock); - } - - /* Called on the last userspace/kernel unreference of the BO. Returns -@@ -248,21 +248,25 @@ void vc4_free_object(struct drm_gem_obje - /* If the object references someone else's memory, we can't cache it. - */ - if (gem_bo->import_attach) { -+ mutex_lock(&vc4->bo_lock); - vc4_bo_destroy(bo); -+ mutex_unlock(&vc4->bo_lock); - return; - } - - /* Don't cache if it was publicly named. */ - if (gem_bo->name) { -+ mutex_lock(&vc4->bo_lock); - vc4_bo_destroy(bo); -+ mutex_unlock(&vc4->bo_lock); - return; - } - -- spin_lock(&vc4->bo_lock); -+ mutex_lock(&vc4->bo_lock); - cache_list = vc4_get_cache_list_for_size(dev, gem_bo->size); - if (!cache_list) { - vc4_bo_destroy(bo); -- spin_unlock(&vc4->bo_lock); -+ mutex_unlock(&vc4->bo_lock); - return; - } - -@@ -278,7 +282,7 @@ void vc4_free_object(struct drm_gem_obje - - vc4->bo_stats.num_cached++; - vc4->bo_stats.size_cached += gem_bo->size; -- spin_unlock(&vc4->bo_lock); -+ mutex_unlock(&vc4->bo_lock); - - vc4_bo_cache_free_old(dev); - } -@@ -465,7 +469,7 @@ void vc4_bo_cache_init(struct drm_device - { - struct vc4_dev *vc4 = to_vc4_dev(dev); - -- spin_lock_init(&vc4->bo_lock); -+ mutex_init(&vc4->bo_lock); - - INIT_LIST_HEAD(&vc4->bo_cache.time_list); - -@@ -498,9 +502,9 @@ int vc4_bo_stats_debugfs(struct seq_file - struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_bo_stats stats; - -- spin_lock(&vc4->bo_lock); -+ mutex_lock(&vc4->bo_lock); - stats = vc4->bo_stats; -- spin_unlock(&vc4->bo_lock); -+ mutex_unlock(&vc4->bo_lock); - - seq_printf(m, "num bos allocated: %d\n", stats.num_allocated); - seq_printf(m, "size bos allocated: %dkb\n", stats.size_allocated / 1024); ---- a/drivers/gpu/drm/vc4/vc4_drv.h -+++ b/drivers/gpu/drm/vc4/vc4_drv.h -@@ -49,7 +49,7 @@ struct vc4_dev { - } bo_stats; - - /* Protects bo_cache and the BO stats. */ -- spinlock_t bo_lock; -+ struct mutex bo_lock; - - /* Sequence number for the last job queued in job_list. - * Starts at 0 (no jobs emitted). diff --git a/target/linux/brcm2708/patches-4.4/0094-drm-vc4-Use-job_lock-to-protect-seqno_cb_list.patch b/target/linux/brcm2708/patches-4.4/0094-drm-vc4-Use-job_lock-to-protect-seqno_cb_list.patch new file mode 100644 index 0000000000..b86d07ec29 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0094-drm-vc4-Use-job_lock-to-protect-seqno_cb_list.patch @@ -0,0 +1,54 @@ +From 1ec9d09f4b9ec69b3b0422fa3c26b3bcc82af64e Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 19 Oct 2015 08:32:24 -0700 +Subject: [PATCH 094/232] drm/vc4: Use job_lock to protect seqno_cb_list. + +We're (mostly) not supposed to be using struct_mutex in drivers these +days. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_gem.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_gem.c ++++ b/drivers/gpu/drm/vc4/vc4_gem.c +@@ -474,7 +474,6 @@ vc4_job_handle_completed(struct vc4_dev + vc4_complete_exec(exec); + spin_lock_irqsave(&vc4->job_lock, irqflags); + } +- spin_unlock_irqrestore(&vc4->job_lock, irqflags); + + list_for_each_entry_safe(cb, cb_temp, &vc4->seqno_cb_list, work.entry) { + if (cb->seqno <= vc4->finished_seqno) { +@@ -482,6 +481,8 @@ vc4_job_handle_completed(struct vc4_dev + schedule_work(&cb->work); + } + } ++ ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); + } + + static void vc4_seqno_cb_work(struct work_struct *work) +@@ -496,18 +497,19 @@ int vc4_queue_seqno_cb(struct drm_device + { + struct vc4_dev *vc4 = to_vc4_dev(dev); + int ret = 0; ++ unsigned long irqflags; + + cb->func = func; + INIT_WORK(&cb->work, vc4_seqno_cb_work); + +- mutex_lock(&dev->struct_mutex); ++ spin_lock_irqsave(&vc4->job_lock, irqflags); + if (seqno > vc4->finished_seqno) { + cb->seqno = seqno; + list_add_tail(&cb->work.entry, &vc4->seqno_cb_list); + } else { + schedule_work(&cb->work); + } +- mutex_unlock(&dev->struct_mutex); ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); + + return ret; + } diff --git a/target/linux/brcm2708/patches-4.4/0094-drm-vc4-bo-cache-locking-cleanup.patch b/target/linux/brcm2708/patches-4.4/0094-drm-vc4-bo-cache-locking-cleanup.patch deleted file mode 100644 index bf61ac14cf..0000000000 --- a/target/linux/brcm2708/patches-4.4/0094-drm-vc4-bo-cache-locking-cleanup.patch +++ /dev/null @@ -1,92 +0,0 @@ -From eafd7a031a15fb0d51a86b89e42ccde4f23434b4 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Mon, 19 Oct 2015 08:29:41 -0700 -Subject: [PATCH 094/170] drm/vc4: bo cache locking cleanup. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/vc4_bo.c | 22 +++++++++------------- - 1 file changed, 9 insertions(+), 13 deletions(-) - ---- a/drivers/gpu/drm/vc4/vc4_bo.c -+++ b/drivers/gpu/drm/vc4/vc4_bo.c -@@ -215,7 +215,6 @@ vc4_bo_cache_free_old(struct drm_device - struct vc4_dev *vc4 = to_vc4_dev(dev); - unsigned long expire_time = jiffies - msecs_to_jiffies(1000); - -- mutex_lock(&vc4->bo_lock); - while (!list_empty(&vc4->bo_cache.time_list)) { - struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, - struct vc4_bo, unref_head); -@@ -223,14 +222,12 @@ vc4_bo_cache_free_old(struct drm_device - mod_timer(&vc4->bo_cache.time_timer, - round_jiffies_up(jiffies + - msecs_to_jiffies(1000))); -- mutex_unlock(&vc4->bo_lock); - return; - } - - vc4_bo_remove_from_cache(bo); - vc4_bo_destroy(bo); - } -- mutex_unlock(&vc4->bo_lock); - } - - /* Called on the last userspace/kernel unreference of the BO. Returns -@@ -245,29 +242,24 @@ void vc4_free_object(struct drm_gem_obje - struct vc4_bo *bo = to_vc4_bo(gem_bo); - struct list_head *cache_list; - -+ mutex_lock(&vc4->bo_lock); - /* If the object references someone else's memory, we can't cache it. - */ - if (gem_bo->import_attach) { -- mutex_lock(&vc4->bo_lock); - vc4_bo_destroy(bo); -- mutex_unlock(&vc4->bo_lock); -- return; -+ goto out; - } - - /* Don't cache if it was publicly named. */ - if (gem_bo->name) { -- mutex_lock(&vc4->bo_lock); - vc4_bo_destroy(bo); -- mutex_unlock(&vc4->bo_lock); -- return; -+ goto out; - } - -- mutex_lock(&vc4->bo_lock); - cache_list = vc4_get_cache_list_for_size(dev, gem_bo->size); - if (!cache_list) { - vc4_bo_destroy(bo); -- mutex_unlock(&vc4->bo_lock); -- return; -+ goto out; - } - - if (bo->validated_shader) { -@@ -282,9 +274,11 @@ void vc4_free_object(struct drm_gem_obje - - vc4->bo_stats.num_cached++; - vc4->bo_stats.size_cached += gem_bo->size; -- mutex_unlock(&vc4->bo_lock); - - vc4_bo_cache_free_old(dev); -+ -+out: -+ mutex_unlock(&vc4->bo_lock); - } - - static void vc4_bo_cache_time_work(struct work_struct *work) -@@ -293,7 +287,9 @@ static void vc4_bo_cache_time_work(struc - container_of(work, struct vc4_dev, bo_cache.time_work); - struct drm_device *dev = vc4->dev; - -+ mutex_lock(&vc4->bo_lock); - vc4_bo_cache_free_old(dev); -+ mutex_unlock(&vc4->bo_lock); - } - - static void vc4_bo_cache_time_timer(unsigned long data) diff --git a/target/linux/brcm2708/patches-4.4/0095-drm-vc4-Drop-struct_mutex-around-CL-validation.patch b/target/linux/brcm2708/patches-4.4/0095-drm-vc4-Drop-struct_mutex-around-CL-validation.patch new file mode 100644 index 0000000000..91f7d7ca64 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0095-drm-vc4-Drop-struct_mutex-around-CL-validation.patch @@ -0,0 +1,63 @@ +From 016847d7cd5222d15f9213dc772d3808cd5d6e17 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 19 Oct 2015 08:44:35 -0700 +Subject: [PATCH 095/232] drm/vc4: Drop struct_mutex around CL validation. + +We were using it so that we could make sure that shader validation +state didn't change while we were validating, but now shader +validation state is immutable. The bcl/rcl generation doesn't do any +other BO dereferencing, and seems to have no other global state +dependency not covered by job_lock / bo_lock. + +Fixes a lock order reversal between mmap_sem and struct_mutex. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_gem.c | 12 ++++-------- + 1 file changed, 4 insertions(+), 8 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_gem.c ++++ b/drivers/gpu/drm/vc4/vc4_gem.c +@@ -244,13 +244,15 @@ static void + vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec) + { + struct vc4_dev *vc4 = to_vc4_dev(dev); +- uint64_t seqno = ++vc4->emit_seqno; ++ uint64_t seqno; + unsigned long irqflags; + ++ spin_lock_irqsave(&vc4->job_lock, irqflags); ++ ++ seqno = ++vc4->emit_seqno; + exec->seqno = seqno; + vc4_update_bo_seqnos(exec, seqno); + +- spin_lock_irqsave(&vc4->job_lock, irqflags); + list_add_tail(&exec->head, &vc4->job_list); + + /* If no job was executing, kick ours off. Otherwise, it'll +@@ -608,8 +610,6 @@ vc4_submit_cl_ioctl(struct drm_device *d + exec->args = args; + INIT_LIST_HEAD(&exec->unref_list); + +- mutex_lock(&dev->struct_mutex); +- + ret = vc4_cl_lookup_bos(dev, file_priv, exec); + if (ret) + goto fail; +@@ -636,15 +636,11 @@ vc4_submit_cl_ioctl(struct drm_device *d + /* Return the seqno for our job. */ + args->seqno = vc4->emit_seqno; + +- mutex_unlock(&dev->struct_mutex); +- + return 0; + + fail: + vc4_complete_exec(exec); + +- mutex_unlock(&dev->struct_mutex); +- + return ret; + } + diff --git a/target/linux/brcm2708/patches-4.4/0095-drm-vc4-Use-job_lock-to-protect-seqno_cb_list.patch b/target/linux/brcm2708/patches-4.4/0095-drm-vc4-Use-job_lock-to-protect-seqno_cb_list.patch deleted file mode 100644 index 730355aea6..0000000000 --- a/target/linux/brcm2708/patches-4.4/0095-drm-vc4-Use-job_lock-to-protect-seqno_cb_list.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 482f2b3e97e919ac0ebc55a6040747495563b9a7 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Mon, 19 Oct 2015 08:32:24 -0700 -Subject: [PATCH 095/170] drm/vc4: Use job_lock to protect seqno_cb_list. - -We're (mostly) not supposed to be using struct_mutex in drivers these -days. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/vc4_gem.c | 8 +++++--- - 1 file changed, 5 insertions(+), 3 deletions(-) - ---- a/drivers/gpu/drm/vc4/vc4_gem.c -+++ b/drivers/gpu/drm/vc4/vc4_gem.c -@@ -474,7 +474,6 @@ vc4_job_handle_completed(struct vc4_dev - vc4_complete_exec(exec); - spin_lock_irqsave(&vc4->job_lock, irqflags); - } -- spin_unlock_irqrestore(&vc4->job_lock, irqflags); - - list_for_each_entry_safe(cb, cb_temp, &vc4->seqno_cb_list, work.entry) { - if (cb->seqno <= vc4->finished_seqno) { -@@ -482,6 +481,8 @@ vc4_job_handle_completed(struct vc4_dev - schedule_work(&cb->work); - } - } -+ -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); - } - - static void vc4_seqno_cb_work(struct work_struct *work) -@@ -496,18 +497,19 @@ int vc4_queue_seqno_cb(struct drm_device - { - struct vc4_dev *vc4 = to_vc4_dev(dev); - int ret = 0; -+ unsigned long irqflags; - - cb->func = func; - INIT_WORK(&cb->work, vc4_seqno_cb_work); - -- mutex_lock(&dev->struct_mutex); -+ spin_lock_irqsave(&vc4->job_lock, irqflags); - if (seqno > vc4->finished_seqno) { - cb->seqno = seqno; - list_add_tail(&cb->work.entry, &vc4->seqno_cb_list); - } else { - schedule_work(&cb->work); - } -- mutex_unlock(&dev->struct_mutex); -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); - - return ret; - } diff --git a/target/linux/brcm2708/patches-4.4/0096-drm-vc4-Drop-struct_mutex-around-CL-validation.patch b/target/linux/brcm2708/patches-4.4/0096-drm-vc4-Drop-struct_mutex-around-CL-validation.patch index 75adab38d9..07c4d93f33 100644 --- a/target/linux/brcm2708/patches-4.4/0096-drm-vc4-Drop-struct_mutex-around-CL-validation.patch +++ b/target/linux/brcm2708/patches-4.4/0096-drm-vc4-Drop-struct_mutex-around-CL-validation.patch @@ -1,63 +1,74 @@ -From 4dc15a296586679d5b014011e44a093cf962123e Mon Sep 17 00:00:00 2001 +From 69aa18660bbe1ee6524c8220beb2af711146bc7f Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 19 Oct 2015 08:44:35 -0700 -Subject: [PATCH 096/170] drm/vc4: Drop struct_mutex around CL validation. +Subject: [PATCH 096/232] drm/vc4: Drop struct_mutex around CL validation. We were using it so that we could make sure that shader validation state didn't change while we were validating, but now shader validation state is immutable. The bcl/rcl generation doesn't do any other BO dereferencing, and seems to have no other global state -dependency not covered by job_lock / bo_lock. +dependency not covered by job_lock / bo_lock. We only need to hold +struct_mutex for object unreferencing. Fixes a lock order reversal between mmap_sem and struct_mutex. Signed-off-by: Eric Anholt --- - drivers/gpu/drm/vc4/vc4_gem.c | 12 ++++-------- - 1 file changed, 4 insertions(+), 8 deletions(-) + drivers/gpu/drm/vc4/vc4_gem.c | 13 ++++++------- + 1 file changed, 6 insertions(+), 7 deletions(-) --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c -@@ -244,13 +244,15 @@ static void - vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec) +@@ -439,10 +439,12 @@ fail: + } + + static void +-vc4_complete_exec(struct vc4_exec_info *exec) ++vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec) { - struct vc4_dev *vc4 = to_vc4_dev(dev); -- uint64_t seqno = ++vc4->emit_seqno; -+ uint64_t seqno; - unsigned long irqflags; + unsigned i; -+ spin_lock_irqsave(&vc4->job_lock, irqflags); -+ -+ seqno = ++vc4->emit_seqno; - exec->seqno = seqno; - vc4_update_bo_seqnos(exec, seqno); ++ /* Need the struct lock for drm_gem_object_unreference(). */ ++ mutex_lock(&dev->struct_mutex); + if (exec->bo) { + for (i = 0; i < exec->bo_count; i++) + drm_gem_object_unreference(&exec->bo[i].bo->base); +@@ -455,6 +457,7 @@ vc4_complete_exec(struct vc4_exec_info * + list_del(&bo->unref_head); + drm_gem_object_unreference(&bo->base.base); + } ++ mutex_unlock(&dev->struct_mutex); -- spin_lock_irqsave(&vc4->job_lock, irqflags); - list_add_tail(&exec->head, &vc4->job_list); + kfree(exec); + } +@@ -473,7 +476,7 @@ vc4_job_handle_completed(struct vc4_dev + list_del(&exec->head); - /* If no job was executing, kick ours off. Otherwise, it'll -@@ -608,8 +610,6 @@ vc4_submit_cl_ioctl(struct drm_device *d - exec->args = args; - INIT_LIST_HEAD(&exec->unref_list); + spin_unlock_irqrestore(&vc4->job_lock, irqflags); +- vc4_complete_exec(exec); ++ vc4_complete_exec(vc4->dev, exec); + spin_lock_irqsave(&vc4->job_lock, irqflags); + } -- mutex_lock(&dev->struct_mutex); -- - ret = vc4_cl_lookup_bos(dev, file_priv, exec); - if (ret) - goto fail; -@@ -636,15 +636,11 @@ vc4_submit_cl_ioctl(struct drm_device *d - /* Return the seqno for our job. */ - args->seqno = vc4->emit_seqno; +@@ -525,12 +528,8 @@ vc4_job_done_work(struct work_struct *wo + { + struct vc4_dev *vc4 = + container_of(work, struct vc4_dev, job_done_work); +- struct drm_device *dev = vc4->dev; +- /* Need the struct lock for drm_gem_object_unreference(). */ +- mutex_lock(&dev->struct_mutex); + vc4_job_handle_completed(vc4); - mutex_unlock(&dev->struct_mutex); -- + } + + static int +@@ -639,7 +638,7 @@ vc4_submit_cl_ioctl(struct drm_device *d return 0; fail: - vc4_complete_exec(exec); +- vc4_complete_exec(exec); ++ vc4_complete_exec(vc4->dev, exec); -- mutex_unlock(&dev->struct_mutex); -- return ret; } - diff --git a/target/linux/brcm2708/patches-4.4/0097-drm-vc4-Add-support-for-more-display-plane-formats.patch b/target/linux/brcm2708/patches-4.4/0097-drm-vc4-Add-support-for-more-display-plane-formats.patch new file mode 100644 index 0000000000..fded87d135 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0097-drm-vc4-Add-support-for-more-display-plane-formats.patch @@ -0,0 +1,35 @@ +From 111e49bbb002b4dd86ef0c7e0b314c336e6a01c3 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Tue, 20 Oct 2015 13:59:15 +0100 +Subject: [PATCH 097/232] drm/vc4: Add support for more display plane formats. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_plane.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -59,6 +59,22 @@ static const struct hvs_format { + .drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888, + .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true, + }, ++ { ++ .drm = DRM_FORMAT_RGB565, .hvs = HVS_PIXEL_FORMAT_RGB565, ++ .pixel_order = HVS_PIXEL_ORDER_XRGB, .has_alpha = false, ++ }, ++ { ++ .drm = DRM_FORMAT_BGR565, .hvs = HVS_PIXEL_FORMAT_RGB565, ++ .pixel_order = HVS_PIXEL_ORDER_XBGR, .has_alpha = false, ++ }, ++ { ++ .drm = DRM_FORMAT_ARGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551, ++ .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true, ++ }, ++ { ++ .drm = DRM_FORMAT_XRGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551, ++ .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false, ++ }, + }; + + static const struct hvs_format *vc4_get_hvs_format(u32 drm_format) diff --git a/target/linux/brcm2708/patches-4.4/0097-drm-vc4-Drop-struct_mutex-around-CL-validation.patch b/target/linux/brcm2708/patches-4.4/0097-drm-vc4-Drop-struct_mutex-around-CL-validation.patch deleted file mode 100644 index 064e1170ed..0000000000 --- a/target/linux/brcm2708/patches-4.4/0097-drm-vc4-Drop-struct_mutex-around-CL-validation.patch +++ /dev/null @@ -1,74 +0,0 @@ -From f1924e632d16f98eee1fe1be636249b8cee4ee6e Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Mon, 19 Oct 2015 08:44:35 -0700 -Subject: [PATCH 097/170] drm/vc4: Drop struct_mutex around CL validation. - -We were using it so that we could make sure that shader validation -state didn't change while we were validating, but now shader -validation state is immutable. The bcl/rcl generation doesn't do any -other BO dereferencing, and seems to have no other global state -dependency not covered by job_lock / bo_lock. We only need to hold -struct_mutex for object unreferencing. - -Fixes a lock order reversal between mmap_sem and struct_mutex. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/vc4_gem.c | 13 ++++++------- - 1 file changed, 6 insertions(+), 7 deletions(-) - ---- a/drivers/gpu/drm/vc4/vc4_gem.c -+++ b/drivers/gpu/drm/vc4/vc4_gem.c -@@ -439,10 +439,12 @@ fail: - } - - static void --vc4_complete_exec(struct vc4_exec_info *exec) -+vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec) - { - unsigned i; - -+ /* Need the struct lock for drm_gem_object_unreference(). */ -+ mutex_lock(&dev->struct_mutex); - if (exec->bo) { - for (i = 0; i < exec->bo_count; i++) - drm_gem_object_unreference(&exec->bo[i].bo->base); -@@ -455,6 +457,7 @@ vc4_complete_exec(struct vc4_exec_info * - list_del(&bo->unref_head); - drm_gem_object_unreference(&bo->base.base); - } -+ mutex_unlock(&dev->struct_mutex); - - kfree(exec); - } -@@ -473,7 +476,7 @@ vc4_job_handle_completed(struct vc4_dev - list_del(&exec->head); - - spin_unlock_irqrestore(&vc4->job_lock, irqflags); -- vc4_complete_exec(exec); -+ vc4_complete_exec(vc4->dev, exec); - spin_lock_irqsave(&vc4->job_lock, irqflags); - } - -@@ -525,12 +528,8 @@ vc4_job_done_work(struct work_struct *wo - { - struct vc4_dev *vc4 = - container_of(work, struct vc4_dev, job_done_work); -- struct drm_device *dev = vc4->dev; - -- /* Need the struct lock for drm_gem_object_unreference(). */ -- mutex_lock(&dev->struct_mutex); - vc4_job_handle_completed(vc4); -- mutex_unlock(&dev->struct_mutex); - } - - static int -@@ -639,7 +638,7 @@ vc4_submit_cl_ioctl(struct drm_device *d - return 0; - - fail: -- vc4_complete_exec(exec); -+ vc4_complete_exec(vc4->dev, exec); - - return ret; - } diff --git a/target/linux/brcm2708/patches-4.4/0098-drm-vc4-Add-support-for-more-display-plane-formats.patch b/target/linux/brcm2708/patches-4.4/0098-drm-vc4-Add-support-for-more-display-plane-formats.patch deleted file mode 100644 index 817271a4a6..0000000000 --- a/target/linux/brcm2708/patches-4.4/0098-drm-vc4-Add-support-for-more-display-plane-formats.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 0193a1e5b931c7363279282b4169c2152af1a621 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Tue, 20 Oct 2015 13:59:15 +0100 -Subject: [PATCH 098/170] drm/vc4: Add support for more display plane formats. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/vc4_plane.c | 16 ++++++++++++++++ - 1 file changed, 16 insertions(+) - ---- a/drivers/gpu/drm/vc4/vc4_plane.c -+++ b/drivers/gpu/drm/vc4/vc4_plane.c -@@ -59,6 +59,22 @@ static const struct hvs_format { - .drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888, - .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true, - }, -+ { -+ .drm = DRM_FORMAT_RGB565, .hvs = HVS_PIXEL_FORMAT_RGB565, -+ .pixel_order = HVS_PIXEL_ORDER_XRGB, .has_alpha = false, -+ }, -+ { -+ .drm = DRM_FORMAT_BGR565, .hvs = HVS_PIXEL_FORMAT_RGB565, -+ .pixel_order = HVS_PIXEL_ORDER_XBGR, .has_alpha = false, -+ }, -+ { -+ .drm = DRM_FORMAT_ARGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551, -+ .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true, -+ }, -+ { -+ .drm = DRM_FORMAT_XRGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551, -+ .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false, -+ }, - }; - - static const struct hvs_format *vc4_get_hvs_format(u32 drm_format) diff --git a/target/linux/brcm2708/patches-4.4/0098-drm-vc4-No-need-to-stop-the-stopped-threads.patch b/target/linux/brcm2708/patches-4.4/0098-drm-vc4-No-need-to-stop-the-stopped-threads.patch new file mode 100644 index 0000000000..9831c9e6c3 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0098-drm-vc4-No-need-to-stop-the-stopped-threads.patch @@ -0,0 +1,26 @@ +From 81dbf19b16cc789162ac94cd4e764a667111386e Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Fri, 23 Oct 2015 12:31:56 +0100 +Subject: [PATCH 098/232] drm/vc4: No need to stop the stopped threads. + +This was leftover debug code from the hackdriver. We never submit +unless the thread is already idle. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_gem.c | 4 ---- + 1 file changed, 4 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_gem.c ++++ b/drivers/gpu/drm/vc4/vc4_gem.c +@@ -104,10 +104,6 @@ submit_cl(struct drm_device *dev, uint32 + { + struct vc4_dev *vc4 = to_vc4_dev(dev); + +- /* Stop any existing thread and set state to "stopped at halt" */ +- V3D_WRITE(V3D_CTNCS(thread), V3D_CTRUN); +- barrier(); +- + V3D_WRITE(V3D_CTNCA(thread), start); + barrier(); + diff --git a/target/linux/brcm2708/patches-4.4/0099-drm-vc4-No-need-to-stop-the-stopped-threads.patch b/target/linux/brcm2708/patches-4.4/0099-drm-vc4-No-need-to-stop-the-stopped-threads.patch deleted file mode 100644 index 7265078cf0..0000000000 --- a/target/linux/brcm2708/patches-4.4/0099-drm-vc4-No-need-to-stop-the-stopped-threads.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 8faa3b83201819d56f997a2b2ecaf8c690080852 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Fri, 23 Oct 2015 12:31:56 +0100 -Subject: [PATCH 099/170] drm/vc4: No need to stop the stopped threads. - -This was leftover debug code from the hackdriver. We never submit -unless the thread is already idle. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/vc4_gem.c | 4 ---- - 1 file changed, 4 deletions(-) - ---- a/drivers/gpu/drm/vc4/vc4_gem.c -+++ b/drivers/gpu/drm/vc4/vc4_gem.c -@@ -104,10 +104,6 @@ submit_cl(struct drm_device *dev, uint32 - { - struct vc4_dev *vc4 = to_vc4_dev(dev); - -- /* Stop any existing thread and set state to "stopped at halt" */ -- V3D_WRITE(V3D_CTNCS(thread), V3D_CTRUN); -- barrier(); -- - V3D_WRITE(V3D_CTNCA(thread), start); - barrier(); - diff --git a/target/linux/brcm2708/patches-4.4/0099-drm-vc4-Remove-extra-barrier-s-aroudn-CTnCA-CTnEA-se.patch b/target/linux/brcm2708/patches-4.4/0099-drm-vc4-Remove-extra-barrier-s-aroudn-CTnCA-CTnEA-se.patch new file mode 100644 index 0000000000..e0862d8e3f --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0099-drm-vc4-Remove-extra-barrier-s-aroudn-CTnCA-CTnEA-se.patch @@ -0,0 +1,33 @@ +From 9db0b8d54024bf5a2d0c3239aa571d890e8c8b3a Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Fri, 23 Oct 2015 12:33:43 +0100 +Subject: [PATCH 099/232] drm/vc4: Remove extra barrier()s aroudn CTnCA/CTnEA + setup. + +The writel() that these expand to already does barriers. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_gem.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_gem.c ++++ b/drivers/gpu/drm/vc4/vc4_gem.c +@@ -104,14 +104,11 @@ submit_cl(struct drm_device *dev, uint32 + { + struct vc4_dev *vc4 = to_vc4_dev(dev); + +- V3D_WRITE(V3D_CTNCA(thread), start); +- barrier(); +- +- /* Set the end address of the control list. Writing this +- * register is what starts the job. ++ /* Set the current and end address of the control list. ++ * Writing the end register is what starts the job. + */ ++ V3D_WRITE(V3D_CTNCA(thread), start); + V3D_WRITE(V3D_CTNEA(thread), end); +- barrier(); + } + + int diff --git a/target/linux/brcm2708/patches-4.4/0100-drm-vc4-Fix-a-typo-in-a-V3D-debug-register.patch b/target/linux/brcm2708/patches-4.4/0100-drm-vc4-Fix-a-typo-in-a-V3D-debug-register.patch new file mode 100644 index 0000000000..6049fb5d72 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0100-drm-vc4-Fix-a-typo-in-a-V3D-debug-register.patch @@ -0,0 +1,33 @@ +From 03b4b90b8e265158ac3529428f7aaf5f3b0f8a7a Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Fri, 23 Oct 2015 14:57:22 +0100 +Subject: [PATCH 100/232] drm/vc4: Fix a typo in a V3D debug register. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_regs.h | 2 +- + drivers/gpu/drm/vc4/vc4_v3d.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_regs.h ++++ b/drivers/gpu/drm/vc4/vc4_regs.h +@@ -154,7 +154,7 @@ + #define V3D_PCTRS14 0x006f4 + #define V3D_PCTR15 0x006f8 + #define V3D_PCTRS15 0x006fc +-#define V3D_BGE 0x00f00 ++#define V3D_DBGE 0x00f00 + #define V3D_FDBGO 0x00f04 + #define V3D_FDBGB 0x00f08 + #define V3D_FDBGR 0x00f0c +--- a/drivers/gpu/drm/vc4/vc4_v3d.c ++++ b/drivers/gpu/drm/vc4/vc4_v3d.c +@@ -99,7 +99,7 @@ static const struct { + REGDEF(V3D_PCTRS14), + REGDEF(V3D_PCTR15), + REGDEF(V3D_PCTRS15), +- REGDEF(V3D_BGE), ++ REGDEF(V3D_DBGE), + REGDEF(V3D_FDBGO), + REGDEF(V3D_FDBGB), + REGDEF(V3D_FDBGR), diff --git a/target/linux/brcm2708/patches-4.4/0100-drm-vc4-Remove-extra-barrier-s-aroudn-CTnCA-CTnEA-se.patch b/target/linux/brcm2708/patches-4.4/0100-drm-vc4-Remove-extra-barrier-s-aroudn-CTnCA-CTnEA-se.patch deleted file mode 100644 index d727a1b8e5..0000000000 --- a/target/linux/brcm2708/patches-4.4/0100-drm-vc4-Remove-extra-barrier-s-aroudn-CTnCA-CTnEA-se.patch +++ /dev/null @@ -1,33 +0,0 @@ -From d102d0e145b58bccbea52e89bbe52dcefc044aaa Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Fri, 23 Oct 2015 12:33:43 +0100 -Subject: [PATCH 100/170] drm/vc4: Remove extra barrier()s aroudn CTnCA/CTnEA - setup. - -The writel() that these expand to already does barriers. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/vc4_gem.c | 9 +++------ - 1 file changed, 3 insertions(+), 6 deletions(-) - ---- a/drivers/gpu/drm/vc4/vc4_gem.c -+++ b/drivers/gpu/drm/vc4/vc4_gem.c -@@ -104,14 +104,11 @@ submit_cl(struct drm_device *dev, uint32 - { - struct vc4_dev *vc4 = to_vc4_dev(dev); - -- V3D_WRITE(V3D_CTNCA(thread), start); -- barrier(); -- -- /* Set the end address of the control list. Writing this -- * register is what starts the job. -+ /* Set the current and end address of the control list. -+ * Writing the end register is what starts the job. - */ -+ V3D_WRITE(V3D_CTNCA(thread), start); - V3D_WRITE(V3D_CTNEA(thread), end); -- barrier(); - } - - int diff --git a/target/linux/brcm2708/patches-4.4/0101-drm-vc4-Enable-VC4-modules-and-increase-CMA-size-wit.patch b/target/linux/brcm2708/patches-4.4/0101-drm-vc4-Enable-VC4-modules-and-increase-CMA-size-wit.patch new file mode 100644 index 0000000000..8426ba6528 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0101-drm-vc4-Enable-VC4-modules-and-increase-CMA-size-wit.patch @@ -0,0 +1,153 @@ +From a5b0c616e9b40bd7907c5157700661004a8ef331 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 2 Nov 2015 17:07:33 +0000 +Subject: [PATCH 101/232] drm/vc4: Enable VC4 modules, and increase CMA size + with overlay + +If using the overlay, be careful not to boot to GUI or run startx, +or the Pi will almost hang, reporting stalls in kernel threads. +--- + arch/arm/boot/dts/overlays/README | 8 ++ + arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts | 95 ++++++++++++++++++++++ + arch/arm/configs/bcm2709_defconfig | 2 + + arch/arm/configs/bcmrpi_defconfig | 2 + + 4 files changed, 107 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -601,6 +601,14 @@ Params: txd1_pin GPIO pi + rxd1_pin GPIO pin for RXD1 (15, 33 or 41 - default 15) + + ++Name: vc4-kms-v3d ++Info: Enable Eric Anholt's DRM VC4 HDMI/HVS/V3D driver. Running startx or ++ booting to GUI while this overlay is in use will cause interesting ++ lockups. ++Load: dtoverlay=vc4-kms-v3d ++Params: ++ ++ + Name: vga666 + Info: Overlay for the Fen Logic VGA666 board + This uses GPIOs 2-21 (so no I2C), and activates the output 2-3 seconds +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts +@@ -0,0 +1,95 @@ ++/* ++ * vc4-kms-v3d-overlay.dts ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++#include "dt-bindings/clock/bcm2835.h" ++#include "dt-bindings/gpio/gpio.h" ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&i2c2>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&cprman>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&fb>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ ++ fragment@3 { ++ target = <&soc>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ pixelvalve@7e206000 { ++ compatible = "brcm,bcm2835-pixelvalve0"; ++ reg = <0x7e206000 0x100>; ++ interrupts = <2 13>; /* pwa0 */ ++ }; ++ ++ pixelvalve@7e207000 { ++ compatible = "brcm,bcm2835-pixelvalve1"; ++ reg = <0x7e207000 0x100>; ++ interrupts = <2 14>; /* pwa1 */ ++ }; ++ ++ hvs@7e400000 { ++ compatible = "brcm,bcm2835-hvs"; ++ reg = <0x7e400000 0x6000>; ++ interrupts = <2 1>; ++ }; ++ ++ pixelvalve@7e807000 { ++ compatible = "brcm,bcm2835-pixelvalve2"; ++ reg = <0x7e807000 0x100>; ++ interrupts = <2 10>; /* pixelvalve */ ++ }; ++ ++ hdmi@7e902000 { ++ compatible = "brcm,bcm2835-hdmi"; ++ reg = <0x7e902000 0x600>, ++ <0x7e808000 0x100>; ++ interrupts = <2 8>, <2 9>; ++ ddc = <&i2c2>; ++ hpd-gpio = <&gpio 46 GPIO_ACTIVE_HIGH>; ++ clocks = <&cprman BCM2835_PLLH_PIX>, ++ <&cprman BCM2835_CLOCK_HSM>; ++ clock-names = "pixel", "hdmi"; ++ }; ++ ++ v3d@7ec00000 { ++ compatible = "brcm,vc4-v3d"; ++ reg = <0x7ec00000 0x1000>; ++ interrupts = <1 10>; ++ }; ++ ++ gpu@7e4c0000 { ++ compatible = "brcm,bcm2835-vc4"; ++ }; ++ }; ++ }; ++ ++ fragment@4 { ++ target-path = "/chosen"; ++ __overlay__ { ++ bootargs = "cma=256M@512M"; ++ }; ++ }; ++}; +--- a/arch/arm/configs/bcm2709_defconfig ++++ b/arch/arm/configs/bcm2709_defconfig +@@ -802,6 +802,8 @@ CONFIG_VIDEO_TW9903=m + CONFIG_VIDEO_TW9906=m + CONFIG_VIDEO_OV7640=m + CONFIG_VIDEO_MT9V011=m ++CONFIG_DRM=m ++CONFIG_DRM_VC4=m + CONFIG_FB=y + CONFIG_FB_BCM2708=y + CONFIG_FB_UDL=m +--- a/arch/arm/configs/bcmrpi_defconfig ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -795,6 +795,8 @@ CONFIG_VIDEO_TW9903=m + CONFIG_VIDEO_TW9906=m + CONFIG_VIDEO_OV7640=m + CONFIG_VIDEO_MT9V011=m ++CONFIG_DRM=m ++CONFIG_DRM_VC4=m + CONFIG_FB=y + CONFIG_FB_BCM2708=y + CONFIG_FB_UDL=m diff --git a/target/linux/brcm2708/patches-4.4/0101-drm-vc4-Fix-a-typo-in-a-V3D-debug-register.patch b/target/linux/brcm2708/patches-4.4/0101-drm-vc4-Fix-a-typo-in-a-V3D-debug-register.patch deleted file mode 100644 index c920f40ba6..0000000000 --- a/target/linux/brcm2708/patches-4.4/0101-drm-vc4-Fix-a-typo-in-a-V3D-debug-register.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 0f099b4b1354b0377376b91ae60c6c16d82683a7 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Fri, 23 Oct 2015 14:57:22 +0100 -Subject: [PATCH 101/170] drm/vc4: Fix a typo in a V3D debug register. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/vc4_regs.h | 2 +- - drivers/gpu/drm/vc4/vc4_v3d.c | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - ---- a/drivers/gpu/drm/vc4/vc4_regs.h -+++ b/drivers/gpu/drm/vc4/vc4_regs.h -@@ -154,7 +154,7 @@ - #define V3D_PCTRS14 0x006f4 - #define V3D_PCTR15 0x006f8 - #define V3D_PCTRS15 0x006fc --#define V3D_BGE 0x00f00 -+#define V3D_DBGE 0x00f00 - #define V3D_FDBGO 0x00f04 - #define V3D_FDBGB 0x00f08 - #define V3D_FDBGR 0x00f0c ---- a/drivers/gpu/drm/vc4/vc4_v3d.c -+++ b/drivers/gpu/drm/vc4/vc4_v3d.c -@@ -99,7 +99,7 @@ static const struct { - REGDEF(V3D_PCTRS14), - REGDEF(V3D_PCTR15), - REGDEF(V3D_PCTRS15), -- REGDEF(V3D_BGE), -+ REGDEF(V3D_DBGE), - REGDEF(V3D_FDBGO), - REGDEF(V3D_FDBGB), - REGDEF(V3D_FDBGR), diff --git a/target/linux/brcm2708/patches-4.4/0102-drm-vc4-Enable-VC4-modules-and-increase-CMA-size-wit.patch b/target/linux/brcm2708/patches-4.4/0102-drm-vc4-Enable-VC4-modules-and-increase-CMA-size-wit.patch deleted file mode 100644 index 5531673c59..0000000000 --- a/target/linux/brcm2708/patches-4.4/0102-drm-vc4-Enable-VC4-modules-and-increase-CMA-size-wit.patch +++ /dev/null @@ -1,153 +0,0 @@ -From 22182768add611b53e33c5dd2e8901acc3d08181 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 2 Nov 2015 17:07:33 +0000 -Subject: [PATCH 102/170] drm/vc4: Enable VC4 modules, and increase CMA size - with overlay - -If using the overlay, be careful not to boot to GUI or run startx, -or the Pi will almost hang, reporting stalls in kernel threads. ---- - arch/arm/boot/dts/overlays/README | 8 ++ - arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts | 95 ++++++++++++++++++++++ - arch/arm/configs/bcm2709_defconfig | 2 + - arch/arm/configs/bcmrpi_defconfig | 2 + - 4 files changed, 107 insertions(+) - create mode 100644 arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts - ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -601,6 +601,14 @@ Params: txd1_pin GPIO pi - rxd1_pin GPIO pin for RXD1 (15, 33 or 41 - default 15) - - -+Name: vc4-kms-v3d -+Info: Enable Eric Anholt's DRM VC4 HDMI/HVS/V3D driver. Running startx or -+ booting to GUI while this overlay is in use will cause interesting -+ lockups. -+Load: dtoverlay=vc4-kms-v3d -+Params: -+ -+ - Name: vga666 - Info: Overlay for the Fen Logic VGA666 board - This uses GPIOs 2-21 (so no I2C), and activates the output 2-3 seconds ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts -@@ -0,0 +1,95 @@ -+/* -+ * vc4-kms-v3d-overlay.dts -+ */ -+ -+/dts-v1/; -+/plugin/; -+ -+#include "dt-bindings/clock/bcm2835.h" -+#include "dt-bindings/gpio/gpio.h" -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&i2c2>; -+ __overlay__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&cprman>; -+ __overlay__ { -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&fb>; -+ __overlay__ { -+ status = "disabled"; -+ }; -+ }; -+ -+ fragment@3 { -+ target = <&soc>; -+ __overlay__ { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ -+ pixelvalve@7e206000 { -+ compatible = "brcm,bcm2835-pixelvalve0"; -+ reg = <0x7e206000 0x100>; -+ interrupts = <2 13>; /* pwa0 */ -+ }; -+ -+ pixelvalve@7e207000 { -+ compatible = "brcm,bcm2835-pixelvalve1"; -+ reg = <0x7e207000 0x100>; -+ interrupts = <2 14>; /* pwa1 */ -+ }; -+ -+ hvs@7e400000 { -+ compatible = "brcm,bcm2835-hvs"; -+ reg = <0x7e400000 0x6000>; -+ interrupts = <2 1>; -+ }; -+ -+ pixelvalve@7e807000 { -+ compatible = "brcm,bcm2835-pixelvalve2"; -+ reg = <0x7e807000 0x100>; -+ interrupts = <2 10>; /* pixelvalve */ -+ }; -+ -+ hdmi@7e902000 { -+ compatible = "brcm,bcm2835-hdmi"; -+ reg = <0x7e902000 0x600>, -+ <0x7e808000 0x100>; -+ interrupts = <2 8>, <2 9>; -+ ddc = <&i2c2>; -+ hpd-gpio = <&gpio 46 GPIO_ACTIVE_HIGH>; -+ clocks = <&cprman BCM2835_PLLH_PIX>, -+ <&cprman BCM2835_CLOCK_HSM>; -+ clock-names = "pixel", "hdmi"; -+ }; -+ -+ v3d@7ec00000 { -+ compatible = "brcm,vc4-v3d"; -+ reg = <0x7ec00000 0x1000>; -+ interrupts = <1 10>; -+ }; -+ -+ gpu@7e4c0000 { -+ compatible = "brcm,bcm2835-vc4"; -+ }; -+ }; -+ }; -+ -+ fragment@4 { -+ target-path = "/chosen"; -+ __overlay__ { -+ bootargs = "cma=256M@512M"; -+ }; -+ }; -+}; ---- a/arch/arm/configs/bcm2709_defconfig -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -802,6 +802,8 @@ CONFIG_VIDEO_TW9903=m - CONFIG_VIDEO_TW9906=m - CONFIG_VIDEO_OV7640=m - CONFIG_VIDEO_MT9V011=m -+CONFIG_DRM=m -+CONFIG_DRM_VC4=m - CONFIG_FB=y - CONFIG_FB_BCM2708=y - CONFIG_FB_UDL=m ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -795,6 +795,8 @@ CONFIG_VIDEO_TW9903=m - CONFIG_VIDEO_TW9906=m - CONFIG_VIDEO_OV7640=m - CONFIG_VIDEO_MT9V011=m -+CONFIG_DRM=m -+CONFIG_DRM_VC4=m - CONFIG_FB=y - CONFIG_FB_BCM2708=y - CONFIG_FB_UDL=m diff --git a/target/linux/brcm2708/patches-4.4/0102-squash-fixups.patch b/target/linux/brcm2708/patches-4.4/0102-squash-fixups.patch new file mode 100644 index 0000000000..018ce67539 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0102-squash-fixups.patch @@ -0,0 +1,43 @@ +From 17f4de0c9daea638476ca0f80123d3523e66fdc0 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 18 Nov 2015 18:29:58 +0000 +Subject: [PATCH 102/232] squash: fixups + +--- + drivers/gpu/drm/vc4/Kconfig | 2 +- + drivers/gpu/drm/vc4/vc4_drv.c | 2 +- + drivers/gpu/drm/vc4/vc4_kms.c | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/gpu/drm/vc4/Kconfig ++++ b/drivers/gpu/drm/vc4/Kconfig +@@ -1,6 +1,6 @@ + config DRM_VC4 + tristate "Broadcom VC4 Graphics" +- depends on ARCH_BCM2835 || COMPILE_TEST ++ depends on ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709 || COMPILE_TEST + depends on DRM && HAVE_DMA_ATTRS + select DRM_KMS_HELPER + select DRM_KMS_CMA_HELPER +--- a/drivers/gpu/drm/vc4/vc4_drv.c ++++ b/drivers/gpu/drm/vc4/vc4_drv.c +@@ -127,7 +127,7 @@ static struct drm_driver vc4_drm_driver + .num_ioctls = ARRAY_SIZE(vc4_drm_ioctls), + .fops = &vc4_drm_fops, + +- .gem_obj_size = sizeof(struct vc4_bo), ++ //.gem_obj_size = sizeof(struct vc4_bo), + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, +--- a/drivers/gpu/drm/vc4/vc4_kms.c ++++ b/drivers/gpu/drm/vc4/vc4_kms.c +@@ -45,7 +45,7 @@ vc4_atomic_complete_commit(struct vc4_co + + drm_atomic_helper_commit_modeset_disables(dev, state); + +- drm_atomic_helper_commit_planes(dev, state); ++ drm_atomic_helper_commit_planes(dev, state, false); + + drm_atomic_helper_commit_modeset_enables(dev, state); + diff --git a/target/linux/brcm2708/patches-4.4/0103-squash-add-missing-vc4-kms-v3d-overlay.dtb-to-makefi.patch b/target/linux/brcm2708/patches-4.4/0103-squash-add-missing-vc4-kms-v3d-overlay.dtb-to-makefi.patch new file mode 100644 index 0000000000..0c77a819be --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0103-squash-add-missing-vc4-kms-v3d-overlay.dtb-to-makefi.patch @@ -0,0 +1,20 @@ +From c1834a750441c3ea55f24377e04cf8d1fb66c877 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 18 Nov 2015 20:26:03 +0000 +Subject: [PATCH 103/232] squash: add missing vc4-kms-v3d-overlay.dtb to + makefile + +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -52,6 +52,7 @@ dtb-$(RPI_DT_OVERLAYS) += smi-overlay.dt + dtb-$(RPI_DT_OVERLAYS) += spi-gpio35-39-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += tinylcd35-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += uart1-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += vc4-kms-v3d-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += vga666-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += w1-gpio-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += w1-gpio-pullup-overlay.dtb diff --git a/target/linux/brcm2708/patches-4.4/0103-squash-fixups.patch b/target/linux/brcm2708/patches-4.4/0103-squash-fixups.patch deleted file mode 100644 index 25c0137905..0000000000 --- a/target/linux/brcm2708/patches-4.4/0103-squash-fixups.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 88315025318218ac04fcc66614083bb1a51f662f Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 18 Nov 2015 18:29:58 +0000 -Subject: [PATCH 103/170] squash: fixups - ---- - drivers/gpu/drm/vc4/Kconfig | 2 +- - drivers/gpu/drm/vc4/vc4_drv.c | 2 +- - drivers/gpu/drm/vc4/vc4_kms.c | 2 +- - 3 files changed, 3 insertions(+), 3 deletions(-) - ---- a/drivers/gpu/drm/vc4/Kconfig -+++ b/drivers/gpu/drm/vc4/Kconfig -@@ -1,6 +1,6 @@ - config DRM_VC4 - tristate "Broadcom VC4 Graphics" -- depends on ARCH_BCM2835 || COMPILE_TEST -+ depends on ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709 || COMPILE_TEST - depends on DRM && HAVE_DMA_ATTRS - select DRM_KMS_HELPER - select DRM_KMS_CMA_HELPER ---- a/drivers/gpu/drm/vc4/vc4_drv.c -+++ b/drivers/gpu/drm/vc4/vc4_drv.c -@@ -127,7 +127,7 @@ static struct drm_driver vc4_drm_driver - .num_ioctls = ARRAY_SIZE(vc4_drm_ioctls), - .fops = &vc4_drm_fops, - -- .gem_obj_size = sizeof(struct vc4_bo), -+ //.gem_obj_size = sizeof(struct vc4_bo), - - .name = DRIVER_NAME, - .desc = DRIVER_DESC, ---- a/drivers/gpu/drm/vc4/vc4_kms.c -+++ b/drivers/gpu/drm/vc4/vc4_kms.c -@@ -45,7 +45,7 @@ vc4_atomic_complete_commit(struct vc4_co - - drm_atomic_helper_commit_modeset_disables(dev, state); - -- drm_atomic_helper_commit_planes(dev, state); -+ drm_atomic_helper_commit_planes(dev, state, false); - - drm_atomic_helper_commit_modeset_enables(dev, state); - diff --git a/target/linux/brcm2708/patches-4.4/0104-clk-bcm2835-Also-build-the-driver-for-downstream-ker.patch b/target/linux/brcm2708/patches-4.4/0104-clk-bcm2835-Also-build-the-driver-for-downstream-ker.patch new file mode 100644 index 0000000000..56cef7e564 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0104-clk-bcm2835-Also-build-the-driver-for-downstream-ker.patch @@ -0,0 +1,22 @@ +From 084eeec470e76e8c84cda1a31e323610467913bd Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 12 Oct 2015 11:23:34 -0700 +Subject: [PATCH 104/232] clk: bcm2835: Also build the driver for downstream + kernels. + +Signed-off-by: Eric Anholt +--- + drivers/clk/bcm/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/clk/bcm/Makefile ++++ b/drivers/clk/bcm/Makefile +@@ -3,7 +3,7 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-s + obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o + obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o + obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o +-obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o ++obj-$(CONFIG_ARCH_BCM2835)$(CONFIG_ARCH_BCM2708)$(CONFIG_ARCH_BCM2709) += clk-bcm2835.o + obj-$(CONFIG_COMMON_CLK_IPROC) += clk-ns2.o + obj-$(CONFIG_ARCH_BCM_CYGNUS) += clk-cygnus.o + obj-$(CONFIG_ARCH_BCM_NSP) += clk-nsp.o diff --git a/target/linux/brcm2708/patches-4.4/0104-squash-add-missing-vc4-kms-v3d-overlay.dtb-to-makefi.patch b/target/linux/brcm2708/patches-4.4/0104-squash-add-missing-vc4-kms-v3d-overlay.dtb-to-makefi.patch deleted file mode 100644 index 23d73e83d4..0000000000 --- a/target/linux/brcm2708/patches-4.4/0104-squash-add-missing-vc4-kms-v3d-overlay.dtb-to-makefi.patch +++ /dev/null @@ -1,20 +0,0 @@ -From 3fc233b798ac8498b4e24b42ad67fb3499a30f07 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 18 Nov 2015 20:26:03 +0000 -Subject: [PATCH 104/170] squash: add missing vc4-kms-v3d-overlay.dtb to - makefile - ---- - arch/arm/boot/dts/overlays/Makefile | 1 + - 1 file changed, 1 insertion(+) - ---- a/arch/arm/boot/dts/overlays/Makefile -+++ b/arch/arm/boot/dts/overlays/Makefile -@@ -52,6 +52,7 @@ dtb-$(RPI_DT_OVERLAYS) += smi-overlay.dt - dtb-$(RPI_DT_OVERLAYS) += spi-gpio35-39-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += tinylcd35-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += uart1-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += vc4-kms-v3d-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += vga666-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += w1-gpio-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += w1-gpio-pullup-overlay.dtb diff --git a/target/linux/brcm2708/patches-4.4/0105-clk-bcm2835-Also-build-the-driver-for-downstream-ker.patch b/target/linux/brcm2708/patches-4.4/0105-clk-bcm2835-Also-build-the-driver-for-downstream-ker.patch deleted file mode 100644 index f66e151e52..0000000000 --- a/target/linux/brcm2708/patches-4.4/0105-clk-bcm2835-Also-build-the-driver-for-downstream-ker.patch +++ /dev/null @@ -1,22 +0,0 @@ -From d9888c76bcee2a7c118ab8ba93a1204fb5ff4e44 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Mon, 12 Oct 2015 11:23:34 -0700 -Subject: [PATCH 105/170] clk: bcm2835: Also build the driver for downstream - kernels. - -Signed-off-by: Eric Anholt ---- - drivers/clk/bcm/Makefile | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/drivers/clk/bcm/Makefile -+++ b/drivers/clk/bcm/Makefile -@@ -3,7 +3,7 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-s - obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o - obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o - obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o --obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o -+obj-$(CONFIG_ARCH_BCM2835)$(CONFIG_ARCH_BCM2708)$(CONFIG_ARCH_BCM2709) += clk-bcm2835.o - obj-$(CONFIG_COMMON_CLK_IPROC) += clk-ns2.o - obj-$(CONFIG_ARCH_BCM_CYGNUS) += clk-cygnus.o - obj-$(CONFIG_ARCH_BCM_NSP) += clk-nsp.o diff --git a/target/linux/brcm2708/patches-4.4/0105-dts-Added-overlay-for-gpio_ir_recv-driver.patch b/target/linux/brcm2708/patches-4.4/0105-dts-Added-overlay-for-gpio_ir_recv-driver.patch new file mode 100644 index 0000000000..22de121f2d --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0105-dts-Added-overlay-for-gpio_ir_recv-driver.patch @@ -0,0 +1,104 @@ +From cbb80e24a9db48390c82c7030cc7f7389aee953f Mon Sep 17 00:00:00 2001 +From: Holger Steinhaus +Date: Sat, 14 Nov 2015 18:37:43 +0100 +Subject: [PATCH 105/232] dts: Added overlay for gpio_ir_recv driver + +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 18 ++++++++++- + arch/arm/boot/dts/overlays/gpio-ir-overlay.dts | 45 ++++++++++++++++++++++++++ + 3 files changed, 63 insertions(+), 1 deletion(-) + create mode 100644 arch/arm/boot/dts/overlays/gpio-ir-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -16,6 +16,7 @@ dtb-$(RPI_DT_OVERLAYS) += ads7846-overla + dtb-$(RPI_DT_OVERLAYS) += bmp085_i2c-sensor-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += dht11-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += enc28j60-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += gpio-ir-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += gpio-poweroff-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += hifiberry-dac-overlay.dtb +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -196,6 +196,22 @@ Params: int_pin GPIO us + speed SPI bus speed (default 12000000) + + ++Name: gpio-ir ++Info: Use GPIO pin as rc-core style infrared receiver input. The rc-core- ++ based gpio_ir_recv driver maps received keys directly to a ++ /dev/input/event* device, all decoding is done by the kernel - LIRC is ++ not required! The key mapping and other decoding parameters can be ++ configured by "ir-keytable" tool. ++Load: dtoverlay=gpio-ir,= ++Params: gpio_pin Input pin number. Default is 18. ++ ++ gpio_pull Desired pull-up/down state (off, down, up) ++ Default is "down". ++ ++ rc-map-name Default rc keymap (can also be changed by ++ ir-keytable), defaults to "rc-rc6-mce" ++ ++ + Name: gpio-poweroff + Info: Drives a GPIO high or low on reboot + Load: dtoverlay=gpio-poweroff,= +@@ -308,7 +324,7 @@ Params: + Name: lirc-rpi + Info: Configures lirc-rpi (Linux Infrared Remote Control for Raspberry Pi) + Consult the module documentation for more details. +-Load: dtoverlay=lirc-rpi,=,... ++Load: dtoverlay=lirc-rpi,= + Params: gpio_out_pin GPIO for output (default "17") + + gpio_in_pin GPIO for input (default "18") +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/gpio-ir-overlay.dts +@@ -0,0 +1,45 @@ ++// Definitions for ir-gpio module ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target-path = "/"; ++ __overlay__ { ++ gpio_ir: ir-receiver { ++ compatible = "gpio-ir-receiver"; ++ ++ // pin number, high or low ++ gpios = <&gpio 18 1>; ++ ++ // parameter for keymap name ++ linux,rc-map-name = "rc-rc6-mce"; ++ ++ status = "okay"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ gpio_ir_pins: gpio_ir_pins { ++ brcm,pins = <18>; // pin 18 ++ brcm,function = <0>; // in ++ brcm,pull = <1>; // down ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ // parameters ++ gpio_pin = <&gpio_ir>,"gpios:4", ++ <&gpio_ir_pins>,"brcm,pins:0", ++ <&gpio_ir_pins>,"brcm,pull:0"; // pin number ++ gpio_pull = <&gpio_ir_pins>,"brcm,pull:0"; // pull-up/down state ++ ++ rc-map-name = <&gpio_ir>,"linux,rc-map-name"; // default rc map ++ }; ++}; diff --git a/target/linux/brcm2708/patches-4.4/0106-Build-i2c_gpio-module-and-add-a-device-tree-overlay-.patch b/target/linux/brcm2708/patches-4.4/0106-Build-i2c_gpio-module-and-add-a-device-tree-overlay-.patch new file mode 100644 index 0000000000..259901616c --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0106-Build-i2c_gpio-module-and-add-a-device-tree-overlay-.patch @@ -0,0 +1,100 @@ +From c644d7cae9394e209fc2451b91d2852ebfc85a09 Mon Sep 17 00:00:00 2001 +From: Alistair Buxton +Date: Sun, 1 Nov 2015 22:27:56 +0000 +Subject: [PATCH 106/232] Build i2c_gpio module and add a device tree overlay + to configure it. + +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 13 +++++++++++- + arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts | 28 +++++++++++++++++++++++++ + arch/arm/configs/bcm2709_defconfig | 1 + + arch/arm/configs/bcmrpi_defconfig | 1 + + 5 files changed, 43 insertions(+), 1 deletion(-) + create mode 100644 arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -25,6 +25,7 @@ dtb-$(RPI_DT_OVERLAYS) += hifiberry-digi + dtb-$(RPI_DT_OVERLAYS) += hy28a-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += hy28b-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += i2c-rtc-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += i2c-gpio-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += i2s-mmap-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -287,9 +287,20 @@ Params: speed Display + ledgpio GPIO used to control backlight + + ++Name: i2c-gpio ++Info: Adds support for software i2c controller on gpio pins ++Load: dtoverlay=i2c-gpio,= ++Params: i2c_gpio_sda GPIO used for I2C data (default "23") ++ ++ i2c_gpio_scl GPIO used for I2C clock (default "24") ++ ++ i2c_gpio_delay_us Clock delay in microseconds ++ (default "2" = ~100kHz) ++ ++ + Name: i2c-rtc + Info: Adds support for a number of I2C Real Time Clock devices +-Load: dtoverlay=i2c-rtc, ++Load: dtoverlay=i2c-rtc,= + Params: ds1307 Select the DS1307 device + + ds3231 Select the DS3231 device +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts +@@ -0,0 +1,28 @@ ++// Overlay for i2c_gpio bitbanging host bus. ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target-path = "/"; ++ __overlay__ { ++ i2c_gpio: i2c@0 { ++ compatible = "i2c-gpio"; ++ gpios = <&gpio 23 0 /* sda */ ++ &gpio 24 0 /* scl */ ++ >; ++ i2c-gpio,delay-us = <2>; /* ~100 kHz */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ i2c_gpio_sda = <&i2c_gpio>,"gpios:4"; ++ i2c_gpio_scl = <&i2c_gpio>,"gpios:16"; ++ i2c_gpio_delay_us = <&i2c_gpio>,"i2c-gpio,delay-us:0"; ++ }; ++}; ++ +--- a/arch/arm/configs/bcm2709_defconfig ++++ b/arch/arm/configs/bcm2709_defconfig +@@ -595,6 +595,7 @@ CONFIG_RAW_DRIVER=y + CONFIG_I2C=y + CONFIG_I2C_CHARDEV=m + CONFIG_I2C_BCM2708=m ++CONFIG_I2C_GPIO=m + CONFIG_SPI=y + CONFIG_SPI_BCM2835=m + CONFIG_SPI_SPIDEV=y +--- a/arch/arm/configs/bcmrpi_defconfig ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -588,6 +588,7 @@ CONFIG_RAW_DRIVER=y + CONFIG_I2C=y + CONFIG_I2C_CHARDEV=m + CONFIG_I2C_BCM2708=m ++CONFIG_I2C_GPIO=m + CONFIG_SPI=y + CONFIG_SPI_BCM2835=m + CONFIG_SPI_SPIDEV=y diff --git a/target/linux/brcm2708/patches-4.4/0106-dts-Added-overlay-for-gpio_ir_recv-driver.patch b/target/linux/brcm2708/patches-4.4/0106-dts-Added-overlay-for-gpio_ir_recv-driver.patch deleted file mode 100644 index 54fb05dd02..0000000000 --- a/target/linux/brcm2708/patches-4.4/0106-dts-Added-overlay-for-gpio_ir_recv-driver.patch +++ /dev/null @@ -1,104 +0,0 @@ -From 0dbc0fe760a54bbcff587657062df49dc0eaf2b9 Mon Sep 17 00:00:00 2001 -From: Holger Steinhaus -Date: Sat, 14 Nov 2015 18:37:43 +0100 -Subject: [PATCH 106/170] dts: Added overlay for gpio_ir_recv driver - ---- - arch/arm/boot/dts/overlays/Makefile | 1 + - arch/arm/boot/dts/overlays/README | 18 ++++++++++- - arch/arm/boot/dts/overlays/gpio-ir-overlay.dts | 45 ++++++++++++++++++++++++++ - 3 files changed, 63 insertions(+), 1 deletion(-) - create mode 100644 arch/arm/boot/dts/overlays/gpio-ir-overlay.dts - ---- a/arch/arm/boot/dts/overlays/Makefile -+++ b/arch/arm/boot/dts/overlays/Makefile -@@ -16,6 +16,7 @@ dtb-$(RPI_DT_OVERLAYS) += ads7846-overla - dtb-$(RPI_DT_OVERLAYS) += bmp085_i2c-sensor-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += dht11-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += enc28j60-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += gpio-ir-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += gpio-poweroff-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += hifiberry-dac-overlay.dtb ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -196,6 +196,22 @@ Params: int_pin GPIO us - speed SPI bus speed (default 12000000) - - -+Name: gpio-ir -+Info: Use GPIO pin as rc-core style infrared receiver input. The rc-core- -+ based gpio_ir_recv driver maps received keys directly to a -+ /dev/input/event* device, all decoding is done by the kernel - LIRC is -+ not required! The key mapping and other decoding parameters can be -+ configured by "ir-keytable" tool. -+Load: dtoverlay=gpio-ir,= -+Params: gpio_pin Input pin number. Default is 18. -+ -+ gpio_pull Desired pull-up/down state (off, down, up) -+ Default is "down". -+ -+ rc-map-name Default rc keymap (can also be changed by -+ ir-keytable), defaults to "rc-rc6-mce" -+ -+ - Name: gpio-poweroff - Info: Drives a GPIO high or low on reboot - Load: dtoverlay=gpio-poweroff,= -@@ -308,7 +324,7 @@ Params: - Name: lirc-rpi - Info: Configures lirc-rpi (Linux Infrared Remote Control for Raspberry Pi) - Consult the module documentation for more details. --Load: dtoverlay=lirc-rpi,=,... -+Load: dtoverlay=lirc-rpi,= - Params: gpio_out_pin GPIO for output (default "17") - - gpio_in_pin GPIO for input (default "18") ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/gpio-ir-overlay.dts -@@ -0,0 +1,45 @@ -+// Definitions for ir-gpio module -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target-path = "/"; -+ __overlay__ { -+ gpio_ir: ir-receiver { -+ compatible = "gpio-ir-receiver"; -+ -+ // pin number, high or low -+ gpios = <&gpio 18 1>; -+ -+ // parameter for keymap name -+ linux,rc-map-name = "rc-rc6-mce"; -+ -+ status = "okay"; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ gpio_ir_pins: gpio_ir_pins { -+ brcm,pins = <18>; // pin 18 -+ brcm,function = <0>; // in -+ brcm,pull = <1>; // down -+ }; -+ }; -+ }; -+ -+ __overrides__ { -+ // parameters -+ gpio_pin = <&gpio_ir>,"gpios:4", -+ <&gpio_ir_pins>,"brcm,pins:0", -+ <&gpio_ir_pins>,"brcm,pull:0"; // pin number -+ gpio_pull = <&gpio_ir_pins>,"brcm,pull:0"; // pull-up/down state -+ -+ rc-map-name = <&gpio_ir>,"linux,rc-map-name"; // default rc map -+ }; -+}; diff --git a/target/linux/brcm2708/patches-4.4/0107-Build-i2c_gpio-module-and-add-a-device-tree-overlay-.patch b/target/linux/brcm2708/patches-4.4/0107-Build-i2c_gpio-module-and-add-a-device-tree-overlay-.patch deleted file mode 100644 index 3654797e66..0000000000 --- a/target/linux/brcm2708/patches-4.4/0107-Build-i2c_gpio-module-and-add-a-device-tree-overlay-.patch +++ /dev/null @@ -1,100 +0,0 @@ -From 938b09ce6c6736d0861c2c9eec3a7e739585b589 Mon Sep 17 00:00:00 2001 -From: Alistair Buxton -Date: Sun, 1 Nov 2015 22:27:56 +0000 -Subject: [PATCH 107/170] Build i2c_gpio module and add a device tree overlay - to configure it. - ---- - arch/arm/boot/dts/overlays/Makefile | 1 + - arch/arm/boot/dts/overlays/README | 13 +++++++++++- - arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts | 28 +++++++++++++++++++++++++ - arch/arm/configs/bcm2709_defconfig | 1 + - arch/arm/configs/bcmrpi_defconfig | 1 + - 5 files changed, 43 insertions(+), 1 deletion(-) - create mode 100644 arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts - ---- a/arch/arm/boot/dts/overlays/Makefile -+++ b/arch/arm/boot/dts/overlays/Makefile -@@ -25,6 +25,7 @@ dtb-$(RPI_DT_OVERLAYS) += hifiberry-digi - dtb-$(RPI_DT_OVERLAYS) += hy28a-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += hy28b-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += i2c-rtc-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += i2c-gpio-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += i2s-mmap-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -287,9 +287,20 @@ Params: speed Display - ledgpio GPIO used to control backlight - - -+Name: i2c-gpio -+Info: Adds support for software i2c controller on gpio pins -+Load: dtoverlay=i2c-gpio,= -+Params: i2c_gpio_sda GPIO used for I2C data (default "23") -+ -+ i2c_gpio_scl GPIO used for I2C clock (default "24") -+ -+ i2c_gpio_delay_us Clock delay in microseconds -+ (default "2" = ~100kHz) -+ -+ - Name: i2c-rtc - Info: Adds support for a number of I2C Real Time Clock devices --Load: dtoverlay=i2c-rtc, -+Load: dtoverlay=i2c-rtc,= - Params: ds1307 Select the DS1307 device - - ds3231 Select the DS3231 device ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts -@@ -0,0 +1,28 @@ -+// Overlay for i2c_gpio bitbanging host bus. -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target-path = "/"; -+ __overlay__ { -+ i2c_gpio: i2c@0 { -+ compatible = "i2c-gpio"; -+ gpios = <&gpio 23 0 /* sda */ -+ &gpio 24 0 /* scl */ -+ >; -+ i2c-gpio,delay-us = <2>; /* ~100 kHz */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ }; -+ }; -+ }; -+ __overrides__ { -+ i2c_gpio_sda = <&i2c_gpio>,"gpios:4"; -+ i2c_gpio_scl = <&i2c_gpio>,"gpios:16"; -+ i2c_gpio_delay_us = <&i2c_gpio>,"i2c-gpio,delay-us:0"; -+ }; -+}; -+ ---- a/arch/arm/configs/bcm2709_defconfig -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -595,6 +595,7 @@ CONFIG_RAW_DRIVER=y - CONFIG_I2C=y - CONFIG_I2C_CHARDEV=m - CONFIG_I2C_BCM2708=m -+CONFIG_I2C_GPIO=m - CONFIG_SPI=y - CONFIG_SPI_BCM2835=m - CONFIG_SPI_SPIDEV=y ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -588,6 +588,7 @@ CONFIG_RAW_DRIVER=y - CONFIG_I2C=y - CONFIG_I2C_CHARDEV=m - CONFIG_I2C_BCM2708=m -+CONFIG_I2C_GPIO=m - CONFIG_SPI=y - CONFIG_SPI_BCM2835=m - CONFIG_SPI_SPIDEV=y diff --git a/target/linux/brcm2708/patches-4.4/0107-New-overlay-for-PiScreen2r.patch b/target/linux/brcm2708/patches-4.4/0107-New-overlay-for-PiScreen2r.patch new file mode 100644 index 0000000000..f57a63d548 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0107-New-overlay-for-PiScreen2r.patch @@ -0,0 +1,148 @@ +From a5a974dbeaaf0be8b1fa5d8c6f117b320ea01b44 Mon Sep 17 00:00:00 2001 +From: mwilliams03 +Date: Sun, 18 Oct 2015 17:07:24 -0700 +Subject: [PATCH 107/232] New overlay for PiScreen2r + +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 14 +++ + arch/arm/boot/dts/overlays/piscreen2r-overlay.dts | 100 ++++++++++++++++++++++ + 3 files changed, 115 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/piscreen2r-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -35,6 +35,7 @@ dtb-$(RPI_DT_OVERLAYS) += mcp2515-can1-o + dtb-$(RPI_DT_OVERLAYS) += mmc-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += mz61581-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += piscreen-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += piscreen2r-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += pitft28-resistive-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += pwm-overlay.dtb +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -425,6 +425,20 @@ Params: speed Display + xohms Touchpanel sensitivity (X-plate resistance) + + ++Name: piscreen2r ++Info: PiScreen 2 with resistive TP display by OzzMaker.com ++Load: dtoverlay=piscreen2r,= ++Params: speed Display SPI bus speed ++ ++ rotate Display rotation {0,90,180,270} ++ ++ fps Delay between frame updates ++ ++ debug Debug output level {0-7} ++ ++ xohms Touchpanel sensitivity (X-plate resistance) ++ ++ + Name: pitft28-resistive + Info: Adafruit PiTFT 2.8" resistive touch screen + Load: dtoverlay=pitft28-resistive,= +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/piscreen2r-overlay.dts +@@ -0,0 +1,100 @@ ++ /* ++ * Device Tree overlay for PiScreen2 3.5" TFT with resistive touch by Ozzmaker.com ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ piscreen2_pins: piscreen2_pins { ++ brcm,pins = <17 25 24 22>; ++ brcm,function = <0 1 1 1>; /* in out out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ piscreen2: piscreen2@0{ ++ compatible = "ilitek,ili9486"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&piscreen2_pins>; ++ bgr; ++ spi-max-frequency = <64000000>; ++ rotate = <90>; ++ fps = <30>; ++ buswidth = <8>; ++ regwidth = <16>; ++ txbuflen = <32768>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 22 1>; ++ debug = <0>; ++ ++ init = <0x10000b0 0x00 ++ 0x1000011 ++ 0x20000ff ++ 0x100003a 0x55 ++ 0x1000036 0x28 ++ 0x10000c0 0x11 0x09 ++ 0x10000c1 0x41 ++ 0x10000c5 0x00 0x00 0x00 0x00 ++ 0x10000b6 0x00 0x02 ++ 0x10000f7 0xa9 0x51 0x2c 0x2 ++ 0x10000be 0x00 0x04 ++ 0x10000e9 0x00 ++ 0x1000011 ++ 0x1000029>; ++ ++ }; ++ ++ piscreen2_ts: piscreen2-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <17 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,swap-xy; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&piscreen2>,"spi-max-frequency:0"; ++ rotate = <&piscreen2>,"rotate:0"; ++ fps = <&piscreen2>,"fps:0"; ++ debug = <&piscreen2>,"debug:0"; ++ xohms = <&piscreen2_ts>,"ti,x-plate-ohms;0"; ++ }; ++}; ++ diff --git a/target/linux/brcm2708/patches-4.4/0108-New-overlay-for-PiScreen2r.patch b/target/linux/brcm2708/patches-4.4/0108-New-overlay-for-PiScreen2r.patch deleted file mode 100644 index f56543ea77..0000000000 --- a/target/linux/brcm2708/patches-4.4/0108-New-overlay-for-PiScreen2r.patch +++ /dev/null @@ -1,148 +0,0 @@ -From a4b5aa412585674d4a4f2ba7c0d818fd796f51c3 Mon Sep 17 00:00:00 2001 -From: mwilliams03 -Date: Sun, 18 Oct 2015 17:07:24 -0700 -Subject: [PATCH 108/170] New overlay for PiScreen2r - ---- - arch/arm/boot/dts/overlays/Makefile | 1 + - arch/arm/boot/dts/overlays/README | 14 +++ - arch/arm/boot/dts/overlays/piscreen2r-overlay.dts | 100 ++++++++++++++++++++++ - 3 files changed, 115 insertions(+) - create mode 100644 arch/arm/boot/dts/overlays/piscreen2r-overlay.dts - ---- a/arch/arm/boot/dts/overlays/Makefile -+++ b/arch/arm/boot/dts/overlays/Makefile -@@ -35,6 +35,7 @@ dtb-$(RPI_DT_OVERLAYS) += mcp2515-can1-o - dtb-$(RPI_DT_OVERLAYS) += mmc-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += mz61581-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += piscreen-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += piscreen2r-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += pitft28-resistive-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += pwm-overlay.dtb ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -425,6 +425,20 @@ Params: speed Display - xohms Touchpanel sensitivity (X-plate resistance) - - -+Name: piscreen2r -+Info: PiScreen 2 with resistive TP display by OzzMaker.com -+Load: dtoverlay=piscreen2r,= -+Params: speed Display SPI bus speed -+ -+ rotate Display rotation {0,90,180,270} -+ -+ fps Delay between frame updates -+ -+ debug Debug output level {0-7} -+ -+ xohms Touchpanel sensitivity (X-plate resistance) -+ -+ - Name: pitft28-resistive - Info: Adafruit PiTFT 2.8" resistive touch screen - Load: dtoverlay=pitft28-resistive,= ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/piscreen2r-overlay.dts -@@ -0,0 +1,100 @@ -+ /* -+ * Device Tree overlay for PiScreen2 3.5" TFT with resistive touch by Ozzmaker.com -+ * -+ */ -+ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&spi0>; -+ __overlay__ { -+ status = "okay"; -+ -+ spidev@0{ -+ status = "disabled"; -+ }; -+ -+ spidev@1{ -+ status = "disabled"; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ piscreen2_pins: piscreen2_pins { -+ brcm,pins = <17 25 24 22>; -+ brcm,function = <0 1 1 1>; /* in out out out */ -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&spi0>; -+ __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ piscreen2: piscreen2@0{ -+ compatible = "ilitek,ili9486"; -+ reg = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&piscreen2_pins>; -+ bgr; -+ spi-max-frequency = <64000000>; -+ rotate = <90>; -+ fps = <30>; -+ buswidth = <8>; -+ regwidth = <16>; -+ txbuflen = <32768>; -+ reset-gpios = <&gpio 25 0>; -+ dc-gpios = <&gpio 24 0>; -+ led-gpios = <&gpio 22 1>; -+ debug = <0>; -+ -+ init = <0x10000b0 0x00 -+ 0x1000011 -+ 0x20000ff -+ 0x100003a 0x55 -+ 0x1000036 0x28 -+ 0x10000c0 0x11 0x09 -+ 0x10000c1 0x41 -+ 0x10000c5 0x00 0x00 0x00 0x00 -+ 0x10000b6 0x00 0x02 -+ 0x10000f7 0xa9 0x51 0x2c 0x2 -+ 0x10000be 0x00 0x04 -+ 0x10000e9 0x00 -+ 0x1000011 -+ 0x1000029>; -+ -+ }; -+ -+ piscreen2_ts: piscreen2-ts@1 { -+ compatible = "ti,ads7846"; -+ reg = <1>; -+ -+ spi-max-frequency = <2000000>; -+ interrupts = <17 2>; /* high-to-low edge triggered */ -+ interrupt-parent = <&gpio>; -+ pendown-gpio = <&gpio 17 0>; -+ ti,swap-xy; -+ ti,x-plate-ohms = /bits/ 16 <100>; -+ ti,pressure-max = /bits/ 16 <255>; -+ }; -+ }; -+ }; -+ __overrides__ { -+ speed = <&piscreen2>,"spi-max-frequency:0"; -+ rotate = <&piscreen2>,"rotate:0"; -+ fps = <&piscreen2>,"fps:0"; -+ debug = <&piscreen2>,"debug:0"; -+ xohms = <&piscreen2_ts>,"ti,x-plate-ohms;0"; -+ }; -+}; -+ diff --git a/target/linux/brcm2708/patches-4.4/0108-dts-Added-overlay-for-Adafruit-PiTFT-2.8-capacitive-.patch b/target/linux/brcm2708/patches-4.4/0108-dts-Added-overlay-for-Adafruit-PiTFT-2.8-capacitive-.patch new file mode 100644 index 0000000000..4aab320147 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0108-dts-Added-overlay-for-Adafruit-PiTFT-2.8-capacitive-.patch @@ -0,0 +1,145 @@ +From 708f013bf40633883113c3fbaca5555b7d115906 Mon Sep 17 00:00:00 2001 +From: Ondrej Wisniewski +Date: Fri, 6 Nov 2015 15:01:28 +0100 +Subject: [PATCH 108/232] dts: Added overlay for Adafruit PiTFT 2.8" capacitive + touch screen + +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 22 ++++++ + .../dts/overlays/pitft28-capacitive-overlay.dts | 88 ++++++++++++++++++++++ + 3 files changed, 111 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/pitft28-capacitive-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -36,6 +36,7 @@ dtb-$(RPI_DT_OVERLAYS) += mmc-overlay.dt + dtb-$(RPI_DT_OVERLAYS) += mz61581-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += piscreen-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += piscreen2r-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += pitft28-capacitive-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += pitft28-resistive-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += pwm-overlay.dtb +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -439,6 +439,28 @@ Params: speed Display + xohms Touchpanel sensitivity (X-plate resistance) + + ++Name: pitft28-capacitive ++Info: Adafruit PiTFT 2.8" capacitive touch screen ++Load: dtoverlay=pitft28-capacitive,= ++Params: speed Display SPI bus speed ++ ++ rotate Display rotation {0,90,180,270} ++ ++ fps Delay between frame updates ++ ++ debug Debug output level {0-7} ++ ++ touch-sizex Touchscreen size x (default 240) ++ ++ touch-sizey Touchscreen size y (default 320) ++ ++ touch-invx Touchscreen inverted x axis ++ ++ touch-invy Touchscreen inverted y axis ++ ++ touch-swapxy Touchscreen swapped x y axis ++ ++ + Name: pitft28-resistive + Info: Adafruit PiTFT 2.8" resistive touch screen + Load: dtoverlay=pitft28-resistive,= +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/pitft28-capacitive-overlay.dts +@@ -0,0 +1,88 @@ ++/* ++ * Device Tree overlay for Adafruit PiTFT 2.8" capacitive touch screen ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ pitft_pins: pitft_pins { ++ brcm,pins = <24 25>; ++ brcm,function = <0 1>; /* in out */ ++ brcm,pull = <2 0>; /* pullup none */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ pitft: pitft@0{ ++ compatible = "ilitek,ili9340"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pitft_pins>; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <90>; ++ fps = <25>; ++ bgr; ++ buswidth = <8>; ++ dc-gpios = <&gpio 25 0>; ++ debug = <0>; ++ }; ++ }; ++ }; ++ ++ fragment@3 { ++ target = <&i2c1>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ ft6236: ft6236@38 { ++ compatible = "focaltech,ft6236"; ++ reg = <0x38>; ++ ++ interrupt-parent = <&gpio>; ++ interrupts = <24 2>; ++ touchscreen-size-x = <240>; ++ touchscreen-size-y = <320>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ speed = <&pitft>,"spi-max-frequency:0"; ++ rotate = <&pitft>,"rotate:0"; ++ fps = <&pitft>,"fps:0"; ++ debug = <&pitft>,"debug:0"; ++ touch-sizex = <&ft6236>,"touchscreen-size-x?"; ++ touch-sizey = <&ft6236>,"touchscreen-size-y?"; ++ touch-invx = <&ft6236>,"touchscreen-inverted-x?"; ++ touch-invy = <&ft6236>,"touchscreen-inverted-y?"; ++ touch-swapxy = <&ft6236>,"touchscreen-swapped-x-y?"; ++ }; ++}; diff --git a/target/linux/brcm2708/patches-4.4/0109-Add-support-for-the-HiFiBerry-DAC-Pro.patch b/target/linux/brcm2708/patches-4.4/0109-Add-support-for-the-HiFiBerry-DAC-Pro.patch new file mode 100644 index 0000000000..272215de9a --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0109-Add-support-for-the-HiFiBerry-DAC-Pro.patch @@ -0,0 +1,539 @@ +From b706bc693765cc80c31076e8fa0e35e798c12bbf Mon Sep 17 00:00:00 2001 +From: Stuart MacLean +Date: Fri, 2 Oct 2015 15:12:59 +0100 +Subject: [PATCH 109/232] Add support for the HiFiBerry DAC+ Pro. + +The HiFiBerry DAC+ and DAC+ Pro products both use the existing bcm sound driver with the DAC+ Pro having a special clock device driver representing the two high precision oscillators. + +An addition bug fix is included for the PCM512x codec where by the physical size of the sample frame is used in the calculation of the LRCK divisor as it was found to be wrong when using 24-bit depth sample contained in a little endian 4-byte sample frame. +--- + .../dts/overlays/hifiberry-dacplus-overlay.dts | 15 +- + drivers/clk/Makefile | 1 + + drivers/clk/clk-hifiberry-dacpro.c | 160 ++++++++++++++ + sound/soc/bcm/hifiberry_dacplus.c | 244 +++++++++++++++++++-- + sound/soc/codecs/pcm512x.c | 3 +- + 5 files changed, 396 insertions(+), 27 deletions(-) + create mode 100644 drivers/clk/clk-hifiberry-dacpro.c + +--- a/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts ++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts +@@ -6,6 +6,16 @@ + compatible = "brcm,bcm2708"; + + fragment@0 { ++ target-path = "/clocks"; ++ __overlay__ { ++ dacpro_osc: dacpro_osc { ++ compatible = "hifiberry,dacpro-clk"; ++ #clock-cells = <0>; ++ }; ++ }; ++ }; ++ ++ fragment@1 { + target = <&sound>; + __overlay__ { + compatible = "hifiberry,hifiberry-dacplus"; +@@ -14,14 +24,14 @@ + }; + }; + +- fragment@1 { ++ fragment@2 { + target = <&i2s>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@2 { ++ fragment@3 { + target = <&i2c1>; + __overlay__ { + #address-cells = <1>; +@@ -32,6 +42,7 @@ + #sound-dai-cells = <0>; + compatible = "ti,pcm5122"; + reg = <0x4d>; ++ clocks = <&dacpro_osc>; + status = "okay"; + }; + }; +--- a/drivers/clk/Makefile ++++ b/drivers/clk/Makefile +@@ -24,6 +24,7 @@ obj-$(CONFIG_COMMON_CLK_CDCE706) += clk- + obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o + obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o + obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o ++obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) += clk-hifiberry-dacpro.o + obj-$(CONFIG_MACH_LOONGSON32) += clk-ls1x.o + obj-$(CONFIG_COMMON_CLK_MAX_GEN) += clk-max-gen.o + obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o +--- /dev/null ++++ b/drivers/clk/clk-hifiberry-dacpro.c +@@ -0,0 +1,160 @@ ++/* ++ * Clock Driver for HiFiBerry DAC Pro ++ * ++ * Author: Stuart MacLean ++ * Copyright 2015 ++ * ++ * 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 ++ ++/* Clock rate of CLK44EN attached to GPIO6 pin */ ++#define CLK_44EN_RATE 22579200UL ++/* Clock rate of CLK48EN attached to GPIO3 pin */ ++#define CLK_48EN_RATE 24576000UL ++ ++/** ++ * struct hifiberry_dacpro_clk - Common struct to the HiFiBerry DAC Pro ++ * @hw: clk_hw for the common clk framework ++ * @mode: 0 => CLK44EN, 1 => CLK48EN ++ */ ++struct clk_hifiberry_hw { ++ struct clk_hw hw; ++ uint8_t mode; ++}; ++ ++#define to_hifiberry_clk(_hw) container_of(_hw, struct clk_hifiberry_hw, hw) ++ ++static const struct of_device_id clk_hifiberry_dacpro_dt_ids[] = { ++ { .compatible = "hifiberry,dacpro-clk",}, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, clk_hifiberry_dacpro_dt_ids); ++ ++static unsigned long clk_hifiberry_dacpro_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ return (to_hifiberry_clk(hw)->mode == 0) ? CLK_44EN_RATE : ++ CLK_48EN_RATE; ++} ++ ++static long clk_hifiberry_dacpro_round_rate(struct clk_hw *hw, ++ unsigned long rate, unsigned long *parent_rate) ++{ ++ long actual_rate; ++ ++ if (rate <= CLK_44EN_RATE) { ++ actual_rate = (long)CLK_44EN_RATE; ++ } else if (rate >= CLK_48EN_RATE) { ++ actual_rate = (long)CLK_48EN_RATE; ++ } else { ++ long diff44Rate = (long)(rate - CLK_44EN_RATE); ++ long diff48Rate = (long)(CLK_48EN_RATE - rate); ++ ++ if (diff44Rate < diff48Rate) ++ actual_rate = (long)CLK_44EN_RATE; ++ else ++ actual_rate = (long)CLK_48EN_RATE; ++ } ++ return actual_rate; ++} ++ ++ ++static int clk_hifiberry_dacpro_set_rate(struct clk_hw *hw, ++ unsigned long rate, unsigned long parent_rate) ++{ ++ unsigned long actual_rate; ++ struct clk_hifiberry_hw *clk = to_hifiberry_clk(hw); ++ ++ actual_rate = (unsigned long)clk_hifiberry_dacpro_round_rate(hw, rate, ++ &parent_rate); ++ clk->mode = (actual_rate == CLK_44EN_RATE) ? 0 : 1; ++ return 0; ++} ++ ++ ++const struct clk_ops clk_hifiberry_dacpro_rate_ops = { ++ .recalc_rate = clk_hifiberry_dacpro_recalc_rate, ++ .round_rate = clk_hifiberry_dacpro_round_rate, ++ .set_rate = clk_hifiberry_dacpro_set_rate, ++}; ++ ++static int clk_hifiberry_dacpro_probe(struct platform_device *pdev) ++{ ++ int ret; ++ struct clk_hifiberry_hw *proclk; ++ struct clk *clk; ++ struct device *dev; ++ struct clk_init_data init; ++ ++ dev = &pdev->dev; ++ ++ proclk = kzalloc(sizeof(struct clk_hifiberry_hw), GFP_KERNEL); ++ if (!proclk) ++ return -ENOMEM; ++ ++ init.name = "clk-hifiberry-dacpro"; ++ init.ops = &clk_hifiberry_dacpro_rate_ops; ++ init.flags = CLK_IS_ROOT | CLK_IS_BASIC; ++ init.parent_names = NULL; ++ init.num_parents = 0; ++ ++ proclk->mode = 0; ++ proclk->hw.init = &init; ++ ++ clk = devm_clk_register(dev, &proclk->hw); ++ if (!IS_ERR(clk)) { ++ ret = of_clk_add_provider(dev->of_node, of_clk_src_simple_get, ++ clk); ++ } else { ++ dev_err(dev, "Fail to register clock driver\n"); ++ kfree(proclk); ++ ret = PTR_ERR(clk); ++ } ++ return ret; ++} ++ ++static int clk_hifiberry_dacpro_remove(struct platform_device *pdev) ++{ ++ of_clk_del_provider(pdev->dev.of_node); ++ return 0; ++} ++ ++static struct platform_driver clk_hifiberry_dacpro_driver = { ++ .probe = clk_hifiberry_dacpro_probe, ++ .remove = clk_hifiberry_dacpro_remove, ++ .driver = { ++ .name = "clk-hifiberry-dacpro", ++ .of_match_table = clk_hifiberry_dacpro_dt_ids, ++ }, ++}; ++ ++static int __init clk_hifiberry_dacpro_init(void) ++{ ++ return platform_driver_register(&clk_hifiberry_dacpro_driver); ++} ++core_initcall(clk_hifiberry_dacpro_init); ++ ++static void __exit clk_hifiberry_dacpro_exit(void) ++{ ++ platform_driver_unregister(&clk_hifiberry_dacpro_driver); ++} ++module_exit(clk_hifiberry_dacpro_exit); ++ ++MODULE_DESCRIPTION("HiFiBerry DAC Pro clock driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:clk-hifiberry-dacpro"); +--- a/sound/soc/bcm/hifiberry_dacplus.c ++++ b/sound/soc/bcm/hifiberry_dacplus.c +@@ -1,8 +1,8 @@ + /* +- * ASoC Driver for HiFiBerry DAC+ ++ * ASoC Driver for HiFiBerry DAC+ / DAC Pro + * +- * Author: Daniel Matuschek +- * Copyright 2014 ++ * Author: Daniel Matuschek, Stuart MacLean ++ * Copyright 2014-2015 + * based on code by Florian Meier + * + * This program is free software; you can redistribute it and/or +@@ -17,6 +17,13 @@ + + #include + #include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + + #include + #include +@@ -26,34 +33,222 @@ + + #include "../codecs/pcm512x.h" + ++#define HIFIBERRY_DACPRO_NOCLOCK 0 ++#define HIFIBERRY_DACPRO_CLK44EN 1 ++#define HIFIBERRY_DACPRO_CLK48EN 2 ++ ++struct pcm512x_priv { ++ struct regmap *regmap; ++ struct clk *sclk; ++}; ++ ++/* Clock rate of CLK44EN attached to GPIO6 pin */ ++#define CLK_44EN_RATE 22579200UL ++/* Clock rate of CLK48EN attached to GPIO3 pin */ ++#define CLK_48EN_RATE 24576000UL ++ ++static bool snd_rpi_hifiberry_is_dacpro; ++ ++static void snd_rpi_hifiberry_dacplus_select_clk(struct snd_soc_codec *codec, ++ int clk_id) ++{ ++ switch (clk_id) { ++ case HIFIBERRY_DACPRO_NOCLOCK: ++ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x24, 0x00); ++ break; ++ case HIFIBERRY_DACPRO_CLK44EN: ++ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x24, 0x20); ++ break; ++ case HIFIBERRY_DACPRO_CLK48EN: ++ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x24, 0x04); ++ break; ++ } ++} ++ ++static void snd_rpi_hifiberry_dacplus_clk_gpio(struct snd_soc_codec *codec) ++{ ++ snd_soc_update_bits(codec, PCM512x_GPIO_EN, 0x24, 0x24); ++ snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_3, 0x0f, 0x02); ++ snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_6, 0x0f, 0x02); ++} ++ ++static bool snd_rpi_hifiberry_dacplus_is_sclk(struct snd_soc_codec *codec) ++{ ++ int sck; ++ ++ sck = snd_soc_read(codec, PCM512x_RATE_DET_4); ++ return (!(sck & 0x40)); ++} ++ ++static bool snd_rpi_hifiberry_dacplus_is_sclk_sleep( ++ struct snd_soc_codec *codec) ++{ ++ msleep(2); ++ return snd_rpi_hifiberry_dacplus_is_sclk(codec); ++} ++ ++static bool snd_rpi_hifiberry_dacplus_is_pro_card(struct snd_soc_codec *codec) ++{ ++ bool isClk44EN, isClk48En, isNoClk; ++ ++ snd_rpi_hifiberry_dacplus_clk_gpio(codec); ++ ++ snd_rpi_hifiberry_dacplus_select_clk(codec, HIFIBERRY_DACPRO_CLK44EN); ++ isClk44EN = snd_rpi_hifiberry_dacplus_is_sclk_sleep(codec); ++ ++ snd_rpi_hifiberry_dacplus_select_clk(codec, HIFIBERRY_DACPRO_NOCLOCK); ++ isNoClk = snd_rpi_hifiberry_dacplus_is_sclk_sleep(codec); ++ ++ snd_rpi_hifiberry_dacplus_select_clk(codec, HIFIBERRY_DACPRO_CLK48EN); ++ isClk48En = snd_rpi_hifiberry_dacplus_is_sclk_sleep(codec); ++ ++ return (isClk44EN && isClk48En && !isNoClk); ++} ++ ++static int snd_rpi_hifiberry_dacplus_clk_for_rate(int sample_rate) ++{ ++ int type; ++ ++ switch (sample_rate) { ++ case 11025: ++ case 22050: ++ case 44100: ++ case 88200: ++ case 176400: ++ type = HIFIBERRY_DACPRO_CLK44EN; ++ break; ++ default: ++ type = HIFIBERRY_DACPRO_CLK48EN; ++ break; ++ } ++ return type; ++} ++ ++static void snd_rpi_hifiberry_dacplus_set_sclk(struct snd_soc_codec *codec, ++ int sample_rate) ++{ ++ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); ++ ++ if (!IS_ERR(pcm512x->sclk)) { ++ int ctype; ++ ++ ctype = snd_rpi_hifiberry_dacplus_clk_for_rate(sample_rate); ++ clk_set_rate(pcm512x->sclk, (ctype == HIFIBERRY_DACPRO_CLK44EN) ++ ? CLK_44EN_RATE : CLK_48EN_RATE); ++ snd_rpi_hifiberry_dacplus_select_clk(codec, ctype); ++ } ++} ++ + static int snd_rpi_hifiberry_dacplus_init(struct snd_soc_pcm_runtime *rtd) + { + struct snd_soc_codec *codec = rtd->codec; ++ struct pcm512x_priv *priv; ++ ++ snd_rpi_hifiberry_is_dacpro ++ = snd_rpi_hifiberry_dacplus_is_pro_card(codec); ++ ++ if (snd_rpi_hifiberry_is_dacpro) { ++ struct snd_soc_dai_link *dai = rtd->dai_link; ++ ++ dai->name = "HiFiBerry DAC+ Pro"; ++ dai->stream_name = "HiFiBerry DAC+ Pro HiFi"; ++ dai->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF ++ | SND_SOC_DAIFMT_CBM_CFM; ++ ++ snd_soc_update_bits(codec, PCM512x_BCLK_LRCLK_CFG, 0x31, 0x11); ++ snd_soc_update_bits(codec, PCM512x_MASTER_MODE, 0x03, 0x03); ++ snd_soc_update_bits(codec, PCM512x_MASTER_CLKDIV_2, 0x7f, 63); ++ } else { ++ priv = snd_soc_codec_get_drvdata(codec); ++ priv->sclk = ERR_PTR(-ENOENT); ++ } ++ + snd_soc_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08); +- snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0xf, 0x02); +- snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08,0x08); ++ snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02); ++ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); ++ ++ return 0; ++} ++ ++static int snd_rpi_hifiberry_dacplus_update_rate_den( ++ 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 pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); ++ struct snd_ratnum *rats_no_pll; ++ unsigned int num = 0, den = 0; ++ int err; ++ ++ rats_no_pll = devm_kzalloc(rtd->dev, sizeof(*rats_no_pll), GFP_KERNEL); ++ if (!rats_no_pll) ++ return -ENOMEM; ++ ++ rats_no_pll->num = clk_get_rate(pcm512x->sclk) / 64; ++ rats_no_pll->den_min = 1; ++ rats_no_pll->den_max = 128; ++ rats_no_pll->den_step = 1; ++ ++ err = snd_interval_ratnum(hw_param_interval(params, ++ SNDRV_PCM_HW_PARAM_RATE), 1, rats_no_pll, &num, &den); ++ if (err >= 0 && den) { ++ params->rate_num = num; ++ params->rate_den = den; ++ } ++ ++ devm_kfree(rtd->dev, rats_no_pll); + return 0; + } + +-static int snd_rpi_hifiberry_dacplus_hw_params(struct snd_pcm_substream *substream, +- struct snd_pcm_hw_params *params) ++static int snd_rpi_hifiberry_dacplus_set_bclk_ratio_pro( ++ struct snd_soc_dai *cpu_dai, struct snd_pcm_hw_params *params) ++{ ++ int bratio = snd_pcm_format_physical_width(params_format(params)) ++ * params_channels(params); ++ return snd_soc_dai_set_bclk_ratio(cpu_dai, bratio); ++} ++ ++static int snd_rpi_hifiberry_dacplus_hw_params( ++ struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) + { ++ int ret; + 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, 64); ++ ++ if (snd_rpi_hifiberry_is_dacpro) { ++ struct snd_soc_codec *codec = rtd->codec; ++ ++ snd_rpi_hifiberry_dacplus_set_sclk(codec, ++ params_rate(params)); ++ ++ ret = snd_rpi_hifiberry_dacplus_set_bclk_ratio_pro(cpu_dai, ++ params); ++ if (!ret) ++ ret = snd_rpi_hifiberry_dacplus_update_rate_den( ++ substream, params); ++ } else { ++ ret = snd_soc_dai_set_bclk_ratio(cpu_dai, 64); ++ } ++ return ret; + } + +-static int snd_rpi_hifiberry_dacplus_startup(struct snd_pcm_substream *substream) { ++static int snd_rpi_hifiberry_dacplus_startup( ++ struct snd_pcm_substream *substream) ++{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; +- snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08,0x08); ++ ++ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); + return 0; + } + +-static void snd_rpi_hifiberry_dacplus_shutdown(struct snd_pcm_substream *substream) { ++static void snd_rpi_hifiberry_dacplus_shutdown( ++ struct snd_pcm_substream *substream) ++{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; +- snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08,0x00); ++ ++ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x00); + } + + /* machine stream operations */ +@@ -90,19 +285,20 @@ static int snd_rpi_hifiberry_dacplus_pro + int ret = 0; + + snd_rpi_hifiberry_dacplus.dev = &pdev->dev; +- + if (pdev->dev.of_node) { +- struct device_node *i2s_node; +- struct snd_soc_dai_link *dai = &snd_rpi_hifiberry_dacplus_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; +- } ++ struct device_node *i2s_node; ++ struct snd_soc_dai_link *dai; ++ ++ dai = &snd_rpi_hifiberry_dacplus_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_hifiberry_dacplus); +--- a/sound/soc/codecs/pcm512x.c ++++ b/sound/soc/codecs/pcm512x.c +@@ -854,7 +854,8 @@ static int pcm512x_set_dividers(struct s + int fssp; + int gpio; + +- lrclk_div = snd_soc_params_to_frame_size(params); ++ lrclk_div = snd_pcm_format_physical_width(params_format(params)) ++ * params_channels(params); + if (lrclk_div == 0) { + dev_err(dev, "No LRCLK?\n"); + return -EINVAL; diff --git a/target/linux/brcm2708/patches-4.4/0109-dts-Added-overlay-for-Adafruit-PiTFT-2.8-capacitive-.patch b/target/linux/brcm2708/patches-4.4/0109-dts-Added-overlay-for-Adafruit-PiTFT-2.8-capacitive-.patch deleted file mode 100644 index 480d8db6a0..0000000000 --- a/target/linux/brcm2708/patches-4.4/0109-dts-Added-overlay-for-Adafruit-PiTFT-2.8-capacitive-.patch +++ /dev/null @@ -1,145 +0,0 @@ -From 1a4fac1ea6bb33a963b06406f6f44b527daa3a90 Mon Sep 17 00:00:00 2001 -From: Ondrej Wisniewski -Date: Fri, 6 Nov 2015 15:01:28 +0100 -Subject: [PATCH 109/170] dts: Added overlay for Adafruit PiTFT 2.8" capacitive - touch screen - ---- - arch/arm/boot/dts/overlays/Makefile | 1 + - arch/arm/boot/dts/overlays/README | 22 ++++++ - .../dts/overlays/pitft28-capacitive-overlay.dts | 88 ++++++++++++++++++++++ - 3 files changed, 111 insertions(+) - create mode 100644 arch/arm/boot/dts/overlays/pitft28-capacitive-overlay.dts - ---- a/arch/arm/boot/dts/overlays/Makefile -+++ b/arch/arm/boot/dts/overlays/Makefile -@@ -36,6 +36,7 @@ dtb-$(RPI_DT_OVERLAYS) += mmc-overlay.dt - dtb-$(RPI_DT_OVERLAYS) += mz61581-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += piscreen-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += piscreen2r-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += pitft28-capacitive-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += pitft28-resistive-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += pwm-overlay.dtb ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -439,6 +439,28 @@ Params: speed Display - xohms Touchpanel sensitivity (X-plate resistance) - - -+Name: pitft28-capacitive -+Info: Adafruit PiTFT 2.8" capacitive touch screen -+Load: dtoverlay=pitft28-capacitive,= -+Params: speed Display SPI bus speed -+ -+ rotate Display rotation {0,90,180,270} -+ -+ fps Delay between frame updates -+ -+ debug Debug output level {0-7} -+ -+ touch-sizex Touchscreen size x (default 240) -+ -+ touch-sizey Touchscreen size y (default 320) -+ -+ touch-invx Touchscreen inverted x axis -+ -+ touch-invy Touchscreen inverted y axis -+ -+ touch-swapxy Touchscreen swapped x y axis -+ -+ - Name: pitft28-resistive - Info: Adafruit PiTFT 2.8" resistive touch screen - Load: dtoverlay=pitft28-resistive,= ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/pitft28-capacitive-overlay.dts -@@ -0,0 +1,88 @@ -+/* -+ * Device Tree overlay for Adafruit PiTFT 2.8" capacitive touch screen -+ * -+ */ -+ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&spi0>; -+ __overlay__ { -+ status = "okay"; -+ -+ spidev@0{ -+ status = "disabled"; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ pitft_pins: pitft_pins { -+ brcm,pins = <24 25>; -+ brcm,function = <0 1>; /* in out */ -+ brcm,pull = <2 0>; /* pullup none */ -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&spi0>; -+ __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ pitft: pitft@0{ -+ compatible = "ilitek,ili9340"; -+ reg = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&pitft_pins>; -+ -+ spi-max-frequency = <32000000>; -+ rotate = <90>; -+ fps = <25>; -+ bgr; -+ buswidth = <8>; -+ dc-gpios = <&gpio 25 0>; -+ debug = <0>; -+ }; -+ }; -+ }; -+ -+ fragment@3 { -+ target = <&i2c1>; -+ __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ ft6236: ft6236@38 { -+ compatible = "focaltech,ft6236"; -+ reg = <0x38>; -+ -+ interrupt-parent = <&gpio>; -+ interrupts = <24 2>; -+ touchscreen-size-x = <240>; -+ touchscreen-size-y = <320>; -+ }; -+ }; -+ }; -+ -+ __overrides__ { -+ speed = <&pitft>,"spi-max-frequency:0"; -+ rotate = <&pitft>,"rotate:0"; -+ fps = <&pitft>,"fps:0"; -+ debug = <&pitft>,"debug:0"; -+ touch-sizex = <&ft6236>,"touchscreen-size-x?"; -+ touch-sizey = <&ft6236>,"touchscreen-size-y?"; -+ touch-invx = <&ft6236>,"touchscreen-inverted-x?"; -+ touch-invy = <&ft6236>,"touchscreen-inverted-y?"; -+ touch-swapxy = <&ft6236>,"touchscreen-swapped-x-y?"; -+ }; -+}; diff --git a/target/linux/brcm2708/patches-4.4/0110-Add-support-for-the-HiFiBerry-DAC-Pro.patch b/target/linux/brcm2708/patches-4.4/0110-Add-support-for-the-HiFiBerry-DAC-Pro.patch deleted file mode 100644 index 594388eda0..0000000000 --- a/target/linux/brcm2708/patches-4.4/0110-Add-support-for-the-HiFiBerry-DAC-Pro.patch +++ /dev/null @@ -1,539 +0,0 @@ -From 660ba9173ca14b48d7cd2f69ff45e67ebe4d947a Mon Sep 17 00:00:00 2001 -From: Stuart MacLean -Date: Fri, 2 Oct 2015 15:12:59 +0100 -Subject: [PATCH 110/170] Add support for the HiFiBerry DAC+ Pro. - -The HiFiBerry DAC+ and DAC+ Pro products both use the existing bcm sound driver with the DAC+ Pro having a special clock device driver representing the two high precision oscillators. - -An addition bug fix is included for the PCM512x codec where by the physical size of the sample frame is used in the calculation of the LRCK divisor as it was found to be wrong when using 24-bit depth sample contained in a little endian 4-byte sample frame. ---- - .../dts/overlays/hifiberry-dacplus-overlay.dts | 15 +- - drivers/clk/Makefile | 1 + - drivers/clk/clk-hifiberry-dacpro.c | 160 ++++++++++++++ - sound/soc/bcm/hifiberry_dacplus.c | 244 +++++++++++++++++++-- - sound/soc/codecs/pcm512x.c | 3 +- - 5 files changed, 396 insertions(+), 27 deletions(-) - create mode 100644 drivers/clk/clk-hifiberry-dacpro.c - ---- a/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts -+++ b/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts -@@ -6,6 +6,16 @@ - compatible = "brcm,bcm2708"; - - fragment@0 { -+ target-path = "/clocks"; -+ __overlay__ { -+ dacpro_osc: dacpro_osc { -+ compatible = "hifiberry,dacpro-clk"; -+ #clock-cells = <0>; -+ }; -+ }; -+ }; -+ -+ fragment@1 { - target = <&sound>; - __overlay__ { - compatible = "hifiberry,hifiberry-dacplus"; -@@ -14,14 +24,14 @@ - }; - }; - -- fragment@1 { -+ fragment@2 { - target = <&i2s>; - __overlay__ { - status = "okay"; - }; - }; - -- fragment@2 { -+ fragment@3 { - target = <&i2c1>; - __overlay__ { - #address-cells = <1>; -@@ -32,6 +42,7 @@ - #sound-dai-cells = <0>; - compatible = "ti,pcm5122"; - reg = <0x4d>; -+ clocks = <&dacpro_osc>; - status = "okay"; - }; - }; ---- a/drivers/clk/Makefile -+++ b/drivers/clk/Makefile -@@ -24,6 +24,7 @@ obj-$(CONFIG_COMMON_CLK_CDCE706) += clk- - obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o - obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o - obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o -+obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) += clk-hifiberry-dacpro.o - obj-$(CONFIG_MACH_LOONGSON32) += clk-ls1x.o - obj-$(CONFIG_COMMON_CLK_MAX_GEN) += clk-max-gen.o - obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o ---- /dev/null -+++ b/drivers/clk/clk-hifiberry-dacpro.c -@@ -0,0 +1,160 @@ -+/* -+ * Clock Driver for HiFiBerry DAC Pro -+ * -+ * Author: Stuart MacLean -+ * Copyright 2015 -+ * -+ * 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 -+ -+/* Clock rate of CLK44EN attached to GPIO6 pin */ -+#define CLK_44EN_RATE 22579200UL -+/* Clock rate of CLK48EN attached to GPIO3 pin */ -+#define CLK_48EN_RATE 24576000UL -+ -+/** -+ * struct hifiberry_dacpro_clk - Common struct to the HiFiBerry DAC Pro -+ * @hw: clk_hw for the common clk framework -+ * @mode: 0 => CLK44EN, 1 => CLK48EN -+ */ -+struct clk_hifiberry_hw { -+ struct clk_hw hw; -+ uint8_t mode; -+}; -+ -+#define to_hifiberry_clk(_hw) container_of(_hw, struct clk_hifiberry_hw, hw) -+ -+static const struct of_device_id clk_hifiberry_dacpro_dt_ids[] = { -+ { .compatible = "hifiberry,dacpro-clk",}, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, clk_hifiberry_dacpro_dt_ids); -+ -+static unsigned long clk_hifiberry_dacpro_recalc_rate(struct clk_hw *hw, -+ unsigned long parent_rate) -+{ -+ return (to_hifiberry_clk(hw)->mode == 0) ? CLK_44EN_RATE : -+ CLK_48EN_RATE; -+} -+ -+static long clk_hifiberry_dacpro_round_rate(struct clk_hw *hw, -+ unsigned long rate, unsigned long *parent_rate) -+{ -+ long actual_rate; -+ -+ if (rate <= CLK_44EN_RATE) { -+ actual_rate = (long)CLK_44EN_RATE; -+ } else if (rate >= CLK_48EN_RATE) { -+ actual_rate = (long)CLK_48EN_RATE; -+ } else { -+ long diff44Rate = (long)(rate - CLK_44EN_RATE); -+ long diff48Rate = (long)(CLK_48EN_RATE - rate); -+ -+ if (diff44Rate < diff48Rate) -+ actual_rate = (long)CLK_44EN_RATE; -+ else -+ actual_rate = (long)CLK_48EN_RATE; -+ } -+ return actual_rate; -+} -+ -+ -+static int clk_hifiberry_dacpro_set_rate(struct clk_hw *hw, -+ unsigned long rate, unsigned long parent_rate) -+{ -+ unsigned long actual_rate; -+ struct clk_hifiberry_hw *clk = to_hifiberry_clk(hw); -+ -+ actual_rate = (unsigned long)clk_hifiberry_dacpro_round_rate(hw, rate, -+ &parent_rate); -+ clk->mode = (actual_rate == CLK_44EN_RATE) ? 0 : 1; -+ return 0; -+} -+ -+ -+const struct clk_ops clk_hifiberry_dacpro_rate_ops = { -+ .recalc_rate = clk_hifiberry_dacpro_recalc_rate, -+ .round_rate = clk_hifiberry_dacpro_round_rate, -+ .set_rate = clk_hifiberry_dacpro_set_rate, -+}; -+ -+static int clk_hifiberry_dacpro_probe(struct platform_device *pdev) -+{ -+ int ret; -+ struct clk_hifiberry_hw *proclk; -+ struct clk *clk; -+ struct device *dev; -+ struct clk_init_data init; -+ -+ dev = &pdev->dev; -+ -+ proclk = kzalloc(sizeof(struct clk_hifiberry_hw), GFP_KERNEL); -+ if (!proclk) -+ return -ENOMEM; -+ -+ init.name = "clk-hifiberry-dacpro"; -+ init.ops = &clk_hifiberry_dacpro_rate_ops; -+ init.flags = CLK_IS_ROOT | CLK_IS_BASIC; -+ init.parent_names = NULL; -+ init.num_parents = 0; -+ -+ proclk->mode = 0; -+ proclk->hw.init = &init; -+ -+ clk = devm_clk_register(dev, &proclk->hw); -+ if (!IS_ERR(clk)) { -+ ret = of_clk_add_provider(dev->of_node, of_clk_src_simple_get, -+ clk); -+ } else { -+ dev_err(dev, "Fail to register clock driver\n"); -+ kfree(proclk); -+ ret = PTR_ERR(clk); -+ } -+ return ret; -+} -+ -+static int clk_hifiberry_dacpro_remove(struct platform_device *pdev) -+{ -+ of_clk_del_provider(pdev->dev.of_node); -+ return 0; -+} -+ -+static struct platform_driver clk_hifiberry_dacpro_driver = { -+ .probe = clk_hifiberry_dacpro_probe, -+ .remove = clk_hifiberry_dacpro_remove, -+ .driver = { -+ .name = "clk-hifiberry-dacpro", -+ .of_match_table = clk_hifiberry_dacpro_dt_ids, -+ }, -+}; -+ -+static int __init clk_hifiberry_dacpro_init(void) -+{ -+ return platform_driver_register(&clk_hifiberry_dacpro_driver); -+} -+core_initcall(clk_hifiberry_dacpro_init); -+ -+static void __exit clk_hifiberry_dacpro_exit(void) -+{ -+ platform_driver_unregister(&clk_hifiberry_dacpro_driver); -+} -+module_exit(clk_hifiberry_dacpro_exit); -+ -+MODULE_DESCRIPTION("HiFiBerry DAC Pro clock driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:clk-hifiberry-dacpro"); ---- a/sound/soc/bcm/hifiberry_dacplus.c -+++ b/sound/soc/bcm/hifiberry_dacplus.c -@@ -1,8 +1,8 @@ - /* -- * ASoC Driver for HiFiBerry DAC+ -+ * ASoC Driver for HiFiBerry DAC+ / DAC Pro - * -- * Author: Daniel Matuschek -- * Copyright 2014 -+ * Author: Daniel Matuschek, Stuart MacLean -+ * Copyright 2014-2015 - * based on code by Florian Meier - * - * This program is free software; you can redistribute it and/or -@@ -17,6 +17,13 @@ - - #include - #include -+#include -+#include -+#include -+#include -+#include -+#include -+#include - - #include - #include -@@ -26,34 +33,222 @@ - - #include "../codecs/pcm512x.h" - -+#define HIFIBERRY_DACPRO_NOCLOCK 0 -+#define HIFIBERRY_DACPRO_CLK44EN 1 -+#define HIFIBERRY_DACPRO_CLK48EN 2 -+ -+struct pcm512x_priv { -+ struct regmap *regmap; -+ struct clk *sclk; -+}; -+ -+/* Clock rate of CLK44EN attached to GPIO6 pin */ -+#define CLK_44EN_RATE 22579200UL -+/* Clock rate of CLK48EN attached to GPIO3 pin */ -+#define CLK_48EN_RATE 24576000UL -+ -+static bool snd_rpi_hifiberry_is_dacpro; -+ -+static void snd_rpi_hifiberry_dacplus_select_clk(struct snd_soc_codec *codec, -+ int clk_id) -+{ -+ switch (clk_id) { -+ case HIFIBERRY_DACPRO_NOCLOCK: -+ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x24, 0x00); -+ break; -+ case HIFIBERRY_DACPRO_CLK44EN: -+ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x24, 0x20); -+ break; -+ case HIFIBERRY_DACPRO_CLK48EN: -+ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x24, 0x04); -+ break; -+ } -+} -+ -+static void snd_rpi_hifiberry_dacplus_clk_gpio(struct snd_soc_codec *codec) -+{ -+ snd_soc_update_bits(codec, PCM512x_GPIO_EN, 0x24, 0x24); -+ snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_3, 0x0f, 0x02); -+ snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_6, 0x0f, 0x02); -+} -+ -+static bool snd_rpi_hifiberry_dacplus_is_sclk(struct snd_soc_codec *codec) -+{ -+ int sck; -+ -+ sck = snd_soc_read(codec, PCM512x_RATE_DET_4); -+ return (!(sck & 0x40)); -+} -+ -+static bool snd_rpi_hifiberry_dacplus_is_sclk_sleep( -+ struct snd_soc_codec *codec) -+{ -+ msleep(2); -+ return snd_rpi_hifiberry_dacplus_is_sclk(codec); -+} -+ -+static bool snd_rpi_hifiberry_dacplus_is_pro_card(struct snd_soc_codec *codec) -+{ -+ bool isClk44EN, isClk48En, isNoClk; -+ -+ snd_rpi_hifiberry_dacplus_clk_gpio(codec); -+ -+ snd_rpi_hifiberry_dacplus_select_clk(codec, HIFIBERRY_DACPRO_CLK44EN); -+ isClk44EN = snd_rpi_hifiberry_dacplus_is_sclk_sleep(codec); -+ -+ snd_rpi_hifiberry_dacplus_select_clk(codec, HIFIBERRY_DACPRO_NOCLOCK); -+ isNoClk = snd_rpi_hifiberry_dacplus_is_sclk_sleep(codec); -+ -+ snd_rpi_hifiberry_dacplus_select_clk(codec, HIFIBERRY_DACPRO_CLK48EN); -+ isClk48En = snd_rpi_hifiberry_dacplus_is_sclk_sleep(codec); -+ -+ return (isClk44EN && isClk48En && !isNoClk); -+} -+ -+static int snd_rpi_hifiberry_dacplus_clk_for_rate(int sample_rate) -+{ -+ int type; -+ -+ switch (sample_rate) { -+ case 11025: -+ case 22050: -+ case 44100: -+ case 88200: -+ case 176400: -+ type = HIFIBERRY_DACPRO_CLK44EN; -+ break; -+ default: -+ type = HIFIBERRY_DACPRO_CLK48EN; -+ break; -+ } -+ return type; -+} -+ -+static void snd_rpi_hifiberry_dacplus_set_sclk(struct snd_soc_codec *codec, -+ int sample_rate) -+{ -+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); -+ -+ if (!IS_ERR(pcm512x->sclk)) { -+ int ctype; -+ -+ ctype = snd_rpi_hifiberry_dacplus_clk_for_rate(sample_rate); -+ clk_set_rate(pcm512x->sclk, (ctype == HIFIBERRY_DACPRO_CLK44EN) -+ ? CLK_44EN_RATE : CLK_48EN_RATE); -+ snd_rpi_hifiberry_dacplus_select_clk(codec, ctype); -+ } -+} -+ - static int snd_rpi_hifiberry_dacplus_init(struct snd_soc_pcm_runtime *rtd) - { - struct snd_soc_codec *codec = rtd->codec; -+ struct pcm512x_priv *priv; -+ -+ snd_rpi_hifiberry_is_dacpro -+ = snd_rpi_hifiberry_dacplus_is_pro_card(codec); -+ -+ if (snd_rpi_hifiberry_is_dacpro) { -+ struct snd_soc_dai_link *dai = rtd->dai_link; -+ -+ dai->name = "HiFiBerry DAC+ Pro"; -+ dai->stream_name = "HiFiBerry DAC+ Pro HiFi"; -+ dai->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF -+ | SND_SOC_DAIFMT_CBM_CFM; -+ -+ snd_soc_update_bits(codec, PCM512x_BCLK_LRCLK_CFG, 0x31, 0x11); -+ snd_soc_update_bits(codec, PCM512x_MASTER_MODE, 0x03, 0x03); -+ snd_soc_update_bits(codec, PCM512x_MASTER_CLKDIV_2, 0x7f, 63); -+ } else { -+ priv = snd_soc_codec_get_drvdata(codec); -+ priv->sclk = ERR_PTR(-ENOENT); -+ } -+ - snd_soc_update_bits(codec, PCM512x_GPIO_EN, 0x08, 0x08); -- snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0xf, 0x02); -- snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08,0x08); -+ snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02); -+ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); -+ -+ return 0; -+} -+ -+static int snd_rpi_hifiberry_dacplus_update_rate_den( -+ 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 pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); -+ struct snd_ratnum *rats_no_pll; -+ unsigned int num = 0, den = 0; -+ int err; -+ -+ rats_no_pll = devm_kzalloc(rtd->dev, sizeof(*rats_no_pll), GFP_KERNEL); -+ if (!rats_no_pll) -+ return -ENOMEM; -+ -+ rats_no_pll->num = clk_get_rate(pcm512x->sclk) / 64; -+ rats_no_pll->den_min = 1; -+ rats_no_pll->den_max = 128; -+ rats_no_pll->den_step = 1; -+ -+ err = snd_interval_ratnum(hw_param_interval(params, -+ SNDRV_PCM_HW_PARAM_RATE), 1, rats_no_pll, &num, &den); -+ if (err >= 0 && den) { -+ params->rate_num = num; -+ params->rate_den = den; -+ } -+ -+ devm_kfree(rtd->dev, rats_no_pll); - return 0; - } - --static int snd_rpi_hifiberry_dacplus_hw_params(struct snd_pcm_substream *substream, -- struct snd_pcm_hw_params *params) -+static int snd_rpi_hifiberry_dacplus_set_bclk_ratio_pro( -+ struct snd_soc_dai *cpu_dai, struct snd_pcm_hw_params *params) -+{ -+ int bratio = snd_pcm_format_physical_width(params_format(params)) -+ * params_channels(params); -+ return snd_soc_dai_set_bclk_ratio(cpu_dai, bratio); -+} -+ -+static int snd_rpi_hifiberry_dacplus_hw_params( -+ struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) - { -+ int ret; - 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, 64); -+ -+ if (snd_rpi_hifiberry_is_dacpro) { -+ struct snd_soc_codec *codec = rtd->codec; -+ -+ snd_rpi_hifiberry_dacplus_set_sclk(codec, -+ params_rate(params)); -+ -+ ret = snd_rpi_hifiberry_dacplus_set_bclk_ratio_pro(cpu_dai, -+ params); -+ if (!ret) -+ ret = snd_rpi_hifiberry_dacplus_update_rate_den( -+ substream, params); -+ } else { -+ ret = snd_soc_dai_set_bclk_ratio(cpu_dai, 64); -+ } -+ return ret; - } - --static int snd_rpi_hifiberry_dacplus_startup(struct snd_pcm_substream *substream) { -+static int snd_rpi_hifiberry_dacplus_startup( -+ struct snd_pcm_substream *substream) -+{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; -- snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08,0x08); -+ -+ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); - return 0; - } - --static void snd_rpi_hifiberry_dacplus_shutdown(struct snd_pcm_substream *substream) { -+static void snd_rpi_hifiberry_dacplus_shutdown( -+ struct snd_pcm_substream *substream) -+{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; -- snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08,0x00); -+ -+ snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x00); - } - - /* machine stream operations */ -@@ -90,19 +285,20 @@ static int snd_rpi_hifiberry_dacplus_pro - int ret = 0; - - snd_rpi_hifiberry_dacplus.dev = &pdev->dev; -- - if (pdev->dev.of_node) { -- struct device_node *i2s_node; -- struct snd_soc_dai_link *dai = &snd_rpi_hifiberry_dacplus_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; -- } -+ struct device_node *i2s_node; -+ struct snd_soc_dai_link *dai; -+ -+ dai = &snd_rpi_hifiberry_dacplus_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_hifiberry_dacplus); ---- a/sound/soc/codecs/pcm512x.c -+++ b/sound/soc/codecs/pcm512x.c -@@ -854,7 +854,8 @@ static int pcm512x_set_dividers(struct s - int fssp; - int gpio; - -- lrclk_div = snd_soc_params_to_frame_size(params); -+ lrclk_div = snd_pcm_format_physical_width(params_format(params)) -+ * params_channels(params); - if (lrclk_div == 0) { - dev_err(dev, "No LRCLK?\n"); - return -EINVAL; diff --git a/target/linux/brcm2708/patches-4.4/0110-BCM270X_DT-Add-at86rf233-overlay.patch b/target/linux/brcm2708/patches-4.4/0110-BCM270X_DT-Add-at86rf233-overlay.patch new file mode 100644 index 0000000000..41ae6ab662 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0110-BCM270X_DT-Add-at86rf233-overlay.patch @@ -0,0 +1,130 @@ +From 9fa71fbc6193d214f9a5432f85bb17f47016ba65 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 5 Oct 2015 10:47:45 +0100 +Subject: [PATCH 110/232] BCM270X_DT: Add at86rf233 overlay + +Add an overlay to support the Atmel AT86RF233 WPAN transceiver on spi0.0. + +See: https://github.com/raspberrypi/linux/issues/1151 +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 21 +++++++-- + arch/arm/boot/dts/overlays/at86rf233-overlay.dts | 54 ++++++++++++++++++++++++ + 3 files changed, 72 insertions(+), 4 deletions(-) + create mode 100644 arch/arm/boot/dts/overlays/at86rf233-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -13,6 +13,7 @@ ifeq ($(CONFIG_ARCH_BCM2835),y) + endif + + dtb-$(RPI_DT_OVERLAYS) += ads7846-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += at86rf233-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += bmp085_i2c-sensor-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += dht11-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += enc28j60-overlay.dtb +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -69,13 +69,14 @@ DT parameters: + + Parameters always have default values, although in some cases (e.g. "w1-gpio") + it is necessary to provided multiple overlays in order to get the desired +-behaviour. See the list of overlays below for a description of the parameters and their defaults. ++behaviour. See the list of overlays below for a description of the parameters ++and their defaults. + + The Overlay and Parameter Reference + =================================== + +-N.B. When editing this file, please preserve the indentation levels to make it simple to parse +-programmatically. NO HARD TABS. ++N.B. When editing this file, please preserve the indentation levels to make it ++simple to parse programmatically. NO HARD TABS. + + + Name: +@@ -149,7 +150,7 @@ Name: ads7846 + Info: ADS7846 Touch controller + Load: dtoverlay=ads7846,= + Params: cs SPI bus Chip Select (default 1) +- speed SPI bus speed (default 2Mhz, max 3.25MHz) ++ speed SPI bus speed (default 2MHz, max 3.25MHz) + penirq GPIO used for PENIRQ. REQUIRED + penirq_pull Set GPIO pull (default 0=none, 2=pullup) + swapxy Swap x and y axis +@@ -170,6 +171,18 @@ Params: cs SPI bus + www.kernel.org/doc/Documentation/devicetree/bindings/input/ads7846.txt + + ++Name: at86rf233 ++Info: Configures the Atmel AT86RF233 802.15.4 low-power WPAN transceiver, ++ connected to spi0.0 ++Load: dtoverlay=at86rf233,= ++Params: interrupt GPIO used for INT (default 23) ++ reset GPIO used for Reset (default 24) ++ sleep GPIO used for Sleep (default 25) ++ speed SPI bus speed in Hz (default 6000000) ++ trim Fine tuning of the internal capacitance ++ arrays (0=+0pF, 15=+4.5pF, default 15) ++ ++ + Name: bmp085_i2c-sensor + Info: Configures the BMP085/BMP180 digital barometric pressure and temperature + sensors from Bosch Sensortec +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/at86rf233-overlay.dts +@@ -0,0 +1,54 @@ ++/dts-v1/; ++/plugin/; ++ ++/* Overlay for Atmel AT86RF233 IEEE 802.15.4 WPAN transceiver on spi0.0 */ ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2836", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ lowpan0: at86rf233@0 { ++ compatible = "atmel,at86rf233"; ++ reg = <0>; ++ interrupt-parent = <&gpio>; ++ interrupts = <23 4>; /* active high */ ++ reset-gpio = <&gpio 24 1>; ++ sleep-gpio = <&gpio 25 1>; ++ spi-max-frequency = <6000000>; ++ xtal-trim = /bits/ 8 <0xf>; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ lowpan0_pins: lowpan0_pins { ++ brcm,pins = <23 24 25>; ++ brcm,function = <0 1 1>; /* in out out */ ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ interrupt = <&lowpan0>, "interrupts:0", ++ <&lowpan0_pins>, "brcm,pins:0"; ++ reset = <&lowpan0>, "reset-gpio:4", ++ <&lowpan0_pins>, "brcm,pins:4"; ++ sleep = <&lowpan0>, "sleep-gpio:4", ++ <&lowpan0_pins>, "brcm,pins:8"; ++ speed = <&lowpan0>, "spi-max-frequency:0"; ++ trim = <&lowpan0>, "xtal-trim.0"; ++ }; ++}; diff --git a/target/linux/brcm2708/patches-4.4/0111-BCM270X_DT-Add-at86rf233-overlay.patch b/target/linux/brcm2708/patches-4.4/0111-BCM270X_DT-Add-at86rf233-overlay.patch deleted file mode 100644 index cc7ec22567..0000000000 --- a/target/linux/brcm2708/patches-4.4/0111-BCM270X_DT-Add-at86rf233-overlay.patch +++ /dev/null @@ -1,130 +0,0 @@ -From 14b6c7013e62b3917ce73841dc8e379d011d37a5 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 5 Oct 2015 10:47:45 +0100 -Subject: [PATCH 111/170] BCM270X_DT: Add at86rf233 overlay - -Add an overlay to support the Atmel AT86RF233 WPAN transceiver on spi0.0. - -See: https://github.com/raspberrypi/linux/issues/1151 ---- - arch/arm/boot/dts/overlays/Makefile | 1 + - arch/arm/boot/dts/overlays/README | 21 +++++++-- - arch/arm/boot/dts/overlays/at86rf233-overlay.dts | 54 ++++++++++++++++++++++++ - 3 files changed, 72 insertions(+), 4 deletions(-) - create mode 100644 arch/arm/boot/dts/overlays/at86rf233-overlay.dts - ---- a/arch/arm/boot/dts/overlays/Makefile -+++ b/arch/arm/boot/dts/overlays/Makefile -@@ -13,6 +13,7 @@ ifeq ($(CONFIG_ARCH_BCM2835),y) - endif - - dtb-$(RPI_DT_OVERLAYS) += ads7846-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += at86rf233-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += bmp085_i2c-sensor-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += dht11-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += enc28j60-overlay.dtb ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -69,13 +69,14 @@ DT parameters: - - Parameters always have default values, although in some cases (e.g. "w1-gpio") - it is necessary to provided multiple overlays in order to get the desired --behaviour. See the list of overlays below for a description of the parameters and their defaults. -+behaviour. See the list of overlays below for a description of the parameters -+and their defaults. - - The Overlay and Parameter Reference - =================================== - --N.B. When editing this file, please preserve the indentation levels to make it simple to parse --programmatically. NO HARD TABS. -+N.B. When editing this file, please preserve the indentation levels to make it -+simple to parse programmatically. NO HARD TABS. - - - Name: -@@ -149,7 +150,7 @@ Name: ads7846 - Info: ADS7846 Touch controller - Load: dtoverlay=ads7846,= - Params: cs SPI bus Chip Select (default 1) -- speed SPI bus speed (default 2Mhz, max 3.25MHz) -+ speed SPI bus speed (default 2MHz, max 3.25MHz) - penirq GPIO used for PENIRQ. REQUIRED - penirq_pull Set GPIO pull (default 0=none, 2=pullup) - swapxy Swap x and y axis -@@ -170,6 +171,18 @@ Params: cs SPI bus - www.kernel.org/doc/Documentation/devicetree/bindings/input/ads7846.txt - - -+Name: at86rf233 -+Info: Configures the Atmel AT86RF233 802.15.4 low-power WPAN transceiver, -+ connected to spi0.0 -+Load: dtoverlay=at86rf233,= -+Params: interrupt GPIO used for INT (default 23) -+ reset GPIO used for Reset (default 24) -+ sleep GPIO used for Sleep (default 25) -+ speed SPI bus speed in Hz (default 6000000) -+ trim Fine tuning of the internal capacitance -+ arrays (0=+0pF, 15=+4.5pF, default 15) -+ -+ - Name: bmp085_i2c-sensor - Info: Configures the BMP085/BMP180 digital barometric pressure and temperature - sensors from Bosch Sensortec ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/at86rf233-overlay.dts -@@ -0,0 +1,54 @@ -+/dts-v1/; -+/plugin/; -+ -+/* Overlay for Atmel AT86RF233 IEEE 802.15.4 WPAN transceiver on spi0.0 */ -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2836", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&spi0>; -+ __overlay__ { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ status = "okay"; -+ -+ spidev@0{ -+ status = "disabled"; -+ }; -+ -+ lowpan0: at86rf233@0 { -+ compatible = "atmel,at86rf233"; -+ reg = <0>; -+ interrupt-parent = <&gpio>; -+ interrupts = <23 4>; /* active high */ -+ reset-gpio = <&gpio 24 1>; -+ sleep-gpio = <&gpio 25 1>; -+ spi-max-frequency = <6000000>; -+ xtal-trim = /bits/ 8 <0xf>; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ lowpan0_pins: lowpan0_pins { -+ brcm,pins = <23 24 25>; -+ brcm,function = <0 1 1>; /* in out out */ -+ }; -+ }; -+ }; -+ -+ __overrides__ { -+ interrupt = <&lowpan0>, "interrupts:0", -+ <&lowpan0_pins>, "brcm,pins:0"; -+ reset = <&lowpan0>, "reset-gpio:4", -+ <&lowpan0_pins>, "brcm,pins:4"; -+ sleep = <&lowpan0>, "sleep-gpio:4", -+ <&lowpan0_pins>, "brcm,pins:8"; -+ speed = <&lowpan0>, "spi-max-frequency:0"; -+ trim = <&lowpan0>, "xtal-trim.0"; -+ }; -+}; diff --git a/target/linux/brcm2708/patches-4.4/0111-mm-Remove-the-PFN-busy-warning.patch b/target/linux/brcm2708/patches-4.4/0111-mm-Remove-the-PFN-busy-warning.patch new file mode 100644 index 0000000000..ecb166c311 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0111-mm-Remove-the-PFN-busy-warning.patch @@ -0,0 +1,25 @@ +From 4df259e844e183bf00e1c5eb8ca25dcf19d396ce Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Thu, 18 Dec 2014 16:07:15 -0800 +Subject: [PATCH 111/232] mm: Remove the PFN busy warning + +See commit dae803e165a11bc88ca8dbc07a11077caf97bbcb -- the warning is +expected sometimes when using CMA. However, that commit still spams +my kernel log with these warnings. + +Signed-off-by: Eric Anholt +--- + mm/page_alloc.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -6780,8 +6780,6 @@ int alloc_contig_range(unsigned long sta + + /* Make sure the range is really isolated. */ + if (test_pages_isolated(outer_start, end, false)) { +- pr_info("%s: [%lx, %lx) PFNs busy\n", +- __func__, outer_start, end); + ret = -EBUSY; + goto done; + } diff --git a/target/linux/brcm2708/patches-4.4/0112-drm-Put-an-optional-field-in-the-driver-struct-for-G.patch b/target/linux/brcm2708/patches-4.4/0112-drm-Put-an-optional-field-in-the-driver-struct-for-G.patch new file mode 100644 index 0000000000..637157b355 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0112-drm-Put-an-optional-field-in-the-driver-struct-for-G.patch @@ -0,0 +1,40 @@ +From b1891f7b0018e7e778f49c1817692e1f85a1f529 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Wed, 19 Nov 2014 12:06:38 -0800 +Subject: [PATCH 112/232] drm: Put an optional field in the driver struct for + GEM obj struct size. + +This allows a driver to derive from the CMA object without copying all +of the code. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/drm_gem_cma_helper.c | 5 ++++- + include/drm/drmP.h | 1 + + 2 files changed, 5 insertions(+), 1 deletion(-) + +--- a/drivers/gpu/drm/drm_gem_cma_helper.c ++++ b/drivers/gpu/drm/drm_gem_cma_helper.c +@@ -58,8 +58,11 @@ __drm_gem_cma_create(struct drm_device * + struct drm_gem_cma_object *cma_obj; + struct drm_gem_object *gem_obj; + int ret; ++ size_t obj_size = (drm->driver->gem_obj_size ? ++ drm->driver->gem_obj_size : ++ sizeof(*cma_obj)); + +- cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL); ++ cma_obj = kzalloc(obj_size, GFP_KERNEL); + if (!cma_obj) + return ERR_PTR(-ENOMEM); + +--- a/include/drm/drmP.h ++++ b/include/drm/drmP.h +@@ -639,6 +639,7 @@ struct drm_driver { + + u32 driver_features; + int dev_priv_size; ++ size_t gem_obj_size; + const struct drm_ioctl_desc *ioctls; + int num_ioctls; + const struct file_operations *fops; diff --git a/target/linux/brcm2708/patches-4.4/0112-mm-Remove-the-PFN-busy-warning.patch b/target/linux/brcm2708/patches-4.4/0112-mm-Remove-the-PFN-busy-warning.patch deleted file mode 100644 index a4f6ec19e3..0000000000 --- a/target/linux/brcm2708/patches-4.4/0112-mm-Remove-the-PFN-busy-warning.patch +++ /dev/null @@ -1,25 +0,0 @@ -From d33b3717e86cd6aa28c58d465a22e6c11a66bc13 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Thu, 18 Dec 2014 16:07:15 -0800 -Subject: [PATCH 112/170] mm: Remove the PFN busy warning - -See commit dae803e165a11bc88ca8dbc07a11077caf97bbcb -- the warning is -expected sometimes when using CMA. However, that commit still spams -my kernel log with these warnings. - -Signed-off-by: Eric Anholt ---- - mm/page_alloc.c | 2 -- - 1 file changed, 2 deletions(-) - ---- a/mm/page_alloc.c -+++ b/mm/page_alloc.c -@@ -6780,8 +6780,6 @@ int alloc_contig_range(unsigned long sta - - /* Make sure the range is really isolated. */ - if (test_pages_isolated(outer_start, end, false)) { -- pr_info("%s: [%lx, %lx) PFNs busy\n", -- __func__, outer_start, end); - ret = -EBUSY; - goto done; - } diff --git a/target/linux/brcm2708/patches-4.4/0113-drm-Put-an-optional-field-in-the-driver-struct-for-G.patch b/target/linux/brcm2708/patches-4.4/0113-drm-Put-an-optional-field-in-the-driver-struct-for-G.patch deleted file mode 100644 index 775d46a553..0000000000 --- a/target/linux/brcm2708/patches-4.4/0113-drm-Put-an-optional-field-in-the-driver-struct-for-G.patch +++ /dev/null @@ -1,40 +0,0 @@ -From a71d6ebfcbc401e520fa4a5aa185d027dfbe5ce1 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Wed, 19 Nov 2014 12:06:38 -0800 -Subject: [PATCH 113/170] drm: Put an optional field in the driver struct for - GEM obj struct size. - -This allows a driver to derive from the CMA object without copying all -of the code. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/drm_gem_cma_helper.c | 5 ++++- - include/drm/drmP.h | 1 + - 2 files changed, 5 insertions(+), 1 deletion(-) - ---- a/drivers/gpu/drm/drm_gem_cma_helper.c -+++ b/drivers/gpu/drm/drm_gem_cma_helper.c -@@ -58,8 +58,11 @@ __drm_gem_cma_create(struct drm_device * - struct drm_gem_cma_object *cma_obj; - struct drm_gem_object *gem_obj; - int ret; -+ size_t obj_size = (drm->driver->gem_obj_size ? -+ drm->driver->gem_obj_size : -+ sizeof(*cma_obj)); - -- cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL); -+ cma_obj = kzalloc(obj_size, GFP_KERNEL); - if (!cma_obj) - return ERR_PTR(-ENOMEM); - ---- a/include/drm/drmP.h -+++ b/include/drm/drmP.h -@@ -639,6 +639,7 @@ struct drm_driver { - - u32 driver_features; - int dev_priv_size; -+ size_t gem_obj_size; - const struct drm_ioctl_desc *ioctls; - int num_ioctls; - const struct file_operations *fops; diff --git a/target/linux/brcm2708/patches-4.4/0113-drm-vc4-Add-an-interface-for-capturing-the-GPU-state.patch b/target/linux/brcm2708/patches-4.4/0113-drm-vc4-Add-an-interface-for-capturing-the-GPU-state.patch new file mode 100644 index 0000000000..e71b82bbe9 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0113-drm-vc4-Add-an-interface-for-capturing-the-GPU-state.patch @@ -0,0 +1,333 @@ +From b9e5697fbec13e6203b63649ee5d7c6819a5fb6b Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Fri, 30 Oct 2015 10:09:02 -0700 +Subject: [PATCH 113/232] drm/vc4: Add an interface for capturing the GPU state + after a hang. + +This can be parsed with vc4-gpu-tools tools for trying to figure out +what was going on. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_bo.c | 4 +- + drivers/gpu/drm/vc4/vc4_drv.c | 1 + + drivers/gpu/drm/vc4/vc4_drv.h | 4 + + drivers/gpu/drm/vc4/vc4_gem.c | 185 ++++++++++++++++++++++++++++++++++++++++++ + include/uapi/drm/vc4_drm.h | 45 ++++++++++ + 5 files changed, 237 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_bo.c ++++ b/drivers/gpu/drm/vc4/vc4_bo.c +@@ -415,8 +415,8 @@ int vc4_mmap(struct file *filp, struct v + gem_obj = vma->vm_private_data; + bo = to_vc4_bo(gem_obj); + +- if (bo->validated_shader) { +- DRM_ERROR("mmaping of shader BOs not allowed.\n"); ++ if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { ++ DRM_ERROR("mmaping of shader BOs for writing not allowed.\n"); + return -EINVAL; + } + +--- a/drivers/gpu/drm/vc4/vc4_drv.c ++++ b/drivers/gpu/drm/vc4/vc4_drv.c +@@ -81,6 +81,7 @@ static const struct drm_ioctl_desc vc4_d + DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, 0), + DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, 0), + DRM_IOCTL_DEF_DRV(VC4_CREATE_SHADER_BO, vc4_create_shader_bo_ioctl, 0), ++ DRM_IOCTL_DEF_DRV(VC4_GET_HANG_STATE, vc4_get_hang_state_ioctl, DRM_ROOT_ONLY), + }; + + static struct drm_driver vc4_drm_driver = { +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -20,6 +20,8 @@ struct vc4_dev { + struct drm_fbdev_cma *fbdev; + struct rpi_firmware *firmware; + ++ struct vc4_hang_state *hang_state; ++ + /* The kernel-space BO cache. Tracks buffers that have been + * unreferenced by all other users (refcounts of 0!) but not + * yet freed, so we can do cheap allocations. +@@ -366,6 +368,8 @@ int vc4_create_shader_bo_ioctl(struct dr + struct drm_file *file_priv); + int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); ++int vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); + int vc4_mmap(struct file *filp, struct vm_area_struct *vma); + int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); + void *vc4_prime_vmap(struct drm_gem_object *obj); +--- a/drivers/gpu/drm/vc4/vc4_gem.c ++++ b/drivers/gpu/drm/vc4/vc4_gem.c +@@ -40,6 +40,186 @@ vc4_queue_hangcheck(struct drm_device *d + round_jiffies_up(jiffies + msecs_to_jiffies(100))); + } + ++struct vc4_hang_state { ++ struct drm_vc4_get_hang_state user_state; ++ ++ u32 bo_count; ++ struct drm_gem_object **bo; ++}; ++ ++static void ++vc4_free_hang_state(struct drm_device *dev, struct vc4_hang_state *state) ++{ ++ unsigned int i; ++ ++ mutex_lock(&dev->struct_mutex); ++ for (i = 0; i < state->user_state.bo_count; i++) { ++ drm_gem_object_unreference(state->bo[i]); ++ } ++ mutex_unlock(&dev->struct_mutex); ++ ++ kfree(state); ++} ++ ++int ++vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ struct drm_vc4_get_hang_state *get_state = data; ++ struct drm_vc4_get_hang_state_bo *bo_state; ++ struct vc4_hang_state *kernel_state; ++ struct drm_vc4_get_hang_state *state; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ unsigned long irqflags; ++ u32 i; ++ int ret; ++ ++ spin_lock_irqsave(&vc4->job_lock, irqflags); ++ kernel_state = vc4->hang_state; ++ if (!kernel_state) { ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ return -ENOENT; ++ } ++ state = &kernel_state->user_state; ++ ++ /* If the user's array isn't big enough, just return the ++ * required array size. ++ */ ++ if (get_state->bo_count < state->bo_count) { ++ get_state->bo_count = state->bo_count; ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ return 0; ++ } ++ ++ vc4->hang_state = NULL; ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ ++ /* Save the user's BO pointer, so we don't stomp it with the memcpy. */ ++ state->bo = get_state->bo; ++ memcpy(get_state, state, sizeof(*state)); ++ ++ bo_state = kcalloc(state->bo_count, sizeof(*bo_state), GFP_KERNEL); ++ if (!bo_state) { ++ ret = -ENOMEM; ++ goto err_free; ++ } ++ ++ for (i = 0; i < state->bo_count; i++) { ++ struct vc4_bo *vc4_bo = to_vc4_bo(kernel_state->bo[i]); ++ u32 handle; ++ ret = drm_gem_handle_create(file_priv, kernel_state->bo[i], ++ &handle); ++ ++ if (ret) { ++ state->bo_count = i - 1; ++ goto err; ++ } ++ bo_state[i].handle = handle; ++ bo_state[i].paddr = vc4_bo->base.paddr; ++ bo_state[i].size = vc4_bo->base.base.size; ++ } ++ ++ ret = copy_to_user((void __user *)(uintptr_t)get_state->bo, ++ bo_state, ++ state->bo_count * sizeof(*bo_state)); ++ kfree(bo_state); ++ ++ err_free: ++ ++ vc4_free_hang_state(dev, kernel_state); ++ ++err: ++ return ret; ++} ++ ++static void ++vc4_save_hang_state(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct drm_vc4_get_hang_state *state; ++ struct vc4_hang_state *kernel_state; ++ struct vc4_exec_info *exec; ++ struct vc4_bo *bo; ++ unsigned long irqflags; ++ unsigned int i, unref_list_count; ++ ++ kernel_state = kcalloc(1, sizeof(*state), GFP_KERNEL); ++ if (!kernel_state) ++ return; ++ ++ state = &kernel_state->user_state; ++ ++ spin_lock_irqsave(&vc4->job_lock, irqflags); ++ exec = vc4_first_job(vc4); ++ if (!exec) { ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ return; ++ } ++ ++ unref_list_count = 0; ++ list_for_each_entry(bo, &exec->unref_list, unref_head) ++ unref_list_count++; ++ ++ state->bo_count = exec->bo_count + unref_list_count; ++ kernel_state->bo = kcalloc(state->bo_count, sizeof(*kernel_state->bo), ++ GFP_ATOMIC); ++ if (!kernel_state->bo) { ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ return; ++ } ++ ++ for (i = 0; i < exec->bo_count; i++) { ++ drm_gem_object_reference(&exec->bo[i].bo->base); ++ kernel_state->bo[i] = &exec->bo[i].bo->base; ++ } ++ ++ list_for_each_entry(bo, &exec->unref_list, unref_head) { ++ drm_gem_object_reference(&bo->base.base); ++ kernel_state->bo[i] = &bo->base.base; ++ i++; ++ } ++ ++ state->start_bin = exec->ct0ca; ++ state->start_render = exec->ct1ca; ++ ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ ++ state->ct0ca = V3D_READ(V3D_CTNCA(0)); ++ state->ct0ea = V3D_READ(V3D_CTNEA(0)); ++ ++ state->ct1ca = V3D_READ(V3D_CTNCA(1)); ++ state->ct1ea = V3D_READ(V3D_CTNEA(1)); ++ ++ state->ct0cs = V3D_READ(V3D_CTNCS(0)); ++ state->ct1cs = V3D_READ(V3D_CTNCS(1)); ++ ++ state->ct0ra0 = V3D_READ(V3D_CT00RA0); ++ state->ct1ra0 = V3D_READ(V3D_CT01RA0); ++ ++ state->bpca = V3D_READ(V3D_BPCA); ++ state->bpcs = V3D_READ(V3D_BPCS); ++ state->bpoa = V3D_READ(V3D_BPOA); ++ state->bpos = V3D_READ(V3D_BPOS); ++ ++ state->vpmbase = V3D_READ(V3D_VPMBASE); ++ ++ state->dbge = V3D_READ(V3D_DBGE); ++ state->fdbgo = V3D_READ(V3D_FDBGO); ++ state->fdbgb = V3D_READ(V3D_FDBGB); ++ state->fdbgr = V3D_READ(V3D_FDBGR); ++ state->fdbgs = V3D_READ(V3D_FDBGS); ++ state->errstat = V3D_READ(V3D_ERRSTAT); ++ ++ spin_lock_irqsave(&vc4->job_lock, irqflags); ++ if (vc4->hang_state) { ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ vc4_free_hang_state(dev, kernel_state); ++ } else { ++ vc4->hang_state = kernel_state; ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ } ++} ++ + static void + vc4_reset(struct drm_device *dev) + { +@@ -64,6 +244,8 @@ vc4_reset_work(struct work_struct *work) + struct vc4_dev *vc4 = + container_of(work, struct vc4_dev, hangcheck.reset_work); + ++ vc4_save_hang_state(vc4->dev); ++ + vc4_reset(vc4->dev); + } + +@@ -673,4 +855,7 @@ vc4_gem_destroy(struct drm_device *dev) + } + + vc4_bo_cache_destroy(dev); ++ ++ if (vc4->hang_state) ++ vc4_free_hang_state(dev, vc4->hang_state); + } +--- a/include/uapi/drm/vc4_drm.h ++++ b/include/uapi/drm/vc4_drm.h +@@ -32,6 +32,7 @@ + #define DRM_VC4_CREATE_BO 0x03 + #define DRM_VC4_MMAP_BO 0x04 + #define DRM_VC4_CREATE_SHADER_BO 0x05 ++#define DRM_VC4_GET_HANG_STATE 0x06 + + #define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) + #define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) +@@ -39,6 +40,7 @@ + #define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) + #define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) + #define DRM_IOCTL_VC4_CREATE_SHADER_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo) ++#define DRM_IOCTL_VC4_GET_HANG_STATE DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_GET_HANG_STATE, struct drm_vc4_get_hang_state) + + struct drm_vc4_submit_rcl_surface { + uint32_t hindex; /* Handle index, or ~0 if not present. */ +@@ -226,4 +228,47 @@ struct drm_vc4_mmap_bo { + uint64_t offset; + }; + ++struct drm_vc4_get_hang_state_bo { ++ uint32_t handle; ++ uint32_t paddr; ++ uint32_t size; ++ uint32_t pad; ++}; ++ ++/** ++ * struct drm_vc4_hang_state - ioctl argument for collecting state ++ * from a GPU hang for analysis. ++*/ ++struct drm_vc4_get_hang_state { ++ /** Pointer to array of struct drm_vc4_get_hang_state_bo. */ ++ uint64_t bo; ++ /** ++ * On input, the size of the bo array. Output is the number ++ * of bos to be returned. ++ */ ++ uint32_t bo_count; ++ ++ uint32_t start_bin, start_render; ++ ++ uint32_t ct0ca, ct0ea; ++ uint32_t ct1ca, ct1ea; ++ uint32_t ct0cs, ct1cs; ++ uint32_t ct0ra0, ct1ra0; ++ ++ uint32_t bpca, bpcs; ++ uint32_t bpoa, bpos; ++ ++ uint32_t vpmbase; ++ ++ uint32_t dbge; ++ uint32_t fdbgo; ++ uint32_t fdbgb; ++ uint32_t fdbgr; ++ uint32_t fdbgs; ++ uint32_t errstat; ++ ++ /* Pad that we may save more registers into in the future. */ ++ uint32_t pad[16]; ++}; ++ + #endif /* _UAPI_VC4_DRM_H_ */ diff --git a/target/linux/brcm2708/patches-4.4/0114-drm-vc4-Add-an-interface-for-capturing-the-GPU-state.patch b/target/linux/brcm2708/patches-4.4/0114-drm-vc4-Add-an-interface-for-capturing-the-GPU-state.patch deleted file mode 100644 index 3e40eb646c..0000000000 --- a/target/linux/brcm2708/patches-4.4/0114-drm-vc4-Add-an-interface-for-capturing-the-GPU-state.patch +++ /dev/null @@ -1,333 +0,0 @@ -From 1c343ed1f86866d2625518774536bbef77f7a20a Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Fri, 30 Oct 2015 10:09:02 -0700 -Subject: [PATCH 114/170] drm/vc4: Add an interface for capturing the GPU state - after a hang. - -This can be parsed with vc4-gpu-tools tools for trying to figure out -what was going on. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/vc4_bo.c | 4 +- - drivers/gpu/drm/vc4/vc4_drv.c | 1 + - drivers/gpu/drm/vc4/vc4_drv.h | 4 + - drivers/gpu/drm/vc4/vc4_gem.c | 185 ++++++++++++++++++++++++++++++++++++++++++ - include/uapi/drm/vc4_drm.h | 45 ++++++++++ - 5 files changed, 237 insertions(+), 2 deletions(-) - ---- a/drivers/gpu/drm/vc4/vc4_bo.c -+++ b/drivers/gpu/drm/vc4/vc4_bo.c -@@ -415,8 +415,8 @@ int vc4_mmap(struct file *filp, struct v - gem_obj = vma->vm_private_data; - bo = to_vc4_bo(gem_obj); - -- if (bo->validated_shader) { -- DRM_ERROR("mmaping of shader BOs not allowed.\n"); -+ if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { -+ DRM_ERROR("mmaping of shader BOs for writing not allowed.\n"); - return -EINVAL; - } - ---- a/drivers/gpu/drm/vc4/vc4_drv.c -+++ b/drivers/gpu/drm/vc4/vc4_drv.c -@@ -81,6 +81,7 @@ static const struct drm_ioctl_desc vc4_d - DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, 0), - DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, 0), - DRM_IOCTL_DEF_DRV(VC4_CREATE_SHADER_BO, vc4_create_shader_bo_ioctl, 0), -+ DRM_IOCTL_DEF_DRV(VC4_GET_HANG_STATE, vc4_get_hang_state_ioctl, DRM_ROOT_ONLY), - }; - - static struct drm_driver vc4_drm_driver = { ---- a/drivers/gpu/drm/vc4/vc4_drv.h -+++ b/drivers/gpu/drm/vc4/vc4_drv.h -@@ -20,6 +20,8 @@ struct vc4_dev { - struct drm_fbdev_cma *fbdev; - struct rpi_firmware *firmware; - -+ struct vc4_hang_state *hang_state; -+ - /* The kernel-space BO cache. Tracks buffers that have been - * unreferenced by all other users (refcounts of 0!) but not - * yet freed, so we can do cheap allocations. -@@ -366,6 +368,8 @@ int vc4_create_shader_bo_ioctl(struct dr - struct drm_file *file_priv); - int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -+int vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv); - int vc4_mmap(struct file *filp, struct vm_area_struct *vma); - int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); - void *vc4_prime_vmap(struct drm_gem_object *obj); ---- a/drivers/gpu/drm/vc4/vc4_gem.c -+++ b/drivers/gpu/drm/vc4/vc4_gem.c -@@ -40,6 +40,186 @@ vc4_queue_hangcheck(struct drm_device *d - round_jiffies_up(jiffies + msecs_to_jiffies(100))); - } - -+struct vc4_hang_state { -+ struct drm_vc4_get_hang_state user_state; -+ -+ u32 bo_count; -+ struct drm_gem_object **bo; -+}; -+ -+static void -+vc4_free_hang_state(struct drm_device *dev, struct vc4_hang_state *state) -+{ -+ unsigned int i; -+ -+ mutex_lock(&dev->struct_mutex); -+ for (i = 0; i < state->user_state.bo_count; i++) { -+ drm_gem_object_unreference(state->bo[i]); -+ } -+ mutex_unlock(&dev->struct_mutex); -+ -+ kfree(state); -+} -+ -+int -+vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv) -+{ -+ struct drm_vc4_get_hang_state *get_state = data; -+ struct drm_vc4_get_hang_state_bo *bo_state; -+ struct vc4_hang_state *kernel_state; -+ struct drm_vc4_get_hang_state *state; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ unsigned long irqflags; -+ u32 i; -+ int ret; -+ -+ spin_lock_irqsave(&vc4->job_lock, irqflags); -+ kernel_state = vc4->hang_state; -+ if (!kernel_state) { -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ return -ENOENT; -+ } -+ state = &kernel_state->user_state; -+ -+ /* If the user's array isn't big enough, just return the -+ * required array size. -+ */ -+ if (get_state->bo_count < state->bo_count) { -+ get_state->bo_count = state->bo_count; -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ return 0; -+ } -+ -+ vc4->hang_state = NULL; -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ -+ /* Save the user's BO pointer, so we don't stomp it with the memcpy. */ -+ state->bo = get_state->bo; -+ memcpy(get_state, state, sizeof(*state)); -+ -+ bo_state = kcalloc(state->bo_count, sizeof(*bo_state), GFP_KERNEL); -+ if (!bo_state) { -+ ret = -ENOMEM; -+ goto err_free; -+ } -+ -+ for (i = 0; i < state->bo_count; i++) { -+ struct vc4_bo *vc4_bo = to_vc4_bo(kernel_state->bo[i]); -+ u32 handle; -+ ret = drm_gem_handle_create(file_priv, kernel_state->bo[i], -+ &handle); -+ -+ if (ret) { -+ state->bo_count = i - 1; -+ goto err; -+ } -+ bo_state[i].handle = handle; -+ bo_state[i].paddr = vc4_bo->base.paddr; -+ bo_state[i].size = vc4_bo->base.base.size; -+ } -+ -+ ret = copy_to_user((void __user *)(uintptr_t)get_state->bo, -+ bo_state, -+ state->bo_count * sizeof(*bo_state)); -+ kfree(bo_state); -+ -+ err_free: -+ -+ vc4_free_hang_state(dev, kernel_state); -+ -+err: -+ return ret; -+} -+ -+static void -+vc4_save_hang_state(struct drm_device *dev) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct drm_vc4_get_hang_state *state; -+ struct vc4_hang_state *kernel_state; -+ struct vc4_exec_info *exec; -+ struct vc4_bo *bo; -+ unsigned long irqflags; -+ unsigned int i, unref_list_count; -+ -+ kernel_state = kcalloc(1, sizeof(*state), GFP_KERNEL); -+ if (!kernel_state) -+ return; -+ -+ state = &kernel_state->user_state; -+ -+ spin_lock_irqsave(&vc4->job_lock, irqflags); -+ exec = vc4_first_job(vc4); -+ if (!exec) { -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ return; -+ } -+ -+ unref_list_count = 0; -+ list_for_each_entry(bo, &exec->unref_list, unref_head) -+ unref_list_count++; -+ -+ state->bo_count = exec->bo_count + unref_list_count; -+ kernel_state->bo = kcalloc(state->bo_count, sizeof(*kernel_state->bo), -+ GFP_ATOMIC); -+ if (!kernel_state->bo) { -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ return; -+ } -+ -+ for (i = 0; i < exec->bo_count; i++) { -+ drm_gem_object_reference(&exec->bo[i].bo->base); -+ kernel_state->bo[i] = &exec->bo[i].bo->base; -+ } -+ -+ list_for_each_entry(bo, &exec->unref_list, unref_head) { -+ drm_gem_object_reference(&bo->base.base); -+ kernel_state->bo[i] = &bo->base.base; -+ i++; -+ } -+ -+ state->start_bin = exec->ct0ca; -+ state->start_render = exec->ct1ca; -+ -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ -+ state->ct0ca = V3D_READ(V3D_CTNCA(0)); -+ state->ct0ea = V3D_READ(V3D_CTNEA(0)); -+ -+ state->ct1ca = V3D_READ(V3D_CTNCA(1)); -+ state->ct1ea = V3D_READ(V3D_CTNEA(1)); -+ -+ state->ct0cs = V3D_READ(V3D_CTNCS(0)); -+ state->ct1cs = V3D_READ(V3D_CTNCS(1)); -+ -+ state->ct0ra0 = V3D_READ(V3D_CT00RA0); -+ state->ct1ra0 = V3D_READ(V3D_CT01RA0); -+ -+ state->bpca = V3D_READ(V3D_BPCA); -+ state->bpcs = V3D_READ(V3D_BPCS); -+ state->bpoa = V3D_READ(V3D_BPOA); -+ state->bpos = V3D_READ(V3D_BPOS); -+ -+ state->vpmbase = V3D_READ(V3D_VPMBASE); -+ -+ state->dbge = V3D_READ(V3D_DBGE); -+ state->fdbgo = V3D_READ(V3D_FDBGO); -+ state->fdbgb = V3D_READ(V3D_FDBGB); -+ state->fdbgr = V3D_READ(V3D_FDBGR); -+ state->fdbgs = V3D_READ(V3D_FDBGS); -+ state->errstat = V3D_READ(V3D_ERRSTAT); -+ -+ spin_lock_irqsave(&vc4->job_lock, irqflags); -+ if (vc4->hang_state) { -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ vc4_free_hang_state(dev, kernel_state); -+ } else { -+ vc4->hang_state = kernel_state; -+ spin_unlock_irqrestore(&vc4->job_lock, irqflags); -+ } -+} -+ - static void - vc4_reset(struct drm_device *dev) - { -@@ -64,6 +244,8 @@ vc4_reset_work(struct work_struct *work) - struct vc4_dev *vc4 = - container_of(work, struct vc4_dev, hangcheck.reset_work); - -+ vc4_save_hang_state(vc4->dev); -+ - vc4_reset(vc4->dev); - } - -@@ -673,4 +855,7 @@ vc4_gem_destroy(struct drm_device *dev) - } - - vc4_bo_cache_destroy(dev); -+ -+ if (vc4->hang_state) -+ vc4_free_hang_state(dev, vc4->hang_state); - } ---- a/include/uapi/drm/vc4_drm.h -+++ b/include/uapi/drm/vc4_drm.h -@@ -32,6 +32,7 @@ - #define DRM_VC4_CREATE_BO 0x03 - #define DRM_VC4_MMAP_BO 0x04 - #define DRM_VC4_CREATE_SHADER_BO 0x05 -+#define DRM_VC4_GET_HANG_STATE 0x06 - - #define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) - #define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) -@@ -39,6 +40,7 @@ - #define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) - #define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) - #define DRM_IOCTL_VC4_CREATE_SHADER_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo) -+#define DRM_IOCTL_VC4_GET_HANG_STATE DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_GET_HANG_STATE, struct drm_vc4_get_hang_state) - - struct drm_vc4_submit_rcl_surface { - uint32_t hindex; /* Handle index, or ~0 if not present. */ -@@ -226,4 +228,47 @@ struct drm_vc4_mmap_bo { - uint64_t offset; - }; - -+struct drm_vc4_get_hang_state_bo { -+ uint32_t handle; -+ uint32_t paddr; -+ uint32_t size; -+ uint32_t pad; -+}; -+ -+/** -+ * struct drm_vc4_hang_state - ioctl argument for collecting state -+ * from a GPU hang for analysis. -+*/ -+struct drm_vc4_get_hang_state { -+ /** Pointer to array of struct drm_vc4_get_hang_state_bo. */ -+ uint64_t bo; -+ /** -+ * On input, the size of the bo array. Output is the number -+ * of bos to be returned. -+ */ -+ uint32_t bo_count; -+ -+ uint32_t start_bin, start_render; -+ -+ uint32_t ct0ca, ct0ea; -+ uint32_t ct1ca, ct1ea; -+ uint32_t ct0cs, ct1cs; -+ uint32_t ct0ra0, ct1ra0; -+ -+ uint32_t bpca, bpcs; -+ uint32_t bpoa, bpos; -+ -+ uint32_t vpmbase; -+ -+ uint32_t dbge; -+ uint32_t fdbgo; -+ uint32_t fdbgb; -+ uint32_t fdbgr; -+ uint32_t fdbgs; -+ uint32_t errstat; -+ -+ /* Pad that we may save more registers into in the future. */ -+ uint32_t pad[16]; -+}; -+ - #endif /* _UAPI_VC4_DRM_H_ */ diff --git a/target/linux/brcm2708/patches-4.4/0114-drm-vc4-Update-a-bunch-of-code-to-match-upstream-sub.patch b/target/linux/brcm2708/patches-4.4/0114-drm-vc4-Update-a-bunch-of-code-to-match-upstream-sub.patch new file mode 100644 index 0000000000..b54c37a377 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0114-drm-vc4-Update-a-bunch-of-code-to-match-upstream-sub.patch @@ -0,0 +1,1894 @@ +From e4058e84edec652f5a7b3e9e4982eac62b22a90c Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Fri, 4 Dec 2015 11:35:34 -0800 +Subject: [PATCH 114/232] drm/vc4: Update a bunch of code to match upstream + submission. + +This gets almost everything matching, except for the MSAA support and +using generic PM domains. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/drm_gem_cma_helper.c | 13 +- + drivers/gpu/drm/vc4/vc4_bo.c | 322 +++++++++++++++++------------ + drivers/gpu/drm/vc4/vc4_crtc.c | 7 +- + drivers/gpu/drm/vc4/vc4_drv.c | 6 +- + drivers/gpu/drm/vc4/vc4_drv.h | 20 +- + drivers/gpu/drm/vc4/vc4_gem.c | 24 ++- + drivers/gpu/drm/vc4/vc4_irq.c | 5 +- + drivers/gpu/drm/vc4/vc4_kms.c | 1 + + drivers/gpu/drm/vc4/vc4_packet.h | 210 +++++++++---------- + drivers/gpu/drm/vc4/vc4_qpu_defines.h | 308 ++++++++++++++------------- + drivers/gpu/drm/vc4/vc4_render_cl.c | 4 +- + drivers/gpu/drm/vc4/vc4_v3d.c | 10 +- + drivers/gpu/drm/vc4/vc4_validate.c | 130 ++++++------ + drivers/gpu/drm/vc4/vc4_validate_shaders.c | 66 +++--- + include/drm/drmP.h | 8 +- + 15 files changed, 598 insertions(+), 536 deletions(-) + +--- a/drivers/gpu/drm/drm_gem_cma_helper.c ++++ b/drivers/gpu/drm/drm_gem_cma_helper.c +@@ -58,15 +58,14 @@ __drm_gem_cma_create(struct drm_device * + struct drm_gem_cma_object *cma_obj; + struct drm_gem_object *gem_obj; + int ret; +- size_t obj_size = (drm->driver->gem_obj_size ? +- drm->driver->gem_obj_size : +- sizeof(*cma_obj)); + +- cma_obj = kzalloc(obj_size, GFP_KERNEL); +- if (!cma_obj) ++ if (drm->driver->gem_create_object) ++ gem_obj = drm->driver->gem_create_object(drm, size); ++ else ++ gem_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL); ++ if (!gem_obj) + return ERR_PTR(-ENOMEM); +- +- gem_obj = &cma_obj->base; ++ cma_obj = container_of(gem_obj, struct drm_gem_cma_object, base); + + ret = drm_gem_object_init(drm, gem_obj, size); + if (ret) +--- a/drivers/gpu/drm/vc4/vc4_bo.c ++++ b/drivers/gpu/drm/vc4/vc4_bo.c +@@ -12,6 +12,10 @@ + * access to system memory with no MMU in between. To support it, we + * use the GEM CMA helper functions to allocate contiguous ranges of + * physical memory for our BOs. ++ * ++ * Since the CMA allocator is very slow, we keep a cache of recently ++ * freed BOs around so that the kernel's allocation of objects for 3D ++ * rendering can return quickly. + */ + + #include "vc4_drv.h" +@@ -34,6 +38,36 @@ static void vc4_bo_stats_dump(struct vc4 + vc4->bo_stats.size_cached / 1024); + } + ++#ifdef CONFIG_DEBUG_FS ++int vc4_bo_stats_debugfs(struct seq_file *m, void *unused) ++{ ++ struct drm_info_node *node = (struct drm_info_node *)m->private; ++ struct drm_device *dev = node->minor->dev; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct vc4_bo_stats stats; ++ ++ /* Take a snapshot of the current stats with the lock held. */ ++ mutex_lock(&vc4->bo_lock); ++ stats = vc4->bo_stats; ++ mutex_unlock(&vc4->bo_lock); ++ ++ seq_printf(m, "num bos allocated: %d\n", ++ stats.num_allocated); ++ seq_printf(m, "size bos allocated: %dkb\n", ++ stats.size_allocated / 1024); ++ seq_printf(m, "num bos used: %d\n", ++ stats.num_allocated - stats.num_cached); ++ seq_printf(m, "size bos used: %dkb\n", ++ (stats.size_allocated - stats.size_cached) / 1024); ++ seq_printf(m, "num bos cached: %d\n", ++ stats.num_cached); ++ seq_printf(m, "size bos cached: %dkb\n", ++ stats.size_cached / 1024); ++ ++ return 0; ++} ++#endif ++ + static uint32_t bo_page_index(size_t size) + { + return (size / PAGE_SIZE) - 1; +@@ -81,8 +115,8 @@ static struct list_head *vc4_get_cache_l + struct list_head *new_list; + uint32_t i; + +- new_list = kmalloc(new_size * sizeof(struct list_head), +- GFP_KERNEL); ++ new_list = kmalloc_array(new_size, sizeof(struct list_head), ++ GFP_KERNEL); + if (!new_list) + return NULL; + +@@ -90,7 +124,9 @@ static struct list_head *vc4_get_cache_l + * head locations. + */ + for (i = 0; i < vc4->bo_cache.size_list_size; i++) { +- struct list_head *old_list = &vc4->bo_cache.size_list[i]; ++ struct list_head *old_list = ++ &vc4->bo_cache.size_list[i]; ++ + if (list_empty(old_list)) + INIT_LIST_HEAD(&new_list[i]); + else +@@ -122,11 +158,60 @@ void vc4_bo_cache_purge(struct drm_devic + mutex_unlock(&vc4->bo_lock); + } + +-struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size) ++static struct vc4_bo *vc4_bo_get_from_cache(struct drm_device *dev, ++ uint32_t size) + { + struct vc4_dev *vc4 = to_vc4_dev(dev); +- uint32_t size = roundup(unaligned_size, PAGE_SIZE); + uint32_t page_index = bo_page_index(size); ++ struct vc4_bo *bo = NULL; ++ ++ size = roundup(size, PAGE_SIZE); ++ ++ mutex_lock(&vc4->bo_lock); ++ if (page_index >= vc4->bo_cache.size_list_size) ++ goto out; ++ ++ if (list_empty(&vc4->bo_cache.size_list[page_index])) ++ goto out; ++ ++ bo = list_first_entry(&vc4->bo_cache.size_list[page_index], ++ struct vc4_bo, size_head); ++ vc4_bo_remove_from_cache(bo); ++ kref_init(&bo->base.base.refcount); ++ ++out: ++ mutex_unlock(&vc4->bo_lock); ++ return bo; ++} ++ ++/** ++ * vc4_gem_create_object - Implementation of driver->gem_create_object. ++ * ++ * This lets the CMA helpers allocate object structs for us, and keep ++ * our BO stats correct. ++ */ ++struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct vc4_bo *bo; ++ ++ bo = kzalloc(sizeof(*bo), GFP_KERNEL); ++ if (!bo) ++ return ERR_PTR(-ENOMEM); ++ ++ mutex_lock(&vc4->bo_lock); ++ vc4->bo_stats.num_allocated++; ++ vc4->bo_stats.size_allocated += size; ++ mutex_unlock(&vc4->bo_lock); ++ ++ return &bo->base.base; ++} ++ ++struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size, ++ bool from_cache) ++{ ++ size_t size = roundup(unaligned_size, PAGE_SIZE); ++ struct vc4_dev *vc4 = to_vc4_dev(dev); + struct drm_gem_cma_object *cma_obj; + int pass; + +@@ -134,18 +219,12 @@ struct vc4_bo *vc4_bo_create(struct drm_ + return NULL; + + /* First, try to get a vc4_bo from the kernel BO cache. */ +- mutex_lock(&vc4->bo_lock); +- if (page_index < vc4->bo_cache.size_list_size && +- !list_empty(&vc4->bo_cache.size_list[page_index])) { +- struct vc4_bo *bo = +- list_first_entry(&vc4->bo_cache.size_list[page_index], +- struct vc4_bo, size_head); +- vc4_bo_remove_from_cache(bo); +- mutex_unlock(&vc4->bo_lock); +- kref_init(&bo->base.base.refcount); +- return bo; ++ if (from_cache) { ++ struct vc4_bo *bo = vc4_bo_get_from_cache(dev, size); ++ ++ if (bo) ++ return bo; + } +- mutex_unlock(&vc4->bo_lock); + + /* Otherwise, make a new BO. */ + for (pass = 0; ; pass++) { +@@ -179,9 +258,6 @@ struct vc4_bo *vc4_bo_create(struct drm_ + } + } + +- vc4->bo_stats.num_allocated++; +- vc4->bo_stats.size_allocated += size; +- + return to_vc4_bo(&cma_obj->base); + } + +@@ -199,7 +275,7 @@ int vc4_dumb_create(struct drm_file *fil + if (args->size < args->pitch * args->height) + args->size = args->pitch * args->height; + +- bo = vc4_bo_create(dev, args->size); ++ bo = vc4_bo_create(dev, args->size, false); + if (!bo) + return -ENOMEM; + +@@ -209,8 +285,8 @@ int vc4_dumb_create(struct drm_file *fil + return ret; + } + +-static void +-vc4_bo_cache_free_old(struct drm_device *dev) ++/* Must be called with bo_lock held. */ ++static void vc4_bo_cache_free_old(struct drm_device *dev) + { + struct vc4_dev *vc4 = to_vc4_dev(dev); + unsigned long expire_time = jiffies - msecs_to_jiffies(1000); +@@ -313,15 +389,77 @@ vc4_prime_export(struct drm_device *dev, + return drm_gem_prime_export(dev, obj, flags); + } + +-int +-vc4_create_bo_ioctl(struct drm_device *dev, void *data, +- struct drm_file *file_priv) ++int vc4_mmap(struct file *filp, struct vm_area_struct *vma) ++{ ++ struct drm_gem_object *gem_obj; ++ struct vc4_bo *bo; ++ int ret; ++ ++ ret = drm_gem_mmap(filp, vma); ++ if (ret) ++ return ret; ++ ++ gem_obj = vma->vm_private_data; ++ bo = to_vc4_bo(gem_obj); ++ ++ if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { ++ DRM_ERROR("mmaping of shader BOs for writing not allowed.\n"); ++ return -EINVAL; ++ } ++ ++ /* ++ * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the ++ * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map ++ * the whole buffer. ++ */ ++ vma->vm_flags &= ~VM_PFNMAP; ++ vma->vm_pgoff = 0; ++ ++ ret = dma_mmap_writecombine(bo->base.base.dev->dev, vma, ++ bo->base.vaddr, bo->base.paddr, ++ vma->vm_end - vma->vm_start); ++ if (ret) ++ drm_gem_vm_close(vma); ++ ++ return ret; ++} ++ ++int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) ++{ ++ struct vc4_bo *bo = to_vc4_bo(obj); ++ ++ if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { ++ DRM_ERROR("mmaping of shader BOs for writing not allowed.\n"); ++ return -EINVAL; ++ } ++ ++ return drm_gem_cma_prime_mmap(obj, vma); ++} ++ ++void *vc4_prime_vmap(struct drm_gem_object *obj) ++{ ++ struct vc4_bo *bo = to_vc4_bo(obj); ++ ++ if (bo->validated_shader) { ++ DRM_ERROR("mmaping of shader BOs not allowed.\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ return drm_gem_cma_prime_vmap(obj); ++} ++ ++int vc4_create_bo_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) + { + struct drm_vc4_create_bo *args = data; + struct vc4_bo *bo = NULL; + int ret; + +- bo = vc4_bo_create(dev, args->size); ++ /* ++ * We can't allocate from the BO cache, because the BOs don't ++ * get zeroed, and that might leak data between users. ++ */ ++ bo = vc4_bo_create(dev, args->size, false); + if (!bo) + return -ENOMEM; + +@@ -331,6 +469,25 @@ vc4_create_bo_ioctl(struct drm_device *d + return ret; + } + ++int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ struct drm_vc4_mmap_bo *args = data; ++ struct drm_gem_object *gem_obj; ++ ++ gem_obj = drm_gem_object_lookup(dev, file_priv, args->handle); ++ if (!gem_obj) { ++ DRM_ERROR("Failed to look up GEM BO %d\n", args->handle); ++ return -EINVAL; ++ } ++ ++ /* The mmap offset was set up at BO allocation time. */ ++ args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); ++ ++ drm_gem_object_unreference_unlocked(gem_obj); ++ return 0; ++} ++ + int + vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +@@ -355,7 +512,7 @@ vc4_create_shader_bo_ioctl(struct drm_de + return -EINVAL; + } + +- bo = vc4_bo_create(dev, args->size); ++ bo = vc4_bo_create(dev, args->size, true); + if (!bo) + return -ENOMEM; + +@@ -364,6 +521,11 @@ vc4_create_shader_bo_ioctl(struct drm_de + args->size); + if (ret != 0) + goto fail; ++ /* Clear the rest of the memory from allocating from the BO ++ * cache. ++ */ ++ memset(bo->base.vaddr + args->size, 0, ++ bo->base.base.size - args->size); + + bo->validated_shader = vc4_validate_shader(&bo->base); + if (!bo->validated_shader) { +@@ -382,85 +544,6 @@ vc4_create_shader_bo_ioctl(struct drm_de + return ret; + } + +-int +-vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, +- struct drm_file *file_priv) +-{ +- struct drm_vc4_mmap_bo *args = data; +- struct drm_gem_object *gem_obj; +- +- gem_obj = drm_gem_object_lookup(dev, file_priv, args->handle); +- if (!gem_obj) { +- DRM_ERROR("Failed to look up GEM BO %d\n", args->handle); +- return -EINVAL; +- } +- +- /* The mmap offset was set up at BO allocation time. */ +- args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); +- +- drm_gem_object_unreference(gem_obj); +- return 0; +-} +- +-int vc4_mmap(struct file *filp, struct vm_area_struct *vma) +-{ +- struct drm_gem_object *gem_obj; +- struct vc4_bo *bo; +- int ret; +- +- ret = drm_gem_mmap(filp, vma); +- if (ret) +- return ret; +- +- gem_obj = vma->vm_private_data; +- bo = to_vc4_bo(gem_obj); +- +- if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { +- DRM_ERROR("mmaping of shader BOs for writing not allowed.\n"); +- return -EINVAL; +- } +- +- /* +- * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the +- * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map +- * the whole buffer. +- */ +- vma->vm_flags &= ~VM_PFNMAP; +- vma->vm_pgoff = 0; +- +- ret = dma_mmap_writecombine(bo->base.base.dev->dev, vma, +- bo->base.vaddr, bo->base.paddr, +- vma->vm_end - vma->vm_start); +- if (ret) +- drm_gem_vm_close(vma); +- +- return ret; +-} +- +-int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) +-{ +- struct vc4_bo *bo = to_vc4_bo(obj); +- +- if (bo->validated_shader) { +- DRM_ERROR("mmaping of shader BOs not allowed.\n"); +- return -EINVAL; +- } +- +- return drm_gem_cma_prime_mmap(obj, vma); +-} +- +-void *vc4_prime_vmap(struct drm_gem_object *obj) +-{ +- struct vc4_bo *bo = to_vc4_bo(obj); +- +- if (bo->validated_shader) { +- DRM_ERROR("mmaping of shader BOs not allowed.\n"); +- return ERR_PTR(-EINVAL); +- } +- +- return drm_gem_cma_prime_vmap(obj); +-} +- + void vc4_bo_cache_init(struct drm_device *dev) + { + struct vc4_dev *vc4 = to_vc4_dev(dev); +@@ -472,7 +555,7 @@ void vc4_bo_cache_init(struct drm_device + INIT_WORK(&vc4->bo_cache.time_work, vc4_bo_cache_time_work); + setup_timer(&vc4->bo_cache.time_timer, + vc4_bo_cache_time_timer, +- (unsigned long) dev); ++ (unsigned long)dev); + } + + void vc4_bo_cache_destroy(struct drm_device *dev) +@@ -489,28 +572,3 @@ void vc4_bo_cache_destroy(struct drm_dev + vc4_bo_stats_dump(vc4); + } + } +- +-#ifdef CONFIG_DEBUG_FS +-int vc4_bo_stats_debugfs(struct seq_file *m, void *unused) +-{ +- struct drm_info_node *node = (struct drm_info_node *) m->private; +- struct drm_device *dev = node->minor->dev; +- struct vc4_dev *vc4 = to_vc4_dev(dev); +- struct vc4_bo_stats stats; +- +- mutex_lock(&vc4->bo_lock); +- stats = vc4->bo_stats; +- mutex_unlock(&vc4->bo_lock); +- +- seq_printf(m, "num bos allocated: %d\n", stats.num_allocated); +- seq_printf(m, "size bos allocated: %dkb\n", stats.size_allocated / 1024); +- seq_printf(m, "num bos used: %d\n", (stats.num_allocated - +- stats.num_cached)); +- seq_printf(m, "size bos used: %dkb\n", (stats.size_allocated - +- stats.size_cached) / 1024); +- seq_printf(m, "num bos cached: %d\n", stats.num_cached); +- seq_printf(m, "size bos cached: %dkb\n", stats.size_cached / 1024); +- +- return 0; +-} +-#endif +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -501,6 +501,7 @@ vc4_async_page_flip_complete(struct vc4_ + vc4_plane_async_set_fb(plane, flip_state->fb); + if (flip_state->event) { + unsigned long flags; ++ + spin_lock_irqsave(&dev->event_lock, flags); + drm_crtc_send_vblank_event(crtc, flip_state->event); + spin_unlock_irqrestore(&dev->event_lock, flags); +@@ -562,9 +563,9 @@ static int vc4_async_page_flip(struct dr + } + + static int vc4_page_flip(struct drm_crtc *crtc, +- struct drm_framebuffer *fb, +- struct drm_pending_vblank_event *event, +- uint32_t flags) ++ struct drm_framebuffer *fb, ++ struct drm_pending_vblank_event *event, ++ uint32_t flags) + { + if (flags & DRM_MODE_PAGE_FLIP_ASYNC) + return vc4_async_page_flip(crtc, fb, event, flags); +--- a/drivers/gpu/drm/vc4/vc4_drv.c ++++ b/drivers/gpu/drm/vc4/vc4_drv.c +@@ -81,7 +81,8 @@ static const struct drm_ioctl_desc vc4_d + DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, 0), + DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, 0), + DRM_IOCTL_DEF_DRV(VC4_CREATE_SHADER_BO, vc4_create_shader_bo_ioctl, 0), +- DRM_IOCTL_DEF_DRV(VC4_GET_HANG_STATE, vc4_get_hang_state_ioctl, DRM_ROOT_ONLY), ++ DRM_IOCTL_DEF_DRV(VC4_GET_HANG_STATE, vc4_get_hang_state_ioctl, ++ DRM_ROOT_ONLY), + }; + + static struct drm_driver vc4_drm_driver = { +@@ -107,6 +108,7 @@ static struct drm_driver vc4_drm_driver + .debugfs_cleanup = vc4_debugfs_cleanup, + #endif + ++ .gem_create_object = vc4_create_object, + .gem_free_object = vc4_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + +@@ -128,8 +130,6 @@ static struct drm_driver vc4_drm_driver + .num_ioctls = ARRAY_SIZE(vc4_drm_ioctls), + .fops = &vc4_drm_fops, + +- //.gem_obj_size = sizeof(struct vc4_bo), +- + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -72,6 +72,9 @@ struct vc4_dev { + * job_done_work. + */ + struct list_head job_done_list; ++ /* Spinlock used to synchronize the job_list and seqno ++ * accesses between the IRQ handler and GEM ioctls. ++ */ + spinlock_t job_lock; + wait_queue_head_t job_wait_queue; + struct work_struct job_done_work; +@@ -318,8 +321,7 @@ struct vc4_texture_sample_info { + * and validate the shader state record's uniforms that define the texture + * samples. + */ +-struct vc4_validated_shader_info +-{ ++struct vc4_validated_shader_info { + uint32_t uniforms_size; + uint32_t uniforms_src_size; + uint32_t num_texture_samples; +@@ -355,8 +357,10 @@ struct vc4_validated_shader_info + #define wait_for(COND, MS) _wait_for(COND, MS, 1) + + /* vc4_bo.c */ ++struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size); + void vc4_free_object(struct drm_gem_object *gem_obj); +-struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size); ++struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size, ++ bool from_cache); + int vc4_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args); +@@ -432,7 +436,8 @@ struct drm_plane *vc4_plane_init(struct + enum drm_plane_type type); + u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); + u32 vc4_plane_dlist_size(struct drm_plane_state *state); +-void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb); ++void vc4_plane_async_set_fb(struct drm_plane *plane, ++ struct drm_framebuffer *fb); + + /* vc4_v3d.c */ + extern struct platform_driver vc4_v3d_driver; +@@ -450,9 +455,6 @@ vc4_validate_bin_cl(struct drm_device *d + int + vc4_validate_shader_recs(struct drm_device *dev, struct vc4_exec_info *exec); + +-struct vc4_validated_shader_info * +-vc4_validate_shader(struct drm_gem_cma_object *shader_obj); +- + bool vc4_use_bo(struct vc4_exec_info *exec, + uint32_t hindex, + enum vc4_bo_mode mode, +@@ -464,3 +466,7 @@ bool vc4_check_tex_size(struct vc4_exec_ + struct drm_gem_cma_object *fbo, + uint32_t offset, uint8_t tiling_format, + uint32_t width, uint32_t height, uint8_t cpp); ++ ++/* vc4_validate_shader.c */ ++struct vc4_validated_shader_info * ++vc4_validate_shader(struct drm_gem_cma_object *shader_obj); +--- a/drivers/gpu/drm/vc4/vc4_gem.c ++++ b/drivers/gpu/drm/vc4/vc4_gem.c +@@ -53,9 +53,8 @@ vc4_free_hang_state(struct drm_device *d + unsigned int i; + + mutex_lock(&dev->struct_mutex); +- for (i = 0; i < state->user_state.bo_count; i++) { ++ for (i = 0; i < state->user_state.bo_count; i++) + drm_gem_object_unreference(state->bo[i]); +- } + mutex_unlock(&dev->struct_mutex); + + kfree(state); +@@ -65,10 +64,10 @@ int + vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) + { +- struct drm_vc4_get_hang_state *get_state = data; ++ struct drm_vc4_get_hang_state *get_state = data; + struct drm_vc4_get_hang_state_bo *bo_state; + struct vc4_hang_state *kernel_state; +- struct drm_vc4_get_hang_state *state; ++ struct drm_vc4_get_hang_state *state; + struct vc4_dev *vc4 = to_vc4_dev(dev); + unsigned long irqflags; + u32 i; +@@ -107,6 +106,7 @@ vc4_get_hang_state_ioctl(struct drm_devi + for (i = 0; i < state->bo_count; i++) { + struct vc4_bo *vc4_bo = to_vc4_bo(kernel_state->bo[i]); + u32 handle; ++ + ret = drm_gem_handle_create(file_priv, kernel_state->bo[i], + &handle); + +@@ -124,7 +124,7 @@ vc4_get_hang_state_ioctl(struct drm_devi + state->bo_count * sizeof(*bo_state)); + kfree(bo_state); + +- err_free: ++err_free: + + vc4_free_hang_state(dev, kernel_state); + +@@ -578,7 +578,7 @@ vc4_get_bcl(struct drm_device *dev, stru + goto fail; + } + +- bo = vc4_bo_create(dev, exec_size); ++ bo = vc4_bo_create(dev, exec_size, true); + if (!bo) { + DRM_ERROR("Couldn't allocate BO for binning\n"); + ret = PTR_ERR(exec->exec_bo); +@@ -668,6 +668,7 @@ vc4_job_handle_completed(struct vc4_dev + static void vc4_seqno_cb_work(struct work_struct *work) + { + struct vc4_seqno_cb *cb = container_of(work, struct vc4_seqno_cb, work); ++ + cb->func(cb); + } + +@@ -717,6 +718,7 @@ vc4_wait_for_seqno_ioctl_helper(struct d + + if ((ret == -EINTR || ret == -ERESTARTSYS) && *timeout_ns != ~0ull) { + uint64_t delta = jiffies_to_nsecs(jiffies - start); ++ + if (*timeout_ns >= delta) + *timeout_ns -= delta; + } +@@ -750,9 +752,10 @@ vc4_wait_bo_ioctl(struct drm_device *dev + } + bo = to_vc4_bo(gem_obj); + +- ret = vc4_wait_for_seqno_ioctl_helper(dev, bo->seqno, &args->timeout_ns); ++ ret = vc4_wait_for_seqno_ioctl_helper(dev, bo->seqno, ++ &args->timeout_ns); + +- drm_gem_object_unreference(gem_obj); ++ drm_gem_object_unreference_unlocked(gem_obj); + return ret; + } + +@@ -793,7 +796,8 @@ vc4_submit_cl_ioctl(struct drm_device *d + if (ret) + goto fail; + } else { +- exec->ct0ca = exec->ct0ea = 0; ++ exec->ct0ca = 0; ++ exec->ct0ea = 0; + } + + ret = vc4_get_rcl(dev, exec); +@@ -831,7 +835,7 @@ vc4_gem_init(struct drm_device *dev) + INIT_WORK(&vc4->hangcheck.reset_work, vc4_reset_work); + setup_timer(&vc4->hangcheck.timer, + vc4_hangcheck_elapsed, +- (unsigned long) dev); ++ (unsigned long)dev); + + INIT_WORK(&vc4->job_done_work, vc4_job_done_work); + } +--- a/drivers/gpu/drm/vc4/vc4_irq.c ++++ b/drivers/gpu/drm/vc4/vc4_irq.c +@@ -56,7 +56,7 @@ vc4_overflow_mem_work(struct work_struct + struct drm_device *dev = vc4->dev; + struct vc4_bo *bo; + +- bo = vc4_bo_create(dev, 256 * 1024); ++ bo = vc4_bo_create(dev, 256 * 1024, true); + if (!bo) { + DRM_ERROR("Couldn't allocate binner overflow mem\n"); + return; +@@ -87,9 +87,8 @@ vc4_overflow_mem_work(struct work_struct + spin_unlock_irqrestore(&vc4->job_lock, irqflags); + } + +- if (vc4->overflow_mem) { ++ if (vc4->overflow_mem) + drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base); +- } + vc4->overflow_mem = bo; + + V3D_WRITE(V3D_BPOA, bo->base.paddr); +--- a/drivers/gpu/drm/vc4/vc4_kms.c ++++ b/drivers/gpu/drm/vc4/vc4_kms.c +@@ -132,6 +132,7 @@ static int vc4_atomic_commit(struct drm_ + struct drm_gem_cma_object *cma_bo = + drm_fb_cma_get_gem_obj(new_state->fb, 0); + struct vc4_bo *bo = to_vc4_bo(&cma_bo->base); ++ + wait_seqno = max(bo->seqno, wait_seqno); + } + } +--- a/drivers/gpu/drm/vc4/vc4_packet.h ++++ b/drivers/gpu/drm/vc4/vc4_packet.h +@@ -27,60 +27,60 @@ + #include "vc4_regs.h" /* for VC4_MASK, VC4_GET_FIELD, VC4_SET_FIELD */ + + enum vc4_packet { +- VC4_PACKET_HALT = 0, +- VC4_PACKET_NOP = 1, ++ VC4_PACKET_HALT = 0, ++ VC4_PACKET_NOP = 1, + +- VC4_PACKET_FLUSH = 4, +- VC4_PACKET_FLUSH_ALL = 5, +- VC4_PACKET_START_TILE_BINNING = 6, +- VC4_PACKET_INCREMENT_SEMAPHORE = 7, +- VC4_PACKET_WAIT_ON_SEMAPHORE = 8, +- +- VC4_PACKET_BRANCH = 16, +- VC4_PACKET_BRANCH_TO_SUB_LIST = 17, +- +- VC4_PACKET_STORE_MS_TILE_BUFFER = 24, +- VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF = 25, +- VC4_PACKET_STORE_FULL_RES_TILE_BUFFER = 26, +- VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER = 27, +- VC4_PACKET_STORE_TILE_BUFFER_GENERAL = 28, +- VC4_PACKET_LOAD_TILE_BUFFER_GENERAL = 29, +- +- VC4_PACKET_GL_INDEXED_PRIMITIVE = 32, +- VC4_PACKET_GL_ARRAY_PRIMITIVE = 33, +- +- VC4_PACKET_COMPRESSED_PRIMITIVE = 48, +- VC4_PACKET_CLIPPED_COMPRESSED_PRIMITIVE = 49, +- +- VC4_PACKET_PRIMITIVE_LIST_FORMAT = 56, +- +- VC4_PACKET_GL_SHADER_STATE = 64, +- VC4_PACKET_NV_SHADER_STATE = 65, +- VC4_PACKET_VG_SHADER_STATE = 66, +- +- VC4_PACKET_CONFIGURATION_BITS = 96, +- VC4_PACKET_FLAT_SHADE_FLAGS = 97, +- VC4_PACKET_POINT_SIZE = 98, +- VC4_PACKET_LINE_WIDTH = 99, +- VC4_PACKET_RHT_X_BOUNDARY = 100, +- VC4_PACKET_DEPTH_OFFSET = 101, +- VC4_PACKET_CLIP_WINDOW = 102, +- VC4_PACKET_VIEWPORT_OFFSET = 103, +- VC4_PACKET_Z_CLIPPING = 104, +- VC4_PACKET_CLIPPER_XY_SCALING = 105, +- VC4_PACKET_CLIPPER_Z_SCALING = 106, +- +- VC4_PACKET_TILE_BINNING_MODE_CONFIG = 112, +- VC4_PACKET_TILE_RENDERING_MODE_CONFIG = 113, +- VC4_PACKET_CLEAR_COLORS = 114, +- VC4_PACKET_TILE_COORDINATES = 115, +- +- /* Not an actual hardware packet -- this is what we use to put +- * references to GEM bos in the command stream, since we need the u32 +- * int the actual address packet in order to store the offset from the +- * start of the BO. +- */ +- VC4_PACKET_GEM_HANDLES = 254, ++ VC4_PACKET_FLUSH = 4, ++ VC4_PACKET_FLUSH_ALL = 5, ++ VC4_PACKET_START_TILE_BINNING = 6, ++ VC4_PACKET_INCREMENT_SEMAPHORE = 7, ++ VC4_PACKET_WAIT_ON_SEMAPHORE = 8, ++ ++ VC4_PACKET_BRANCH = 16, ++ VC4_PACKET_BRANCH_TO_SUB_LIST = 17, ++ ++ VC4_PACKET_STORE_MS_TILE_BUFFER = 24, ++ VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF = 25, ++ VC4_PACKET_STORE_FULL_RES_TILE_BUFFER = 26, ++ VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER = 27, ++ VC4_PACKET_STORE_TILE_BUFFER_GENERAL = 28, ++ VC4_PACKET_LOAD_TILE_BUFFER_GENERAL = 29, ++ ++ VC4_PACKET_GL_INDEXED_PRIMITIVE = 32, ++ VC4_PACKET_GL_ARRAY_PRIMITIVE = 33, ++ ++ VC4_PACKET_COMPRESSED_PRIMITIVE = 48, ++ VC4_PACKET_CLIPPED_COMPRESSED_PRIMITIVE = 49, ++ ++ VC4_PACKET_PRIMITIVE_LIST_FORMAT = 56, ++ ++ VC4_PACKET_GL_SHADER_STATE = 64, ++ VC4_PACKET_NV_SHADER_STATE = 65, ++ VC4_PACKET_VG_SHADER_STATE = 66, ++ ++ VC4_PACKET_CONFIGURATION_BITS = 96, ++ VC4_PACKET_FLAT_SHADE_FLAGS = 97, ++ VC4_PACKET_POINT_SIZE = 98, ++ VC4_PACKET_LINE_WIDTH = 99, ++ VC4_PACKET_RHT_X_BOUNDARY = 100, ++ VC4_PACKET_DEPTH_OFFSET = 101, ++ VC4_PACKET_CLIP_WINDOW = 102, ++ VC4_PACKET_VIEWPORT_OFFSET = 103, ++ VC4_PACKET_Z_CLIPPING = 104, ++ VC4_PACKET_CLIPPER_XY_SCALING = 105, ++ VC4_PACKET_CLIPPER_Z_SCALING = 106, ++ ++ VC4_PACKET_TILE_BINNING_MODE_CONFIG = 112, ++ VC4_PACKET_TILE_RENDERING_MODE_CONFIG = 113, ++ VC4_PACKET_CLEAR_COLORS = 114, ++ VC4_PACKET_TILE_COORDINATES = 115, ++ ++ /* Not an actual hardware packet -- this is what we use to put ++ * references to GEM bos in the command stream, since we need the u32 ++ * int the actual address packet in order to store the offset from the ++ * start of the BO. ++ */ ++ VC4_PACKET_GEM_HANDLES = 254, + } __attribute__ ((__packed__)); + + #define VC4_PACKET_HALT_SIZE 1 +@@ -148,10 +148,10 @@ enum vc4_packet { + * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL (low bits of the address) + */ + +-#define VC4_LOADSTORE_TILE_BUFFER_EOF (1 << 3) +-#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_VG_MASK (1 << 2) +-#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_ZS (1 << 1) +-#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_COLOR (1 << 0) ++#define VC4_LOADSTORE_TILE_BUFFER_EOF BIT(3) ++#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_VG_MASK BIT(2) ++#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_ZS BIT(1) ++#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_COLOR BIT(0) + + /** @} */ + +@@ -160,10 +160,10 @@ enum vc4_packet { + * byte 0-1 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and + * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL + */ +-#define VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR (1 << 15) +-#define VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR (1 << 14) +-#define VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR (1 << 13) +-#define VC4_STORE_TILE_BUFFER_DISABLE_SWAP (1 << 12) ++#define VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR BIT(15) ++#define VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR BIT(14) ++#define VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR BIT(13) ++#define VC4_STORE_TILE_BUFFER_DISABLE_SWAP BIT(12) + + #define VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK VC4_MASK(9, 8) + #define VC4_LOADSTORE_TILE_BUFFER_FORMAT_SHIFT 8 +@@ -201,28 +201,28 @@ enum vc4_packet { + #define VC4_INDEX_BUFFER_U16 (1 << 4) + + /* This flag is only present in NV shader state. */ +-#define VC4_SHADER_FLAG_SHADED_CLIP_COORDS (1 << 3) +-#define VC4_SHADER_FLAG_ENABLE_CLIPPING (1 << 2) +-#define VC4_SHADER_FLAG_VS_POINT_SIZE (1 << 1) +-#define VC4_SHADER_FLAG_FS_SINGLE_THREAD (1 << 0) ++#define VC4_SHADER_FLAG_SHADED_CLIP_COORDS BIT(3) ++#define VC4_SHADER_FLAG_ENABLE_CLIPPING BIT(2) ++#define VC4_SHADER_FLAG_VS_POINT_SIZE BIT(1) ++#define VC4_SHADER_FLAG_FS_SINGLE_THREAD BIT(0) + + /** @{ byte 2 of config bits. */ +-#define VC4_CONFIG_BITS_EARLY_Z_UPDATE (1 << 1) +-#define VC4_CONFIG_BITS_EARLY_Z (1 << 0) ++#define VC4_CONFIG_BITS_EARLY_Z_UPDATE BIT(1) ++#define VC4_CONFIG_BITS_EARLY_Z BIT(0) + /** @} */ + + /** @{ byte 1 of config bits. */ +-#define VC4_CONFIG_BITS_Z_UPDATE (1 << 7) ++#define VC4_CONFIG_BITS_Z_UPDATE BIT(7) + /** same values in this 3-bit field as PIPE_FUNC_* */ + #define VC4_CONFIG_BITS_DEPTH_FUNC_SHIFT 4 +-#define VC4_CONFIG_BITS_COVERAGE_READ_LEAVE (1 << 3) ++#define VC4_CONFIG_BITS_COVERAGE_READ_LEAVE BIT(3) + + #define VC4_CONFIG_BITS_COVERAGE_UPDATE_NONZERO (0 << 1) + #define VC4_CONFIG_BITS_COVERAGE_UPDATE_ODD (1 << 1) + #define VC4_CONFIG_BITS_COVERAGE_UPDATE_OR (2 << 1) + #define VC4_CONFIG_BITS_COVERAGE_UPDATE_ZERO (3 << 1) + +-#define VC4_CONFIG_BITS_COVERAGE_PIPE_SELECT (1 << 0) ++#define VC4_CONFIG_BITS_COVERAGE_PIPE_SELECT BIT(0) + /** @} */ + + /** @{ byte 0 of config bits. */ +@@ -230,15 +230,15 @@ enum vc4_packet { + #define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_4X (1 << 6) + #define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_16X (2 << 6) + +-#define VC4_CONFIG_BITS_AA_POINTS_AND_LINES (1 << 4) +-#define VC4_CONFIG_BITS_ENABLE_DEPTH_OFFSET (1 << 3) +-#define VC4_CONFIG_BITS_CW_PRIMITIVES (1 << 2) +-#define VC4_CONFIG_BITS_ENABLE_PRIM_BACK (1 << 1) +-#define VC4_CONFIG_BITS_ENABLE_PRIM_FRONT (1 << 0) ++#define VC4_CONFIG_BITS_AA_POINTS_AND_LINES BIT(4) ++#define VC4_CONFIG_BITS_ENABLE_DEPTH_OFFSET BIT(3) ++#define VC4_CONFIG_BITS_CW_PRIMITIVES BIT(2) ++#define VC4_CONFIG_BITS_ENABLE_PRIM_BACK BIT(1) ++#define VC4_CONFIG_BITS_ENABLE_PRIM_FRONT BIT(0) + /** @} */ + + /** @{ bits in the last u8 of VC4_PACKET_TILE_BINNING_MODE_CONFIG */ +-#define VC4_BIN_CONFIG_DB_NON_MS (1 << 7) ++#define VC4_BIN_CONFIG_DB_NON_MS BIT(7) + + #define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_MASK VC4_MASK(6, 5) + #define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_SHIFT 5 +@@ -254,17 +254,17 @@ enum vc4_packet { + #define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_128 2 + #define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_256 3 + +-#define VC4_BIN_CONFIG_AUTO_INIT_TSDA (1 << 2) +-#define VC4_BIN_CONFIG_TILE_BUFFER_64BIT (1 << 1) +-#define VC4_BIN_CONFIG_MS_MODE_4X (1 << 0) ++#define VC4_BIN_CONFIG_AUTO_INIT_TSDA BIT(2) ++#define VC4_BIN_CONFIG_TILE_BUFFER_64BIT BIT(1) ++#define VC4_BIN_CONFIG_MS_MODE_4X BIT(0) + /** @} */ + + /** @{ bits in the last u16 of VC4_PACKET_TILE_RENDERING_MODE_CONFIG */ +-#define VC4_RENDER_CONFIG_DB_NON_MS (1 << 12) +-#define VC4_RENDER_CONFIG_EARLY_Z_COVERAGE_DISABLE (1 << 11) +-#define VC4_RENDER_CONFIG_EARLY_Z_DIRECTION_G (1 << 10) +-#define VC4_RENDER_CONFIG_COVERAGE_MODE (1 << 9) +-#define VC4_RENDER_CONFIG_ENABLE_VG_MASK (1 << 8) ++#define VC4_RENDER_CONFIG_DB_NON_MS BIT(12) ++#define VC4_RENDER_CONFIG_EARLY_Z_COVERAGE_DISABLE BIT(11) ++#define VC4_RENDER_CONFIG_EARLY_Z_DIRECTION_G BIT(10) ++#define VC4_RENDER_CONFIG_COVERAGE_MODE BIT(9) ++#define VC4_RENDER_CONFIG_ENABLE_VG_MASK BIT(8) + + /** The values of the field are VC4_TILING_FORMAT_* */ + #define VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK VC4_MASK(7, 6) +@@ -280,8 +280,8 @@ enum vc4_packet { + #define VC4_RENDER_CONFIG_FORMAT_RGBA8888 1 + #define VC4_RENDER_CONFIG_FORMAT_BGR565 2 + +-#define VC4_RENDER_CONFIG_TILE_BUFFER_64BIT (1 << 1) +-#define VC4_RENDER_CONFIG_MS_MODE_4X (1 << 0) ++#define VC4_RENDER_CONFIG_TILE_BUFFER_64BIT BIT(1) ++#define VC4_RENDER_CONFIG_MS_MODE_4X BIT(0) + + #define VC4_PRIMITIVE_LIST_FORMAT_16_INDEX (1 << 4) + #define VC4_PRIMITIVE_LIST_FORMAT_32_XY (3 << 4) +@@ -291,24 +291,24 @@ enum vc4_packet { + #define VC4_PRIMITIVE_LIST_FORMAT_TYPE_RHT (3 << 0) + + enum vc4_texture_data_type { +- VC4_TEXTURE_TYPE_RGBA8888 = 0, +- VC4_TEXTURE_TYPE_RGBX8888 = 1, +- VC4_TEXTURE_TYPE_RGBA4444 = 2, +- VC4_TEXTURE_TYPE_RGBA5551 = 3, +- VC4_TEXTURE_TYPE_RGB565 = 4, +- VC4_TEXTURE_TYPE_LUMINANCE = 5, +- VC4_TEXTURE_TYPE_ALPHA = 6, +- VC4_TEXTURE_TYPE_LUMALPHA = 7, +- VC4_TEXTURE_TYPE_ETC1 = 8, +- VC4_TEXTURE_TYPE_S16F = 9, +- VC4_TEXTURE_TYPE_S8 = 10, +- VC4_TEXTURE_TYPE_S16 = 11, +- VC4_TEXTURE_TYPE_BW1 = 12, +- VC4_TEXTURE_TYPE_A4 = 13, +- VC4_TEXTURE_TYPE_A1 = 14, +- VC4_TEXTURE_TYPE_RGBA64 = 15, +- VC4_TEXTURE_TYPE_RGBA32R = 16, +- VC4_TEXTURE_TYPE_YUV422R = 17, ++ VC4_TEXTURE_TYPE_RGBA8888 = 0, ++ VC4_TEXTURE_TYPE_RGBX8888 = 1, ++ VC4_TEXTURE_TYPE_RGBA4444 = 2, ++ VC4_TEXTURE_TYPE_RGBA5551 = 3, ++ VC4_TEXTURE_TYPE_RGB565 = 4, ++ VC4_TEXTURE_TYPE_LUMINANCE = 5, ++ VC4_TEXTURE_TYPE_ALPHA = 6, ++ VC4_TEXTURE_TYPE_LUMALPHA = 7, ++ VC4_TEXTURE_TYPE_ETC1 = 8, ++ VC4_TEXTURE_TYPE_S16F = 9, ++ VC4_TEXTURE_TYPE_S8 = 10, ++ VC4_TEXTURE_TYPE_S16 = 11, ++ VC4_TEXTURE_TYPE_BW1 = 12, ++ VC4_TEXTURE_TYPE_A4 = 13, ++ VC4_TEXTURE_TYPE_A1 = 14, ++ VC4_TEXTURE_TYPE_RGBA64 = 15, ++ VC4_TEXTURE_TYPE_RGBA32R = 16, ++ VC4_TEXTURE_TYPE_YUV422R = 17, + }; + + #define VC4_TEX_P0_OFFSET_MASK VC4_MASK(31, 12) +--- a/drivers/gpu/drm/vc4/vc4_qpu_defines.h ++++ b/drivers/gpu/drm/vc4/vc4_qpu_defines.h +@@ -25,194 +25,190 @@ + #define VC4_QPU_DEFINES_H + + enum qpu_op_add { +- QPU_A_NOP, +- QPU_A_FADD, +- QPU_A_FSUB, +- QPU_A_FMIN, +- QPU_A_FMAX, +- QPU_A_FMINABS, +- QPU_A_FMAXABS, +- QPU_A_FTOI, +- QPU_A_ITOF, +- QPU_A_ADD = 12, +- QPU_A_SUB, +- QPU_A_SHR, +- QPU_A_ASR, +- QPU_A_ROR, +- QPU_A_SHL, +- QPU_A_MIN, +- QPU_A_MAX, +- QPU_A_AND, +- QPU_A_OR, +- QPU_A_XOR, +- QPU_A_NOT, +- QPU_A_CLZ, +- QPU_A_V8ADDS = 30, +- QPU_A_V8SUBS = 31, ++ QPU_A_NOP, ++ QPU_A_FADD, ++ QPU_A_FSUB, ++ QPU_A_FMIN, ++ QPU_A_FMAX, ++ QPU_A_FMINABS, ++ QPU_A_FMAXABS, ++ QPU_A_FTOI, ++ QPU_A_ITOF, ++ QPU_A_ADD = 12, ++ QPU_A_SUB, ++ QPU_A_SHR, ++ QPU_A_ASR, ++ QPU_A_ROR, ++ QPU_A_SHL, ++ QPU_A_MIN, ++ QPU_A_MAX, ++ QPU_A_AND, ++ QPU_A_OR, ++ QPU_A_XOR, ++ QPU_A_NOT, ++ QPU_A_CLZ, ++ QPU_A_V8ADDS = 30, ++ QPU_A_V8SUBS = 31, + }; + + enum qpu_op_mul { +- QPU_M_NOP, +- QPU_M_FMUL, +- QPU_M_MUL24, +- QPU_M_V8MULD, +- QPU_M_V8MIN, +- QPU_M_V8MAX, +- QPU_M_V8ADDS, +- QPU_M_V8SUBS, ++ QPU_M_NOP, ++ QPU_M_FMUL, ++ QPU_M_MUL24, ++ QPU_M_V8MULD, ++ QPU_M_V8MIN, ++ QPU_M_V8MAX, ++ QPU_M_V8ADDS, ++ QPU_M_V8SUBS, + }; + + enum qpu_raddr { +- QPU_R_FRAG_PAYLOAD_ZW = 15, /* W for A file, Z for B file */ +- /* 0-31 are the plain regfile a or b fields */ +- QPU_R_UNIF = 32, +- QPU_R_VARY = 35, +- QPU_R_ELEM_QPU = 38, +- QPU_R_NOP, +- QPU_R_XY_PIXEL_COORD = 41, +- QPU_R_MS_REV_FLAGS = 41, +- QPU_R_VPM = 48, +- QPU_R_VPM_LD_BUSY, +- QPU_R_VPM_LD_WAIT, +- QPU_R_MUTEX_ACQUIRE, ++ QPU_R_FRAG_PAYLOAD_ZW = 15, /* W for A file, Z for B file */ ++ /* 0-31 are the plain regfile a or b fields */ ++ QPU_R_UNIF = 32, ++ QPU_R_VARY = 35, ++ QPU_R_ELEM_QPU = 38, ++ QPU_R_NOP, ++ QPU_R_XY_PIXEL_COORD = 41, ++ QPU_R_MS_REV_FLAGS = 41, ++ QPU_R_VPM = 48, ++ QPU_R_VPM_LD_BUSY, ++ QPU_R_VPM_LD_WAIT, ++ QPU_R_MUTEX_ACQUIRE, + }; + + enum qpu_waddr { +- /* 0-31 are the plain regfile a or b fields */ +- QPU_W_ACC0 = 32, /* aka r0 */ +- QPU_W_ACC1, +- QPU_W_ACC2, +- QPU_W_ACC3, +- QPU_W_TMU_NOSWAP, +- QPU_W_ACC5, +- QPU_W_HOST_INT, +- QPU_W_NOP, +- QPU_W_UNIFORMS_ADDRESS, +- QPU_W_QUAD_XY, /* X for regfile a, Y for regfile b */ +- QPU_W_MS_FLAGS = 42, +- QPU_W_REV_FLAG = 42, +- QPU_W_TLB_STENCIL_SETUP = 43, +- QPU_W_TLB_Z, +- QPU_W_TLB_COLOR_MS, +- QPU_W_TLB_COLOR_ALL, +- QPU_W_TLB_ALPHA_MASK, +- QPU_W_VPM, +- QPU_W_VPMVCD_SETUP, /* LD for regfile a, ST for regfile b */ +- QPU_W_VPM_ADDR, /* LD for regfile a, ST for regfile b */ +- QPU_W_MUTEX_RELEASE, +- QPU_W_SFU_RECIP, +- QPU_W_SFU_RECIPSQRT, +- QPU_W_SFU_EXP, +- QPU_W_SFU_LOG, +- QPU_W_TMU0_S, +- QPU_W_TMU0_T, +- QPU_W_TMU0_R, +- QPU_W_TMU0_B, +- QPU_W_TMU1_S, +- QPU_W_TMU1_T, +- QPU_W_TMU1_R, +- QPU_W_TMU1_B, ++ /* 0-31 are the plain regfile a or b fields */ ++ QPU_W_ACC0 = 32, /* aka r0 */ ++ QPU_W_ACC1, ++ QPU_W_ACC2, ++ QPU_W_ACC3, ++ QPU_W_TMU_NOSWAP, ++ QPU_W_ACC5, ++ QPU_W_HOST_INT, ++ QPU_W_NOP, ++ QPU_W_UNIFORMS_ADDRESS, ++ QPU_W_QUAD_XY, /* X for regfile a, Y for regfile b */ ++ QPU_W_MS_FLAGS = 42, ++ QPU_W_REV_FLAG = 42, ++ QPU_W_TLB_STENCIL_SETUP = 43, ++ QPU_W_TLB_Z, ++ QPU_W_TLB_COLOR_MS, ++ QPU_W_TLB_COLOR_ALL, ++ QPU_W_TLB_ALPHA_MASK, ++ QPU_W_VPM, ++ QPU_W_VPMVCD_SETUP, /* LD for regfile a, ST for regfile b */ ++ QPU_W_VPM_ADDR, /* LD for regfile a, ST for regfile b */ ++ QPU_W_MUTEX_RELEASE, ++ QPU_W_SFU_RECIP, ++ QPU_W_SFU_RECIPSQRT, ++ QPU_W_SFU_EXP, ++ QPU_W_SFU_LOG, ++ QPU_W_TMU0_S, ++ QPU_W_TMU0_T, ++ QPU_W_TMU0_R, ++ QPU_W_TMU0_B, ++ QPU_W_TMU1_S, ++ QPU_W_TMU1_T, ++ QPU_W_TMU1_R, ++ QPU_W_TMU1_B, + }; + + enum qpu_sig_bits { +- QPU_SIG_SW_BREAKPOINT, +- QPU_SIG_NONE, +- QPU_SIG_THREAD_SWITCH, +- QPU_SIG_PROG_END, +- QPU_SIG_WAIT_FOR_SCOREBOARD, +- QPU_SIG_SCOREBOARD_UNLOCK, +- QPU_SIG_LAST_THREAD_SWITCH, +- QPU_SIG_COVERAGE_LOAD, +- QPU_SIG_COLOR_LOAD, +- QPU_SIG_COLOR_LOAD_END, +- QPU_SIG_LOAD_TMU0, +- QPU_SIG_LOAD_TMU1, +- QPU_SIG_ALPHA_MASK_LOAD, +- QPU_SIG_SMALL_IMM, +- QPU_SIG_LOAD_IMM, +- QPU_SIG_BRANCH ++ QPU_SIG_SW_BREAKPOINT, ++ QPU_SIG_NONE, ++ QPU_SIG_THREAD_SWITCH, ++ QPU_SIG_PROG_END, ++ QPU_SIG_WAIT_FOR_SCOREBOARD, ++ QPU_SIG_SCOREBOARD_UNLOCK, ++ QPU_SIG_LAST_THREAD_SWITCH, ++ QPU_SIG_COVERAGE_LOAD, ++ QPU_SIG_COLOR_LOAD, ++ QPU_SIG_COLOR_LOAD_END, ++ QPU_SIG_LOAD_TMU0, ++ QPU_SIG_LOAD_TMU1, ++ QPU_SIG_ALPHA_MASK_LOAD, ++ QPU_SIG_SMALL_IMM, ++ QPU_SIG_LOAD_IMM, ++ QPU_SIG_BRANCH + }; + + enum qpu_mux { +- /* hardware mux values */ +- QPU_MUX_R0, +- QPU_MUX_R1, +- QPU_MUX_R2, +- QPU_MUX_R3, +- QPU_MUX_R4, +- QPU_MUX_R5, +- QPU_MUX_A, +- QPU_MUX_B, ++ /* hardware mux values */ ++ QPU_MUX_R0, ++ QPU_MUX_R1, ++ QPU_MUX_R2, ++ QPU_MUX_R3, ++ QPU_MUX_R4, ++ QPU_MUX_R5, ++ QPU_MUX_A, ++ QPU_MUX_B, + +- /* non-hardware mux values */ +- QPU_MUX_IMM, ++ /* non-hardware mux values */ ++ QPU_MUX_IMM, + }; + + enum qpu_cond { +- QPU_COND_NEVER, +- QPU_COND_ALWAYS, +- QPU_COND_ZS, +- QPU_COND_ZC, +- QPU_COND_NS, +- QPU_COND_NC, +- QPU_COND_CS, +- QPU_COND_CC, ++ QPU_COND_NEVER, ++ QPU_COND_ALWAYS, ++ QPU_COND_ZS, ++ QPU_COND_ZC, ++ QPU_COND_NS, ++ QPU_COND_NC, ++ QPU_COND_CS, ++ QPU_COND_CC, + }; + + enum qpu_pack_mul { +- QPU_PACK_MUL_NOP, +- QPU_PACK_MUL_8888 = 3, /* replicated to each 8 bits of the 32-bit dst. */ +- QPU_PACK_MUL_8A, +- QPU_PACK_MUL_8B, +- QPU_PACK_MUL_8C, +- QPU_PACK_MUL_8D, ++ QPU_PACK_MUL_NOP, ++ /* replicated to each 8 bits of the 32-bit dst. */ ++ QPU_PACK_MUL_8888 = 3, ++ QPU_PACK_MUL_8A, ++ QPU_PACK_MUL_8B, ++ QPU_PACK_MUL_8C, ++ QPU_PACK_MUL_8D, + }; + + enum qpu_pack_a { +- QPU_PACK_A_NOP, +- /* convert to 16 bit float if float input, or to int16. */ +- QPU_PACK_A_16A, +- QPU_PACK_A_16B, +- /* replicated to each 8 bits of the 32-bit dst. */ +- QPU_PACK_A_8888, +- /* Convert to 8-bit unsigned int. */ +- QPU_PACK_A_8A, +- QPU_PACK_A_8B, +- QPU_PACK_A_8C, +- QPU_PACK_A_8D, +- +- /* Saturating variants of the previous instructions. */ +- QPU_PACK_A_32_SAT, /* int-only */ +- QPU_PACK_A_16A_SAT, /* int or float */ +- QPU_PACK_A_16B_SAT, +- QPU_PACK_A_8888_SAT, +- QPU_PACK_A_8A_SAT, +- QPU_PACK_A_8B_SAT, +- QPU_PACK_A_8C_SAT, +- QPU_PACK_A_8D_SAT, ++ QPU_PACK_A_NOP, ++ /* convert to 16 bit float if float input, or to int16. */ ++ QPU_PACK_A_16A, ++ QPU_PACK_A_16B, ++ /* replicated to each 8 bits of the 32-bit dst. */ ++ QPU_PACK_A_8888, ++ /* Convert to 8-bit unsigned int. */ ++ QPU_PACK_A_8A, ++ QPU_PACK_A_8B, ++ QPU_PACK_A_8C, ++ QPU_PACK_A_8D, ++ ++ /* Saturating variants of the previous instructions. */ ++ QPU_PACK_A_32_SAT, /* int-only */ ++ QPU_PACK_A_16A_SAT, /* int or float */ ++ QPU_PACK_A_16B_SAT, ++ QPU_PACK_A_8888_SAT, ++ QPU_PACK_A_8A_SAT, ++ QPU_PACK_A_8B_SAT, ++ QPU_PACK_A_8C_SAT, ++ QPU_PACK_A_8D_SAT, + }; + + enum qpu_unpack_r4 { +- QPU_UNPACK_R4_NOP, +- QPU_UNPACK_R4_F16A_TO_F32, +- QPU_UNPACK_R4_F16B_TO_F32, +- QPU_UNPACK_R4_8D_REP, +- QPU_UNPACK_R4_8A, +- QPU_UNPACK_R4_8B, +- QPU_UNPACK_R4_8C, +- QPU_UNPACK_R4_8D, +-}; +- +-#define QPU_MASK(high, low) ((((uint64_t)1<<((high)-(low)+1))-1)<<(low)) +-/* Using the GNU statement expression extension */ +-#define QPU_SET_FIELD(value, field) \ +- ({ \ +- uint64_t fieldval = (uint64_t)(value) << field ## _SHIFT; \ +- assert((fieldval & ~ field ## _MASK) == 0); \ +- fieldval & field ## _MASK; \ +- }) ++ QPU_UNPACK_R4_NOP, ++ QPU_UNPACK_R4_F16A_TO_F32, ++ QPU_UNPACK_R4_F16B_TO_F32, ++ QPU_UNPACK_R4_8D_REP, ++ QPU_UNPACK_R4_8A, ++ QPU_UNPACK_R4_8B, ++ QPU_UNPACK_R4_8C, ++ QPU_UNPACK_R4_8D, ++}; ++ ++#define QPU_MASK(high, low) \ ++ ((((uint64_t)1 << ((high) - (low) + 1)) - 1) << (low)) + +-#define QPU_GET_FIELD(word, field) ((uint32_t)(((word) & field ## _MASK) >> field ## _SHIFT)) ++#define QPU_GET_FIELD(word, field) \ ++ ((uint32_t)(((word) & field ## _MASK) >> field ## _SHIFT)) + + #define QPU_SIG_SHIFT 60 + #define QPU_SIG_MASK QPU_MASK(63, 60) +--- a/drivers/gpu/drm/vc4/vc4_render_cl.c ++++ b/drivers/gpu/drm/vc4/vc4_render_cl.c +@@ -63,7 +63,6 @@ static inline void rcl_u32(struct vc4_rc + setup->next_offset += 4; + } + +- + /* + * Emits a no-op STORE_TILE_BUFFER_GENERAL. + * +@@ -217,7 +216,7 @@ static int vc4_create_rcl_bo(struct drm_ + } + size += xtiles * ytiles * loop_body_size; + +- setup->rcl = &vc4_bo_create(dev, size)->base; ++ setup->rcl = &vc4_bo_create(dev, size, true)->base; + if (!setup->rcl) + return -ENOMEM; + list_add_tail(&to_vc4_bo(&setup->rcl->base)->unref_head, +@@ -256,6 +255,7 @@ static int vc4_create_rcl_bo(struct drm_ + for (x = min_x_tile; x <= max_x_tile; x++) { + bool first = (x == min_x_tile && y == min_y_tile); + bool last = (x == max_x_tile && y == max_y_tile); ++ + emit_tile(exec, setup, x, y, first, last); + } + } +--- a/drivers/gpu/drm/vc4/vc4_v3d.c ++++ b/drivers/gpu/drm/vc4/vc4_v3d.c +@@ -125,7 +125,7 @@ int vc4_v3d_debugfs_regs(struct seq_file + + int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused) + { +- struct drm_info_node *node = (struct drm_info_node *) m->private; ++ struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *dev = node->minor->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + uint32_t ident1 = V3D_READ(V3D_IDENT1); +@@ -133,11 +133,13 @@ int vc4_v3d_debugfs_ident(struct seq_fil + uint32_t tups = VC4_GET_FIELD(ident1, V3D_IDENT1_TUPS); + uint32_t qups = VC4_GET_FIELD(ident1, V3D_IDENT1_QUPS); + +- seq_printf(m, "Revision: %d\n", VC4_GET_FIELD(ident1, V3D_IDENT1_REV)); ++ seq_printf(m, "Revision: %d\n", ++ VC4_GET_FIELD(ident1, V3D_IDENT1_REV)); + seq_printf(m, "Slices: %d\n", nslc); + seq_printf(m, "TMUs: %d\n", nslc * tups); + seq_printf(m, "QPUs: %d\n", nslc * qups); +- seq_printf(m, "Semaphores: %d\n", VC4_GET_FIELD(ident1, V3D_IDENT1_NSEM)); ++ seq_printf(m, "Semaphores: %d\n", ++ VC4_GET_FIELD(ident1, V3D_IDENT1_NSEM)); + + return 0; + } +@@ -218,7 +220,7 @@ static int vc4_v3d_bind(struct device *d + } + + static void vc4_v3d_unbind(struct device *dev, struct device *master, +- void *data) ++ void *data) + { + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = to_vc4_dev(drm); +--- a/drivers/gpu/drm/vc4/vc4_validate.c ++++ b/drivers/gpu/drm/vc4/vc4_validate.c +@@ -48,7 +48,6 @@ + void *validated, \ + void *untrusted + +- + /** Return the width in pixels of a 64-byte microtile. */ + static uint32_t + utile_width(int cpp) +@@ -192,7 +191,7 @@ vc4_check_tex_size(struct vc4_exec_info + + if (size + offset < size || + size + offset > fbo->base.size) { +- DRM_ERROR("Overflow in %dx%d (%dx%d) fbo size (%d + %d > %d)\n", ++ DRM_ERROR("Overflow in %dx%d (%dx%d) fbo size (%d + %d > %zd)\n", + width, height, + aligned_width, aligned_height, + size, offset, fbo->base.size); +@@ -278,7 +277,7 @@ validate_indexed_prim_list(VALIDATE_ARGS + + if (offset > ib->base.size || + (ib->base.size - offset) / index_size < length) { +- DRM_ERROR("IB access overflow (%d + %d*%d > %d)\n", ++ DRM_ERROR("IB access overflow (%d + %d*%d > %zd)\n", + offset, length, index_size, ib->base.size); + return -EINVAL; + } +@@ -377,6 +376,7 @@ static int + validate_tile_binning_config(VALIDATE_ARGS) + { + struct drm_device *dev = exec->exec_bo->base.dev; ++ struct vc4_bo *tile_bo; + uint8_t flags; + uint32_t tile_state_size, tile_alloc_size; + uint32_t tile_count; +@@ -438,12 +438,12 @@ validate_tile_binning_config(VALIDATE_AR + */ + tile_alloc_size += 1024 * 1024; + +- exec->tile_bo = &vc4_bo_create(dev, exec->tile_alloc_offset + +- tile_alloc_size)->base; ++ tile_bo = vc4_bo_create(dev, exec->tile_alloc_offset + tile_alloc_size, ++ true); ++ exec->tile_bo = &tile_bo->base; + if (!exec->tile_bo) + return -ENOMEM; +- list_add_tail(&to_vc4_bo(&exec->tile_bo->base)->unref_head, +- &exec->unref_list); ++ list_add_tail(&tile_bo->unref_head, &exec->unref_list); + + /* tile alloc address. */ + *(uint32_t *)(validated + 0) = (exec->tile_bo->paddr + +@@ -463,8 +463,8 @@ validate_gem_handles(VALIDATE_ARGS) + return 0; + } + +-#define VC4_DEFINE_PACKET(packet, name, func) \ +- [packet] = { packet ## _SIZE, name, func } ++#define VC4_DEFINE_PACKET(packet, func) \ ++ [packet] = { packet ## _SIZE, #packet, func } + + static const struct cmd_info { + uint16_t len; +@@ -472,42 +472,43 @@ static const struct cmd_info { + int (*func)(struct vc4_exec_info *exec, void *validated, + void *untrusted); + } cmd_info[] = { +- VC4_DEFINE_PACKET(VC4_PACKET_HALT, "halt", NULL), +- VC4_DEFINE_PACKET(VC4_PACKET_NOP, "nop", NULL), +- VC4_DEFINE_PACKET(VC4_PACKET_FLUSH, "flush", NULL), +- VC4_DEFINE_PACKET(VC4_PACKET_FLUSH_ALL, "flush all state", validate_flush_all), +- VC4_DEFINE_PACKET(VC4_PACKET_START_TILE_BINNING, "start tile binning", validate_start_tile_binning), +- VC4_DEFINE_PACKET(VC4_PACKET_INCREMENT_SEMAPHORE, "increment semaphore", validate_increment_semaphore), +- +- VC4_DEFINE_PACKET(VC4_PACKET_GL_INDEXED_PRIMITIVE, "Indexed Primitive List", validate_indexed_prim_list), +- +- VC4_DEFINE_PACKET(VC4_PACKET_GL_ARRAY_PRIMITIVE, "Vertex Array Primitives", validate_gl_array_primitive), +- +- /* This is only used by clipped primitives (packets 48 and 49), which +- * we don't support parsing yet. +- */ +- VC4_DEFINE_PACKET(VC4_PACKET_PRIMITIVE_LIST_FORMAT, "primitive list format", NULL), +- +- VC4_DEFINE_PACKET(VC4_PACKET_GL_SHADER_STATE, "GL Shader State", validate_gl_shader_state), +- VC4_DEFINE_PACKET(VC4_PACKET_NV_SHADER_STATE, "NV Shader State", validate_nv_shader_state), +- +- VC4_DEFINE_PACKET(VC4_PACKET_CONFIGURATION_BITS, "configuration bits", NULL), +- VC4_DEFINE_PACKET(VC4_PACKET_FLAT_SHADE_FLAGS, "flat shade flags", NULL), +- VC4_DEFINE_PACKET(VC4_PACKET_POINT_SIZE, "point size", NULL), +- VC4_DEFINE_PACKET(VC4_PACKET_LINE_WIDTH, "line width", NULL), +- VC4_DEFINE_PACKET(VC4_PACKET_RHT_X_BOUNDARY, "RHT X boundary", NULL), +- VC4_DEFINE_PACKET(VC4_PACKET_DEPTH_OFFSET, "Depth Offset", NULL), +- VC4_DEFINE_PACKET(VC4_PACKET_CLIP_WINDOW, "Clip Window", NULL), +- VC4_DEFINE_PACKET(VC4_PACKET_VIEWPORT_OFFSET, "Viewport Offset", NULL), +- VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_XY_SCALING, "Clipper XY Scaling", NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_HALT, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_NOP, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_FLUSH, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_FLUSH_ALL, validate_flush_all), ++ VC4_DEFINE_PACKET(VC4_PACKET_START_TILE_BINNING, ++ validate_start_tile_binning), ++ VC4_DEFINE_PACKET(VC4_PACKET_INCREMENT_SEMAPHORE, ++ validate_increment_semaphore), ++ ++ VC4_DEFINE_PACKET(VC4_PACKET_GL_INDEXED_PRIMITIVE, ++ validate_indexed_prim_list), ++ VC4_DEFINE_PACKET(VC4_PACKET_GL_ARRAY_PRIMITIVE, ++ validate_gl_array_primitive), ++ ++ VC4_DEFINE_PACKET(VC4_PACKET_PRIMITIVE_LIST_FORMAT, NULL), ++ ++ VC4_DEFINE_PACKET(VC4_PACKET_GL_SHADER_STATE, validate_gl_shader_state), ++ VC4_DEFINE_PACKET(VC4_PACKET_NV_SHADER_STATE, validate_nv_shader_state), ++ ++ VC4_DEFINE_PACKET(VC4_PACKET_CONFIGURATION_BITS, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_FLAT_SHADE_FLAGS, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_POINT_SIZE, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_LINE_WIDTH, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_RHT_X_BOUNDARY, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_DEPTH_OFFSET, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_CLIP_WINDOW, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_VIEWPORT_OFFSET, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_XY_SCALING, NULL), + /* Note: The docs say this was also 105, but it was 106 in the + * initial userland code drop. + */ +- VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_Z_SCALING, "Clipper Z Scale and Offset", NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_Z_SCALING, NULL), + +- VC4_DEFINE_PACKET(VC4_PACKET_TILE_BINNING_MODE_CONFIG, "tile binning configuration", validate_tile_binning_config), ++ VC4_DEFINE_PACKET(VC4_PACKET_TILE_BINNING_MODE_CONFIG, ++ validate_tile_binning_config), + +- VC4_DEFINE_PACKET(VC4_PACKET_GEM_HANDLES, "GEM handles", validate_gem_handles), ++ VC4_DEFINE_PACKET(VC4_PACKET_GEM_HANDLES, validate_gem_handles), + }; + + int +@@ -526,7 +527,7 @@ vc4_validate_bin_cl(struct drm_device *d + u8 cmd = *(uint8_t *)src_pkt; + const struct cmd_info *info; + +- if (cmd > ARRAY_SIZE(cmd_info)) { ++ if (cmd >= ARRAY_SIZE(cmd_info)) { + DRM_ERROR("0x%08x: packet %d out of bounds\n", + src_offset, cmd); + return -EINVAL; +@@ -539,11 +540,6 @@ vc4_validate_bin_cl(struct drm_device *d + return -EINVAL; + } + +-#if 0 +- DRM_INFO("0x%08x: packet %d (%s) size %d processing...\n", +- src_offset, cmd, info->name, info->len); +-#endif +- + if (src_offset + info->len > len) { + DRM_ERROR("0x%08x: packet %d (%s) length 0x%08x " + "exceeds bounds (0x%08x)\n", +@@ -558,8 +554,7 @@ vc4_validate_bin_cl(struct drm_device *d + if (info->func && info->func(exec, + dst_pkt + 1, + src_pkt + 1)) { +- DRM_ERROR("0x%08x: packet %d (%s) failed to " +- "validate\n", ++ DRM_ERROR("0x%08x: packet %d (%s) failed to validate\n", + src_offset, cmd, info->name); + return -EINVAL; + } +@@ -618,12 +613,14 @@ reloc_tex(struct vc4_exec_info *exec, + + if (sample->is_direct) { + uint32_t remaining_size = tex->base.size - p0; ++ + if (p0 > tex->base.size - 4) { + DRM_ERROR("UBO offset greater than UBO size\n"); + goto fail; + } + if (p1 > remaining_size - 4) { +- DRM_ERROR("UBO clamp would allow reads outside of UBO\n"); ++ DRM_ERROR("UBO clamp would allow reads " ++ "outside of UBO\n"); + goto fail; + } + *validated_p0 = tex->paddr + p0; +@@ -786,7 +783,7 @@ validate_shader_rec(struct drm_device *d + struct drm_gem_cma_object *bo[ARRAY_SIZE(gl_relocs) + 8]; + uint32_t nr_attributes = 0, nr_fixed_relocs, nr_relocs, packet_size; + int i; +- struct vc4_validated_shader_info *validated_shader; ++ struct vc4_validated_shader_info *shader; + + if (state->packet == VC4_PACKET_NV_SHADER_STATE) { + relocs = nv_relocs; +@@ -841,12 +838,12 @@ validate_shader_rec(struct drm_device *d + else + mode = VC4_MODE_RENDER; + +- if (!vc4_use_bo(exec, src_handles[i], mode, &bo[i])) { ++ if (!vc4_use_bo(exec, src_handles[i], mode, &bo[i])) + return false; +- } + } + + for (i = 0; i < nr_fixed_relocs; i++) { ++ struct vc4_bo *vc4_bo; + uint32_t o = relocs[i].offset; + uint32_t src_offset = *(uint32_t *)(pkt_u + o); + uint32_t *texture_handles_u; +@@ -858,34 +855,34 @@ validate_shader_rec(struct drm_device *d + switch (relocs[i].type) { + case RELOC_CODE: + if (src_offset != 0) { +- DRM_ERROR("Shaders must be at offset 0 of " +- "the BO.\n"); ++ DRM_ERROR("Shaders must be at offset 0 " ++ "of the BO.\n"); + goto fail; + } + +- validated_shader = to_vc4_bo(&bo[i]->base)->validated_shader; +- if (!validated_shader) ++ vc4_bo = to_vc4_bo(&bo[i]->base); ++ shader = vc4_bo->validated_shader; ++ if (!shader) + goto fail; + +- if (validated_shader->uniforms_src_size > +- exec->uniforms_size) { ++ if (shader->uniforms_src_size > exec->uniforms_size) { + DRM_ERROR("Uniforms src buffer overflow\n"); + goto fail; + } + + texture_handles_u = exec->uniforms_u; + uniform_data_u = (texture_handles_u + +- validated_shader->num_texture_samples); ++ shader->num_texture_samples); + + memcpy(exec->uniforms_v, uniform_data_u, +- validated_shader->uniforms_size); ++ shader->uniforms_size); + + for (tex = 0; +- tex < validated_shader->num_texture_samples; ++ tex < shader->num_texture_samples; + tex++) { + if (!reloc_tex(exec, + uniform_data_u, +- &validated_shader->texture_samples[tex], ++ &shader->texture_samples[tex], + texture_handles_u[tex])) { + goto fail; + } +@@ -893,9 +890,9 @@ validate_shader_rec(struct drm_device *d + + *(uint32_t *)(pkt_v + o + 4) = exec->uniforms_p; + +- exec->uniforms_u += validated_shader->uniforms_src_size; +- exec->uniforms_v += validated_shader->uniforms_size; +- exec->uniforms_p += validated_shader->uniforms_size; ++ exec->uniforms_u += shader->uniforms_src_size; ++ exec->uniforms_v += shader->uniforms_size; ++ exec->uniforms_p += shader->uniforms_size; + + break; + +@@ -926,7 +923,8 @@ validate_shader_rec(struct drm_device *d + max_index = ((vbo->base.size - offset - attr_size) / + stride); + if (state->max_index > max_index) { +- DRM_ERROR("primitives use index %d out of supplied %d\n", ++ DRM_ERROR("primitives use index %d out of " ++ "supplied %d\n", + state->max_index, max_index); + return -EINVAL; + } +--- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c ++++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c +@@ -24,24 +24,16 @@ + /** + * DOC: Shader validator for VC4. + * +- * The VC4 has no IOMMU between it and system memory. So, a user with access +- * to execute shaders could escalate privilege by overwriting system memory +- * (using the VPM write address register in the general-purpose DMA mode) or +- * reading system memory it shouldn't (reading it as a texture, or uniform +- * data, or vertex data). ++ * The VC4 has no IOMMU between it and system memory, so a user with ++ * access to execute shaders could escalate privilege by overwriting ++ * system memory (using the VPM write address register in the ++ * general-purpose DMA mode) or reading system memory it shouldn't ++ * (reading it as a texture, or uniform data, or vertex data). + * +- * This walks over a shader starting from some offset within a BO, ensuring +- * that its accesses are appropriately bounded, and recording how many texture +- * accesses are made and where so that we can do relocations for them in the ++ * This walks over a shader BO, ensuring that its accesses are ++ * appropriately bounded, and recording how many texture accesses are ++ * made and where so that we can do relocations for them in the + * uniform stream. +- * +- * The kernel API has shaders stored in user-mapped BOs. The BOs will be +- * forcibly unmapped from the process before validation, and any cache of +- * validated state will be flushed if the mapping is faulted back in. +- * +- * Storing the shaders in BOs means that the validation process will be slow +- * due to uncached reads, but since shaders are long-lived and shader BOs are +- * never actually modified, this shouldn't be a problem. + */ + + #include "vc4_drv.h" +@@ -70,7 +62,6 @@ waddr_to_live_reg_index(uint32_t waddr, + else + return waddr; + } else if (waddr <= QPU_W_ACC3) { +- + return 64 + waddr - QPU_W_ACC0; + } else { + return ~0; +@@ -85,15 +76,14 @@ raddr_add_a_to_live_reg_index(uint64_t i + uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); + uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); + +- if (add_a == QPU_MUX_A) { ++ if (add_a == QPU_MUX_A) + return raddr_a; +- } else if (add_a == QPU_MUX_B && sig != QPU_SIG_SMALL_IMM) { ++ else if (add_a == QPU_MUX_B && sig != QPU_SIG_SMALL_IMM) + return 32 + raddr_b; +- } else if (add_a <= QPU_MUX_R3) { ++ else if (add_a <= QPU_MUX_R3) + return 64 + add_a; +- } else { ++ else + return ~0; +- } + } + + static bool +@@ -111,9 +101,9 @@ is_tmu_write(uint32_t waddr) + } + + static bool +-record_validated_texture_sample(struct vc4_validated_shader_info *validated_shader, +- struct vc4_shader_validation_state *validation_state, +- int tmu) ++record_texture_sample(struct vc4_validated_shader_info *validated_shader, ++ struct vc4_shader_validation_state *validation_state, ++ int tmu) + { + uint32_t s = validated_shader->num_texture_samples; + int i; +@@ -226,8 +216,8 @@ check_tmu_write(uint64_t inst, + validated_shader->uniforms_size += 4; + + if (submit) { +- if (!record_validated_texture_sample(validated_shader, +- validation_state, tmu)) { ++ if (!record_texture_sample(validated_shader, ++ validation_state, tmu)) { + return false; + } + +@@ -238,10 +228,10 @@ check_tmu_write(uint64_t inst, + } + + static bool +-check_register_write(uint64_t inst, +- struct vc4_validated_shader_info *validated_shader, +- struct vc4_shader_validation_state *validation_state, +- bool is_mul) ++check_reg_write(uint64_t inst, ++ struct vc4_validated_shader_info *validated_shader, ++ struct vc4_shader_validation_state *validation_state, ++ bool is_mul) + { + uint32_t waddr = (is_mul ? + QPU_GET_FIELD(inst, QPU_WADDR_MUL) : +@@ -297,7 +287,7 @@ check_register_write(uint64_t inst, + return true; + + case QPU_W_TLB_STENCIL_SETUP: +- return true; ++ return true; + } + + return true; +@@ -360,7 +350,7 @@ track_live_clamps(uint64_t inst, + } + + validation_state->live_max_clamp_regs[lri_add] = true; +- } if (op_add == QPU_A_MIN) { ++ } else if (op_add == QPU_A_MIN) { + /* Track live clamps of a value clamped to a minimum of 0 and + * a maximum of some uniform's offset. + */ +@@ -392,8 +382,10 @@ check_instruction_writes(uint64_t inst, + return false; + } + +- ok = (check_register_write(inst, validated_shader, validation_state, false) && +- check_register_write(inst, validated_shader, validation_state, true)); ++ ok = (check_reg_write(inst, validated_shader, validation_state, ++ false) && ++ check_reg_write(inst, validated_shader, validation_state, ++ true)); + + track_live_clamps(inst, validated_shader, validation_state); + +@@ -441,7 +433,7 @@ vc4_validate_shader(struct drm_gem_cma_o + shader = shader_obj->vaddr; + max_ip = shader_obj->base.size / sizeof(uint64_t); + +- validated_shader = kcalloc(sizeof(*validated_shader), 1, GFP_KERNEL); ++ validated_shader = kcalloc(1, sizeof(*validated_shader), GFP_KERNEL); + if (!validated_shader) + return NULL; + +@@ -497,7 +489,7 @@ vc4_validate_shader(struct drm_gem_cma_o + + if (ip == max_ip) { + DRM_ERROR("shader failed to terminate before " +- "shader BO end at %d\n", ++ "shader BO end at %zd\n", + shader_obj->base.size); + goto fail; + } +--- a/include/drm/drmP.h ++++ b/include/drm/drmP.h +@@ -585,6 +585,13 @@ struct drm_driver { + int (*gem_open_object) (struct drm_gem_object *, struct drm_file *); + void (*gem_close_object) (struct drm_gem_object *, struct drm_file *); + ++ /** ++ * Hook for allocating the GEM object struct, for use by core ++ * helpers. ++ */ ++ struct drm_gem_object *(*gem_create_object)(struct drm_device *dev, ++ size_t size); ++ + /* prime: */ + /* export handle -> fd (see drm_gem_prime_handle_to_fd() helper) */ + int (*prime_handle_to_fd)(struct drm_device *dev, struct drm_file *file_priv, +@@ -639,7 +646,6 @@ struct drm_driver { + + u32 driver_features; + int dev_priv_size; +- size_t gem_obj_size; + const struct drm_ioctl_desc *ioctls; + int num_ioctls; + const struct file_operations *fops; diff --git a/target/linux/brcm2708/patches-4.4/0115-drm-Use-the-driver-s-gem_object_free-function-from-C.patch b/target/linux/brcm2708/patches-4.4/0115-drm-Use-the-driver-s-gem_object_free-function-from-C.patch new file mode 100644 index 0000000000..db69082284 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0115-drm-Use-the-driver-s-gem_object_free-function-from-C.patch @@ -0,0 +1,59 @@ +From 659b247a25ea9439be74087726d6adfae7570d12 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Fri, 11 Dec 2015 19:45:03 -0800 +Subject: [PATCH 115/232] drm: Use the driver's gem_object_free function from + CMA helpers. + +VC4 wraps the CMA objects in its own structures, so it needs to do its +own teardown (waiting for GPU to finish, updating bo_stats tracking). +The other CMA drivers are using drm_gem_cma_free_object as their +gem_free_object, so this should be a no-op for them. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/drm_fb_cma_helper.c | 6 +++--- + drivers/gpu/drm/drm_gem_cma_helper.c | 4 ++-- + 2 files changed, 5 insertions(+), 5 deletions(-) + +--- a/drivers/gpu/drm/drm_fb_cma_helper.c ++++ b/drivers/gpu/drm/drm_fb_cma_helper.c +@@ -266,7 +266,7 @@ static int drm_fbdev_cma_create(struct d + fbi = drm_fb_helper_alloc_fbi(helper); + if (IS_ERR(fbi)) { + ret = PTR_ERR(fbi); +- goto err_drm_gem_cma_free_object; ++ goto err_gem_free_object; + } + + fbdev_cma->fb = drm_fb_cma_alloc(dev, &mode_cmd, &obj, 1); +@@ -299,8 +299,8 @@ static int drm_fbdev_cma_create(struct d + + err_fb_info_destroy: + drm_fb_helper_release_fbi(helper); +-err_drm_gem_cma_free_object: +- drm_gem_cma_free_object(&obj->base); ++err_gem_free_object: ++ dev->driver->gem_free_object(&obj->base); + return ret; + } + +--- a/drivers/gpu/drm/drm_gem_cma_helper.c ++++ b/drivers/gpu/drm/drm_gem_cma_helper.c +@@ -121,7 +121,7 @@ struct drm_gem_cma_object *drm_gem_cma_c + return cma_obj; + + error: +- drm_gem_cma_free_object(&cma_obj->base); ++ drm->driver->gem_free_object(&cma_obj->base); + return ERR_PTR(ret); + } + EXPORT_SYMBOL_GPL(drm_gem_cma_create); +@@ -171,7 +171,7 @@ drm_gem_cma_create_with_handle(struct dr + return cma_obj; + + err_handle_create: +- drm_gem_cma_free_object(gem_obj); ++ drm->driver->gem_free_object(gem_obj); + + return ERR_PTR(ret); + } diff --git a/target/linux/brcm2708/patches-4.4/0115-drm-vc4-Update-a-bunch-of-code-to-match-upstream-sub.patch b/target/linux/brcm2708/patches-4.4/0115-drm-vc4-Update-a-bunch-of-code-to-match-upstream-sub.patch deleted file mode 100644 index 70f32ca552..0000000000 --- a/target/linux/brcm2708/patches-4.4/0115-drm-vc4-Update-a-bunch-of-code-to-match-upstream-sub.patch +++ /dev/null @@ -1,1894 +0,0 @@ -From 431be9af0aa88da94986effdff649330cfde1f5a Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Fri, 4 Dec 2015 11:35:34 -0800 -Subject: [PATCH 115/170] drm/vc4: Update a bunch of code to match upstream - submission. - -This gets almost everything matching, except for the MSAA support and -using generic PM domains. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/drm_gem_cma_helper.c | 13 +- - drivers/gpu/drm/vc4/vc4_bo.c | 322 +++++++++++++++++------------ - drivers/gpu/drm/vc4/vc4_crtc.c | 7 +- - drivers/gpu/drm/vc4/vc4_drv.c | 6 +- - drivers/gpu/drm/vc4/vc4_drv.h | 20 +- - drivers/gpu/drm/vc4/vc4_gem.c | 24 ++- - drivers/gpu/drm/vc4/vc4_irq.c | 5 +- - drivers/gpu/drm/vc4/vc4_kms.c | 1 + - drivers/gpu/drm/vc4/vc4_packet.h | 210 +++++++++---------- - drivers/gpu/drm/vc4/vc4_qpu_defines.h | 308 ++++++++++++++------------- - drivers/gpu/drm/vc4/vc4_render_cl.c | 4 +- - drivers/gpu/drm/vc4/vc4_v3d.c | 10 +- - drivers/gpu/drm/vc4/vc4_validate.c | 130 ++++++------ - drivers/gpu/drm/vc4/vc4_validate_shaders.c | 66 +++--- - include/drm/drmP.h | 8 +- - 15 files changed, 598 insertions(+), 536 deletions(-) - ---- a/drivers/gpu/drm/drm_gem_cma_helper.c -+++ b/drivers/gpu/drm/drm_gem_cma_helper.c -@@ -58,15 +58,14 @@ __drm_gem_cma_create(struct drm_device * - struct drm_gem_cma_object *cma_obj; - struct drm_gem_object *gem_obj; - int ret; -- size_t obj_size = (drm->driver->gem_obj_size ? -- drm->driver->gem_obj_size : -- sizeof(*cma_obj)); - -- cma_obj = kzalloc(obj_size, GFP_KERNEL); -- if (!cma_obj) -+ if (drm->driver->gem_create_object) -+ gem_obj = drm->driver->gem_create_object(drm, size); -+ else -+ gem_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL); -+ if (!gem_obj) - return ERR_PTR(-ENOMEM); -- -- gem_obj = &cma_obj->base; -+ cma_obj = container_of(gem_obj, struct drm_gem_cma_object, base); - - ret = drm_gem_object_init(drm, gem_obj, size); - if (ret) ---- a/drivers/gpu/drm/vc4/vc4_bo.c -+++ b/drivers/gpu/drm/vc4/vc4_bo.c -@@ -12,6 +12,10 @@ - * access to system memory with no MMU in between. To support it, we - * use the GEM CMA helper functions to allocate contiguous ranges of - * physical memory for our BOs. -+ * -+ * Since the CMA allocator is very slow, we keep a cache of recently -+ * freed BOs around so that the kernel's allocation of objects for 3D -+ * rendering can return quickly. - */ - - #include "vc4_drv.h" -@@ -34,6 +38,36 @@ static void vc4_bo_stats_dump(struct vc4 - vc4->bo_stats.size_cached / 1024); - } - -+#ifdef CONFIG_DEBUG_FS -+int vc4_bo_stats_debugfs(struct seq_file *m, void *unused) -+{ -+ struct drm_info_node *node = (struct drm_info_node *)m->private; -+ struct drm_device *dev = node->minor->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct vc4_bo_stats stats; -+ -+ /* Take a snapshot of the current stats with the lock held. */ -+ mutex_lock(&vc4->bo_lock); -+ stats = vc4->bo_stats; -+ mutex_unlock(&vc4->bo_lock); -+ -+ seq_printf(m, "num bos allocated: %d\n", -+ stats.num_allocated); -+ seq_printf(m, "size bos allocated: %dkb\n", -+ stats.size_allocated / 1024); -+ seq_printf(m, "num bos used: %d\n", -+ stats.num_allocated - stats.num_cached); -+ seq_printf(m, "size bos used: %dkb\n", -+ (stats.size_allocated - stats.size_cached) / 1024); -+ seq_printf(m, "num bos cached: %d\n", -+ stats.num_cached); -+ seq_printf(m, "size bos cached: %dkb\n", -+ stats.size_cached / 1024); -+ -+ return 0; -+} -+#endif -+ - static uint32_t bo_page_index(size_t size) - { - return (size / PAGE_SIZE) - 1; -@@ -81,8 +115,8 @@ static struct list_head *vc4_get_cache_l - struct list_head *new_list; - uint32_t i; - -- new_list = kmalloc(new_size * sizeof(struct list_head), -- GFP_KERNEL); -+ new_list = kmalloc_array(new_size, sizeof(struct list_head), -+ GFP_KERNEL); - if (!new_list) - return NULL; - -@@ -90,7 +124,9 @@ static struct list_head *vc4_get_cache_l - * head locations. - */ - for (i = 0; i < vc4->bo_cache.size_list_size; i++) { -- struct list_head *old_list = &vc4->bo_cache.size_list[i]; -+ struct list_head *old_list = -+ &vc4->bo_cache.size_list[i]; -+ - if (list_empty(old_list)) - INIT_LIST_HEAD(&new_list[i]); - else -@@ -122,11 +158,60 @@ void vc4_bo_cache_purge(struct drm_devic - mutex_unlock(&vc4->bo_lock); - } - --struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size) -+static struct vc4_bo *vc4_bo_get_from_cache(struct drm_device *dev, -+ uint32_t size) - { - struct vc4_dev *vc4 = to_vc4_dev(dev); -- uint32_t size = roundup(unaligned_size, PAGE_SIZE); - uint32_t page_index = bo_page_index(size); -+ struct vc4_bo *bo = NULL; -+ -+ size = roundup(size, PAGE_SIZE); -+ -+ mutex_lock(&vc4->bo_lock); -+ if (page_index >= vc4->bo_cache.size_list_size) -+ goto out; -+ -+ if (list_empty(&vc4->bo_cache.size_list[page_index])) -+ goto out; -+ -+ bo = list_first_entry(&vc4->bo_cache.size_list[page_index], -+ struct vc4_bo, size_head); -+ vc4_bo_remove_from_cache(bo); -+ kref_init(&bo->base.base.refcount); -+ -+out: -+ mutex_unlock(&vc4->bo_lock); -+ return bo; -+} -+ -+/** -+ * vc4_gem_create_object - Implementation of driver->gem_create_object. -+ * -+ * This lets the CMA helpers allocate object structs for us, and keep -+ * our BO stats correct. -+ */ -+struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size) -+{ -+ struct vc4_dev *vc4 = to_vc4_dev(dev); -+ struct vc4_bo *bo; -+ -+ bo = kzalloc(sizeof(*bo), GFP_KERNEL); -+ if (!bo) -+ return ERR_PTR(-ENOMEM); -+ -+ mutex_lock(&vc4->bo_lock); -+ vc4->bo_stats.num_allocated++; -+ vc4->bo_stats.size_allocated += size; -+ mutex_unlock(&vc4->bo_lock); -+ -+ return &bo->base.base; -+} -+ -+struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size, -+ bool from_cache) -+{ -+ size_t size = roundup(unaligned_size, PAGE_SIZE); -+ struct vc4_dev *vc4 = to_vc4_dev(dev); - struct drm_gem_cma_object *cma_obj; - int pass; - -@@ -134,18 +219,12 @@ struct vc4_bo *vc4_bo_create(struct drm_ - return NULL; - - /* First, try to get a vc4_bo from the kernel BO cache. */ -- mutex_lock(&vc4->bo_lock); -- if (page_index < vc4->bo_cache.size_list_size && -- !list_empty(&vc4->bo_cache.size_list[page_index])) { -- struct vc4_bo *bo = -- list_first_entry(&vc4->bo_cache.size_list[page_index], -- struct vc4_bo, size_head); -- vc4_bo_remove_from_cache(bo); -- mutex_unlock(&vc4->bo_lock); -- kref_init(&bo->base.base.refcount); -- return bo; -+ if (from_cache) { -+ struct vc4_bo *bo = vc4_bo_get_from_cache(dev, size); -+ -+ if (bo) -+ return bo; - } -- mutex_unlock(&vc4->bo_lock); - - /* Otherwise, make a new BO. */ - for (pass = 0; ; pass++) { -@@ -179,9 +258,6 @@ struct vc4_bo *vc4_bo_create(struct drm_ - } - } - -- vc4->bo_stats.num_allocated++; -- vc4->bo_stats.size_allocated += size; -- - return to_vc4_bo(&cma_obj->base); - } - -@@ -199,7 +275,7 @@ int vc4_dumb_create(struct drm_file *fil - if (args->size < args->pitch * args->height) - args->size = args->pitch * args->height; - -- bo = vc4_bo_create(dev, args->size); -+ bo = vc4_bo_create(dev, args->size, false); - if (!bo) - return -ENOMEM; - -@@ -209,8 +285,8 @@ int vc4_dumb_create(struct drm_file *fil - return ret; - } - --static void --vc4_bo_cache_free_old(struct drm_device *dev) -+/* Must be called with bo_lock held. */ -+static void vc4_bo_cache_free_old(struct drm_device *dev) - { - struct vc4_dev *vc4 = to_vc4_dev(dev); - unsigned long expire_time = jiffies - msecs_to_jiffies(1000); -@@ -313,15 +389,77 @@ vc4_prime_export(struct drm_device *dev, - return drm_gem_prime_export(dev, obj, flags); - } - --int --vc4_create_bo_ioctl(struct drm_device *dev, void *data, -- struct drm_file *file_priv) -+int vc4_mmap(struct file *filp, struct vm_area_struct *vma) -+{ -+ struct drm_gem_object *gem_obj; -+ struct vc4_bo *bo; -+ int ret; -+ -+ ret = drm_gem_mmap(filp, vma); -+ if (ret) -+ return ret; -+ -+ gem_obj = vma->vm_private_data; -+ bo = to_vc4_bo(gem_obj); -+ -+ if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { -+ DRM_ERROR("mmaping of shader BOs for writing not allowed.\n"); -+ return -EINVAL; -+ } -+ -+ /* -+ * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the -+ * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map -+ * the whole buffer. -+ */ -+ vma->vm_flags &= ~VM_PFNMAP; -+ vma->vm_pgoff = 0; -+ -+ ret = dma_mmap_writecombine(bo->base.base.dev->dev, vma, -+ bo->base.vaddr, bo->base.paddr, -+ vma->vm_end - vma->vm_start); -+ if (ret) -+ drm_gem_vm_close(vma); -+ -+ return ret; -+} -+ -+int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) -+{ -+ struct vc4_bo *bo = to_vc4_bo(obj); -+ -+ if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { -+ DRM_ERROR("mmaping of shader BOs for writing not allowed.\n"); -+ return -EINVAL; -+ } -+ -+ return drm_gem_cma_prime_mmap(obj, vma); -+} -+ -+void *vc4_prime_vmap(struct drm_gem_object *obj) -+{ -+ struct vc4_bo *bo = to_vc4_bo(obj); -+ -+ if (bo->validated_shader) { -+ DRM_ERROR("mmaping of shader BOs not allowed.\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ return drm_gem_cma_prime_vmap(obj); -+} -+ -+int vc4_create_bo_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv) - { - struct drm_vc4_create_bo *args = data; - struct vc4_bo *bo = NULL; - int ret; - -- bo = vc4_bo_create(dev, args->size); -+ /* -+ * We can't allocate from the BO cache, because the BOs don't -+ * get zeroed, and that might leak data between users. -+ */ -+ bo = vc4_bo_create(dev, args->size, false); - if (!bo) - return -ENOMEM; - -@@ -331,6 +469,25 @@ vc4_create_bo_ioctl(struct drm_device *d - return ret; - } - -+int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, -+ struct drm_file *file_priv) -+{ -+ struct drm_vc4_mmap_bo *args = data; -+ struct drm_gem_object *gem_obj; -+ -+ gem_obj = drm_gem_object_lookup(dev, file_priv, args->handle); -+ if (!gem_obj) { -+ DRM_ERROR("Failed to look up GEM BO %d\n", args->handle); -+ return -EINVAL; -+ } -+ -+ /* The mmap offset was set up at BO allocation time. */ -+ args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); -+ -+ drm_gem_object_unreference_unlocked(gem_obj); -+ return 0; -+} -+ - int - vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -@@ -355,7 +512,7 @@ vc4_create_shader_bo_ioctl(struct drm_de - return -EINVAL; - } - -- bo = vc4_bo_create(dev, args->size); -+ bo = vc4_bo_create(dev, args->size, true); - if (!bo) - return -ENOMEM; - -@@ -364,6 +521,11 @@ vc4_create_shader_bo_ioctl(struct drm_de - args->size); - if (ret != 0) - goto fail; -+ /* Clear the rest of the memory from allocating from the BO -+ * cache. -+ */ -+ memset(bo->base.vaddr + args->size, 0, -+ bo->base.base.size - args->size); - - bo->validated_shader = vc4_validate_shader(&bo->base); - if (!bo->validated_shader) { -@@ -382,85 +544,6 @@ vc4_create_shader_bo_ioctl(struct drm_de - return ret; - } - --int --vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, -- struct drm_file *file_priv) --{ -- struct drm_vc4_mmap_bo *args = data; -- struct drm_gem_object *gem_obj; -- -- gem_obj = drm_gem_object_lookup(dev, file_priv, args->handle); -- if (!gem_obj) { -- DRM_ERROR("Failed to look up GEM BO %d\n", args->handle); -- return -EINVAL; -- } -- -- /* The mmap offset was set up at BO allocation time. */ -- args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); -- -- drm_gem_object_unreference(gem_obj); -- return 0; --} -- --int vc4_mmap(struct file *filp, struct vm_area_struct *vma) --{ -- struct drm_gem_object *gem_obj; -- struct vc4_bo *bo; -- int ret; -- -- ret = drm_gem_mmap(filp, vma); -- if (ret) -- return ret; -- -- gem_obj = vma->vm_private_data; -- bo = to_vc4_bo(gem_obj); -- -- if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { -- DRM_ERROR("mmaping of shader BOs for writing not allowed.\n"); -- return -EINVAL; -- } -- -- /* -- * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the -- * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map -- * the whole buffer. -- */ -- vma->vm_flags &= ~VM_PFNMAP; -- vma->vm_pgoff = 0; -- -- ret = dma_mmap_writecombine(bo->base.base.dev->dev, vma, -- bo->base.vaddr, bo->base.paddr, -- vma->vm_end - vma->vm_start); -- if (ret) -- drm_gem_vm_close(vma); -- -- return ret; --} -- --int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) --{ -- struct vc4_bo *bo = to_vc4_bo(obj); -- -- if (bo->validated_shader) { -- DRM_ERROR("mmaping of shader BOs not allowed.\n"); -- return -EINVAL; -- } -- -- return drm_gem_cma_prime_mmap(obj, vma); --} -- --void *vc4_prime_vmap(struct drm_gem_object *obj) --{ -- struct vc4_bo *bo = to_vc4_bo(obj); -- -- if (bo->validated_shader) { -- DRM_ERROR("mmaping of shader BOs not allowed.\n"); -- return ERR_PTR(-EINVAL); -- } -- -- return drm_gem_cma_prime_vmap(obj); --} -- - void vc4_bo_cache_init(struct drm_device *dev) - { - struct vc4_dev *vc4 = to_vc4_dev(dev); -@@ -472,7 +555,7 @@ void vc4_bo_cache_init(struct drm_device - INIT_WORK(&vc4->bo_cache.time_work, vc4_bo_cache_time_work); - setup_timer(&vc4->bo_cache.time_timer, - vc4_bo_cache_time_timer, -- (unsigned long) dev); -+ (unsigned long)dev); - } - - void vc4_bo_cache_destroy(struct drm_device *dev) -@@ -489,28 +572,3 @@ void vc4_bo_cache_destroy(struct drm_dev - vc4_bo_stats_dump(vc4); - } - } -- --#ifdef CONFIG_DEBUG_FS --int vc4_bo_stats_debugfs(struct seq_file *m, void *unused) --{ -- struct drm_info_node *node = (struct drm_info_node *) m->private; -- struct drm_device *dev = node->minor->dev; -- struct vc4_dev *vc4 = to_vc4_dev(dev); -- struct vc4_bo_stats stats; -- -- mutex_lock(&vc4->bo_lock); -- stats = vc4->bo_stats; -- mutex_unlock(&vc4->bo_lock); -- -- seq_printf(m, "num bos allocated: %d\n", stats.num_allocated); -- seq_printf(m, "size bos allocated: %dkb\n", stats.size_allocated / 1024); -- seq_printf(m, "num bos used: %d\n", (stats.num_allocated - -- stats.num_cached)); -- seq_printf(m, "size bos used: %dkb\n", (stats.size_allocated - -- stats.size_cached) / 1024); -- seq_printf(m, "num bos cached: %d\n", stats.num_cached); -- seq_printf(m, "size bos cached: %dkb\n", stats.size_cached / 1024); -- -- return 0; --} --#endif ---- a/drivers/gpu/drm/vc4/vc4_crtc.c -+++ b/drivers/gpu/drm/vc4/vc4_crtc.c -@@ -501,6 +501,7 @@ vc4_async_page_flip_complete(struct vc4_ - vc4_plane_async_set_fb(plane, flip_state->fb); - if (flip_state->event) { - unsigned long flags; -+ - spin_lock_irqsave(&dev->event_lock, flags); - drm_crtc_send_vblank_event(crtc, flip_state->event); - spin_unlock_irqrestore(&dev->event_lock, flags); -@@ -562,9 +563,9 @@ static int vc4_async_page_flip(struct dr - } - - static int vc4_page_flip(struct drm_crtc *crtc, -- struct drm_framebuffer *fb, -- struct drm_pending_vblank_event *event, -- uint32_t flags) -+ struct drm_framebuffer *fb, -+ struct drm_pending_vblank_event *event, -+ uint32_t flags) - { - if (flags & DRM_MODE_PAGE_FLIP_ASYNC) - return vc4_async_page_flip(crtc, fb, event, flags); ---- a/drivers/gpu/drm/vc4/vc4_drv.c -+++ b/drivers/gpu/drm/vc4/vc4_drv.c -@@ -81,7 +81,8 @@ static const struct drm_ioctl_desc vc4_d - DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, 0), - DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, 0), - DRM_IOCTL_DEF_DRV(VC4_CREATE_SHADER_BO, vc4_create_shader_bo_ioctl, 0), -- DRM_IOCTL_DEF_DRV(VC4_GET_HANG_STATE, vc4_get_hang_state_ioctl, DRM_ROOT_ONLY), -+ DRM_IOCTL_DEF_DRV(VC4_GET_HANG_STATE, vc4_get_hang_state_ioctl, -+ DRM_ROOT_ONLY), - }; - - static struct drm_driver vc4_drm_driver = { -@@ -107,6 +108,7 @@ static struct drm_driver vc4_drm_driver - .debugfs_cleanup = vc4_debugfs_cleanup, - #endif - -+ .gem_create_object = vc4_create_object, - .gem_free_object = vc4_free_object, - .gem_vm_ops = &drm_gem_cma_vm_ops, - -@@ -128,8 +130,6 @@ static struct drm_driver vc4_drm_driver - .num_ioctls = ARRAY_SIZE(vc4_drm_ioctls), - .fops = &vc4_drm_fops, - -- //.gem_obj_size = sizeof(struct vc4_bo), -- - .name = DRIVER_NAME, - .desc = DRIVER_DESC, - .date = DRIVER_DATE, ---- a/drivers/gpu/drm/vc4/vc4_drv.h -+++ b/drivers/gpu/drm/vc4/vc4_drv.h -@@ -72,6 +72,9 @@ struct vc4_dev { - * job_done_work. - */ - struct list_head job_done_list; -+ /* Spinlock used to synchronize the job_list and seqno -+ * accesses between the IRQ handler and GEM ioctls. -+ */ - spinlock_t job_lock; - wait_queue_head_t job_wait_queue; - struct work_struct job_done_work; -@@ -318,8 +321,7 @@ struct vc4_texture_sample_info { - * and validate the shader state record's uniforms that define the texture - * samples. - */ --struct vc4_validated_shader_info --{ -+struct vc4_validated_shader_info { - uint32_t uniforms_size; - uint32_t uniforms_src_size; - uint32_t num_texture_samples; -@@ -355,8 +357,10 @@ struct vc4_validated_shader_info - #define wait_for(COND, MS) _wait_for(COND, MS, 1) - - /* vc4_bo.c */ -+struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size); - void vc4_free_object(struct drm_gem_object *gem_obj); --struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size); -+struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size, -+ bool from_cache); - int vc4_dumb_create(struct drm_file *file_priv, - struct drm_device *dev, - struct drm_mode_create_dumb *args); -@@ -432,7 +436,8 @@ struct drm_plane *vc4_plane_init(struct - enum drm_plane_type type); - u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); - u32 vc4_plane_dlist_size(struct drm_plane_state *state); --void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb); -+void vc4_plane_async_set_fb(struct drm_plane *plane, -+ struct drm_framebuffer *fb); - - /* vc4_v3d.c */ - extern struct platform_driver vc4_v3d_driver; -@@ -450,9 +455,6 @@ vc4_validate_bin_cl(struct drm_device *d - int - vc4_validate_shader_recs(struct drm_device *dev, struct vc4_exec_info *exec); - --struct vc4_validated_shader_info * --vc4_validate_shader(struct drm_gem_cma_object *shader_obj); -- - bool vc4_use_bo(struct vc4_exec_info *exec, - uint32_t hindex, - enum vc4_bo_mode mode, -@@ -464,3 +466,7 @@ bool vc4_check_tex_size(struct vc4_exec_ - struct drm_gem_cma_object *fbo, - uint32_t offset, uint8_t tiling_format, - uint32_t width, uint32_t height, uint8_t cpp); -+ -+/* vc4_validate_shader.c */ -+struct vc4_validated_shader_info * -+vc4_validate_shader(struct drm_gem_cma_object *shader_obj); ---- a/drivers/gpu/drm/vc4/vc4_gem.c -+++ b/drivers/gpu/drm/vc4/vc4_gem.c -@@ -53,9 +53,8 @@ vc4_free_hang_state(struct drm_device *d - unsigned int i; - - mutex_lock(&dev->struct_mutex); -- for (i = 0; i < state->user_state.bo_count; i++) { -+ for (i = 0; i < state->user_state.bo_count; i++) - drm_gem_object_unreference(state->bo[i]); -- } - mutex_unlock(&dev->struct_mutex); - - kfree(state); -@@ -65,10 +64,10 @@ int - vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) - { -- struct drm_vc4_get_hang_state *get_state = data; -+ struct drm_vc4_get_hang_state *get_state = data; - struct drm_vc4_get_hang_state_bo *bo_state; - struct vc4_hang_state *kernel_state; -- struct drm_vc4_get_hang_state *state; -+ struct drm_vc4_get_hang_state *state; - struct vc4_dev *vc4 = to_vc4_dev(dev); - unsigned long irqflags; - u32 i; -@@ -107,6 +106,7 @@ vc4_get_hang_state_ioctl(struct drm_devi - for (i = 0; i < state->bo_count; i++) { - struct vc4_bo *vc4_bo = to_vc4_bo(kernel_state->bo[i]); - u32 handle; -+ - ret = drm_gem_handle_create(file_priv, kernel_state->bo[i], - &handle); - -@@ -124,7 +124,7 @@ vc4_get_hang_state_ioctl(struct drm_devi - state->bo_count * sizeof(*bo_state)); - kfree(bo_state); - -- err_free: -+err_free: - - vc4_free_hang_state(dev, kernel_state); - -@@ -578,7 +578,7 @@ vc4_get_bcl(struct drm_device *dev, stru - goto fail; - } - -- bo = vc4_bo_create(dev, exec_size); -+ bo = vc4_bo_create(dev, exec_size, true); - if (!bo) { - DRM_ERROR("Couldn't allocate BO for binning\n"); - ret = PTR_ERR(exec->exec_bo); -@@ -668,6 +668,7 @@ vc4_job_handle_completed(struct vc4_dev - static void vc4_seqno_cb_work(struct work_struct *work) - { - struct vc4_seqno_cb *cb = container_of(work, struct vc4_seqno_cb, work); -+ - cb->func(cb); - } - -@@ -717,6 +718,7 @@ vc4_wait_for_seqno_ioctl_helper(struct d - - if ((ret == -EINTR || ret == -ERESTARTSYS) && *timeout_ns != ~0ull) { - uint64_t delta = jiffies_to_nsecs(jiffies - start); -+ - if (*timeout_ns >= delta) - *timeout_ns -= delta; - } -@@ -750,9 +752,10 @@ vc4_wait_bo_ioctl(struct drm_device *dev - } - bo = to_vc4_bo(gem_obj); - -- ret = vc4_wait_for_seqno_ioctl_helper(dev, bo->seqno, &args->timeout_ns); -+ ret = vc4_wait_for_seqno_ioctl_helper(dev, bo->seqno, -+ &args->timeout_ns); - -- drm_gem_object_unreference(gem_obj); -+ drm_gem_object_unreference_unlocked(gem_obj); - return ret; - } - -@@ -793,7 +796,8 @@ vc4_submit_cl_ioctl(struct drm_device *d - if (ret) - goto fail; - } else { -- exec->ct0ca = exec->ct0ea = 0; -+ exec->ct0ca = 0; -+ exec->ct0ea = 0; - } - - ret = vc4_get_rcl(dev, exec); -@@ -831,7 +835,7 @@ vc4_gem_init(struct drm_device *dev) - INIT_WORK(&vc4->hangcheck.reset_work, vc4_reset_work); - setup_timer(&vc4->hangcheck.timer, - vc4_hangcheck_elapsed, -- (unsigned long) dev); -+ (unsigned long)dev); - - INIT_WORK(&vc4->job_done_work, vc4_job_done_work); - } ---- a/drivers/gpu/drm/vc4/vc4_irq.c -+++ b/drivers/gpu/drm/vc4/vc4_irq.c -@@ -56,7 +56,7 @@ vc4_overflow_mem_work(struct work_struct - struct drm_device *dev = vc4->dev; - struct vc4_bo *bo; - -- bo = vc4_bo_create(dev, 256 * 1024); -+ bo = vc4_bo_create(dev, 256 * 1024, true); - if (!bo) { - DRM_ERROR("Couldn't allocate binner overflow mem\n"); - return; -@@ -87,9 +87,8 @@ vc4_overflow_mem_work(struct work_struct - spin_unlock_irqrestore(&vc4->job_lock, irqflags); - } - -- if (vc4->overflow_mem) { -+ if (vc4->overflow_mem) - drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base); -- } - vc4->overflow_mem = bo; - - V3D_WRITE(V3D_BPOA, bo->base.paddr); ---- a/drivers/gpu/drm/vc4/vc4_kms.c -+++ b/drivers/gpu/drm/vc4/vc4_kms.c -@@ -132,6 +132,7 @@ static int vc4_atomic_commit(struct drm_ - struct drm_gem_cma_object *cma_bo = - drm_fb_cma_get_gem_obj(new_state->fb, 0); - struct vc4_bo *bo = to_vc4_bo(&cma_bo->base); -+ - wait_seqno = max(bo->seqno, wait_seqno); - } - } ---- a/drivers/gpu/drm/vc4/vc4_packet.h -+++ b/drivers/gpu/drm/vc4/vc4_packet.h -@@ -27,60 +27,60 @@ - #include "vc4_regs.h" /* for VC4_MASK, VC4_GET_FIELD, VC4_SET_FIELD */ - - enum vc4_packet { -- VC4_PACKET_HALT = 0, -- VC4_PACKET_NOP = 1, -+ VC4_PACKET_HALT = 0, -+ VC4_PACKET_NOP = 1, - -- VC4_PACKET_FLUSH = 4, -- VC4_PACKET_FLUSH_ALL = 5, -- VC4_PACKET_START_TILE_BINNING = 6, -- VC4_PACKET_INCREMENT_SEMAPHORE = 7, -- VC4_PACKET_WAIT_ON_SEMAPHORE = 8, -- -- VC4_PACKET_BRANCH = 16, -- VC4_PACKET_BRANCH_TO_SUB_LIST = 17, -- -- VC4_PACKET_STORE_MS_TILE_BUFFER = 24, -- VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF = 25, -- VC4_PACKET_STORE_FULL_RES_TILE_BUFFER = 26, -- VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER = 27, -- VC4_PACKET_STORE_TILE_BUFFER_GENERAL = 28, -- VC4_PACKET_LOAD_TILE_BUFFER_GENERAL = 29, -- -- VC4_PACKET_GL_INDEXED_PRIMITIVE = 32, -- VC4_PACKET_GL_ARRAY_PRIMITIVE = 33, -- -- VC4_PACKET_COMPRESSED_PRIMITIVE = 48, -- VC4_PACKET_CLIPPED_COMPRESSED_PRIMITIVE = 49, -- -- VC4_PACKET_PRIMITIVE_LIST_FORMAT = 56, -- -- VC4_PACKET_GL_SHADER_STATE = 64, -- VC4_PACKET_NV_SHADER_STATE = 65, -- VC4_PACKET_VG_SHADER_STATE = 66, -- -- VC4_PACKET_CONFIGURATION_BITS = 96, -- VC4_PACKET_FLAT_SHADE_FLAGS = 97, -- VC4_PACKET_POINT_SIZE = 98, -- VC4_PACKET_LINE_WIDTH = 99, -- VC4_PACKET_RHT_X_BOUNDARY = 100, -- VC4_PACKET_DEPTH_OFFSET = 101, -- VC4_PACKET_CLIP_WINDOW = 102, -- VC4_PACKET_VIEWPORT_OFFSET = 103, -- VC4_PACKET_Z_CLIPPING = 104, -- VC4_PACKET_CLIPPER_XY_SCALING = 105, -- VC4_PACKET_CLIPPER_Z_SCALING = 106, -- -- VC4_PACKET_TILE_BINNING_MODE_CONFIG = 112, -- VC4_PACKET_TILE_RENDERING_MODE_CONFIG = 113, -- VC4_PACKET_CLEAR_COLORS = 114, -- VC4_PACKET_TILE_COORDINATES = 115, -- -- /* Not an actual hardware packet -- this is what we use to put -- * references to GEM bos in the command stream, since we need the u32 -- * int the actual address packet in order to store the offset from the -- * start of the BO. -- */ -- VC4_PACKET_GEM_HANDLES = 254, -+ VC4_PACKET_FLUSH = 4, -+ VC4_PACKET_FLUSH_ALL = 5, -+ VC4_PACKET_START_TILE_BINNING = 6, -+ VC4_PACKET_INCREMENT_SEMAPHORE = 7, -+ VC4_PACKET_WAIT_ON_SEMAPHORE = 8, -+ -+ VC4_PACKET_BRANCH = 16, -+ VC4_PACKET_BRANCH_TO_SUB_LIST = 17, -+ -+ VC4_PACKET_STORE_MS_TILE_BUFFER = 24, -+ VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF = 25, -+ VC4_PACKET_STORE_FULL_RES_TILE_BUFFER = 26, -+ VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER = 27, -+ VC4_PACKET_STORE_TILE_BUFFER_GENERAL = 28, -+ VC4_PACKET_LOAD_TILE_BUFFER_GENERAL = 29, -+ -+ VC4_PACKET_GL_INDEXED_PRIMITIVE = 32, -+ VC4_PACKET_GL_ARRAY_PRIMITIVE = 33, -+ -+ VC4_PACKET_COMPRESSED_PRIMITIVE = 48, -+ VC4_PACKET_CLIPPED_COMPRESSED_PRIMITIVE = 49, -+ -+ VC4_PACKET_PRIMITIVE_LIST_FORMAT = 56, -+ -+ VC4_PACKET_GL_SHADER_STATE = 64, -+ VC4_PACKET_NV_SHADER_STATE = 65, -+ VC4_PACKET_VG_SHADER_STATE = 66, -+ -+ VC4_PACKET_CONFIGURATION_BITS = 96, -+ VC4_PACKET_FLAT_SHADE_FLAGS = 97, -+ VC4_PACKET_POINT_SIZE = 98, -+ VC4_PACKET_LINE_WIDTH = 99, -+ VC4_PACKET_RHT_X_BOUNDARY = 100, -+ VC4_PACKET_DEPTH_OFFSET = 101, -+ VC4_PACKET_CLIP_WINDOW = 102, -+ VC4_PACKET_VIEWPORT_OFFSET = 103, -+ VC4_PACKET_Z_CLIPPING = 104, -+ VC4_PACKET_CLIPPER_XY_SCALING = 105, -+ VC4_PACKET_CLIPPER_Z_SCALING = 106, -+ -+ VC4_PACKET_TILE_BINNING_MODE_CONFIG = 112, -+ VC4_PACKET_TILE_RENDERING_MODE_CONFIG = 113, -+ VC4_PACKET_CLEAR_COLORS = 114, -+ VC4_PACKET_TILE_COORDINATES = 115, -+ -+ /* Not an actual hardware packet -- this is what we use to put -+ * references to GEM bos in the command stream, since we need the u32 -+ * int the actual address packet in order to store the offset from the -+ * start of the BO. -+ */ -+ VC4_PACKET_GEM_HANDLES = 254, - } __attribute__ ((__packed__)); - - #define VC4_PACKET_HALT_SIZE 1 -@@ -148,10 +148,10 @@ enum vc4_packet { - * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL (low bits of the address) - */ - --#define VC4_LOADSTORE_TILE_BUFFER_EOF (1 << 3) --#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_VG_MASK (1 << 2) --#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_ZS (1 << 1) --#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_COLOR (1 << 0) -+#define VC4_LOADSTORE_TILE_BUFFER_EOF BIT(3) -+#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_VG_MASK BIT(2) -+#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_ZS BIT(1) -+#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_COLOR BIT(0) - - /** @} */ - -@@ -160,10 +160,10 @@ enum vc4_packet { - * byte 0-1 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and - * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL - */ --#define VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR (1 << 15) --#define VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR (1 << 14) --#define VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR (1 << 13) --#define VC4_STORE_TILE_BUFFER_DISABLE_SWAP (1 << 12) -+#define VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR BIT(15) -+#define VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR BIT(14) -+#define VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR BIT(13) -+#define VC4_STORE_TILE_BUFFER_DISABLE_SWAP BIT(12) - - #define VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK VC4_MASK(9, 8) - #define VC4_LOADSTORE_TILE_BUFFER_FORMAT_SHIFT 8 -@@ -201,28 +201,28 @@ enum vc4_packet { - #define VC4_INDEX_BUFFER_U16 (1 << 4) - - /* This flag is only present in NV shader state. */ --#define VC4_SHADER_FLAG_SHADED_CLIP_COORDS (1 << 3) --#define VC4_SHADER_FLAG_ENABLE_CLIPPING (1 << 2) --#define VC4_SHADER_FLAG_VS_POINT_SIZE (1 << 1) --#define VC4_SHADER_FLAG_FS_SINGLE_THREAD (1 << 0) -+#define VC4_SHADER_FLAG_SHADED_CLIP_COORDS BIT(3) -+#define VC4_SHADER_FLAG_ENABLE_CLIPPING BIT(2) -+#define VC4_SHADER_FLAG_VS_POINT_SIZE BIT(1) -+#define VC4_SHADER_FLAG_FS_SINGLE_THREAD BIT(0) - - /** @{ byte 2 of config bits. */ --#define VC4_CONFIG_BITS_EARLY_Z_UPDATE (1 << 1) --#define VC4_CONFIG_BITS_EARLY_Z (1 << 0) -+#define VC4_CONFIG_BITS_EARLY_Z_UPDATE BIT(1) -+#define VC4_CONFIG_BITS_EARLY_Z BIT(0) - /** @} */ - - /** @{ byte 1 of config bits. */ --#define VC4_CONFIG_BITS_Z_UPDATE (1 << 7) -+#define VC4_CONFIG_BITS_Z_UPDATE BIT(7) - /** same values in this 3-bit field as PIPE_FUNC_* */ - #define VC4_CONFIG_BITS_DEPTH_FUNC_SHIFT 4 --#define VC4_CONFIG_BITS_COVERAGE_READ_LEAVE (1 << 3) -+#define VC4_CONFIG_BITS_COVERAGE_READ_LEAVE BIT(3) - - #define VC4_CONFIG_BITS_COVERAGE_UPDATE_NONZERO (0 << 1) - #define VC4_CONFIG_BITS_COVERAGE_UPDATE_ODD (1 << 1) - #define VC4_CONFIG_BITS_COVERAGE_UPDATE_OR (2 << 1) - #define VC4_CONFIG_BITS_COVERAGE_UPDATE_ZERO (3 << 1) - --#define VC4_CONFIG_BITS_COVERAGE_PIPE_SELECT (1 << 0) -+#define VC4_CONFIG_BITS_COVERAGE_PIPE_SELECT BIT(0) - /** @} */ - - /** @{ byte 0 of config bits. */ -@@ -230,15 +230,15 @@ enum vc4_packet { - #define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_4X (1 << 6) - #define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_16X (2 << 6) - --#define VC4_CONFIG_BITS_AA_POINTS_AND_LINES (1 << 4) --#define VC4_CONFIG_BITS_ENABLE_DEPTH_OFFSET (1 << 3) --#define VC4_CONFIG_BITS_CW_PRIMITIVES (1 << 2) --#define VC4_CONFIG_BITS_ENABLE_PRIM_BACK (1 << 1) --#define VC4_CONFIG_BITS_ENABLE_PRIM_FRONT (1 << 0) -+#define VC4_CONFIG_BITS_AA_POINTS_AND_LINES BIT(4) -+#define VC4_CONFIG_BITS_ENABLE_DEPTH_OFFSET BIT(3) -+#define VC4_CONFIG_BITS_CW_PRIMITIVES BIT(2) -+#define VC4_CONFIG_BITS_ENABLE_PRIM_BACK BIT(1) -+#define VC4_CONFIG_BITS_ENABLE_PRIM_FRONT BIT(0) - /** @} */ - - /** @{ bits in the last u8 of VC4_PACKET_TILE_BINNING_MODE_CONFIG */ --#define VC4_BIN_CONFIG_DB_NON_MS (1 << 7) -+#define VC4_BIN_CONFIG_DB_NON_MS BIT(7) - - #define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_MASK VC4_MASK(6, 5) - #define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_SHIFT 5 -@@ -254,17 +254,17 @@ enum vc4_packet { - #define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_128 2 - #define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_256 3 - --#define VC4_BIN_CONFIG_AUTO_INIT_TSDA (1 << 2) --#define VC4_BIN_CONFIG_TILE_BUFFER_64BIT (1 << 1) --#define VC4_BIN_CONFIG_MS_MODE_4X (1 << 0) -+#define VC4_BIN_CONFIG_AUTO_INIT_TSDA BIT(2) -+#define VC4_BIN_CONFIG_TILE_BUFFER_64BIT BIT(1) -+#define VC4_BIN_CONFIG_MS_MODE_4X BIT(0) - /** @} */ - - /** @{ bits in the last u16 of VC4_PACKET_TILE_RENDERING_MODE_CONFIG */ --#define VC4_RENDER_CONFIG_DB_NON_MS (1 << 12) --#define VC4_RENDER_CONFIG_EARLY_Z_COVERAGE_DISABLE (1 << 11) --#define VC4_RENDER_CONFIG_EARLY_Z_DIRECTION_G (1 << 10) --#define VC4_RENDER_CONFIG_COVERAGE_MODE (1 << 9) --#define VC4_RENDER_CONFIG_ENABLE_VG_MASK (1 << 8) -+#define VC4_RENDER_CONFIG_DB_NON_MS BIT(12) -+#define VC4_RENDER_CONFIG_EARLY_Z_COVERAGE_DISABLE BIT(11) -+#define VC4_RENDER_CONFIG_EARLY_Z_DIRECTION_G BIT(10) -+#define VC4_RENDER_CONFIG_COVERAGE_MODE BIT(9) -+#define VC4_RENDER_CONFIG_ENABLE_VG_MASK BIT(8) - - /** The values of the field are VC4_TILING_FORMAT_* */ - #define VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK VC4_MASK(7, 6) -@@ -280,8 +280,8 @@ enum vc4_packet { - #define VC4_RENDER_CONFIG_FORMAT_RGBA8888 1 - #define VC4_RENDER_CONFIG_FORMAT_BGR565 2 - --#define VC4_RENDER_CONFIG_TILE_BUFFER_64BIT (1 << 1) --#define VC4_RENDER_CONFIG_MS_MODE_4X (1 << 0) -+#define VC4_RENDER_CONFIG_TILE_BUFFER_64BIT BIT(1) -+#define VC4_RENDER_CONFIG_MS_MODE_4X BIT(0) - - #define VC4_PRIMITIVE_LIST_FORMAT_16_INDEX (1 << 4) - #define VC4_PRIMITIVE_LIST_FORMAT_32_XY (3 << 4) -@@ -291,24 +291,24 @@ enum vc4_packet { - #define VC4_PRIMITIVE_LIST_FORMAT_TYPE_RHT (3 << 0) - - enum vc4_texture_data_type { -- VC4_TEXTURE_TYPE_RGBA8888 = 0, -- VC4_TEXTURE_TYPE_RGBX8888 = 1, -- VC4_TEXTURE_TYPE_RGBA4444 = 2, -- VC4_TEXTURE_TYPE_RGBA5551 = 3, -- VC4_TEXTURE_TYPE_RGB565 = 4, -- VC4_TEXTURE_TYPE_LUMINANCE = 5, -- VC4_TEXTURE_TYPE_ALPHA = 6, -- VC4_TEXTURE_TYPE_LUMALPHA = 7, -- VC4_TEXTURE_TYPE_ETC1 = 8, -- VC4_TEXTURE_TYPE_S16F = 9, -- VC4_TEXTURE_TYPE_S8 = 10, -- VC4_TEXTURE_TYPE_S16 = 11, -- VC4_TEXTURE_TYPE_BW1 = 12, -- VC4_TEXTURE_TYPE_A4 = 13, -- VC4_TEXTURE_TYPE_A1 = 14, -- VC4_TEXTURE_TYPE_RGBA64 = 15, -- VC4_TEXTURE_TYPE_RGBA32R = 16, -- VC4_TEXTURE_TYPE_YUV422R = 17, -+ VC4_TEXTURE_TYPE_RGBA8888 = 0, -+ VC4_TEXTURE_TYPE_RGBX8888 = 1, -+ VC4_TEXTURE_TYPE_RGBA4444 = 2, -+ VC4_TEXTURE_TYPE_RGBA5551 = 3, -+ VC4_TEXTURE_TYPE_RGB565 = 4, -+ VC4_TEXTURE_TYPE_LUMINANCE = 5, -+ VC4_TEXTURE_TYPE_ALPHA = 6, -+ VC4_TEXTURE_TYPE_LUMALPHA = 7, -+ VC4_TEXTURE_TYPE_ETC1 = 8, -+ VC4_TEXTURE_TYPE_S16F = 9, -+ VC4_TEXTURE_TYPE_S8 = 10, -+ VC4_TEXTURE_TYPE_S16 = 11, -+ VC4_TEXTURE_TYPE_BW1 = 12, -+ VC4_TEXTURE_TYPE_A4 = 13, -+ VC4_TEXTURE_TYPE_A1 = 14, -+ VC4_TEXTURE_TYPE_RGBA64 = 15, -+ VC4_TEXTURE_TYPE_RGBA32R = 16, -+ VC4_TEXTURE_TYPE_YUV422R = 17, - }; - - #define VC4_TEX_P0_OFFSET_MASK VC4_MASK(31, 12) ---- a/drivers/gpu/drm/vc4/vc4_qpu_defines.h -+++ b/drivers/gpu/drm/vc4/vc4_qpu_defines.h -@@ -25,194 +25,190 @@ - #define VC4_QPU_DEFINES_H - - enum qpu_op_add { -- QPU_A_NOP, -- QPU_A_FADD, -- QPU_A_FSUB, -- QPU_A_FMIN, -- QPU_A_FMAX, -- QPU_A_FMINABS, -- QPU_A_FMAXABS, -- QPU_A_FTOI, -- QPU_A_ITOF, -- QPU_A_ADD = 12, -- QPU_A_SUB, -- QPU_A_SHR, -- QPU_A_ASR, -- QPU_A_ROR, -- QPU_A_SHL, -- QPU_A_MIN, -- QPU_A_MAX, -- QPU_A_AND, -- QPU_A_OR, -- QPU_A_XOR, -- QPU_A_NOT, -- QPU_A_CLZ, -- QPU_A_V8ADDS = 30, -- QPU_A_V8SUBS = 31, -+ QPU_A_NOP, -+ QPU_A_FADD, -+ QPU_A_FSUB, -+ QPU_A_FMIN, -+ QPU_A_FMAX, -+ QPU_A_FMINABS, -+ QPU_A_FMAXABS, -+ QPU_A_FTOI, -+ QPU_A_ITOF, -+ QPU_A_ADD = 12, -+ QPU_A_SUB, -+ QPU_A_SHR, -+ QPU_A_ASR, -+ QPU_A_ROR, -+ QPU_A_SHL, -+ QPU_A_MIN, -+ QPU_A_MAX, -+ QPU_A_AND, -+ QPU_A_OR, -+ QPU_A_XOR, -+ QPU_A_NOT, -+ QPU_A_CLZ, -+ QPU_A_V8ADDS = 30, -+ QPU_A_V8SUBS = 31, - }; - - enum qpu_op_mul { -- QPU_M_NOP, -- QPU_M_FMUL, -- QPU_M_MUL24, -- QPU_M_V8MULD, -- QPU_M_V8MIN, -- QPU_M_V8MAX, -- QPU_M_V8ADDS, -- QPU_M_V8SUBS, -+ QPU_M_NOP, -+ QPU_M_FMUL, -+ QPU_M_MUL24, -+ QPU_M_V8MULD, -+ QPU_M_V8MIN, -+ QPU_M_V8MAX, -+ QPU_M_V8ADDS, -+ QPU_M_V8SUBS, - }; - - enum qpu_raddr { -- QPU_R_FRAG_PAYLOAD_ZW = 15, /* W for A file, Z for B file */ -- /* 0-31 are the plain regfile a or b fields */ -- QPU_R_UNIF = 32, -- QPU_R_VARY = 35, -- QPU_R_ELEM_QPU = 38, -- QPU_R_NOP, -- QPU_R_XY_PIXEL_COORD = 41, -- QPU_R_MS_REV_FLAGS = 41, -- QPU_R_VPM = 48, -- QPU_R_VPM_LD_BUSY, -- QPU_R_VPM_LD_WAIT, -- QPU_R_MUTEX_ACQUIRE, -+ QPU_R_FRAG_PAYLOAD_ZW = 15, /* W for A file, Z for B file */ -+ /* 0-31 are the plain regfile a or b fields */ -+ QPU_R_UNIF = 32, -+ QPU_R_VARY = 35, -+ QPU_R_ELEM_QPU = 38, -+ QPU_R_NOP, -+ QPU_R_XY_PIXEL_COORD = 41, -+ QPU_R_MS_REV_FLAGS = 41, -+ QPU_R_VPM = 48, -+ QPU_R_VPM_LD_BUSY, -+ QPU_R_VPM_LD_WAIT, -+ QPU_R_MUTEX_ACQUIRE, - }; - - enum qpu_waddr { -- /* 0-31 are the plain regfile a or b fields */ -- QPU_W_ACC0 = 32, /* aka r0 */ -- QPU_W_ACC1, -- QPU_W_ACC2, -- QPU_W_ACC3, -- QPU_W_TMU_NOSWAP, -- QPU_W_ACC5, -- QPU_W_HOST_INT, -- QPU_W_NOP, -- QPU_W_UNIFORMS_ADDRESS, -- QPU_W_QUAD_XY, /* X for regfile a, Y for regfile b */ -- QPU_W_MS_FLAGS = 42, -- QPU_W_REV_FLAG = 42, -- QPU_W_TLB_STENCIL_SETUP = 43, -- QPU_W_TLB_Z, -- QPU_W_TLB_COLOR_MS, -- QPU_W_TLB_COLOR_ALL, -- QPU_W_TLB_ALPHA_MASK, -- QPU_W_VPM, -- QPU_W_VPMVCD_SETUP, /* LD for regfile a, ST for regfile b */ -- QPU_W_VPM_ADDR, /* LD for regfile a, ST for regfile b */ -- QPU_W_MUTEX_RELEASE, -- QPU_W_SFU_RECIP, -- QPU_W_SFU_RECIPSQRT, -- QPU_W_SFU_EXP, -- QPU_W_SFU_LOG, -- QPU_W_TMU0_S, -- QPU_W_TMU0_T, -- QPU_W_TMU0_R, -- QPU_W_TMU0_B, -- QPU_W_TMU1_S, -- QPU_W_TMU1_T, -- QPU_W_TMU1_R, -- QPU_W_TMU1_B, -+ /* 0-31 are the plain regfile a or b fields */ -+ QPU_W_ACC0 = 32, /* aka r0 */ -+ QPU_W_ACC1, -+ QPU_W_ACC2, -+ QPU_W_ACC3, -+ QPU_W_TMU_NOSWAP, -+ QPU_W_ACC5, -+ QPU_W_HOST_INT, -+ QPU_W_NOP, -+ QPU_W_UNIFORMS_ADDRESS, -+ QPU_W_QUAD_XY, /* X for regfile a, Y for regfile b */ -+ QPU_W_MS_FLAGS = 42, -+ QPU_W_REV_FLAG = 42, -+ QPU_W_TLB_STENCIL_SETUP = 43, -+ QPU_W_TLB_Z, -+ QPU_W_TLB_COLOR_MS, -+ QPU_W_TLB_COLOR_ALL, -+ QPU_W_TLB_ALPHA_MASK, -+ QPU_W_VPM, -+ QPU_W_VPMVCD_SETUP, /* LD for regfile a, ST for regfile b */ -+ QPU_W_VPM_ADDR, /* LD for regfile a, ST for regfile b */ -+ QPU_W_MUTEX_RELEASE, -+ QPU_W_SFU_RECIP, -+ QPU_W_SFU_RECIPSQRT, -+ QPU_W_SFU_EXP, -+ QPU_W_SFU_LOG, -+ QPU_W_TMU0_S, -+ QPU_W_TMU0_T, -+ QPU_W_TMU0_R, -+ QPU_W_TMU0_B, -+ QPU_W_TMU1_S, -+ QPU_W_TMU1_T, -+ QPU_W_TMU1_R, -+ QPU_W_TMU1_B, - }; - - enum qpu_sig_bits { -- QPU_SIG_SW_BREAKPOINT, -- QPU_SIG_NONE, -- QPU_SIG_THREAD_SWITCH, -- QPU_SIG_PROG_END, -- QPU_SIG_WAIT_FOR_SCOREBOARD, -- QPU_SIG_SCOREBOARD_UNLOCK, -- QPU_SIG_LAST_THREAD_SWITCH, -- QPU_SIG_COVERAGE_LOAD, -- QPU_SIG_COLOR_LOAD, -- QPU_SIG_COLOR_LOAD_END, -- QPU_SIG_LOAD_TMU0, -- QPU_SIG_LOAD_TMU1, -- QPU_SIG_ALPHA_MASK_LOAD, -- QPU_SIG_SMALL_IMM, -- QPU_SIG_LOAD_IMM, -- QPU_SIG_BRANCH -+ QPU_SIG_SW_BREAKPOINT, -+ QPU_SIG_NONE, -+ QPU_SIG_THREAD_SWITCH, -+ QPU_SIG_PROG_END, -+ QPU_SIG_WAIT_FOR_SCOREBOARD, -+ QPU_SIG_SCOREBOARD_UNLOCK, -+ QPU_SIG_LAST_THREAD_SWITCH, -+ QPU_SIG_COVERAGE_LOAD, -+ QPU_SIG_COLOR_LOAD, -+ QPU_SIG_COLOR_LOAD_END, -+ QPU_SIG_LOAD_TMU0, -+ QPU_SIG_LOAD_TMU1, -+ QPU_SIG_ALPHA_MASK_LOAD, -+ QPU_SIG_SMALL_IMM, -+ QPU_SIG_LOAD_IMM, -+ QPU_SIG_BRANCH - }; - - enum qpu_mux { -- /* hardware mux values */ -- QPU_MUX_R0, -- QPU_MUX_R1, -- QPU_MUX_R2, -- QPU_MUX_R3, -- QPU_MUX_R4, -- QPU_MUX_R5, -- QPU_MUX_A, -- QPU_MUX_B, -+ /* hardware mux values */ -+ QPU_MUX_R0, -+ QPU_MUX_R1, -+ QPU_MUX_R2, -+ QPU_MUX_R3, -+ QPU_MUX_R4, -+ QPU_MUX_R5, -+ QPU_MUX_A, -+ QPU_MUX_B, - -- /* non-hardware mux values */ -- QPU_MUX_IMM, -+ /* non-hardware mux values */ -+ QPU_MUX_IMM, - }; - - enum qpu_cond { -- QPU_COND_NEVER, -- QPU_COND_ALWAYS, -- QPU_COND_ZS, -- QPU_COND_ZC, -- QPU_COND_NS, -- QPU_COND_NC, -- QPU_COND_CS, -- QPU_COND_CC, -+ QPU_COND_NEVER, -+ QPU_COND_ALWAYS, -+ QPU_COND_ZS, -+ QPU_COND_ZC, -+ QPU_COND_NS, -+ QPU_COND_NC, -+ QPU_COND_CS, -+ QPU_COND_CC, - }; - - enum qpu_pack_mul { -- QPU_PACK_MUL_NOP, -- QPU_PACK_MUL_8888 = 3, /* replicated to each 8 bits of the 32-bit dst. */ -- QPU_PACK_MUL_8A, -- QPU_PACK_MUL_8B, -- QPU_PACK_MUL_8C, -- QPU_PACK_MUL_8D, -+ QPU_PACK_MUL_NOP, -+ /* replicated to each 8 bits of the 32-bit dst. */ -+ QPU_PACK_MUL_8888 = 3, -+ QPU_PACK_MUL_8A, -+ QPU_PACK_MUL_8B, -+ QPU_PACK_MUL_8C, -+ QPU_PACK_MUL_8D, - }; - - enum qpu_pack_a { -- QPU_PACK_A_NOP, -- /* convert to 16 bit float if float input, or to int16. */ -- QPU_PACK_A_16A, -- QPU_PACK_A_16B, -- /* replicated to each 8 bits of the 32-bit dst. */ -- QPU_PACK_A_8888, -- /* Convert to 8-bit unsigned int. */ -- QPU_PACK_A_8A, -- QPU_PACK_A_8B, -- QPU_PACK_A_8C, -- QPU_PACK_A_8D, -- -- /* Saturating variants of the previous instructions. */ -- QPU_PACK_A_32_SAT, /* int-only */ -- QPU_PACK_A_16A_SAT, /* int or float */ -- QPU_PACK_A_16B_SAT, -- QPU_PACK_A_8888_SAT, -- QPU_PACK_A_8A_SAT, -- QPU_PACK_A_8B_SAT, -- QPU_PACK_A_8C_SAT, -- QPU_PACK_A_8D_SAT, -+ QPU_PACK_A_NOP, -+ /* convert to 16 bit float if float input, or to int16. */ -+ QPU_PACK_A_16A, -+ QPU_PACK_A_16B, -+ /* replicated to each 8 bits of the 32-bit dst. */ -+ QPU_PACK_A_8888, -+ /* Convert to 8-bit unsigned int. */ -+ QPU_PACK_A_8A, -+ QPU_PACK_A_8B, -+ QPU_PACK_A_8C, -+ QPU_PACK_A_8D, -+ -+ /* Saturating variants of the previous instructions. */ -+ QPU_PACK_A_32_SAT, /* int-only */ -+ QPU_PACK_A_16A_SAT, /* int or float */ -+ QPU_PACK_A_16B_SAT, -+ QPU_PACK_A_8888_SAT, -+ QPU_PACK_A_8A_SAT, -+ QPU_PACK_A_8B_SAT, -+ QPU_PACK_A_8C_SAT, -+ QPU_PACK_A_8D_SAT, - }; - - enum qpu_unpack_r4 { -- QPU_UNPACK_R4_NOP, -- QPU_UNPACK_R4_F16A_TO_F32, -- QPU_UNPACK_R4_F16B_TO_F32, -- QPU_UNPACK_R4_8D_REP, -- QPU_UNPACK_R4_8A, -- QPU_UNPACK_R4_8B, -- QPU_UNPACK_R4_8C, -- QPU_UNPACK_R4_8D, --}; -- --#define QPU_MASK(high, low) ((((uint64_t)1<<((high)-(low)+1))-1)<<(low)) --/* Using the GNU statement expression extension */ --#define QPU_SET_FIELD(value, field) \ -- ({ \ -- uint64_t fieldval = (uint64_t)(value) << field ## _SHIFT; \ -- assert((fieldval & ~ field ## _MASK) == 0); \ -- fieldval & field ## _MASK; \ -- }) -+ QPU_UNPACK_R4_NOP, -+ QPU_UNPACK_R4_F16A_TO_F32, -+ QPU_UNPACK_R4_F16B_TO_F32, -+ QPU_UNPACK_R4_8D_REP, -+ QPU_UNPACK_R4_8A, -+ QPU_UNPACK_R4_8B, -+ QPU_UNPACK_R4_8C, -+ QPU_UNPACK_R4_8D, -+}; -+ -+#define QPU_MASK(high, low) \ -+ ((((uint64_t)1 << ((high) - (low) + 1)) - 1) << (low)) - --#define QPU_GET_FIELD(word, field) ((uint32_t)(((word) & field ## _MASK) >> field ## _SHIFT)) -+#define QPU_GET_FIELD(word, field) \ -+ ((uint32_t)(((word) & field ## _MASK) >> field ## _SHIFT)) - - #define QPU_SIG_SHIFT 60 - #define QPU_SIG_MASK QPU_MASK(63, 60) ---- a/drivers/gpu/drm/vc4/vc4_render_cl.c -+++ b/drivers/gpu/drm/vc4/vc4_render_cl.c -@@ -63,7 +63,6 @@ static inline void rcl_u32(struct vc4_rc - setup->next_offset += 4; - } - -- - /* - * Emits a no-op STORE_TILE_BUFFER_GENERAL. - * -@@ -217,7 +216,7 @@ static int vc4_create_rcl_bo(struct drm_ - } - size += xtiles * ytiles * loop_body_size; - -- setup->rcl = &vc4_bo_create(dev, size)->base; -+ setup->rcl = &vc4_bo_create(dev, size, true)->base; - if (!setup->rcl) - return -ENOMEM; - list_add_tail(&to_vc4_bo(&setup->rcl->base)->unref_head, -@@ -256,6 +255,7 @@ static int vc4_create_rcl_bo(struct drm_ - for (x = min_x_tile; x <= max_x_tile; x++) { - bool first = (x == min_x_tile && y == min_y_tile); - bool last = (x == max_x_tile && y == max_y_tile); -+ - emit_tile(exec, setup, x, y, first, last); - } - } ---- a/drivers/gpu/drm/vc4/vc4_v3d.c -+++ b/drivers/gpu/drm/vc4/vc4_v3d.c -@@ -125,7 +125,7 @@ int vc4_v3d_debugfs_regs(struct seq_file - - int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused) - { -- struct drm_info_node *node = (struct drm_info_node *) m->private; -+ struct drm_info_node *node = (struct drm_info_node *)m->private; - struct drm_device *dev = node->minor->dev; - struct vc4_dev *vc4 = to_vc4_dev(dev); - uint32_t ident1 = V3D_READ(V3D_IDENT1); -@@ -133,11 +133,13 @@ int vc4_v3d_debugfs_ident(struct seq_fil - uint32_t tups = VC4_GET_FIELD(ident1, V3D_IDENT1_TUPS); - uint32_t qups = VC4_GET_FIELD(ident1, V3D_IDENT1_QUPS); - -- seq_printf(m, "Revision: %d\n", VC4_GET_FIELD(ident1, V3D_IDENT1_REV)); -+ seq_printf(m, "Revision: %d\n", -+ VC4_GET_FIELD(ident1, V3D_IDENT1_REV)); - seq_printf(m, "Slices: %d\n", nslc); - seq_printf(m, "TMUs: %d\n", nslc * tups); - seq_printf(m, "QPUs: %d\n", nslc * qups); -- seq_printf(m, "Semaphores: %d\n", VC4_GET_FIELD(ident1, V3D_IDENT1_NSEM)); -+ seq_printf(m, "Semaphores: %d\n", -+ VC4_GET_FIELD(ident1, V3D_IDENT1_NSEM)); - - return 0; - } -@@ -218,7 +220,7 @@ static int vc4_v3d_bind(struct device *d - } - - static void vc4_v3d_unbind(struct device *dev, struct device *master, -- void *data) -+ void *data) - { - struct drm_device *drm = dev_get_drvdata(master); - struct vc4_dev *vc4 = to_vc4_dev(drm); ---- a/drivers/gpu/drm/vc4/vc4_validate.c -+++ b/drivers/gpu/drm/vc4/vc4_validate.c -@@ -48,7 +48,6 @@ - void *validated, \ - void *untrusted - -- - /** Return the width in pixels of a 64-byte microtile. */ - static uint32_t - utile_width(int cpp) -@@ -192,7 +191,7 @@ vc4_check_tex_size(struct vc4_exec_info - - if (size + offset < size || - size + offset > fbo->base.size) { -- DRM_ERROR("Overflow in %dx%d (%dx%d) fbo size (%d + %d > %d)\n", -+ DRM_ERROR("Overflow in %dx%d (%dx%d) fbo size (%d + %d > %zd)\n", - width, height, - aligned_width, aligned_height, - size, offset, fbo->base.size); -@@ -278,7 +277,7 @@ validate_indexed_prim_list(VALIDATE_ARGS - - if (offset > ib->base.size || - (ib->base.size - offset) / index_size < length) { -- DRM_ERROR("IB access overflow (%d + %d*%d > %d)\n", -+ DRM_ERROR("IB access overflow (%d + %d*%d > %zd)\n", - offset, length, index_size, ib->base.size); - return -EINVAL; - } -@@ -377,6 +376,7 @@ static int - validate_tile_binning_config(VALIDATE_ARGS) - { - struct drm_device *dev = exec->exec_bo->base.dev; -+ struct vc4_bo *tile_bo; - uint8_t flags; - uint32_t tile_state_size, tile_alloc_size; - uint32_t tile_count; -@@ -438,12 +438,12 @@ validate_tile_binning_config(VALIDATE_AR - */ - tile_alloc_size += 1024 * 1024; - -- exec->tile_bo = &vc4_bo_create(dev, exec->tile_alloc_offset + -- tile_alloc_size)->base; -+ tile_bo = vc4_bo_create(dev, exec->tile_alloc_offset + tile_alloc_size, -+ true); -+ exec->tile_bo = &tile_bo->base; - if (!exec->tile_bo) - return -ENOMEM; -- list_add_tail(&to_vc4_bo(&exec->tile_bo->base)->unref_head, -- &exec->unref_list); -+ list_add_tail(&tile_bo->unref_head, &exec->unref_list); - - /* tile alloc address. */ - *(uint32_t *)(validated + 0) = (exec->tile_bo->paddr + -@@ -463,8 +463,8 @@ validate_gem_handles(VALIDATE_ARGS) - return 0; - } - --#define VC4_DEFINE_PACKET(packet, name, func) \ -- [packet] = { packet ## _SIZE, name, func } -+#define VC4_DEFINE_PACKET(packet, func) \ -+ [packet] = { packet ## _SIZE, #packet, func } - - static const struct cmd_info { - uint16_t len; -@@ -472,42 +472,43 @@ static const struct cmd_info { - int (*func)(struct vc4_exec_info *exec, void *validated, - void *untrusted); - } cmd_info[] = { -- VC4_DEFINE_PACKET(VC4_PACKET_HALT, "halt", NULL), -- VC4_DEFINE_PACKET(VC4_PACKET_NOP, "nop", NULL), -- VC4_DEFINE_PACKET(VC4_PACKET_FLUSH, "flush", NULL), -- VC4_DEFINE_PACKET(VC4_PACKET_FLUSH_ALL, "flush all state", validate_flush_all), -- VC4_DEFINE_PACKET(VC4_PACKET_START_TILE_BINNING, "start tile binning", validate_start_tile_binning), -- VC4_DEFINE_PACKET(VC4_PACKET_INCREMENT_SEMAPHORE, "increment semaphore", validate_increment_semaphore), -- -- VC4_DEFINE_PACKET(VC4_PACKET_GL_INDEXED_PRIMITIVE, "Indexed Primitive List", validate_indexed_prim_list), -- -- VC4_DEFINE_PACKET(VC4_PACKET_GL_ARRAY_PRIMITIVE, "Vertex Array Primitives", validate_gl_array_primitive), -- -- /* This is only used by clipped primitives (packets 48 and 49), which -- * we don't support parsing yet. -- */ -- VC4_DEFINE_PACKET(VC4_PACKET_PRIMITIVE_LIST_FORMAT, "primitive list format", NULL), -- -- VC4_DEFINE_PACKET(VC4_PACKET_GL_SHADER_STATE, "GL Shader State", validate_gl_shader_state), -- VC4_DEFINE_PACKET(VC4_PACKET_NV_SHADER_STATE, "NV Shader State", validate_nv_shader_state), -- -- VC4_DEFINE_PACKET(VC4_PACKET_CONFIGURATION_BITS, "configuration bits", NULL), -- VC4_DEFINE_PACKET(VC4_PACKET_FLAT_SHADE_FLAGS, "flat shade flags", NULL), -- VC4_DEFINE_PACKET(VC4_PACKET_POINT_SIZE, "point size", NULL), -- VC4_DEFINE_PACKET(VC4_PACKET_LINE_WIDTH, "line width", NULL), -- VC4_DEFINE_PACKET(VC4_PACKET_RHT_X_BOUNDARY, "RHT X boundary", NULL), -- VC4_DEFINE_PACKET(VC4_PACKET_DEPTH_OFFSET, "Depth Offset", NULL), -- VC4_DEFINE_PACKET(VC4_PACKET_CLIP_WINDOW, "Clip Window", NULL), -- VC4_DEFINE_PACKET(VC4_PACKET_VIEWPORT_OFFSET, "Viewport Offset", NULL), -- VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_XY_SCALING, "Clipper XY Scaling", NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_HALT, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_NOP, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_FLUSH, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_FLUSH_ALL, validate_flush_all), -+ VC4_DEFINE_PACKET(VC4_PACKET_START_TILE_BINNING, -+ validate_start_tile_binning), -+ VC4_DEFINE_PACKET(VC4_PACKET_INCREMENT_SEMAPHORE, -+ validate_increment_semaphore), -+ -+ VC4_DEFINE_PACKET(VC4_PACKET_GL_INDEXED_PRIMITIVE, -+ validate_indexed_prim_list), -+ VC4_DEFINE_PACKET(VC4_PACKET_GL_ARRAY_PRIMITIVE, -+ validate_gl_array_primitive), -+ -+ VC4_DEFINE_PACKET(VC4_PACKET_PRIMITIVE_LIST_FORMAT, NULL), -+ -+ VC4_DEFINE_PACKET(VC4_PACKET_GL_SHADER_STATE, validate_gl_shader_state), -+ VC4_DEFINE_PACKET(VC4_PACKET_NV_SHADER_STATE, validate_nv_shader_state), -+ -+ VC4_DEFINE_PACKET(VC4_PACKET_CONFIGURATION_BITS, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_FLAT_SHADE_FLAGS, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_POINT_SIZE, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_LINE_WIDTH, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_RHT_X_BOUNDARY, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_DEPTH_OFFSET, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_CLIP_WINDOW, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_VIEWPORT_OFFSET, NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_XY_SCALING, NULL), - /* Note: The docs say this was also 105, but it was 106 in the - * initial userland code drop. - */ -- VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_Z_SCALING, "Clipper Z Scale and Offset", NULL), -+ VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_Z_SCALING, NULL), - -- VC4_DEFINE_PACKET(VC4_PACKET_TILE_BINNING_MODE_CONFIG, "tile binning configuration", validate_tile_binning_config), -+ VC4_DEFINE_PACKET(VC4_PACKET_TILE_BINNING_MODE_CONFIG, -+ validate_tile_binning_config), - -- VC4_DEFINE_PACKET(VC4_PACKET_GEM_HANDLES, "GEM handles", validate_gem_handles), -+ VC4_DEFINE_PACKET(VC4_PACKET_GEM_HANDLES, validate_gem_handles), - }; - - int -@@ -526,7 +527,7 @@ vc4_validate_bin_cl(struct drm_device *d - u8 cmd = *(uint8_t *)src_pkt; - const struct cmd_info *info; - -- if (cmd > ARRAY_SIZE(cmd_info)) { -+ if (cmd >= ARRAY_SIZE(cmd_info)) { - DRM_ERROR("0x%08x: packet %d out of bounds\n", - src_offset, cmd); - return -EINVAL; -@@ -539,11 +540,6 @@ vc4_validate_bin_cl(struct drm_device *d - return -EINVAL; - } - --#if 0 -- DRM_INFO("0x%08x: packet %d (%s) size %d processing...\n", -- src_offset, cmd, info->name, info->len); --#endif -- - if (src_offset + info->len > len) { - DRM_ERROR("0x%08x: packet %d (%s) length 0x%08x " - "exceeds bounds (0x%08x)\n", -@@ -558,8 +554,7 @@ vc4_validate_bin_cl(struct drm_device *d - if (info->func && info->func(exec, - dst_pkt + 1, - src_pkt + 1)) { -- DRM_ERROR("0x%08x: packet %d (%s) failed to " -- "validate\n", -+ DRM_ERROR("0x%08x: packet %d (%s) failed to validate\n", - src_offset, cmd, info->name); - return -EINVAL; - } -@@ -618,12 +613,14 @@ reloc_tex(struct vc4_exec_info *exec, - - if (sample->is_direct) { - uint32_t remaining_size = tex->base.size - p0; -+ - if (p0 > tex->base.size - 4) { - DRM_ERROR("UBO offset greater than UBO size\n"); - goto fail; - } - if (p1 > remaining_size - 4) { -- DRM_ERROR("UBO clamp would allow reads outside of UBO\n"); -+ DRM_ERROR("UBO clamp would allow reads " -+ "outside of UBO\n"); - goto fail; - } - *validated_p0 = tex->paddr + p0; -@@ -786,7 +783,7 @@ validate_shader_rec(struct drm_device *d - struct drm_gem_cma_object *bo[ARRAY_SIZE(gl_relocs) + 8]; - uint32_t nr_attributes = 0, nr_fixed_relocs, nr_relocs, packet_size; - int i; -- struct vc4_validated_shader_info *validated_shader; -+ struct vc4_validated_shader_info *shader; - - if (state->packet == VC4_PACKET_NV_SHADER_STATE) { - relocs = nv_relocs; -@@ -841,12 +838,12 @@ validate_shader_rec(struct drm_device *d - else - mode = VC4_MODE_RENDER; - -- if (!vc4_use_bo(exec, src_handles[i], mode, &bo[i])) { -+ if (!vc4_use_bo(exec, src_handles[i], mode, &bo[i])) - return false; -- } - } - - for (i = 0; i < nr_fixed_relocs; i++) { -+ struct vc4_bo *vc4_bo; - uint32_t o = relocs[i].offset; - uint32_t src_offset = *(uint32_t *)(pkt_u + o); - uint32_t *texture_handles_u; -@@ -858,34 +855,34 @@ validate_shader_rec(struct drm_device *d - switch (relocs[i].type) { - case RELOC_CODE: - if (src_offset != 0) { -- DRM_ERROR("Shaders must be at offset 0 of " -- "the BO.\n"); -+ DRM_ERROR("Shaders must be at offset 0 " -+ "of the BO.\n"); - goto fail; - } - -- validated_shader = to_vc4_bo(&bo[i]->base)->validated_shader; -- if (!validated_shader) -+ vc4_bo = to_vc4_bo(&bo[i]->base); -+ shader = vc4_bo->validated_shader; -+ if (!shader) - goto fail; - -- if (validated_shader->uniforms_src_size > -- exec->uniforms_size) { -+ if (shader->uniforms_src_size > exec->uniforms_size) { - DRM_ERROR("Uniforms src buffer overflow\n"); - goto fail; - } - - texture_handles_u = exec->uniforms_u; - uniform_data_u = (texture_handles_u + -- validated_shader->num_texture_samples); -+ shader->num_texture_samples); - - memcpy(exec->uniforms_v, uniform_data_u, -- validated_shader->uniforms_size); -+ shader->uniforms_size); - - for (tex = 0; -- tex < validated_shader->num_texture_samples; -+ tex < shader->num_texture_samples; - tex++) { - if (!reloc_tex(exec, - uniform_data_u, -- &validated_shader->texture_samples[tex], -+ &shader->texture_samples[tex], - texture_handles_u[tex])) { - goto fail; - } -@@ -893,9 +890,9 @@ validate_shader_rec(struct drm_device *d - - *(uint32_t *)(pkt_v + o + 4) = exec->uniforms_p; - -- exec->uniforms_u += validated_shader->uniforms_src_size; -- exec->uniforms_v += validated_shader->uniforms_size; -- exec->uniforms_p += validated_shader->uniforms_size; -+ exec->uniforms_u += shader->uniforms_src_size; -+ exec->uniforms_v += shader->uniforms_size; -+ exec->uniforms_p += shader->uniforms_size; - - break; - -@@ -926,7 +923,8 @@ validate_shader_rec(struct drm_device *d - max_index = ((vbo->base.size - offset - attr_size) / - stride); - if (state->max_index > max_index) { -- DRM_ERROR("primitives use index %d out of supplied %d\n", -+ DRM_ERROR("primitives use index %d out of " -+ "supplied %d\n", - state->max_index, max_index); - return -EINVAL; - } ---- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c -+++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c -@@ -24,24 +24,16 @@ - /** - * DOC: Shader validator for VC4. - * -- * The VC4 has no IOMMU between it and system memory. So, a user with access -- * to execute shaders could escalate privilege by overwriting system memory -- * (using the VPM write address register in the general-purpose DMA mode) or -- * reading system memory it shouldn't (reading it as a texture, or uniform -- * data, or vertex data). -+ * The VC4 has no IOMMU between it and system memory, so a user with -+ * access to execute shaders could escalate privilege by overwriting -+ * system memory (using the VPM write address register in the -+ * general-purpose DMA mode) or reading system memory it shouldn't -+ * (reading it as a texture, or uniform data, or vertex data). - * -- * This walks over a shader starting from some offset within a BO, ensuring -- * that its accesses are appropriately bounded, and recording how many texture -- * accesses are made and where so that we can do relocations for them in the -+ * This walks over a shader BO, ensuring that its accesses are -+ * appropriately bounded, and recording how many texture accesses are -+ * made and where so that we can do relocations for them in the - * uniform stream. -- * -- * The kernel API has shaders stored in user-mapped BOs. The BOs will be -- * forcibly unmapped from the process before validation, and any cache of -- * validated state will be flushed if the mapping is faulted back in. -- * -- * Storing the shaders in BOs means that the validation process will be slow -- * due to uncached reads, but since shaders are long-lived and shader BOs are -- * never actually modified, this shouldn't be a problem. - */ - - #include "vc4_drv.h" -@@ -70,7 +62,6 @@ waddr_to_live_reg_index(uint32_t waddr, - else - return waddr; - } else if (waddr <= QPU_W_ACC3) { -- - return 64 + waddr - QPU_W_ACC0; - } else { - return ~0; -@@ -85,15 +76,14 @@ raddr_add_a_to_live_reg_index(uint64_t i - uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); - uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); - -- if (add_a == QPU_MUX_A) { -+ if (add_a == QPU_MUX_A) - return raddr_a; -- } else if (add_a == QPU_MUX_B && sig != QPU_SIG_SMALL_IMM) { -+ else if (add_a == QPU_MUX_B && sig != QPU_SIG_SMALL_IMM) - return 32 + raddr_b; -- } else if (add_a <= QPU_MUX_R3) { -+ else if (add_a <= QPU_MUX_R3) - return 64 + add_a; -- } else { -+ else - return ~0; -- } - } - - static bool -@@ -111,9 +101,9 @@ is_tmu_write(uint32_t waddr) - } - - static bool --record_validated_texture_sample(struct vc4_validated_shader_info *validated_shader, -- struct vc4_shader_validation_state *validation_state, -- int tmu) -+record_texture_sample(struct vc4_validated_shader_info *validated_shader, -+ struct vc4_shader_validation_state *validation_state, -+ int tmu) - { - uint32_t s = validated_shader->num_texture_samples; - int i; -@@ -226,8 +216,8 @@ check_tmu_write(uint64_t inst, - validated_shader->uniforms_size += 4; - - if (submit) { -- if (!record_validated_texture_sample(validated_shader, -- validation_state, tmu)) { -+ if (!record_texture_sample(validated_shader, -+ validation_state, tmu)) { - return false; - } - -@@ -238,10 +228,10 @@ check_tmu_write(uint64_t inst, - } - - static bool --check_register_write(uint64_t inst, -- struct vc4_validated_shader_info *validated_shader, -- struct vc4_shader_validation_state *validation_state, -- bool is_mul) -+check_reg_write(uint64_t inst, -+ struct vc4_validated_shader_info *validated_shader, -+ struct vc4_shader_validation_state *validation_state, -+ bool is_mul) - { - uint32_t waddr = (is_mul ? - QPU_GET_FIELD(inst, QPU_WADDR_MUL) : -@@ -297,7 +287,7 @@ check_register_write(uint64_t inst, - return true; - - case QPU_W_TLB_STENCIL_SETUP: -- return true; -+ return true; - } - - return true; -@@ -360,7 +350,7 @@ track_live_clamps(uint64_t inst, - } - - validation_state->live_max_clamp_regs[lri_add] = true; -- } if (op_add == QPU_A_MIN) { -+ } else if (op_add == QPU_A_MIN) { - /* Track live clamps of a value clamped to a minimum of 0 and - * a maximum of some uniform's offset. - */ -@@ -392,8 +382,10 @@ check_instruction_writes(uint64_t inst, - return false; - } - -- ok = (check_register_write(inst, validated_shader, validation_state, false) && -- check_register_write(inst, validated_shader, validation_state, true)); -+ ok = (check_reg_write(inst, validated_shader, validation_state, -+ false) && -+ check_reg_write(inst, validated_shader, validation_state, -+ true)); - - track_live_clamps(inst, validated_shader, validation_state); - -@@ -441,7 +433,7 @@ vc4_validate_shader(struct drm_gem_cma_o - shader = shader_obj->vaddr; - max_ip = shader_obj->base.size / sizeof(uint64_t); - -- validated_shader = kcalloc(sizeof(*validated_shader), 1, GFP_KERNEL); -+ validated_shader = kcalloc(1, sizeof(*validated_shader), GFP_KERNEL); - if (!validated_shader) - return NULL; - -@@ -497,7 +489,7 @@ vc4_validate_shader(struct drm_gem_cma_o - - if (ip == max_ip) { - DRM_ERROR("shader failed to terminate before " -- "shader BO end at %d\n", -+ "shader BO end at %zd\n", - shader_obj->base.size); - goto fail; - } ---- a/include/drm/drmP.h -+++ b/include/drm/drmP.h -@@ -585,6 +585,13 @@ struct drm_driver { - int (*gem_open_object) (struct drm_gem_object *, struct drm_file *); - void (*gem_close_object) (struct drm_gem_object *, struct drm_file *); - -+ /** -+ * Hook for allocating the GEM object struct, for use by core -+ * helpers. -+ */ -+ struct drm_gem_object *(*gem_create_object)(struct drm_device *dev, -+ size_t size); -+ - /* prime: */ - /* export handle -> fd (see drm_gem_prime_handle_to_fd() helper) */ - int (*prime_handle_to_fd)(struct drm_device *dev, struct drm_file *file_priv, -@@ -639,7 +646,6 @@ struct drm_driver { - - u32 driver_features; - int dev_priv_size; -- size_t gem_obj_size; - const struct drm_ioctl_desc *ioctls; - int num_ioctls; - const struct file_operations *fops; diff --git a/target/linux/brcm2708/patches-4.4/0116-drm-Use-the-driver-s-gem_object_free-function-from-C.patch b/target/linux/brcm2708/patches-4.4/0116-drm-Use-the-driver-s-gem_object_free-function-from-C.patch deleted file mode 100644 index 7f1ce7b15d..0000000000 --- a/target/linux/brcm2708/patches-4.4/0116-drm-Use-the-driver-s-gem_object_free-function-from-C.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 7d4ed9f40e9484ded3965b13fbb7914a1daf85d7 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Fri, 11 Dec 2015 19:45:03 -0800 -Subject: [PATCH 116/170] drm: Use the driver's gem_object_free function from - CMA helpers. - -VC4 wraps the CMA objects in its own structures, so it needs to do its -own teardown (waiting for GPU to finish, updating bo_stats tracking). -The other CMA drivers are using drm_gem_cma_free_object as their -gem_free_object, so this should be a no-op for them. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/drm_fb_cma_helper.c | 6 +++--- - drivers/gpu/drm/drm_gem_cma_helper.c | 4 ++-- - 2 files changed, 5 insertions(+), 5 deletions(-) - ---- a/drivers/gpu/drm/drm_fb_cma_helper.c -+++ b/drivers/gpu/drm/drm_fb_cma_helper.c -@@ -266,7 +266,7 @@ static int drm_fbdev_cma_create(struct d - fbi = drm_fb_helper_alloc_fbi(helper); - if (IS_ERR(fbi)) { - ret = PTR_ERR(fbi); -- goto err_drm_gem_cma_free_object; -+ goto err_gem_free_object; - } - - fbdev_cma->fb = drm_fb_cma_alloc(dev, &mode_cmd, &obj, 1); -@@ -299,8 +299,8 @@ static int drm_fbdev_cma_create(struct d - - err_fb_info_destroy: - drm_fb_helper_release_fbi(helper); --err_drm_gem_cma_free_object: -- drm_gem_cma_free_object(&obj->base); -+err_gem_free_object: -+ dev->driver->gem_free_object(&obj->base); - return ret; - } - ---- a/drivers/gpu/drm/drm_gem_cma_helper.c -+++ b/drivers/gpu/drm/drm_gem_cma_helper.c -@@ -121,7 +121,7 @@ struct drm_gem_cma_object *drm_gem_cma_c - return cma_obj; - - error: -- drm_gem_cma_free_object(&cma_obj->base); -+ drm->driver->gem_free_object(&cma_obj->base); - return ERR_PTR(ret); - } - EXPORT_SYMBOL_GPL(drm_gem_cma_create); -@@ -171,7 +171,7 @@ drm_gem_cma_create_with_handle(struct dr - return cma_obj; - - err_handle_create: -- drm_gem_cma_free_object(gem_obj); -+ drm->driver->gem_free_object(gem_obj); - - return ERR_PTR(ret); - } diff --git a/target/linux/brcm2708/patches-4.4/0116-drm-vc4-Add-support-for-MSAA-rendering.patch b/target/linux/brcm2708/patches-4.4/0116-drm-vc4-Add-support-for-MSAA-rendering.patch new file mode 100644 index 0000000000..ffa382bf44 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0116-drm-vc4-Add-support-for-MSAA-rendering.patch @@ -0,0 +1,518 @@ +From 878a974dd326f144ba90c1cf018db604bf127835 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Fri, 17 Jul 2015 13:15:50 -0700 +Subject: [PATCH 116/232] drm/vc4: Add support for MSAA rendering. + +For MSAA, you set a bit in the binner that halves the size of tiles in +each direction, so you can pack 4 samples per pixel in the tile +buffer. During rendering, you can load and store raw tile buffer +contents (to save the per-sample MSAA contents), or you can load/store +resolved tile buffer contents (loads spam the pixel value to all 4 +samples, and stores either average the 4 color samples, or store the +first sample for Z/S). + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_packet.h | 23 ++- + drivers/gpu/drm/vc4/vc4_render_cl.c | 274 ++++++++++++++++++++++++++++++------ + drivers/gpu/drm/vc4/vc4_validate.c | 5 +- + include/uapi/drm/vc4_drm.h | 11 +- + 4 files changed, 258 insertions(+), 55 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_packet.h ++++ b/drivers/gpu/drm/vc4/vc4_packet.h +@@ -123,6 +123,11 @@ enum vc4_packet { + #define VC4_PACKET_TILE_COORDINATES_SIZE 3 + #define VC4_PACKET_GEM_HANDLES_SIZE 9 + ++/* Number of multisamples supported. */ ++#define VC4_MAX_SAMPLES 4 ++/* Size of a full resolution color or Z tile buffer load/store. */ ++#define VC4_TILE_BUFFER_SIZE (64 * 64 * 4) ++ + /** @{ + * Bits used by packets like VC4_PACKET_STORE_TILE_BUFFER_GENERAL and + * VC4_PACKET_TILE_RENDERING_MODE_CONFIG. +@@ -137,10 +142,20 @@ enum vc4_packet { + * low bits of VC4_PACKET_STORE_FULL_RES_TILE_BUFFER and + * VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER. + */ +-#define VC4_LOADSTORE_FULL_RES_EOF (1 << 3) +-#define VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL (1 << 2) +-#define VC4_LOADSTORE_FULL_RES_DISABLE_ZS (1 << 1) +-#define VC4_LOADSTORE_FULL_RES_DISABLE_COLOR (1 << 0) ++#define VC4_LOADSTORE_FULL_RES_EOF BIT(3) ++#define VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL BIT(2) ++#define VC4_LOADSTORE_FULL_RES_DISABLE_ZS BIT(1) ++#define VC4_LOADSTORE_FULL_RES_DISABLE_COLOR BIT(0) ++ ++/** @{ ++ * ++ * low bits of VC4_PACKET_STORE_FULL_RES_TILE_BUFFER and ++ * VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER. ++ */ ++#define VC4_LOADSTORE_FULL_RES_EOF BIT(3) ++#define VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL BIT(2) ++#define VC4_LOADSTORE_FULL_RES_DISABLE_ZS BIT(1) ++#define VC4_LOADSTORE_FULL_RES_DISABLE_COLOR BIT(0) + + /** @{ + * +--- a/drivers/gpu/drm/vc4/vc4_render_cl.c ++++ b/drivers/gpu/drm/vc4/vc4_render_cl.c +@@ -37,9 +37,11 @@ + + struct vc4_rcl_setup { + struct drm_gem_cma_object *color_read; +- struct drm_gem_cma_object *color_ms_write; ++ struct drm_gem_cma_object *color_write; + struct drm_gem_cma_object *zs_read; + struct drm_gem_cma_object *zs_write; ++ struct drm_gem_cma_object *msaa_color_write; ++ struct drm_gem_cma_object *msaa_zs_write; + + struct drm_gem_cma_object *rcl; + u32 next_offset; +@@ -82,6 +84,22 @@ static void vc4_store_before_load(struct + } + + /* ++ * Calculates the physical address of the start of a tile in a RCL surface. ++ * ++ * Unlike the other load/store packets, ++ * VC4_PACKET_LOAD/STORE_FULL_RES_TILE_BUFFER don't look at the tile ++ * coordinates packet, and instead just store to the address given. ++ */ ++static uint32_t vc4_full_res_offset(struct vc4_exec_info *exec, ++ struct drm_gem_cma_object *bo, ++ struct drm_vc4_submit_rcl_surface *surf, ++ uint8_t x, uint8_t y) ++{ ++ return bo->paddr + surf->offset + VC4_TILE_BUFFER_SIZE * ++ (DIV_ROUND_UP(exec->args->width, 32) * y + x); ++} ++ ++/* + * Emits a PACKET_TILE_COORDINATES if one isn't already pending. + * + * The tile coordinates packet triggers a pending load if there is one, are +@@ -108,22 +126,41 @@ static void emit_tile(struct vc4_exec_in + * may be outstanding at a time. + */ + if (setup->color_read) { +- rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); +- rcl_u16(setup, args->color_read.bits); +- rcl_u32(setup, +- setup->color_read->paddr + args->color_read.offset); ++ if (args->color_read.flags & ++ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { ++ rcl_u8(setup, VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER); ++ rcl_u32(setup, ++ vc4_full_res_offset(exec, setup->color_read, ++ &args->color_read, x, y) | ++ VC4_LOADSTORE_FULL_RES_DISABLE_ZS); ++ } else { ++ rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); ++ rcl_u16(setup, args->color_read.bits); ++ rcl_u32(setup, setup->color_read->paddr + ++ args->color_read.offset); ++ } + } + + if (setup->zs_read) { +- if (setup->color_read) { +- /* Exec previous load. */ +- vc4_tile_coordinates(setup, x, y); +- vc4_store_before_load(setup); ++ if (args->zs_read.flags & ++ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { ++ rcl_u8(setup, VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER); ++ rcl_u32(setup, ++ vc4_full_res_offset(exec, setup->zs_read, ++ &args->zs_read, x, y) | ++ VC4_LOADSTORE_FULL_RES_DISABLE_COLOR); ++ } else { ++ if (setup->color_read) { ++ /* Exec previous load. */ ++ vc4_tile_coordinates(setup, x, y); ++ vc4_store_before_load(setup); ++ } ++ ++ rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); ++ rcl_u16(setup, args->zs_read.bits); ++ rcl_u32(setup, setup->zs_read->paddr + ++ args->zs_read.offset); + } +- +- rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); +- rcl_u16(setup, args->zs_read.bits); +- rcl_u32(setup, setup->zs_read->paddr + args->zs_read.offset); + } + + /* Clipping depends on tile coordinates having been +@@ -144,20 +181,60 @@ static void emit_tile(struct vc4_exec_in + (y * exec->bin_tiles_x + x) * 32)); + } + ++ if (setup->msaa_color_write) { ++ bool last_tile_write = (!setup->msaa_zs_write && ++ !setup->zs_write && ++ !setup->color_write); ++ uint32_t bits = VC4_LOADSTORE_FULL_RES_DISABLE_ZS; ++ ++ if (!last_tile_write) ++ bits |= VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL; ++ else if (last) ++ bits |= VC4_LOADSTORE_FULL_RES_EOF; ++ rcl_u8(setup, VC4_PACKET_STORE_FULL_RES_TILE_BUFFER); ++ rcl_u32(setup, ++ vc4_full_res_offset(exec, setup->msaa_color_write, ++ &args->msaa_color_write, x, y) | ++ bits); ++ } ++ ++ if (setup->msaa_zs_write) { ++ bool last_tile_write = (!setup->zs_write && ++ !setup->color_write); ++ uint32_t bits = VC4_LOADSTORE_FULL_RES_DISABLE_COLOR; ++ ++ if (setup->msaa_color_write) ++ vc4_tile_coordinates(setup, x, y); ++ if (!last_tile_write) ++ bits |= VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL; ++ else if (last) ++ bits |= VC4_LOADSTORE_FULL_RES_EOF; ++ rcl_u8(setup, VC4_PACKET_STORE_FULL_RES_TILE_BUFFER); ++ rcl_u32(setup, ++ vc4_full_res_offset(exec, setup->msaa_zs_write, ++ &args->msaa_zs_write, x, y) | ++ bits); ++ } ++ + if (setup->zs_write) { ++ bool last_tile_write = !setup->color_write; ++ ++ if (setup->msaa_color_write || setup->msaa_zs_write) ++ vc4_tile_coordinates(setup, x, y); ++ + rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL); + rcl_u16(setup, args->zs_write.bits | +- (setup->color_ms_write ? +- VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR : 0)); ++ (last_tile_write ? ++ 0 : VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR)); + rcl_u32(setup, + (setup->zs_write->paddr + args->zs_write.offset) | +- ((last && !setup->color_ms_write) ? ++ ((last && last_tile_write) ? + VC4_LOADSTORE_TILE_BUFFER_EOF : 0)); + } + +- if (setup->color_ms_write) { +- if (setup->zs_write) { +- /* Reset after previous store */ ++ if (setup->color_write) { ++ if (setup->msaa_color_write || setup->msaa_zs_write || ++ setup->zs_write) { + vc4_tile_coordinates(setup, x, y); + } + +@@ -192,14 +269,26 @@ static int vc4_create_rcl_bo(struct drm_ + } + + if (setup->color_read) { +- loop_body_size += (VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE); ++ if (args->color_read.flags & ++ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { ++ loop_body_size += VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER_SIZE; ++ } else { ++ loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE; ++ } + } + if (setup->zs_read) { +- if (setup->color_read) { +- loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE; +- loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; ++ if (args->zs_read.flags & ++ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { ++ loop_body_size += VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER_SIZE; ++ } else { ++ if (setup->color_read && ++ !(args->color_read.flags & ++ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES)) { ++ loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE; ++ loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; ++ } ++ loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE; + } +- loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE; + } + + if (has_bin) { +@@ -207,13 +296,23 @@ static int vc4_create_rcl_bo(struct drm_ + loop_body_size += VC4_PACKET_BRANCH_TO_SUB_LIST_SIZE; + } + ++ if (setup->msaa_color_write) ++ loop_body_size += VC4_PACKET_STORE_FULL_RES_TILE_BUFFER_SIZE; ++ if (setup->msaa_zs_write) ++ loop_body_size += VC4_PACKET_STORE_FULL_RES_TILE_BUFFER_SIZE; ++ + if (setup->zs_write) + loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; +- if (setup->color_ms_write) { +- if (setup->zs_write) +- loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE; ++ if (setup->color_write) + loop_body_size += VC4_PACKET_STORE_MS_TILE_BUFFER_SIZE; +- } ++ ++ /* We need a VC4_PACKET_TILE_COORDINATES in between each store. */ ++ loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE * ++ ((setup->msaa_color_write != NULL) + ++ (setup->msaa_zs_write != NULL) + ++ (setup->color_write != NULL) + ++ (setup->zs_write != NULL) - 1); ++ + size += xtiles * ytiles * loop_body_size; + + setup->rcl = &vc4_bo_create(dev, size, true)->base; +@@ -224,13 +323,12 @@ static int vc4_create_rcl_bo(struct drm_ + + rcl_u8(setup, VC4_PACKET_TILE_RENDERING_MODE_CONFIG); + rcl_u32(setup, +- (setup->color_ms_write ? +- (setup->color_ms_write->paddr + +- args->color_ms_write.offset) : ++ (setup->color_write ? (setup->color_write->paddr + ++ args->color_write.offset) : + 0)); + rcl_u16(setup, args->width); + rcl_u16(setup, args->height); +- rcl_u16(setup, args->color_ms_write.bits); ++ rcl_u16(setup, args->color_write.bits); + + /* The tile buffer gets cleared when the previous tile is stored. If + * the clear values changed between frames, then the tile buffer has +@@ -267,6 +365,56 @@ static int vc4_create_rcl_bo(struct drm_ + return 0; + } + ++static int vc4_full_res_bounds_check(struct vc4_exec_info *exec, ++ struct drm_gem_cma_object *obj, ++ struct drm_vc4_submit_rcl_surface *surf) ++{ ++ struct drm_vc4_submit_cl *args = exec->args; ++ u32 render_tiles_stride = DIV_ROUND_UP(exec->args->width, 32); ++ ++ if (surf->offset > obj->base.size) { ++ DRM_ERROR("surface offset %d > BO size %zd\n", ++ surf->offset, obj->base.size); ++ return -EINVAL; ++ } ++ ++ if ((obj->base.size - surf->offset) / VC4_TILE_BUFFER_SIZE < ++ render_tiles_stride * args->max_y_tile + args->max_x_tile) { ++ DRM_ERROR("MSAA tile %d, %d out of bounds " ++ "(bo size %zd, offset %d).\n", ++ args->max_x_tile, args->max_y_tile, ++ obj->base.size, ++ surf->offset); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int vc4_rcl_msaa_surface_setup(struct vc4_exec_info *exec, ++ struct drm_gem_cma_object **obj, ++ struct drm_vc4_submit_rcl_surface *surf) ++{ ++ if (surf->flags != 0 || surf->bits != 0) { ++ DRM_ERROR("MSAA surface had nonzero flags/bits\n"); ++ return -EINVAL; ++ } ++ ++ if (surf->hindex == ~0) ++ return 0; ++ ++ *obj = vc4_use_bo(exec, surf->hindex); ++ if (!*obj) ++ return -EINVAL; ++ ++ if (surf->offset & 0xf) { ++ DRM_ERROR("MSAA write must be 16b aligned.\n"); ++ return -EINVAL; ++ } ++ ++ return vc4_full_res_bounds_check(exec, *obj, surf); ++} ++ + static int vc4_rcl_surface_setup(struct vc4_exec_info *exec, + struct drm_gem_cma_object **obj, + struct drm_vc4_submit_rcl_surface *surf) +@@ -278,9 +426,10 @@ static int vc4_rcl_surface_setup(struct + uint8_t format = VC4_GET_FIELD(surf->bits, + VC4_LOADSTORE_TILE_BUFFER_FORMAT); + int cpp; ++ int ret; + +- if (surf->pad != 0) { +- DRM_ERROR("Padding unset\n"); ++ if (surf->flags & ~VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { ++ DRM_ERROR("Extra flags set\n"); + return -EINVAL; + } + +@@ -290,6 +439,25 @@ static int vc4_rcl_surface_setup(struct + if (!vc4_use_bo(exec, surf->hindex, VC4_MODE_RENDER, obj)) + return -EINVAL; + ++ if (surf->flags & VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { ++ if (surf == &exec->args->zs_write) { ++ DRM_ERROR("general zs write may not be a full-res.\n"); ++ return -EINVAL; ++ } ++ ++ if (surf->bits != 0) { ++ DRM_ERROR("load/store general bits set with " ++ "full res load/store.\n"); ++ return -EINVAL; ++ } ++ ++ ret = vc4_full_res_bounds_check(exec, *obj, surf); ++ if (!ret) ++ return ret; ++ ++ return 0; ++ } ++ + if (surf->bits & ~(VC4_LOADSTORE_TILE_BUFFER_TILING_MASK | + VC4_LOADSTORE_TILE_BUFFER_BUFFER_MASK | + VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK)) { +@@ -341,9 +509,10 @@ static int vc4_rcl_surface_setup(struct + } + + static int +-vc4_rcl_ms_surface_setup(struct vc4_exec_info *exec, +- struct drm_gem_cma_object **obj, +- struct drm_vc4_submit_rcl_surface *surf) ++vc4_rcl_render_config_surface_setup(struct vc4_exec_info *exec, ++ struct vc4_rcl_setup *setup, ++ struct drm_gem_cma_object **obj, ++ struct drm_vc4_submit_rcl_surface *surf) + { + uint8_t tiling = VC4_GET_FIELD(surf->bits, + VC4_RENDER_CONFIG_MEMORY_FORMAT); +@@ -351,13 +520,15 @@ vc4_rcl_ms_surface_setup(struct vc4_exec + VC4_RENDER_CONFIG_FORMAT); + int cpp; + +- if (surf->pad != 0) { +- DRM_ERROR("Padding unset\n"); ++ if (surf->flags != 0) { ++ DRM_ERROR("No flags supported on render config.\n"); + return -EINVAL; + } + + if (surf->bits & ~(VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK | +- VC4_RENDER_CONFIG_FORMAT_MASK)) { ++ VC4_RENDER_CONFIG_FORMAT_MASK | ++ VC4_RENDER_CONFIG_MS_MODE_4X | ++ VC4_RENDER_CONFIG_DECIMATE_MODE_4X)) { + DRM_ERROR("Unknown bits in render config: 0x%04x\n", + surf->bits); + return -EINVAL; +@@ -413,18 +584,20 @@ int vc4_get_rcl(struct drm_device *dev, + if (has_bin && + (args->max_x_tile > exec->bin_tiles_x || + args->max_y_tile > exec->bin_tiles_y)) { +- DRM_ERROR("Render tiles (%d,%d) outside of bin config (%d,%d)\n", ++ DRM_ERROR("Render tiles (%d,%d) outside of bin config " ++ "(%d,%d)\n", + args->max_x_tile, args->max_y_tile, + exec->bin_tiles_x, exec->bin_tiles_y); + return -EINVAL; + } + +- ret = vc4_rcl_surface_setup(exec, &setup.color_read, &args->color_read); ++ ret = vc4_rcl_render_config_surface_setup(exec, &setup, ++ &setup.color_write, ++ &args->color_write); + if (ret) + return ret; + +- ret = vc4_rcl_ms_surface_setup(exec, &setup.color_ms_write, +- &args->color_ms_write); ++ ret = vc4_rcl_surface_setup(exec, &setup.color_read, &args->color_read); + if (ret) + return ret; + +@@ -436,10 +609,21 @@ int vc4_get_rcl(struct drm_device *dev, + if (ret) + return ret; + ++ ret = vc4_rcl_msaa_surface_setup(exec, &setup.msaa_color_write, ++ &args->msaa_color_write); ++ if (ret) ++ return ret; ++ ++ ret = vc4_rcl_msaa_surface_setup(exec, &setup.msaa_zs_write, ++ &args->msaa_zs_write); ++ if (ret) ++ return ret; ++ + /* We shouldn't even have the job submitted to us if there's no + * surface to write out. + */ +- if (!setup.color_ms_write && !setup.zs_write) { ++ if (!setup.color_write && !setup.zs_write && ++ !setup.msaa_color_write && !setup.msaa_zs_write) { + DRM_ERROR("RCL requires color or Z/S write\n"); + return -EINVAL; + } +--- a/drivers/gpu/drm/vc4/vc4_validate.c ++++ b/drivers/gpu/drm/vc4/vc4_validate.c +@@ -400,9 +400,8 @@ validate_tile_binning_config(VALIDATE_AR + } + + if (flags & (VC4_BIN_CONFIG_DB_NON_MS | +- VC4_BIN_CONFIG_TILE_BUFFER_64BIT | +- VC4_BIN_CONFIG_MS_MODE_4X)) { +- DRM_ERROR("unsupported bining config flags 0x%02x\n", flags); ++ VC4_BIN_CONFIG_TILE_BUFFER_64BIT)) { ++ DRM_ERROR("unsupported binning config flags 0x%02x\n", flags); + return -EINVAL; + } + +--- a/include/uapi/drm/vc4_drm.h ++++ b/include/uapi/drm/vc4_drm.h +@@ -46,10 +46,13 @@ struct drm_vc4_submit_rcl_surface { + uint32_t hindex; /* Handle index, or ~0 if not present. */ + uint32_t offset; /* Offset to start of buffer. */ + /* +- * Bits for either render config (color_ms_write) or load/store packet. ++ * Bits for either render config (color_write) or load/store packet. ++ * Bits should all be 0 for MSAA load/stores. + */ + uint16_t bits; +- uint16_t pad; ++ ++#define VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES (1 << 0) ++ uint16_t flags; + }; + + /** +@@ -128,9 +131,11 @@ struct drm_vc4_submit_cl { + uint8_t max_x_tile; + uint8_t max_y_tile; + struct drm_vc4_submit_rcl_surface color_read; +- struct drm_vc4_submit_rcl_surface color_ms_write; ++ struct drm_vc4_submit_rcl_surface color_write; + struct drm_vc4_submit_rcl_surface zs_read; + struct drm_vc4_submit_rcl_surface zs_write; ++ struct drm_vc4_submit_rcl_surface msaa_color_write; ++ struct drm_vc4_submit_rcl_surface msaa_zs_write; + uint32_t clear_color[2]; + uint32_t clear_z; + uint8_t clear_s; diff --git a/target/linux/brcm2708/patches-4.4/0117-drm-vc4-A-few-more-non-functional-changes-to-sync-to.patch b/target/linux/brcm2708/patches-4.4/0117-drm-vc4-A-few-more-non-functional-changes-to-sync-to.patch new file mode 100644 index 0000000000..3b5b8b42bb --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0117-drm-vc4-A-few-more-non-functional-changes-to-sync-to.patch @@ -0,0 +1,345 @@ +From 6e271c79f63541cbc2e8101a39aa2b0e8d2634c4 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Tue, 8 Dec 2015 14:00:43 -0800 +Subject: [PATCH 117/232] drm/vc4: A few more non-functional changes to sync to + upstream. + +At this point all that's left is the force-enable of HDMI connector, +and using direct firmware calls to turn on V3D instead of the generic +power domain support. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_v3d.c | 2 +- + include/uapi/drm/vc4_drm.h | 182 +++++++++++++++++++++--------------------- + 2 files changed, 92 insertions(+), 92 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_v3d.c ++++ b/drivers/gpu/drm/vc4/vc4_v3d.c +@@ -109,7 +109,7 @@ static const struct { + + int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused) + { +- struct drm_info_node *node = (struct drm_info_node *) m->private; ++ struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *dev = node->minor->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + int i; +--- a/include/uapi/drm/vc4_drm.h ++++ b/include/uapi/drm/vc4_drm.h +@@ -24,7 +24,7 @@ + #ifndef _UAPI_VC4_DRM_H_ + #define _UAPI_VC4_DRM_H_ + +-#include ++#include "drm.h" + + #define DRM_VC4_SUBMIT_CL 0x00 + #define DRM_VC4_WAIT_SEQNO 0x01 +@@ -34,25 +34,25 @@ + #define DRM_VC4_CREATE_SHADER_BO 0x05 + #define DRM_VC4_GET_HANG_STATE 0x06 + +-#define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) +-#define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) +-#define DRM_IOCTL_VC4_WAIT_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_WAIT_BO, struct drm_vc4_wait_bo) +-#define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) +-#define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) +-#define DRM_IOCTL_VC4_CREATE_SHADER_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo) +-#define DRM_IOCTL_VC4_GET_HANG_STATE DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_GET_HANG_STATE, struct drm_vc4_get_hang_state) ++#define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) ++#define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) ++#define DRM_IOCTL_VC4_WAIT_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_BO, struct drm_vc4_wait_bo) ++#define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) ++#define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) ++#define DRM_IOCTL_VC4_CREATE_SHADER_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo) ++#define DRM_IOCTL_VC4_GET_HANG_STATE DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_HANG_STATE, struct drm_vc4_get_hang_state) + + struct drm_vc4_submit_rcl_surface { +- uint32_t hindex; /* Handle index, or ~0 if not present. */ +- uint32_t offset; /* Offset to start of buffer. */ ++ __u32 hindex; /* Handle index, or ~0 if not present. */ ++ __u32 offset; /* Offset to start of buffer. */ + /* +- * Bits for either render config (color_write) or load/store packet. +- * Bits should all be 0 for MSAA load/stores. ++ * Bits for either render config (color_write) or load/store packet. ++ * Bits should all be 0 for MSAA load/stores. + */ +- uint16_t bits; ++ __u16 bits; + + #define VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES (1 << 0) +- uint16_t flags; ++ __u16 flags; + }; + + /** +@@ -76,7 +76,7 @@ struct drm_vc4_submit_cl { + * then writes out the state updates and draw calls necessary per tile + * to the tile allocation BO. + */ +- uint64_t bin_cl; ++ __u64 bin_cl; + + /* Pointer to the shader records. + * +@@ -85,16 +85,16 @@ struct drm_vc4_submit_cl { + * reference to the shader record has enough information to determine + * how many pointers are necessary (fixed number for shaders/uniforms, + * and an attribute count), so those BO indices into bo_handles are +- * just stored as uint32_ts before each shader record passed in. ++ * just stored as __u32s before each shader record passed in. + */ +- uint64_t shader_rec; ++ __u64 shader_rec; + + /* Pointer to uniform data and texture handles for the textures + * referenced by the shader. + * + * For each shader state record, there is a set of uniform data in the + * order referenced by the record (FS, VS, then CS). Each set of +- * uniform data has a uint32_t index into bo_handles per texture ++ * uniform data has a __u32 index into bo_handles per texture + * sample operation, in the order the QPU_W_TMUn_S writes appear in + * the program. Following the texture BO handle indices is the actual + * uniform data. +@@ -103,52 +103,52 @@ struct drm_vc4_submit_cl { + * because the kernel has to determine the sizes anyway during shader + * code validation. + */ +- uint64_t uniforms; +- uint64_t bo_handles; ++ __u64 uniforms; ++ __u64 bo_handles; + + /* Size in bytes of the binner command list. */ +- uint32_t bin_cl_size; ++ __u32 bin_cl_size; + /* Size in bytes of the set of shader records. */ +- uint32_t shader_rec_size; ++ __u32 shader_rec_size; + /* Number of shader records. + * + * This could just be computed from the contents of shader_records and + * the address bits of references to them from the bin CL, but it + * keeps the kernel from having to resize some allocations it makes. + */ +- uint32_t shader_rec_count; ++ __u32 shader_rec_count; + /* Size in bytes of the uniform state. */ +- uint32_t uniforms_size; ++ __u32 uniforms_size; + + /* Number of BO handles passed in (size is that times 4). */ +- uint32_t bo_handle_count; ++ __u32 bo_handle_count; + + /* RCL setup: */ +- uint16_t width; +- uint16_t height; +- uint8_t min_x_tile; +- uint8_t min_y_tile; +- uint8_t max_x_tile; +- uint8_t max_y_tile; ++ __u16 width; ++ __u16 height; ++ __u8 min_x_tile; ++ __u8 min_y_tile; ++ __u8 max_x_tile; ++ __u8 max_y_tile; + struct drm_vc4_submit_rcl_surface color_read; + struct drm_vc4_submit_rcl_surface color_write; + struct drm_vc4_submit_rcl_surface zs_read; + struct drm_vc4_submit_rcl_surface zs_write; + struct drm_vc4_submit_rcl_surface msaa_color_write; + struct drm_vc4_submit_rcl_surface msaa_zs_write; +- uint32_t clear_color[2]; +- uint32_t clear_z; +- uint8_t clear_s; ++ __u32 clear_color[2]; ++ __u32 clear_z; ++ __u8 clear_s; + +- uint32_t pad:24; ++ __u32 pad:24; + + #define VC4_SUBMIT_CL_USE_CLEAR_COLOR (1 << 0) +- uint32_t flags; ++ __u32 flags; + + /* Returned value of the seqno of this render job (for the + * wait ioctl). + */ +- uint64_t seqno; ++ __u64 seqno; + }; + + /** +@@ -159,8 +159,8 @@ struct drm_vc4_submit_cl { + * block, just return the status." + */ + struct drm_vc4_wait_seqno { +- uint64_t seqno; +- uint64_t timeout_ns; ++ __u64 seqno; ++ __u64 timeout_ns; + }; + + /** +@@ -172,9 +172,9 @@ struct drm_vc4_wait_seqno { + * completed. + */ + struct drm_vc4_wait_bo { +- uint32_t handle; +- uint32_t pad; +- uint64_t timeout_ns; ++ __u32 handle; ++ __u32 pad; ++ __u64 timeout_ns; + }; + + /** +@@ -184,11 +184,30 @@ struct drm_vc4_wait_bo { + * used in a future extension. + */ + struct drm_vc4_create_bo { +- uint32_t size; +- uint32_t flags; ++ __u32 size; ++ __u32 flags; + /** Returned GEM handle for the BO. */ +- uint32_t handle; +- uint32_t pad; ++ __u32 handle; ++ __u32 pad; ++}; ++ ++/** ++ * struct drm_vc4_mmap_bo - ioctl argument for mapping VC4 BOs. ++ * ++ * This doesn't actually perform an mmap. Instead, it returns the ++ * offset you need to use in an mmap on the DRM device node. This ++ * means that tools like valgrind end up knowing about the mapped ++ * memory. ++ * ++ * There are currently no values for the flags argument, but it may be ++ * used in a future extension. ++ */ ++struct drm_vc4_mmap_bo { ++ /** Handle for the object being mapped. */ ++ __u32 handle; ++ __u32 flags; ++ /** offset into the drm node to use for subsequent mmap call. */ ++ __u64 offset; + }; + + /** +@@ -201,43 +220,24 @@ struct drm_vc4_create_bo { + */ + struct drm_vc4_create_shader_bo { + /* Size of the data argument. */ +- uint32_t size; ++ __u32 size; + /* Flags, currently must be 0. */ +- uint32_t flags; ++ __u32 flags; + + /* Pointer to the data. */ +- uint64_t data; ++ __u64 data; + + /** Returned GEM handle for the BO. */ +- uint32_t handle; ++ __u32 handle; + /* Pad, must be 0. */ +- uint32_t pad; +-}; +- +-/** +- * struct drm_vc4_mmap_bo - ioctl argument for mapping VC4 BOs. +- * +- * This doesn't actually perform an mmap. Instead, it returns the +- * offset you need to use in an mmap on the DRM device node. This +- * means that tools like valgrind end up knowing about the mapped +- * memory. +- * +- * There are currently no values for the flags argument, but it may be +- * used in a future extension. +- */ +-struct drm_vc4_mmap_bo { +- /** Handle for the object being mapped. */ +- uint32_t handle; +- uint32_t flags; +- /** offset into the drm node to use for subsequent mmap call. */ +- uint64_t offset; ++ __u32 pad; + }; + + struct drm_vc4_get_hang_state_bo { +- uint32_t handle; +- uint32_t paddr; +- uint32_t size; +- uint32_t pad; ++ __u32 handle; ++ __u32 paddr; ++ __u32 size; ++ __u32 pad; + }; + + /** +@@ -246,34 +246,34 @@ struct drm_vc4_get_hang_state_bo { + */ + struct drm_vc4_get_hang_state { + /** Pointer to array of struct drm_vc4_get_hang_state_bo. */ +- uint64_t bo; ++ __u64 bo; + /** + * On input, the size of the bo array. Output is the number + * of bos to be returned. + */ +- uint32_t bo_count; ++ __u32 bo_count; + +- uint32_t start_bin, start_render; ++ __u32 start_bin, start_render; + +- uint32_t ct0ca, ct0ea; +- uint32_t ct1ca, ct1ea; +- uint32_t ct0cs, ct1cs; +- uint32_t ct0ra0, ct1ra0; +- +- uint32_t bpca, bpcs; +- uint32_t bpoa, bpos; +- +- uint32_t vpmbase; +- +- uint32_t dbge; +- uint32_t fdbgo; +- uint32_t fdbgb; +- uint32_t fdbgr; +- uint32_t fdbgs; +- uint32_t errstat; ++ __u32 ct0ca, ct0ea; ++ __u32 ct1ca, ct1ea; ++ __u32 ct0cs, ct1cs; ++ __u32 ct0ra0, ct1ra0; ++ ++ __u32 bpca, bpcs; ++ __u32 bpoa, bpos; ++ ++ __u32 vpmbase; ++ ++ __u32 dbge; ++ __u32 fdbgo; ++ __u32 fdbgb; ++ __u32 fdbgr; ++ __u32 fdbgs; ++ __u32 errstat; + + /* Pad that we may save more registers into in the future. */ +- uint32_t pad[16]; ++ __u32 pad[16]; + }; + + #endif /* _UAPI_VC4_DRM_H_ */ diff --git a/target/linux/brcm2708/patches-4.4/0117-drm-vc4-Add-support-for-MSAA-rendering.patch b/target/linux/brcm2708/patches-4.4/0117-drm-vc4-Add-support-for-MSAA-rendering.patch deleted file mode 100644 index 92d7eedcdb..0000000000 --- a/target/linux/brcm2708/patches-4.4/0117-drm-vc4-Add-support-for-MSAA-rendering.patch +++ /dev/null @@ -1,518 +0,0 @@ -From 522e6f9cc4376fe49e8b41b40e7ddb98cf385a9e Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Fri, 17 Jul 2015 13:15:50 -0700 -Subject: [PATCH 117/170] drm/vc4: Add support for MSAA rendering. - -For MSAA, you set a bit in the binner that halves the size of tiles in -each direction, so you can pack 4 samples per pixel in the tile -buffer. During rendering, you can load and store raw tile buffer -contents (to save the per-sample MSAA contents), or you can load/store -resolved tile buffer contents (loads spam the pixel value to all 4 -samples, and stores either average the 4 color samples, or store the -first sample for Z/S). - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/vc4_packet.h | 23 ++- - drivers/gpu/drm/vc4/vc4_render_cl.c | 274 ++++++++++++++++++++++++++++++------ - drivers/gpu/drm/vc4/vc4_validate.c | 5 +- - include/uapi/drm/vc4_drm.h | 11 +- - 4 files changed, 258 insertions(+), 55 deletions(-) - ---- a/drivers/gpu/drm/vc4/vc4_packet.h -+++ b/drivers/gpu/drm/vc4/vc4_packet.h -@@ -123,6 +123,11 @@ enum vc4_packet { - #define VC4_PACKET_TILE_COORDINATES_SIZE 3 - #define VC4_PACKET_GEM_HANDLES_SIZE 9 - -+/* Number of multisamples supported. */ -+#define VC4_MAX_SAMPLES 4 -+/* Size of a full resolution color or Z tile buffer load/store. */ -+#define VC4_TILE_BUFFER_SIZE (64 * 64 * 4) -+ - /** @{ - * Bits used by packets like VC4_PACKET_STORE_TILE_BUFFER_GENERAL and - * VC4_PACKET_TILE_RENDERING_MODE_CONFIG. -@@ -137,10 +142,20 @@ enum vc4_packet { - * low bits of VC4_PACKET_STORE_FULL_RES_TILE_BUFFER and - * VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER. - */ --#define VC4_LOADSTORE_FULL_RES_EOF (1 << 3) --#define VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL (1 << 2) --#define VC4_LOADSTORE_FULL_RES_DISABLE_ZS (1 << 1) --#define VC4_LOADSTORE_FULL_RES_DISABLE_COLOR (1 << 0) -+#define VC4_LOADSTORE_FULL_RES_EOF BIT(3) -+#define VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL BIT(2) -+#define VC4_LOADSTORE_FULL_RES_DISABLE_ZS BIT(1) -+#define VC4_LOADSTORE_FULL_RES_DISABLE_COLOR BIT(0) -+ -+/** @{ -+ * -+ * low bits of VC4_PACKET_STORE_FULL_RES_TILE_BUFFER and -+ * VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER. -+ */ -+#define VC4_LOADSTORE_FULL_RES_EOF BIT(3) -+#define VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL BIT(2) -+#define VC4_LOADSTORE_FULL_RES_DISABLE_ZS BIT(1) -+#define VC4_LOADSTORE_FULL_RES_DISABLE_COLOR BIT(0) - - /** @{ - * ---- a/drivers/gpu/drm/vc4/vc4_render_cl.c -+++ b/drivers/gpu/drm/vc4/vc4_render_cl.c -@@ -37,9 +37,11 @@ - - struct vc4_rcl_setup { - struct drm_gem_cma_object *color_read; -- struct drm_gem_cma_object *color_ms_write; -+ struct drm_gem_cma_object *color_write; - struct drm_gem_cma_object *zs_read; - struct drm_gem_cma_object *zs_write; -+ struct drm_gem_cma_object *msaa_color_write; -+ struct drm_gem_cma_object *msaa_zs_write; - - struct drm_gem_cma_object *rcl; - u32 next_offset; -@@ -82,6 +84,22 @@ static void vc4_store_before_load(struct - } - - /* -+ * Calculates the physical address of the start of a tile in a RCL surface. -+ * -+ * Unlike the other load/store packets, -+ * VC4_PACKET_LOAD/STORE_FULL_RES_TILE_BUFFER don't look at the tile -+ * coordinates packet, and instead just store to the address given. -+ */ -+static uint32_t vc4_full_res_offset(struct vc4_exec_info *exec, -+ struct drm_gem_cma_object *bo, -+ struct drm_vc4_submit_rcl_surface *surf, -+ uint8_t x, uint8_t y) -+{ -+ return bo->paddr + surf->offset + VC4_TILE_BUFFER_SIZE * -+ (DIV_ROUND_UP(exec->args->width, 32) * y + x); -+} -+ -+/* - * Emits a PACKET_TILE_COORDINATES if one isn't already pending. - * - * The tile coordinates packet triggers a pending load if there is one, are -@@ -108,22 +126,41 @@ static void emit_tile(struct vc4_exec_in - * may be outstanding at a time. - */ - if (setup->color_read) { -- rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); -- rcl_u16(setup, args->color_read.bits); -- rcl_u32(setup, -- setup->color_read->paddr + args->color_read.offset); -+ if (args->color_read.flags & -+ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { -+ rcl_u8(setup, VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER); -+ rcl_u32(setup, -+ vc4_full_res_offset(exec, setup->color_read, -+ &args->color_read, x, y) | -+ VC4_LOADSTORE_FULL_RES_DISABLE_ZS); -+ } else { -+ rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); -+ rcl_u16(setup, args->color_read.bits); -+ rcl_u32(setup, setup->color_read->paddr + -+ args->color_read.offset); -+ } - } - - if (setup->zs_read) { -- if (setup->color_read) { -- /* Exec previous load. */ -- vc4_tile_coordinates(setup, x, y); -- vc4_store_before_load(setup); -+ if (args->zs_read.flags & -+ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { -+ rcl_u8(setup, VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER); -+ rcl_u32(setup, -+ vc4_full_res_offset(exec, setup->zs_read, -+ &args->zs_read, x, y) | -+ VC4_LOADSTORE_FULL_RES_DISABLE_COLOR); -+ } else { -+ if (setup->color_read) { -+ /* Exec previous load. */ -+ vc4_tile_coordinates(setup, x, y); -+ vc4_store_before_load(setup); -+ } -+ -+ rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); -+ rcl_u16(setup, args->zs_read.bits); -+ rcl_u32(setup, setup->zs_read->paddr + -+ args->zs_read.offset); - } -- -- rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); -- rcl_u16(setup, args->zs_read.bits); -- rcl_u32(setup, setup->zs_read->paddr + args->zs_read.offset); - } - - /* Clipping depends on tile coordinates having been -@@ -144,20 +181,60 @@ static void emit_tile(struct vc4_exec_in - (y * exec->bin_tiles_x + x) * 32)); - } - -+ if (setup->msaa_color_write) { -+ bool last_tile_write = (!setup->msaa_zs_write && -+ !setup->zs_write && -+ !setup->color_write); -+ uint32_t bits = VC4_LOADSTORE_FULL_RES_DISABLE_ZS; -+ -+ if (!last_tile_write) -+ bits |= VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL; -+ else if (last) -+ bits |= VC4_LOADSTORE_FULL_RES_EOF; -+ rcl_u8(setup, VC4_PACKET_STORE_FULL_RES_TILE_BUFFER); -+ rcl_u32(setup, -+ vc4_full_res_offset(exec, setup->msaa_color_write, -+ &args->msaa_color_write, x, y) | -+ bits); -+ } -+ -+ if (setup->msaa_zs_write) { -+ bool last_tile_write = (!setup->zs_write && -+ !setup->color_write); -+ uint32_t bits = VC4_LOADSTORE_FULL_RES_DISABLE_COLOR; -+ -+ if (setup->msaa_color_write) -+ vc4_tile_coordinates(setup, x, y); -+ if (!last_tile_write) -+ bits |= VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL; -+ else if (last) -+ bits |= VC4_LOADSTORE_FULL_RES_EOF; -+ rcl_u8(setup, VC4_PACKET_STORE_FULL_RES_TILE_BUFFER); -+ rcl_u32(setup, -+ vc4_full_res_offset(exec, setup->msaa_zs_write, -+ &args->msaa_zs_write, x, y) | -+ bits); -+ } -+ - if (setup->zs_write) { -+ bool last_tile_write = !setup->color_write; -+ -+ if (setup->msaa_color_write || setup->msaa_zs_write) -+ vc4_tile_coordinates(setup, x, y); -+ - rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL); - rcl_u16(setup, args->zs_write.bits | -- (setup->color_ms_write ? -- VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR : 0)); -+ (last_tile_write ? -+ 0 : VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR)); - rcl_u32(setup, - (setup->zs_write->paddr + args->zs_write.offset) | -- ((last && !setup->color_ms_write) ? -+ ((last && last_tile_write) ? - VC4_LOADSTORE_TILE_BUFFER_EOF : 0)); - } - -- if (setup->color_ms_write) { -- if (setup->zs_write) { -- /* Reset after previous store */ -+ if (setup->color_write) { -+ if (setup->msaa_color_write || setup->msaa_zs_write || -+ setup->zs_write) { - vc4_tile_coordinates(setup, x, y); - } - -@@ -192,14 +269,26 @@ static int vc4_create_rcl_bo(struct drm_ - } - - if (setup->color_read) { -- loop_body_size += (VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE); -+ if (args->color_read.flags & -+ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { -+ loop_body_size += VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER_SIZE; -+ } else { -+ loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE; -+ } - } - if (setup->zs_read) { -- if (setup->color_read) { -- loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE; -- loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; -+ if (args->zs_read.flags & -+ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { -+ loop_body_size += VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER_SIZE; -+ } else { -+ if (setup->color_read && -+ !(args->color_read.flags & -+ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES)) { -+ loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE; -+ loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; -+ } -+ loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE; - } -- loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE; - } - - if (has_bin) { -@@ -207,13 +296,23 @@ static int vc4_create_rcl_bo(struct drm_ - loop_body_size += VC4_PACKET_BRANCH_TO_SUB_LIST_SIZE; - } - -+ if (setup->msaa_color_write) -+ loop_body_size += VC4_PACKET_STORE_FULL_RES_TILE_BUFFER_SIZE; -+ if (setup->msaa_zs_write) -+ loop_body_size += VC4_PACKET_STORE_FULL_RES_TILE_BUFFER_SIZE; -+ - if (setup->zs_write) - loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; -- if (setup->color_ms_write) { -- if (setup->zs_write) -- loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE; -+ if (setup->color_write) - loop_body_size += VC4_PACKET_STORE_MS_TILE_BUFFER_SIZE; -- } -+ -+ /* We need a VC4_PACKET_TILE_COORDINATES in between each store. */ -+ loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE * -+ ((setup->msaa_color_write != NULL) + -+ (setup->msaa_zs_write != NULL) + -+ (setup->color_write != NULL) + -+ (setup->zs_write != NULL) - 1); -+ - size += xtiles * ytiles * loop_body_size; - - setup->rcl = &vc4_bo_create(dev, size, true)->base; -@@ -224,13 +323,12 @@ static int vc4_create_rcl_bo(struct drm_ - - rcl_u8(setup, VC4_PACKET_TILE_RENDERING_MODE_CONFIG); - rcl_u32(setup, -- (setup->color_ms_write ? -- (setup->color_ms_write->paddr + -- args->color_ms_write.offset) : -+ (setup->color_write ? (setup->color_write->paddr + -+ args->color_write.offset) : - 0)); - rcl_u16(setup, args->width); - rcl_u16(setup, args->height); -- rcl_u16(setup, args->color_ms_write.bits); -+ rcl_u16(setup, args->color_write.bits); - - /* The tile buffer gets cleared when the previous tile is stored. If - * the clear values changed between frames, then the tile buffer has -@@ -267,6 +365,56 @@ static int vc4_create_rcl_bo(struct drm_ - return 0; - } - -+static int vc4_full_res_bounds_check(struct vc4_exec_info *exec, -+ struct drm_gem_cma_object *obj, -+ struct drm_vc4_submit_rcl_surface *surf) -+{ -+ struct drm_vc4_submit_cl *args = exec->args; -+ u32 render_tiles_stride = DIV_ROUND_UP(exec->args->width, 32); -+ -+ if (surf->offset > obj->base.size) { -+ DRM_ERROR("surface offset %d > BO size %zd\n", -+ surf->offset, obj->base.size); -+ return -EINVAL; -+ } -+ -+ if ((obj->base.size - surf->offset) / VC4_TILE_BUFFER_SIZE < -+ render_tiles_stride * args->max_y_tile + args->max_x_tile) { -+ DRM_ERROR("MSAA tile %d, %d out of bounds " -+ "(bo size %zd, offset %d).\n", -+ args->max_x_tile, args->max_y_tile, -+ obj->base.size, -+ surf->offset); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int vc4_rcl_msaa_surface_setup(struct vc4_exec_info *exec, -+ struct drm_gem_cma_object **obj, -+ struct drm_vc4_submit_rcl_surface *surf) -+{ -+ if (surf->flags != 0 || surf->bits != 0) { -+ DRM_ERROR("MSAA surface had nonzero flags/bits\n"); -+ return -EINVAL; -+ } -+ -+ if (surf->hindex == ~0) -+ return 0; -+ -+ *obj = vc4_use_bo(exec, surf->hindex); -+ if (!*obj) -+ return -EINVAL; -+ -+ if (surf->offset & 0xf) { -+ DRM_ERROR("MSAA write must be 16b aligned.\n"); -+ return -EINVAL; -+ } -+ -+ return vc4_full_res_bounds_check(exec, *obj, surf); -+} -+ - static int vc4_rcl_surface_setup(struct vc4_exec_info *exec, - struct drm_gem_cma_object **obj, - struct drm_vc4_submit_rcl_surface *surf) -@@ -278,9 +426,10 @@ static int vc4_rcl_surface_setup(struct - uint8_t format = VC4_GET_FIELD(surf->bits, - VC4_LOADSTORE_TILE_BUFFER_FORMAT); - int cpp; -+ int ret; - -- if (surf->pad != 0) { -- DRM_ERROR("Padding unset\n"); -+ if (surf->flags & ~VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { -+ DRM_ERROR("Extra flags set\n"); - return -EINVAL; - } - -@@ -290,6 +439,25 @@ static int vc4_rcl_surface_setup(struct - if (!vc4_use_bo(exec, surf->hindex, VC4_MODE_RENDER, obj)) - return -EINVAL; - -+ if (surf->flags & VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { -+ if (surf == &exec->args->zs_write) { -+ DRM_ERROR("general zs write may not be a full-res.\n"); -+ return -EINVAL; -+ } -+ -+ if (surf->bits != 0) { -+ DRM_ERROR("load/store general bits set with " -+ "full res load/store.\n"); -+ return -EINVAL; -+ } -+ -+ ret = vc4_full_res_bounds_check(exec, *obj, surf); -+ if (!ret) -+ return ret; -+ -+ return 0; -+ } -+ - if (surf->bits & ~(VC4_LOADSTORE_TILE_BUFFER_TILING_MASK | - VC4_LOADSTORE_TILE_BUFFER_BUFFER_MASK | - VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK)) { -@@ -341,9 +509,10 @@ static int vc4_rcl_surface_setup(struct - } - - static int --vc4_rcl_ms_surface_setup(struct vc4_exec_info *exec, -- struct drm_gem_cma_object **obj, -- struct drm_vc4_submit_rcl_surface *surf) -+vc4_rcl_render_config_surface_setup(struct vc4_exec_info *exec, -+ struct vc4_rcl_setup *setup, -+ struct drm_gem_cma_object **obj, -+ struct drm_vc4_submit_rcl_surface *surf) - { - uint8_t tiling = VC4_GET_FIELD(surf->bits, - VC4_RENDER_CONFIG_MEMORY_FORMAT); -@@ -351,13 +520,15 @@ vc4_rcl_ms_surface_setup(struct vc4_exec - VC4_RENDER_CONFIG_FORMAT); - int cpp; - -- if (surf->pad != 0) { -- DRM_ERROR("Padding unset\n"); -+ if (surf->flags != 0) { -+ DRM_ERROR("No flags supported on render config.\n"); - return -EINVAL; - } - - if (surf->bits & ~(VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK | -- VC4_RENDER_CONFIG_FORMAT_MASK)) { -+ VC4_RENDER_CONFIG_FORMAT_MASK | -+ VC4_RENDER_CONFIG_MS_MODE_4X | -+ VC4_RENDER_CONFIG_DECIMATE_MODE_4X)) { - DRM_ERROR("Unknown bits in render config: 0x%04x\n", - surf->bits); - return -EINVAL; -@@ -413,18 +584,20 @@ int vc4_get_rcl(struct drm_device *dev, - if (has_bin && - (args->max_x_tile > exec->bin_tiles_x || - args->max_y_tile > exec->bin_tiles_y)) { -- DRM_ERROR("Render tiles (%d,%d) outside of bin config (%d,%d)\n", -+ DRM_ERROR("Render tiles (%d,%d) outside of bin config " -+ "(%d,%d)\n", - args->max_x_tile, args->max_y_tile, - exec->bin_tiles_x, exec->bin_tiles_y); - return -EINVAL; - } - -- ret = vc4_rcl_surface_setup(exec, &setup.color_read, &args->color_read); -+ ret = vc4_rcl_render_config_surface_setup(exec, &setup, -+ &setup.color_write, -+ &args->color_write); - if (ret) - return ret; - -- ret = vc4_rcl_ms_surface_setup(exec, &setup.color_ms_write, -- &args->color_ms_write); -+ ret = vc4_rcl_surface_setup(exec, &setup.color_read, &args->color_read); - if (ret) - return ret; - -@@ -436,10 +609,21 @@ int vc4_get_rcl(struct drm_device *dev, - if (ret) - return ret; - -+ ret = vc4_rcl_msaa_surface_setup(exec, &setup.msaa_color_write, -+ &args->msaa_color_write); -+ if (ret) -+ return ret; -+ -+ ret = vc4_rcl_msaa_surface_setup(exec, &setup.msaa_zs_write, -+ &args->msaa_zs_write); -+ if (ret) -+ return ret; -+ - /* We shouldn't even have the job submitted to us if there's no - * surface to write out. - */ -- if (!setup.color_ms_write && !setup.zs_write) { -+ if (!setup.color_write && !setup.zs_write && -+ !setup.msaa_color_write && !setup.msaa_zs_write) { - DRM_ERROR("RCL requires color or Z/S write\n"); - return -EINVAL; - } ---- a/drivers/gpu/drm/vc4/vc4_validate.c -+++ b/drivers/gpu/drm/vc4/vc4_validate.c -@@ -400,9 +400,8 @@ validate_tile_binning_config(VALIDATE_AR - } - - if (flags & (VC4_BIN_CONFIG_DB_NON_MS | -- VC4_BIN_CONFIG_TILE_BUFFER_64BIT | -- VC4_BIN_CONFIG_MS_MODE_4X)) { -- DRM_ERROR("unsupported bining config flags 0x%02x\n", flags); -+ VC4_BIN_CONFIG_TILE_BUFFER_64BIT)) { -+ DRM_ERROR("unsupported binning config flags 0x%02x\n", flags); - return -EINVAL; - } - ---- a/include/uapi/drm/vc4_drm.h -+++ b/include/uapi/drm/vc4_drm.h -@@ -46,10 +46,13 @@ struct drm_vc4_submit_rcl_surface { - uint32_t hindex; /* Handle index, or ~0 if not present. */ - uint32_t offset; /* Offset to start of buffer. */ - /* -- * Bits for either render config (color_ms_write) or load/store packet. -+ * Bits for either render config (color_write) or load/store packet. -+ * Bits should all be 0 for MSAA load/stores. - */ - uint16_t bits; -- uint16_t pad; -+ -+#define VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES (1 << 0) -+ uint16_t flags; - }; - - /** -@@ -128,9 +131,11 @@ struct drm_vc4_submit_cl { - uint8_t max_x_tile; - uint8_t max_y_tile; - struct drm_vc4_submit_rcl_surface color_read; -- struct drm_vc4_submit_rcl_surface color_ms_write; -+ struct drm_vc4_submit_rcl_surface color_write; - struct drm_vc4_submit_rcl_surface zs_read; - struct drm_vc4_submit_rcl_surface zs_write; -+ struct drm_vc4_submit_rcl_surface msaa_color_write; -+ struct drm_vc4_submit_rcl_surface msaa_zs_write; - uint32_t clear_color[2]; - uint32_t clear_z; - uint8_t clear_s; diff --git a/target/linux/brcm2708/patches-4.4/0118-drm-vc4-A-few-more-non-functional-changes-to-sync-to.patch b/target/linux/brcm2708/patches-4.4/0118-drm-vc4-A-few-more-non-functional-changes-to-sync-to.patch deleted file mode 100644 index 9e6989ca12..0000000000 --- a/target/linux/brcm2708/patches-4.4/0118-drm-vc4-A-few-more-non-functional-changes-to-sync-to.patch +++ /dev/null @@ -1,345 +0,0 @@ -From 50a1843ffd2e6f408dcb6a64ca0a2ef3b062fd5a Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Tue, 8 Dec 2015 14:00:43 -0800 -Subject: [PATCH 118/170] drm/vc4: A few more non-functional changes to sync to - upstream. - -At this point all that's left is the force-enable of HDMI connector, -and using direct firmware calls to turn on V3D instead of the generic -power domain support. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/vc4_v3d.c | 2 +- - include/uapi/drm/vc4_drm.h | 182 +++++++++++++++++++++--------------------- - 2 files changed, 92 insertions(+), 92 deletions(-) - ---- a/drivers/gpu/drm/vc4/vc4_v3d.c -+++ b/drivers/gpu/drm/vc4/vc4_v3d.c -@@ -109,7 +109,7 @@ static const struct { - - int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused) - { -- struct drm_info_node *node = (struct drm_info_node *) m->private; -+ struct drm_info_node *node = (struct drm_info_node *)m->private; - struct drm_device *dev = node->minor->dev; - struct vc4_dev *vc4 = to_vc4_dev(dev); - int i; ---- a/include/uapi/drm/vc4_drm.h -+++ b/include/uapi/drm/vc4_drm.h -@@ -24,7 +24,7 @@ - #ifndef _UAPI_VC4_DRM_H_ - #define _UAPI_VC4_DRM_H_ - --#include -+#include "drm.h" - - #define DRM_VC4_SUBMIT_CL 0x00 - #define DRM_VC4_WAIT_SEQNO 0x01 -@@ -34,25 +34,25 @@ - #define DRM_VC4_CREATE_SHADER_BO 0x05 - #define DRM_VC4_GET_HANG_STATE 0x06 - --#define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) --#define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) --#define DRM_IOCTL_VC4_WAIT_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_WAIT_BO, struct drm_vc4_wait_bo) --#define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) --#define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) --#define DRM_IOCTL_VC4_CREATE_SHADER_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo) --#define DRM_IOCTL_VC4_GET_HANG_STATE DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_GET_HANG_STATE, struct drm_vc4_get_hang_state) -+#define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) -+#define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) -+#define DRM_IOCTL_VC4_WAIT_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_BO, struct drm_vc4_wait_bo) -+#define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) -+#define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) -+#define DRM_IOCTL_VC4_CREATE_SHADER_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo) -+#define DRM_IOCTL_VC4_GET_HANG_STATE DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_HANG_STATE, struct drm_vc4_get_hang_state) - - struct drm_vc4_submit_rcl_surface { -- uint32_t hindex; /* Handle index, or ~0 if not present. */ -- uint32_t offset; /* Offset to start of buffer. */ -+ __u32 hindex; /* Handle index, or ~0 if not present. */ -+ __u32 offset; /* Offset to start of buffer. */ - /* -- * Bits for either render config (color_write) or load/store packet. -- * Bits should all be 0 for MSAA load/stores. -+ * Bits for either render config (color_write) or load/store packet. -+ * Bits should all be 0 for MSAA load/stores. - */ -- uint16_t bits; -+ __u16 bits; - - #define VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES (1 << 0) -- uint16_t flags; -+ __u16 flags; - }; - - /** -@@ -76,7 +76,7 @@ struct drm_vc4_submit_cl { - * then writes out the state updates and draw calls necessary per tile - * to the tile allocation BO. - */ -- uint64_t bin_cl; -+ __u64 bin_cl; - - /* Pointer to the shader records. - * -@@ -85,16 +85,16 @@ struct drm_vc4_submit_cl { - * reference to the shader record has enough information to determine - * how many pointers are necessary (fixed number for shaders/uniforms, - * and an attribute count), so those BO indices into bo_handles are -- * just stored as uint32_ts before each shader record passed in. -+ * just stored as __u32s before each shader record passed in. - */ -- uint64_t shader_rec; -+ __u64 shader_rec; - - /* Pointer to uniform data and texture handles for the textures - * referenced by the shader. - * - * For each shader state record, there is a set of uniform data in the - * order referenced by the record (FS, VS, then CS). Each set of -- * uniform data has a uint32_t index into bo_handles per texture -+ * uniform data has a __u32 index into bo_handles per texture - * sample operation, in the order the QPU_W_TMUn_S writes appear in - * the program. Following the texture BO handle indices is the actual - * uniform data. -@@ -103,52 +103,52 @@ struct drm_vc4_submit_cl { - * because the kernel has to determine the sizes anyway during shader - * code validation. - */ -- uint64_t uniforms; -- uint64_t bo_handles; -+ __u64 uniforms; -+ __u64 bo_handles; - - /* Size in bytes of the binner command list. */ -- uint32_t bin_cl_size; -+ __u32 bin_cl_size; - /* Size in bytes of the set of shader records. */ -- uint32_t shader_rec_size; -+ __u32 shader_rec_size; - /* Number of shader records. - * - * This could just be computed from the contents of shader_records and - * the address bits of references to them from the bin CL, but it - * keeps the kernel from having to resize some allocations it makes. - */ -- uint32_t shader_rec_count; -+ __u32 shader_rec_count; - /* Size in bytes of the uniform state. */ -- uint32_t uniforms_size; -+ __u32 uniforms_size; - - /* Number of BO handles passed in (size is that times 4). */ -- uint32_t bo_handle_count; -+ __u32 bo_handle_count; - - /* RCL setup: */ -- uint16_t width; -- uint16_t height; -- uint8_t min_x_tile; -- uint8_t min_y_tile; -- uint8_t max_x_tile; -- uint8_t max_y_tile; -+ __u16 width; -+ __u16 height; -+ __u8 min_x_tile; -+ __u8 min_y_tile; -+ __u8 max_x_tile; -+ __u8 max_y_tile; - struct drm_vc4_submit_rcl_surface color_read; - struct drm_vc4_submit_rcl_surface color_write; - struct drm_vc4_submit_rcl_surface zs_read; - struct drm_vc4_submit_rcl_surface zs_write; - struct drm_vc4_submit_rcl_surface msaa_color_write; - struct drm_vc4_submit_rcl_surface msaa_zs_write; -- uint32_t clear_color[2]; -- uint32_t clear_z; -- uint8_t clear_s; -+ __u32 clear_color[2]; -+ __u32 clear_z; -+ __u8 clear_s; - -- uint32_t pad:24; -+ __u32 pad:24; - - #define VC4_SUBMIT_CL_USE_CLEAR_COLOR (1 << 0) -- uint32_t flags; -+ __u32 flags; - - /* Returned value of the seqno of this render job (for the - * wait ioctl). - */ -- uint64_t seqno; -+ __u64 seqno; - }; - - /** -@@ -159,8 +159,8 @@ struct drm_vc4_submit_cl { - * block, just return the status." - */ - struct drm_vc4_wait_seqno { -- uint64_t seqno; -- uint64_t timeout_ns; -+ __u64 seqno; -+ __u64 timeout_ns; - }; - - /** -@@ -172,9 +172,9 @@ struct drm_vc4_wait_seqno { - * completed. - */ - struct drm_vc4_wait_bo { -- uint32_t handle; -- uint32_t pad; -- uint64_t timeout_ns; -+ __u32 handle; -+ __u32 pad; -+ __u64 timeout_ns; - }; - - /** -@@ -184,11 +184,30 @@ struct drm_vc4_wait_bo { - * used in a future extension. - */ - struct drm_vc4_create_bo { -- uint32_t size; -- uint32_t flags; -+ __u32 size; -+ __u32 flags; - /** Returned GEM handle for the BO. */ -- uint32_t handle; -- uint32_t pad; -+ __u32 handle; -+ __u32 pad; -+}; -+ -+/** -+ * struct drm_vc4_mmap_bo - ioctl argument for mapping VC4 BOs. -+ * -+ * This doesn't actually perform an mmap. Instead, it returns the -+ * offset you need to use in an mmap on the DRM device node. This -+ * means that tools like valgrind end up knowing about the mapped -+ * memory. -+ * -+ * There are currently no values for the flags argument, but it may be -+ * used in a future extension. -+ */ -+struct drm_vc4_mmap_bo { -+ /** Handle for the object being mapped. */ -+ __u32 handle; -+ __u32 flags; -+ /** offset into the drm node to use for subsequent mmap call. */ -+ __u64 offset; - }; - - /** -@@ -201,43 +220,24 @@ struct drm_vc4_create_bo { - */ - struct drm_vc4_create_shader_bo { - /* Size of the data argument. */ -- uint32_t size; -+ __u32 size; - /* Flags, currently must be 0. */ -- uint32_t flags; -+ __u32 flags; - - /* Pointer to the data. */ -- uint64_t data; -+ __u64 data; - - /** Returned GEM handle for the BO. */ -- uint32_t handle; -+ __u32 handle; - /* Pad, must be 0. */ -- uint32_t pad; --}; -- --/** -- * struct drm_vc4_mmap_bo - ioctl argument for mapping VC4 BOs. -- * -- * This doesn't actually perform an mmap. Instead, it returns the -- * offset you need to use in an mmap on the DRM device node. This -- * means that tools like valgrind end up knowing about the mapped -- * memory. -- * -- * There are currently no values for the flags argument, but it may be -- * used in a future extension. -- */ --struct drm_vc4_mmap_bo { -- /** Handle for the object being mapped. */ -- uint32_t handle; -- uint32_t flags; -- /** offset into the drm node to use for subsequent mmap call. */ -- uint64_t offset; -+ __u32 pad; - }; - - struct drm_vc4_get_hang_state_bo { -- uint32_t handle; -- uint32_t paddr; -- uint32_t size; -- uint32_t pad; -+ __u32 handle; -+ __u32 paddr; -+ __u32 size; -+ __u32 pad; - }; - - /** -@@ -246,34 +246,34 @@ struct drm_vc4_get_hang_state_bo { - */ - struct drm_vc4_get_hang_state { - /** Pointer to array of struct drm_vc4_get_hang_state_bo. */ -- uint64_t bo; -+ __u64 bo; - /** - * On input, the size of the bo array. Output is the number - * of bos to be returned. - */ -- uint32_t bo_count; -+ __u32 bo_count; - -- uint32_t start_bin, start_render; -+ __u32 start_bin, start_render; - -- uint32_t ct0ca, ct0ea; -- uint32_t ct1ca, ct1ea; -- uint32_t ct0cs, ct1cs; -- uint32_t ct0ra0, ct1ra0; -- -- uint32_t bpca, bpcs; -- uint32_t bpoa, bpos; -- -- uint32_t vpmbase; -- -- uint32_t dbge; -- uint32_t fdbgo; -- uint32_t fdbgb; -- uint32_t fdbgr; -- uint32_t fdbgs; -- uint32_t errstat; -+ __u32 ct0ca, ct0ea; -+ __u32 ct1ca, ct1ea; -+ __u32 ct0cs, ct1cs; -+ __u32 ct0ra0, ct1ra0; -+ -+ __u32 bpca, bpcs; -+ __u32 bpoa, bpos; -+ -+ __u32 vpmbase; -+ -+ __u32 dbge; -+ __u32 fdbgo; -+ __u32 fdbgb; -+ __u32 fdbgr; -+ __u32 fdbgs; -+ __u32 errstat; - - /* Pad that we may save more registers into in the future. */ -- uint32_t pad[16]; -+ __u32 pad[16]; - }; - - #endif /* _UAPI_VC4_DRM_H_ */ diff --git a/target/linux/brcm2708/patches-4.4/0118-drm-vc4-Use-hpd-gpios-for-HDMI-GPIO-like-what-landed.patch b/target/linux/brcm2708/patches-4.4/0118-drm-vc4-Use-hpd-gpios-for-HDMI-GPIO-like-what-landed.patch new file mode 100644 index 0000000000..401340b94a --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0118-drm-vc4-Use-hpd-gpios-for-HDMI-GPIO-like-what-landed.patch @@ -0,0 +1,22 @@ +From 6fa2e872ce528f957b0c3aa735aa46b24ed6601a Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Tue, 15 Dec 2015 23:46:32 +0000 +Subject: [PATCH 118/232] drm/vc4: Use "hpd-gpios" for HDMI GPIO, like what + landed upstream. + +Signed-off-by: Eric Anholt +--- + arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts +@@ -68,7 +68,7 @@ + <0x7e808000 0x100>; + interrupts = <2 8>, <2 9>; + ddc = <&i2c2>; +- hpd-gpio = <&gpio 46 GPIO_ACTIVE_HIGH>; ++ hpd-gpios = <&gpio 46 GPIO_ACTIVE_HIGH>; + clocks = <&cprman BCM2835_PLLH_PIX>, + <&cprman BCM2835_CLOCK_HSM>; + clock-names = "pixel", "hdmi"; diff --git a/target/linux/brcm2708/patches-4.4/0119-drm-vc4-Synchronize-validation-code-for-v2-submissio.patch b/target/linux/brcm2708/patches-4.4/0119-drm-vc4-Synchronize-validation-code-for-v2-submissio.patch new file mode 100644 index 0000000000..44b3e5a069 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0119-drm-vc4-Synchronize-validation-code-for-v2-submissio.patch @@ -0,0 +1,612 @@ +From 76172185d9852c3e2bf7ace7c040b62dd220d9aa Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 7 Dec 2015 12:35:01 -0800 +Subject: [PATCH 119/232] drm/vc4: Synchronize validation code for v2 + submission upstream. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_drv.h | 24 +-- + drivers/gpu/drm/vc4/vc4_gem.c | 14 +- + drivers/gpu/drm/vc4/vc4_render_cl.c | 6 +- + drivers/gpu/drm/vc4/vc4_validate.c | 287 +++++++++++++++--------------------- + 4 files changed, 135 insertions(+), 196 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -189,17 +189,6 @@ to_vc4_encoder(struct drm_encoder *encod + #define HVS_READ(offset) readl(vc4->hvs->regs + offset) + #define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset) + +-enum vc4_bo_mode { +- VC4_MODE_UNDECIDED, +- VC4_MODE_RENDER, +- VC4_MODE_SHADER, +-}; +- +-struct vc4_bo_exec_state { +- struct drm_gem_cma_object *bo; +- enum vc4_bo_mode mode; +-}; +- + struct vc4_exec_info { + /* Sequence number for this bin/render job. */ + uint64_t seqno; +@@ -210,7 +199,7 @@ struct vc4_exec_info { + /* This is the array of BOs that were looked up at the start of exec. + * Command validation will use indices into this array. + */ +- struct vc4_bo_exec_state *bo; ++ struct drm_gem_cma_object **bo; + uint32_t bo_count; + + /* Pointers for our position in vc4->job_list */ +@@ -238,7 +227,6 @@ struct vc4_exec_info { + * command lists. + */ + struct vc4_shader_state { +- uint8_t packet; + uint32_t addr; + /* Maximum vertex index referenced by any primitive using this + * shader state. +@@ -254,6 +242,7 @@ struct vc4_exec_info { + bool found_tile_binning_mode_config_packet; + bool found_start_tile_binning_packet; + bool found_increment_semaphore_packet; ++ bool found_flush; + uint8_t bin_tiles_x, bin_tiles_y; + struct drm_gem_cma_object *tile_bo; + uint32_t tile_alloc_offset; +@@ -265,6 +254,9 @@ struct vc4_exec_info { + uint32_t ct0ca, ct0ea; + uint32_t ct1ca, ct1ea; + ++ /* Pointer to the unvalidated bin CL (if present). */ ++ void *bin_u; ++ + /* Pointers to the shader recs. These paddr gets incremented as CL + * packets are relocated in validate_gl_shader_state, and the vaddrs + * (u and v) get incremented and size decremented as the shader recs +@@ -455,10 +447,8 @@ vc4_validate_bin_cl(struct drm_device *d + int + vc4_validate_shader_recs(struct drm_device *dev, struct vc4_exec_info *exec); + +-bool vc4_use_bo(struct vc4_exec_info *exec, +- uint32_t hindex, +- enum vc4_bo_mode mode, +- struct drm_gem_cma_object **obj); ++struct drm_gem_cma_object *vc4_use_bo(struct vc4_exec_info *exec, ++ uint32_t hindex); + + int vc4_get_rcl(struct drm_device *dev, struct vc4_exec_info *exec); + +--- a/drivers/gpu/drm/vc4/vc4_gem.c ++++ b/drivers/gpu/drm/vc4/vc4_gem.c +@@ -169,8 +169,8 @@ vc4_save_hang_state(struct drm_device *d + } + + for (i = 0; i < exec->bo_count; i++) { +- drm_gem_object_reference(&exec->bo[i].bo->base); +- kernel_state->bo[i] = &exec->bo[i].bo->base; ++ drm_gem_object_reference(&exec->bo[i]->base); ++ kernel_state->bo[i] = &exec->bo[i]->base; + } + + list_for_each_entry(bo, &exec->unref_list, unref_head) { +@@ -397,7 +397,7 @@ vc4_update_bo_seqnos(struct vc4_exec_inf + unsigned i; + + for (i = 0; i < exec->bo_count; i++) { +- bo = to_vc4_bo(&exec->bo[i].bo->base); ++ bo = to_vc4_bo(&exec->bo[i]->base); + bo->seqno = seqno; + } + +@@ -467,7 +467,7 @@ vc4_cl_lookup_bos(struct drm_device *dev + return -EINVAL; + } + +- exec->bo = kcalloc(exec->bo_count, sizeof(struct vc4_bo_exec_state), ++ exec->bo = kcalloc(exec->bo_count, sizeof(struct drm_gem_cma_object *), + GFP_KERNEL); + if (!exec->bo) { + DRM_ERROR("Failed to allocate validated BO pointers\n"); +@@ -500,7 +500,7 @@ vc4_cl_lookup_bos(struct drm_device *dev + goto fail; + } + drm_gem_object_reference(bo); +- exec->bo[i].bo = (struct drm_gem_cma_object *)bo; ++ exec->bo[i] = (struct drm_gem_cma_object *)bo; + } + spin_unlock(&file_priv->table_lock); + +@@ -591,6 +591,8 @@ vc4_get_bcl(struct drm_device *dev, stru + + exec->ct0ca = exec->exec_bo->paddr + bin_offset; + ++ exec->bin_u = bin; ++ + exec->shader_rec_v = exec->exec_bo->vaddr + shader_rec_offset; + exec->shader_rec_p = exec->exec_bo->paddr + shader_rec_offset; + exec->shader_rec_size = args->shader_rec_size; +@@ -622,7 +624,7 @@ vc4_complete_exec(struct drm_device *dev + mutex_lock(&dev->struct_mutex); + if (exec->bo) { + for (i = 0; i < exec->bo_count; i++) +- drm_gem_object_unreference(&exec->bo[i].bo->base); ++ drm_gem_object_unreference(&exec->bo[i]->base); + kfree(exec->bo); + } + +--- a/drivers/gpu/drm/vc4/vc4_render_cl.c ++++ b/drivers/gpu/drm/vc4/vc4_render_cl.c +@@ -436,7 +436,8 @@ static int vc4_rcl_surface_setup(struct + if (surf->hindex == ~0) + return 0; + +- if (!vc4_use_bo(exec, surf->hindex, VC4_MODE_RENDER, obj)) ++ *obj = vc4_use_bo(exec, surf->hindex); ++ if (!*obj) + return -EINVAL; + + if (surf->flags & VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { +@@ -537,7 +538,8 @@ vc4_rcl_render_config_surface_setup(stru + if (surf->hindex == ~0) + return 0; + +- if (!vc4_use_bo(exec, surf->hindex, VC4_MODE_RENDER, obj)) ++ *obj = vc4_use_bo(exec, surf->hindex); ++ if (!*obj) + return -EINVAL; + + if (tiling > VC4_TILING_FORMAT_LT) { +--- a/drivers/gpu/drm/vc4/vc4_validate.c ++++ b/drivers/gpu/drm/vc4/vc4_validate.c +@@ -94,42 +94,42 @@ size_is_lt(uint32_t width, uint32_t heig + height <= 4 * utile_height(cpp)); + } + +-bool +-vc4_use_bo(struct vc4_exec_info *exec, +- uint32_t hindex, +- enum vc4_bo_mode mode, +- struct drm_gem_cma_object **obj) ++struct drm_gem_cma_object * ++vc4_use_bo(struct vc4_exec_info *exec, uint32_t hindex) + { +- *obj = NULL; ++ struct drm_gem_cma_object *obj; ++ struct vc4_bo *bo; + + if (hindex >= exec->bo_count) { + DRM_ERROR("BO index %d greater than BO count %d\n", + hindex, exec->bo_count); +- return false; ++ return NULL; + } ++ obj = exec->bo[hindex]; ++ bo = to_vc4_bo(&obj->base); + +- if (exec->bo[hindex].mode != mode) { +- if (exec->bo[hindex].mode == VC4_MODE_UNDECIDED) { +- exec->bo[hindex].mode = mode; +- } else { +- DRM_ERROR("BO index %d reused with mode %d vs %d\n", +- hindex, exec->bo[hindex].mode, mode); +- return false; +- } ++ if (bo->validated_shader) { ++ DRM_ERROR("Trying to use shader BO as something other than " ++ "a shader\n"); ++ return NULL; + } + +- *obj = exec->bo[hindex].bo; +- return true; ++ return obj; ++} ++ ++static struct drm_gem_cma_object * ++vc4_use_handle(struct vc4_exec_info *exec, uint32_t gem_handles_packet_index) ++{ ++ return vc4_use_bo(exec, exec->bo_index[gem_handles_packet_index]); + } + + static bool +-vc4_use_handle(struct vc4_exec_info *exec, +- uint32_t gem_handles_packet_index, +- enum vc4_bo_mode mode, +- struct drm_gem_cma_object **obj) ++validate_bin_pos(struct vc4_exec_info *exec, void *untrusted, uint32_t pos) + { +- return vc4_use_bo(exec, exec->bo_index[gem_handles_packet_index], +- mode, obj); ++ /* Note that the untrusted pointer passed to these functions is ++ * incremented past the packet byte. ++ */ ++ return (untrusted - 1 == exec->bin_u + pos); + } + + static uint32_t +@@ -202,13 +202,13 @@ vc4_check_tex_size(struct vc4_exec_info + } + + static int +-validate_flush_all(VALIDATE_ARGS) ++validate_flush(VALIDATE_ARGS) + { +- if (exec->found_increment_semaphore_packet) { +- DRM_ERROR("VC4_PACKET_FLUSH_ALL after " +- "VC4_PACKET_INCREMENT_SEMAPHORE\n"); ++ if (!validate_bin_pos(exec, untrusted, exec->args->bin_cl_size - 1)) { ++ DRM_ERROR("Bin CL must end with VC4_PACKET_FLUSH\n"); + return -EINVAL; + } ++ exec->found_flush = true; + + return 0; + } +@@ -233,17 +233,13 @@ validate_start_tile_binning(VALIDATE_ARG + static int + validate_increment_semaphore(VALIDATE_ARGS) + { +- if (exec->found_increment_semaphore_packet) { +- DRM_ERROR("Duplicate VC4_PACKET_INCREMENT_SEMAPHORE\n"); ++ if (!validate_bin_pos(exec, untrusted, exec->args->bin_cl_size - 2)) { ++ DRM_ERROR("Bin CL must end with " ++ "VC4_PACKET_INCREMENT_SEMAPHORE\n"); + return -EINVAL; + } + exec->found_increment_semaphore_packet = true; + +- /* Once we've found the semaphore increment, there should be one FLUSH +- * then the end of the command list. The FLUSH actually triggers the +- * increment, so we only need to make sure there +- */ +- + return 0; + } + +@@ -257,11 +253,6 @@ validate_indexed_prim_list(VALIDATE_ARGS + uint32_t index_size = (*(uint8_t *)(untrusted + 0) >> 4) ? 2 : 1; + struct vc4_shader_state *shader_state; + +- if (exec->found_increment_semaphore_packet) { +- DRM_ERROR("Drawing after VC4_PACKET_INCREMENT_SEMAPHORE\n"); +- return -EINVAL; +- } +- + /* Check overflow condition */ + if (exec->shader_state_count == 0) { + DRM_ERROR("shader state must precede primitives\n"); +@@ -272,7 +263,8 @@ validate_indexed_prim_list(VALIDATE_ARGS + if (max_index > shader_state->max_index) + shader_state->max_index = max_index; + +- if (!vc4_use_handle(exec, 0, VC4_MODE_RENDER, &ib)) ++ ib = vc4_use_handle(exec, 0); ++ if (!ib) + return -EINVAL; + + if (offset > ib->base.size || +@@ -295,11 +287,6 @@ validate_gl_array_primitive(VALIDATE_ARG + uint32_t max_index; + struct vc4_shader_state *shader_state; + +- if (exec->found_increment_semaphore_packet) { +- DRM_ERROR("Drawing after VC4_PACKET_INCREMENT_SEMAPHORE\n"); +- return -EINVAL; +- } +- + /* Check overflow condition */ + if (exec->shader_state_count == 0) { + DRM_ERROR("shader state must precede primitives\n"); +@@ -329,7 +316,6 @@ validate_gl_shader_state(VALIDATE_ARGS) + return -EINVAL; + } + +- exec->shader_state[i].packet = VC4_PACKET_GL_SHADER_STATE; + exec->shader_state[i].addr = *(uint32_t *)untrusted; + exec->shader_state[i].max_index = 0; + +@@ -348,31 +334,6 @@ validate_gl_shader_state(VALIDATE_ARGS) + } + + static int +-validate_nv_shader_state(VALIDATE_ARGS) +-{ +- uint32_t i = exec->shader_state_count++; +- +- if (i >= exec->shader_state_size) { +- DRM_ERROR("More requests for shader states than declared\n"); +- return -EINVAL; +- } +- +- exec->shader_state[i].packet = VC4_PACKET_NV_SHADER_STATE; +- exec->shader_state[i].addr = *(uint32_t *)untrusted; +- +- if (exec->shader_state[i].addr & 15) { +- DRM_ERROR("NV shader state address 0x%08x misaligned\n", +- exec->shader_state[i].addr); +- return -EINVAL; +- } +- +- *(uint32_t *)validated = (exec->shader_state[i].addr + +- exec->shader_rec_p); +- +- return 0; +-} +- +-static int + validate_tile_binning_config(VALIDATE_ARGS) + { + struct drm_device *dev = exec->exec_bo->base.dev; +@@ -473,8 +434,8 @@ static const struct cmd_info { + } cmd_info[] = { + VC4_DEFINE_PACKET(VC4_PACKET_HALT, NULL), + VC4_DEFINE_PACKET(VC4_PACKET_NOP, NULL), +- VC4_DEFINE_PACKET(VC4_PACKET_FLUSH, NULL), +- VC4_DEFINE_PACKET(VC4_PACKET_FLUSH_ALL, validate_flush_all), ++ VC4_DEFINE_PACKET(VC4_PACKET_FLUSH, validate_flush), ++ VC4_DEFINE_PACKET(VC4_PACKET_FLUSH_ALL, NULL), + VC4_DEFINE_PACKET(VC4_PACKET_START_TILE_BINNING, + validate_start_tile_binning), + VC4_DEFINE_PACKET(VC4_PACKET_INCREMENT_SEMAPHORE, +@@ -488,7 +449,6 @@ static const struct cmd_info { + VC4_DEFINE_PACKET(VC4_PACKET_PRIMITIVE_LIST_FORMAT, NULL), + + VC4_DEFINE_PACKET(VC4_PACKET_GL_SHADER_STATE, validate_gl_shader_state), +- VC4_DEFINE_PACKET(VC4_PACKET_NV_SHADER_STATE, validate_nv_shader_state), + + VC4_DEFINE_PACKET(VC4_PACKET_CONFIGURATION_BITS, NULL), + VC4_DEFINE_PACKET(VC4_PACKET_FLAT_SHADE_FLAGS, NULL), +@@ -575,8 +535,16 @@ vc4_validate_bin_cl(struct drm_device *d + return -EINVAL; + } + +- if (!exec->found_increment_semaphore_packet) { +- DRM_ERROR("Bin CL missing VC4_PACKET_INCREMENT_SEMAPHORE\n"); ++ /* The bin CL must be ended with INCREMENT_SEMAPHORE and FLUSH. The ++ * semaphore is used to trigger the render CL to start up, and the ++ * FLUSH is what caps the bin lists with ++ * VC4_PACKET_RETURN_FROM_SUB_LIST (so they jump back to the main ++ * render CL when they get called to) and actually triggers the queued ++ * semaphore increment. ++ */ ++ if (!exec->found_increment_semaphore_packet || !exec->found_flush) { ++ DRM_ERROR("Bin CL missing VC4_PACKET_INCREMENT_SEMAPHORE + " ++ "VC4_PACKET_FLUSH\n"); + return -EINVAL; + } + +@@ -607,7 +575,8 @@ reloc_tex(struct vc4_exec_info *exec, + uint32_t cube_map_stride = 0; + enum vc4_texture_data_type type; + +- if (!vc4_use_bo(exec, texture_handle_index, VC4_MODE_RENDER, &tex)) ++ tex = vc4_use_bo(exec, texture_handle_index); ++ if (!tex) + return false; + + if (sample->is_direct) { +@@ -755,51 +724,28 @@ reloc_tex(struct vc4_exec_info *exec, + } + + static int +-validate_shader_rec(struct drm_device *dev, +- struct vc4_exec_info *exec, +- struct vc4_shader_state *state) ++validate_gl_shader_rec(struct drm_device *dev, ++ struct vc4_exec_info *exec, ++ struct vc4_shader_state *state) + { + uint32_t *src_handles; + void *pkt_u, *pkt_v; +- enum shader_rec_reloc_type { +- RELOC_CODE, +- RELOC_VBO, +- }; +- struct shader_rec_reloc { +- enum shader_rec_reloc_type type; +- uint32_t offset; +- }; +- static const struct shader_rec_reloc gl_relocs[] = { +- { RELOC_CODE, 4 }, /* fs */ +- { RELOC_CODE, 16 }, /* vs */ +- { RELOC_CODE, 28 }, /* cs */ +- }; +- static const struct shader_rec_reloc nv_relocs[] = { +- { RELOC_CODE, 4 }, /* fs */ +- { RELOC_VBO, 12 } ++ static const uint32_t shader_reloc_offsets[] = { ++ 4, /* fs */ ++ 16, /* vs */ ++ 28, /* cs */ + }; +- const struct shader_rec_reloc *relocs; +- struct drm_gem_cma_object *bo[ARRAY_SIZE(gl_relocs) + 8]; +- uint32_t nr_attributes = 0, nr_fixed_relocs, nr_relocs, packet_size; ++ uint32_t shader_reloc_count = ARRAY_SIZE(shader_reloc_offsets); ++ struct drm_gem_cma_object *bo[shader_reloc_count + 8]; ++ uint32_t nr_attributes, nr_relocs, packet_size; + int i; +- struct vc4_validated_shader_info *shader; + +- if (state->packet == VC4_PACKET_NV_SHADER_STATE) { +- relocs = nv_relocs; +- nr_fixed_relocs = ARRAY_SIZE(nv_relocs); +- +- packet_size = 16; +- } else { +- relocs = gl_relocs; +- nr_fixed_relocs = ARRAY_SIZE(gl_relocs); +- +- nr_attributes = state->addr & 0x7; +- if (nr_attributes == 0) +- nr_attributes = 8; +- packet_size = gl_shader_rec_size(state->addr); +- } +- nr_relocs = nr_fixed_relocs + nr_attributes; ++ nr_attributes = state->addr & 0x7; ++ if (nr_attributes == 0) ++ nr_attributes = 8; ++ packet_size = gl_shader_rec_size(state->addr); + ++ nr_relocs = ARRAY_SIZE(shader_reloc_offsets) + nr_attributes; + if (nr_relocs * 4 > exec->shader_rec_size) { + DRM_ERROR("overflowed shader recs reading %d handles " + "from %d bytes left\n", +@@ -829,21 +775,30 @@ validate_shader_rec(struct drm_device *d + exec->shader_rec_v += roundup(packet_size, 16); + exec->shader_rec_size -= packet_size; + +- for (i = 0; i < nr_relocs; i++) { +- enum vc4_bo_mode mode; ++ if (!(*(uint16_t *)pkt_u & VC4_SHADER_FLAG_FS_SINGLE_THREAD)) { ++ DRM_ERROR("Multi-threaded fragment shaders not supported.\n"); ++ return -EINVAL; ++ } + +- if (i < nr_fixed_relocs && relocs[i].type == RELOC_CODE) +- mode = VC4_MODE_SHADER; +- else +- mode = VC4_MODE_RENDER; ++ for (i = 0; i < shader_reloc_count; i++) { ++ if (src_handles[i] > exec->bo_count) { ++ DRM_ERROR("Shader handle %d too big\n", src_handles[i]); ++ return -EINVAL; ++ } + +- if (!vc4_use_bo(exec, src_handles[i], mode, &bo[i])) +- return false; ++ bo[i] = exec->bo[src_handles[i]]; ++ if (!bo[i]) ++ return -EINVAL; ++ } ++ for (i = shader_reloc_count; i < nr_relocs; i++) { ++ bo[i] = vc4_use_bo(exec, src_handles[i]); ++ if (!bo[i]) ++ return -EINVAL; + } + +- for (i = 0; i < nr_fixed_relocs; i++) { +- struct vc4_bo *vc4_bo; +- uint32_t o = relocs[i].offset; ++ for (i = 0; i < shader_reloc_count; i++) { ++ struct vc4_validated_shader_info *validated_shader; ++ uint32_t o = shader_reloc_offsets[i]; + uint32_t src_offset = *(uint32_t *)(pkt_u + o); + uint32_t *texture_handles_u; + void *uniform_data_u; +@@ -851,57 +806,50 @@ validate_shader_rec(struct drm_device *d + + *(uint32_t *)(pkt_v + o) = bo[i]->paddr + src_offset; + +- switch (relocs[i].type) { +- case RELOC_CODE: +- if (src_offset != 0) { +- DRM_ERROR("Shaders must be at offset 0 " +- "of the BO.\n"); +- goto fail; +- } ++ if (src_offset != 0) { ++ DRM_ERROR("Shaders must be at offset 0 of " ++ "the BO.\n"); ++ return -EINVAL; ++ } + +- vc4_bo = to_vc4_bo(&bo[i]->base); +- shader = vc4_bo->validated_shader; +- if (!shader) +- goto fail; ++ validated_shader = to_vc4_bo(&bo[i]->base)->validated_shader; ++ if (!validated_shader) ++ return -EINVAL; + +- if (shader->uniforms_src_size > exec->uniforms_size) { +- DRM_ERROR("Uniforms src buffer overflow\n"); +- goto fail; +- } ++ if (validated_shader->uniforms_src_size > ++ exec->uniforms_size) { ++ DRM_ERROR("Uniforms src buffer overflow\n"); ++ return -EINVAL; ++ } + +- texture_handles_u = exec->uniforms_u; +- uniform_data_u = (texture_handles_u + +- shader->num_texture_samples); +- +- memcpy(exec->uniforms_v, uniform_data_u, +- shader->uniforms_size); +- +- for (tex = 0; +- tex < shader->num_texture_samples; +- tex++) { +- if (!reloc_tex(exec, +- uniform_data_u, +- &shader->texture_samples[tex], +- texture_handles_u[tex])) { +- goto fail; +- } ++ texture_handles_u = exec->uniforms_u; ++ uniform_data_u = (texture_handles_u + ++ validated_shader->num_texture_samples); ++ ++ memcpy(exec->uniforms_v, uniform_data_u, ++ validated_shader->uniforms_size); ++ ++ for (tex = 0; ++ tex < validated_shader->num_texture_samples; ++ tex++) { ++ if (!reloc_tex(exec, ++ uniform_data_u, ++ &validated_shader->texture_samples[tex], ++ texture_handles_u[tex])) { ++ return -EINVAL; + } ++ } + +- *(uint32_t *)(pkt_v + o + 4) = exec->uniforms_p; +- +- exec->uniforms_u += shader->uniforms_src_size; +- exec->uniforms_v += shader->uniforms_size; +- exec->uniforms_p += shader->uniforms_size; +- +- break; ++ *(uint32_t *)(pkt_v + o + 4) = exec->uniforms_p; + +- case RELOC_VBO: +- break; +- } ++ exec->uniforms_u += validated_shader->uniforms_src_size; ++ exec->uniforms_v += validated_shader->uniforms_size; ++ exec->uniforms_p += validated_shader->uniforms_size; + } + + for (i = 0; i < nr_attributes; i++) { +- struct drm_gem_cma_object *vbo = bo[nr_fixed_relocs + i]; ++ struct drm_gem_cma_object *vbo = ++ bo[ARRAY_SIZE(shader_reloc_offsets) + i]; + uint32_t o = 36 + i * 8; + uint32_t offset = *(uint32_t *)(pkt_u + o + 0); + uint32_t attr_size = *(uint8_t *)(pkt_u + o + 4) + 1; +@@ -933,9 +881,6 @@ validate_shader_rec(struct drm_device *d + } + + return 0; +- +-fail: +- return -EINVAL; + } + + int +@@ -946,7 +891,7 @@ vc4_validate_shader_recs(struct drm_devi + int ret = 0; + + for (i = 0; i < exec->shader_state_count; i++) { +- ret = validate_shader_rec(dev, exec, &exec->shader_state[i]); ++ ret = validate_gl_shader_rec(dev, exec, &exec->shader_state[i]); + if (ret) + return ret; + } diff --git a/target/linux/brcm2708/patches-4.4/0119-drm-vc4-Use-hpd-gpios-for-HDMI-GPIO-like-what-landed.patch b/target/linux/brcm2708/patches-4.4/0119-drm-vc4-Use-hpd-gpios-for-HDMI-GPIO-like-what-landed.patch deleted file mode 100644 index e66eaed9b0..0000000000 --- a/target/linux/brcm2708/patches-4.4/0119-drm-vc4-Use-hpd-gpios-for-HDMI-GPIO-like-what-landed.patch +++ /dev/null @@ -1,22 +0,0 @@ -From 0a373a81664b9e9b8994512fb3d15ed296526e89 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Tue, 15 Dec 2015 23:46:32 +0000 -Subject: [PATCH 119/170] drm/vc4: Use "hpd-gpios" for HDMI GPIO, like what - landed upstream. - -Signed-off-by: Eric Anholt ---- - arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts -+++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts -@@ -68,7 +68,7 @@ - <0x7e808000 0x100>; - interrupts = <2 8>, <2 9>; - ddc = <&i2c2>; -- hpd-gpio = <&gpio 46 GPIO_ACTIVE_HIGH>; -+ hpd-gpios = <&gpio 46 GPIO_ACTIVE_HIGH>; - clocks = <&cprman BCM2835_PLLH_PIX>, - <&cprman BCM2835_CLOCK_HSM>; - clock-names = "pixel", "hdmi"; diff --git a/target/linux/brcm2708/patches-4.4/0120-MMC-Do-not-use-mmc_debug-if-CONFIG_MMC_BCM2835-is-no.patch b/target/linux/brcm2708/patches-4.4/0120-MMC-Do-not-use-mmc_debug-if-CONFIG_MMC_BCM2835-is-no.patch new file mode 100644 index 0000000000..8b09850e62 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0120-MMC-Do-not-use-mmc_debug-if-CONFIG_MMC_BCM2835-is-no.patch @@ -0,0 +1,37 @@ +From f825bd28e15f4a4e653f8a81e2a02ac9da1068ca Mon Sep 17 00:00:00 2001 +From: janluca +Date: Sun, 27 Dec 2015 14:34:04 +0100 +Subject: [PATCH 120/232] MMC: Do not use mmc_debug if CONFIG_MMC_BCM2835 is + not set + +If CONFIG_MMC_BCM2835 was not set the compiling of the kernel failed +since mmc_debug was not defined but used in drivers/mmc/core/quirks.c. + +This patch add a ifdef-check for CONFIG_MMC_BCM2835 to the change of +commit 64d395457f793250d2e582eeb38cc3403b1db98c +--- + drivers/mmc/core/quirks.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/mmc/core/quirks.c ++++ b/drivers/mmc/core/quirks.c +@@ -53,7 +53,9 @@ static const struct mmc_fixup mmc_fixup_ + + void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table) + { ++#ifdef CONFIG_MMC_BCM2835 + extern unsigned mmc_debug; ++#endif + const struct mmc_fixup *f; + u64 rev = cid_rev_card(card); + +@@ -81,7 +83,9 @@ void mmc_fixup_device(struct mmc_card *c + /* 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). + */ ++#ifdef CONFIG_MMC_BCM2835 + if (mmc_debug & (1<<13)) + card->quirks |= MMC_QUIRK_BLK_NO_CMD23; ++#endif + } + EXPORT_SYMBOL(mmc_fixup_device); diff --git a/target/linux/brcm2708/patches-4.4/0120-drm-vc4-Synchronize-validation-code-for-v2-submissio.patch b/target/linux/brcm2708/patches-4.4/0120-drm-vc4-Synchronize-validation-code-for-v2-submissio.patch deleted file mode 100644 index a6a4d5e039..0000000000 --- a/target/linux/brcm2708/patches-4.4/0120-drm-vc4-Synchronize-validation-code-for-v2-submissio.patch +++ /dev/null @@ -1,612 +0,0 @@ -From f8453aacbe60712c31c57580a126017b798bd339 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Mon, 7 Dec 2015 12:35:01 -0800 -Subject: [PATCH 120/170] drm/vc4: Synchronize validation code for v2 - submission upstream. - -Signed-off-by: Eric Anholt ---- - drivers/gpu/drm/vc4/vc4_drv.h | 24 +-- - drivers/gpu/drm/vc4/vc4_gem.c | 14 +- - drivers/gpu/drm/vc4/vc4_render_cl.c | 6 +- - drivers/gpu/drm/vc4/vc4_validate.c | 287 +++++++++++++++--------------------- - 4 files changed, 135 insertions(+), 196 deletions(-) - ---- a/drivers/gpu/drm/vc4/vc4_drv.h -+++ b/drivers/gpu/drm/vc4/vc4_drv.h -@@ -189,17 +189,6 @@ to_vc4_encoder(struct drm_encoder *encod - #define HVS_READ(offset) readl(vc4->hvs->regs + offset) - #define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset) - --enum vc4_bo_mode { -- VC4_MODE_UNDECIDED, -- VC4_MODE_RENDER, -- VC4_MODE_SHADER, --}; -- --struct vc4_bo_exec_state { -- struct drm_gem_cma_object *bo; -- enum vc4_bo_mode mode; --}; -- - struct vc4_exec_info { - /* Sequence number for this bin/render job. */ - uint64_t seqno; -@@ -210,7 +199,7 @@ struct vc4_exec_info { - /* This is the array of BOs that were looked up at the start of exec. - * Command validation will use indices into this array. - */ -- struct vc4_bo_exec_state *bo; -+ struct drm_gem_cma_object **bo; - uint32_t bo_count; - - /* Pointers for our position in vc4->job_list */ -@@ -238,7 +227,6 @@ struct vc4_exec_info { - * command lists. - */ - struct vc4_shader_state { -- uint8_t packet; - uint32_t addr; - /* Maximum vertex index referenced by any primitive using this - * shader state. -@@ -254,6 +242,7 @@ struct vc4_exec_info { - bool found_tile_binning_mode_config_packet; - bool found_start_tile_binning_packet; - bool found_increment_semaphore_packet; -+ bool found_flush; - uint8_t bin_tiles_x, bin_tiles_y; - struct drm_gem_cma_object *tile_bo; - uint32_t tile_alloc_offset; -@@ -265,6 +254,9 @@ struct vc4_exec_info { - uint32_t ct0ca, ct0ea; - uint32_t ct1ca, ct1ea; - -+ /* Pointer to the unvalidated bin CL (if present). */ -+ void *bin_u; -+ - /* Pointers to the shader recs. These paddr gets incremented as CL - * packets are relocated in validate_gl_shader_state, and the vaddrs - * (u and v) get incremented and size decremented as the shader recs -@@ -455,10 +447,8 @@ vc4_validate_bin_cl(struct drm_device *d - int - vc4_validate_shader_recs(struct drm_device *dev, struct vc4_exec_info *exec); - --bool vc4_use_bo(struct vc4_exec_info *exec, -- uint32_t hindex, -- enum vc4_bo_mode mode, -- struct drm_gem_cma_object **obj); -+struct drm_gem_cma_object *vc4_use_bo(struct vc4_exec_info *exec, -+ uint32_t hindex); - - int vc4_get_rcl(struct drm_device *dev, struct vc4_exec_info *exec); - ---- a/drivers/gpu/drm/vc4/vc4_gem.c -+++ b/drivers/gpu/drm/vc4/vc4_gem.c -@@ -169,8 +169,8 @@ vc4_save_hang_state(struct drm_device *d - } - - for (i = 0; i < exec->bo_count; i++) { -- drm_gem_object_reference(&exec->bo[i].bo->base); -- kernel_state->bo[i] = &exec->bo[i].bo->base; -+ drm_gem_object_reference(&exec->bo[i]->base); -+ kernel_state->bo[i] = &exec->bo[i]->base; - } - - list_for_each_entry(bo, &exec->unref_list, unref_head) { -@@ -397,7 +397,7 @@ vc4_update_bo_seqnos(struct vc4_exec_inf - unsigned i; - - for (i = 0; i < exec->bo_count; i++) { -- bo = to_vc4_bo(&exec->bo[i].bo->base); -+ bo = to_vc4_bo(&exec->bo[i]->base); - bo->seqno = seqno; - } - -@@ -467,7 +467,7 @@ vc4_cl_lookup_bos(struct drm_device *dev - return -EINVAL; - } - -- exec->bo = kcalloc(exec->bo_count, sizeof(struct vc4_bo_exec_state), -+ exec->bo = kcalloc(exec->bo_count, sizeof(struct drm_gem_cma_object *), - GFP_KERNEL); - if (!exec->bo) { - DRM_ERROR("Failed to allocate validated BO pointers\n"); -@@ -500,7 +500,7 @@ vc4_cl_lookup_bos(struct drm_device *dev - goto fail; - } - drm_gem_object_reference(bo); -- exec->bo[i].bo = (struct drm_gem_cma_object *)bo; -+ exec->bo[i] = (struct drm_gem_cma_object *)bo; - } - spin_unlock(&file_priv->table_lock); - -@@ -591,6 +591,8 @@ vc4_get_bcl(struct drm_device *dev, stru - - exec->ct0ca = exec->exec_bo->paddr + bin_offset; - -+ exec->bin_u = bin; -+ - exec->shader_rec_v = exec->exec_bo->vaddr + shader_rec_offset; - exec->shader_rec_p = exec->exec_bo->paddr + shader_rec_offset; - exec->shader_rec_size = args->shader_rec_size; -@@ -622,7 +624,7 @@ vc4_complete_exec(struct drm_device *dev - mutex_lock(&dev->struct_mutex); - if (exec->bo) { - for (i = 0; i < exec->bo_count; i++) -- drm_gem_object_unreference(&exec->bo[i].bo->base); -+ drm_gem_object_unreference(&exec->bo[i]->base); - kfree(exec->bo); - } - ---- a/drivers/gpu/drm/vc4/vc4_render_cl.c -+++ b/drivers/gpu/drm/vc4/vc4_render_cl.c -@@ -436,7 +436,8 @@ static int vc4_rcl_surface_setup(struct - if (surf->hindex == ~0) - return 0; - -- if (!vc4_use_bo(exec, surf->hindex, VC4_MODE_RENDER, obj)) -+ *obj = vc4_use_bo(exec, surf->hindex); -+ if (!*obj) - return -EINVAL; - - if (surf->flags & VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { -@@ -537,7 +538,8 @@ vc4_rcl_render_config_surface_setup(stru - if (surf->hindex == ~0) - return 0; - -- if (!vc4_use_bo(exec, surf->hindex, VC4_MODE_RENDER, obj)) -+ *obj = vc4_use_bo(exec, surf->hindex); -+ if (!*obj) - return -EINVAL; - - if (tiling > VC4_TILING_FORMAT_LT) { ---- a/drivers/gpu/drm/vc4/vc4_validate.c -+++ b/drivers/gpu/drm/vc4/vc4_validate.c -@@ -94,42 +94,42 @@ size_is_lt(uint32_t width, uint32_t heig - height <= 4 * utile_height(cpp)); - } - --bool --vc4_use_bo(struct vc4_exec_info *exec, -- uint32_t hindex, -- enum vc4_bo_mode mode, -- struct drm_gem_cma_object **obj) -+struct drm_gem_cma_object * -+vc4_use_bo(struct vc4_exec_info *exec, uint32_t hindex) - { -- *obj = NULL; -+ struct drm_gem_cma_object *obj; -+ struct vc4_bo *bo; - - if (hindex >= exec->bo_count) { - DRM_ERROR("BO index %d greater than BO count %d\n", - hindex, exec->bo_count); -- return false; -+ return NULL; - } -+ obj = exec->bo[hindex]; -+ bo = to_vc4_bo(&obj->base); - -- if (exec->bo[hindex].mode != mode) { -- if (exec->bo[hindex].mode == VC4_MODE_UNDECIDED) { -- exec->bo[hindex].mode = mode; -- } else { -- DRM_ERROR("BO index %d reused with mode %d vs %d\n", -- hindex, exec->bo[hindex].mode, mode); -- return false; -- } -+ if (bo->validated_shader) { -+ DRM_ERROR("Trying to use shader BO as something other than " -+ "a shader\n"); -+ return NULL; - } - -- *obj = exec->bo[hindex].bo; -- return true; -+ return obj; -+} -+ -+static struct drm_gem_cma_object * -+vc4_use_handle(struct vc4_exec_info *exec, uint32_t gem_handles_packet_index) -+{ -+ return vc4_use_bo(exec, exec->bo_index[gem_handles_packet_index]); - } - - static bool --vc4_use_handle(struct vc4_exec_info *exec, -- uint32_t gem_handles_packet_index, -- enum vc4_bo_mode mode, -- struct drm_gem_cma_object **obj) -+validate_bin_pos(struct vc4_exec_info *exec, void *untrusted, uint32_t pos) - { -- return vc4_use_bo(exec, exec->bo_index[gem_handles_packet_index], -- mode, obj); -+ /* Note that the untrusted pointer passed to these functions is -+ * incremented past the packet byte. -+ */ -+ return (untrusted - 1 == exec->bin_u + pos); - } - - static uint32_t -@@ -202,13 +202,13 @@ vc4_check_tex_size(struct vc4_exec_info - } - - static int --validate_flush_all(VALIDATE_ARGS) -+validate_flush(VALIDATE_ARGS) - { -- if (exec->found_increment_semaphore_packet) { -- DRM_ERROR("VC4_PACKET_FLUSH_ALL after " -- "VC4_PACKET_INCREMENT_SEMAPHORE\n"); -+ if (!validate_bin_pos(exec, untrusted, exec->args->bin_cl_size - 1)) { -+ DRM_ERROR("Bin CL must end with VC4_PACKET_FLUSH\n"); - return -EINVAL; - } -+ exec->found_flush = true; - - return 0; - } -@@ -233,17 +233,13 @@ validate_start_tile_binning(VALIDATE_ARG - static int - validate_increment_semaphore(VALIDATE_ARGS) - { -- if (exec->found_increment_semaphore_packet) { -- DRM_ERROR("Duplicate VC4_PACKET_INCREMENT_SEMAPHORE\n"); -+ if (!validate_bin_pos(exec, untrusted, exec->args->bin_cl_size - 2)) { -+ DRM_ERROR("Bin CL must end with " -+ "VC4_PACKET_INCREMENT_SEMAPHORE\n"); - return -EINVAL; - } - exec->found_increment_semaphore_packet = true; - -- /* Once we've found the semaphore increment, there should be one FLUSH -- * then the end of the command list. The FLUSH actually triggers the -- * increment, so we only need to make sure there -- */ -- - return 0; - } - -@@ -257,11 +253,6 @@ validate_indexed_prim_list(VALIDATE_ARGS - uint32_t index_size = (*(uint8_t *)(untrusted + 0) >> 4) ? 2 : 1; - struct vc4_shader_state *shader_state; - -- if (exec->found_increment_semaphore_packet) { -- DRM_ERROR("Drawing after VC4_PACKET_INCREMENT_SEMAPHORE\n"); -- return -EINVAL; -- } -- - /* Check overflow condition */ - if (exec->shader_state_count == 0) { - DRM_ERROR("shader state must precede primitives\n"); -@@ -272,7 +263,8 @@ validate_indexed_prim_list(VALIDATE_ARGS - if (max_index > shader_state->max_index) - shader_state->max_index = max_index; - -- if (!vc4_use_handle(exec, 0, VC4_MODE_RENDER, &ib)) -+ ib = vc4_use_handle(exec, 0); -+ if (!ib) - return -EINVAL; - - if (offset > ib->base.size || -@@ -295,11 +287,6 @@ validate_gl_array_primitive(VALIDATE_ARG - uint32_t max_index; - struct vc4_shader_state *shader_state; - -- if (exec->found_increment_semaphore_packet) { -- DRM_ERROR("Drawing after VC4_PACKET_INCREMENT_SEMAPHORE\n"); -- return -EINVAL; -- } -- - /* Check overflow condition */ - if (exec->shader_state_count == 0) { - DRM_ERROR("shader state must precede primitives\n"); -@@ -329,7 +316,6 @@ validate_gl_shader_state(VALIDATE_ARGS) - return -EINVAL; - } - -- exec->shader_state[i].packet = VC4_PACKET_GL_SHADER_STATE; - exec->shader_state[i].addr = *(uint32_t *)untrusted; - exec->shader_state[i].max_index = 0; - -@@ -348,31 +334,6 @@ validate_gl_shader_state(VALIDATE_ARGS) - } - - static int --validate_nv_shader_state(VALIDATE_ARGS) --{ -- uint32_t i = exec->shader_state_count++; -- -- if (i >= exec->shader_state_size) { -- DRM_ERROR("More requests for shader states than declared\n"); -- return -EINVAL; -- } -- -- exec->shader_state[i].packet = VC4_PACKET_NV_SHADER_STATE; -- exec->shader_state[i].addr = *(uint32_t *)untrusted; -- -- if (exec->shader_state[i].addr & 15) { -- DRM_ERROR("NV shader state address 0x%08x misaligned\n", -- exec->shader_state[i].addr); -- return -EINVAL; -- } -- -- *(uint32_t *)validated = (exec->shader_state[i].addr + -- exec->shader_rec_p); -- -- return 0; --} -- --static int - validate_tile_binning_config(VALIDATE_ARGS) - { - struct drm_device *dev = exec->exec_bo->base.dev; -@@ -473,8 +434,8 @@ static const struct cmd_info { - } cmd_info[] = { - VC4_DEFINE_PACKET(VC4_PACKET_HALT, NULL), - VC4_DEFINE_PACKET(VC4_PACKET_NOP, NULL), -- VC4_DEFINE_PACKET(VC4_PACKET_FLUSH, NULL), -- VC4_DEFINE_PACKET(VC4_PACKET_FLUSH_ALL, validate_flush_all), -+ VC4_DEFINE_PACKET(VC4_PACKET_FLUSH, validate_flush), -+ VC4_DEFINE_PACKET(VC4_PACKET_FLUSH_ALL, NULL), - VC4_DEFINE_PACKET(VC4_PACKET_START_TILE_BINNING, - validate_start_tile_binning), - VC4_DEFINE_PACKET(VC4_PACKET_INCREMENT_SEMAPHORE, -@@ -488,7 +449,6 @@ static const struct cmd_info { - VC4_DEFINE_PACKET(VC4_PACKET_PRIMITIVE_LIST_FORMAT, NULL), - - VC4_DEFINE_PACKET(VC4_PACKET_GL_SHADER_STATE, validate_gl_shader_state), -- VC4_DEFINE_PACKET(VC4_PACKET_NV_SHADER_STATE, validate_nv_shader_state), - - VC4_DEFINE_PACKET(VC4_PACKET_CONFIGURATION_BITS, NULL), - VC4_DEFINE_PACKET(VC4_PACKET_FLAT_SHADE_FLAGS, NULL), -@@ -575,8 +535,16 @@ vc4_validate_bin_cl(struct drm_device *d - return -EINVAL; - } - -- if (!exec->found_increment_semaphore_packet) { -- DRM_ERROR("Bin CL missing VC4_PACKET_INCREMENT_SEMAPHORE\n"); -+ /* The bin CL must be ended with INCREMENT_SEMAPHORE and FLUSH. The -+ * semaphore is used to trigger the render CL to start up, and the -+ * FLUSH is what caps the bin lists with -+ * VC4_PACKET_RETURN_FROM_SUB_LIST (so they jump back to the main -+ * render CL when they get called to) and actually triggers the queued -+ * semaphore increment. -+ */ -+ if (!exec->found_increment_semaphore_packet || !exec->found_flush) { -+ DRM_ERROR("Bin CL missing VC4_PACKET_INCREMENT_SEMAPHORE + " -+ "VC4_PACKET_FLUSH\n"); - return -EINVAL; - } - -@@ -607,7 +575,8 @@ reloc_tex(struct vc4_exec_info *exec, - uint32_t cube_map_stride = 0; - enum vc4_texture_data_type type; - -- if (!vc4_use_bo(exec, texture_handle_index, VC4_MODE_RENDER, &tex)) -+ tex = vc4_use_bo(exec, texture_handle_index); -+ if (!tex) - return false; - - if (sample->is_direct) { -@@ -755,51 +724,28 @@ reloc_tex(struct vc4_exec_info *exec, - } - - static int --validate_shader_rec(struct drm_device *dev, -- struct vc4_exec_info *exec, -- struct vc4_shader_state *state) -+validate_gl_shader_rec(struct drm_device *dev, -+ struct vc4_exec_info *exec, -+ struct vc4_shader_state *state) - { - uint32_t *src_handles; - void *pkt_u, *pkt_v; -- enum shader_rec_reloc_type { -- RELOC_CODE, -- RELOC_VBO, -- }; -- struct shader_rec_reloc { -- enum shader_rec_reloc_type type; -- uint32_t offset; -- }; -- static const struct shader_rec_reloc gl_relocs[] = { -- { RELOC_CODE, 4 }, /* fs */ -- { RELOC_CODE, 16 }, /* vs */ -- { RELOC_CODE, 28 }, /* cs */ -- }; -- static const struct shader_rec_reloc nv_relocs[] = { -- { RELOC_CODE, 4 }, /* fs */ -- { RELOC_VBO, 12 } -+ static const uint32_t shader_reloc_offsets[] = { -+ 4, /* fs */ -+ 16, /* vs */ -+ 28, /* cs */ - }; -- const struct shader_rec_reloc *relocs; -- struct drm_gem_cma_object *bo[ARRAY_SIZE(gl_relocs) + 8]; -- uint32_t nr_attributes = 0, nr_fixed_relocs, nr_relocs, packet_size; -+ uint32_t shader_reloc_count = ARRAY_SIZE(shader_reloc_offsets); -+ struct drm_gem_cma_object *bo[shader_reloc_count + 8]; -+ uint32_t nr_attributes, nr_relocs, packet_size; - int i; -- struct vc4_validated_shader_info *shader; - -- if (state->packet == VC4_PACKET_NV_SHADER_STATE) { -- relocs = nv_relocs; -- nr_fixed_relocs = ARRAY_SIZE(nv_relocs); -- -- packet_size = 16; -- } else { -- relocs = gl_relocs; -- nr_fixed_relocs = ARRAY_SIZE(gl_relocs); -- -- nr_attributes = state->addr & 0x7; -- if (nr_attributes == 0) -- nr_attributes = 8; -- packet_size = gl_shader_rec_size(state->addr); -- } -- nr_relocs = nr_fixed_relocs + nr_attributes; -+ nr_attributes = state->addr & 0x7; -+ if (nr_attributes == 0) -+ nr_attributes = 8; -+ packet_size = gl_shader_rec_size(state->addr); - -+ nr_relocs = ARRAY_SIZE(shader_reloc_offsets) + nr_attributes; - if (nr_relocs * 4 > exec->shader_rec_size) { - DRM_ERROR("overflowed shader recs reading %d handles " - "from %d bytes left\n", -@@ -829,21 +775,30 @@ validate_shader_rec(struct drm_device *d - exec->shader_rec_v += roundup(packet_size, 16); - exec->shader_rec_size -= packet_size; - -- for (i = 0; i < nr_relocs; i++) { -- enum vc4_bo_mode mode; -+ if (!(*(uint16_t *)pkt_u & VC4_SHADER_FLAG_FS_SINGLE_THREAD)) { -+ DRM_ERROR("Multi-threaded fragment shaders not supported.\n"); -+ return -EINVAL; -+ } - -- if (i < nr_fixed_relocs && relocs[i].type == RELOC_CODE) -- mode = VC4_MODE_SHADER; -- else -- mode = VC4_MODE_RENDER; -+ for (i = 0; i < shader_reloc_count; i++) { -+ if (src_handles[i] > exec->bo_count) { -+ DRM_ERROR("Shader handle %d too big\n", src_handles[i]); -+ return -EINVAL; -+ } - -- if (!vc4_use_bo(exec, src_handles[i], mode, &bo[i])) -- return false; -+ bo[i] = exec->bo[src_handles[i]]; -+ if (!bo[i]) -+ return -EINVAL; -+ } -+ for (i = shader_reloc_count; i < nr_relocs; i++) { -+ bo[i] = vc4_use_bo(exec, src_handles[i]); -+ if (!bo[i]) -+ return -EINVAL; - } - -- for (i = 0; i < nr_fixed_relocs; i++) { -- struct vc4_bo *vc4_bo; -- uint32_t o = relocs[i].offset; -+ for (i = 0; i < shader_reloc_count; i++) { -+ struct vc4_validated_shader_info *validated_shader; -+ uint32_t o = shader_reloc_offsets[i]; - uint32_t src_offset = *(uint32_t *)(pkt_u + o); - uint32_t *texture_handles_u; - void *uniform_data_u; -@@ -851,57 +806,50 @@ validate_shader_rec(struct drm_device *d - - *(uint32_t *)(pkt_v + o) = bo[i]->paddr + src_offset; - -- switch (relocs[i].type) { -- case RELOC_CODE: -- if (src_offset != 0) { -- DRM_ERROR("Shaders must be at offset 0 " -- "of the BO.\n"); -- goto fail; -- } -+ if (src_offset != 0) { -+ DRM_ERROR("Shaders must be at offset 0 of " -+ "the BO.\n"); -+ return -EINVAL; -+ } - -- vc4_bo = to_vc4_bo(&bo[i]->base); -- shader = vc4_bo->validated_shader; -- if (!shader) -- goto fail; -+ validated_shader = to_vc4_bo(&bo[i]->base)->validated_shader; -+ if (!validated_shader) -+ return -EINVAL; - -- if (shader->uniforms_src_size > exec->uniforms_size) { -- DRM_ERROR("Uniforms src buffer overflow\n"); -- goto fail; -- } -+ if (validated_shader->uniforms_src_size > -+ exec->uniforms_size) { -+ DRM_ERROR("Uniforms src buffer overflow\n"); -+ return -EINVAL; -+ } - -- texture_handles_u = exec->uniforms_u; -- uniform_data_u = (texture_handles_u + -- shader->num_texture_samples); -- -- memcpy(exec->uniforms_v, uniform_data_u, -- shader->uniforms_size); -- -- for (tex = 0; -- tex < shader->num_texture_samples; -- tex++) { -- if (!reloc_tex(exec, -- uniform_data_u, -- &shader->texture_samples[tex], -- texture_handles_u[tex])) { -- goto fail; -- } -+ texture_handles_u = exec->uniforms_u; -+ uniform_data_u = (texture_handles_u + -+ validated_shader->num_texture_samples); -+ -+ memcpy(exec->uniforms_v, uniform_data_u, -+ validated_shader->uniforms_size); -+ -+ for (tex = 0; -+ tex < validated_shader->num_texture_samples; -+ tex++) { -+ if (!reloc_tex(exec, -+ uniform_data_u, -+ &validated_shader->texture_samples[tex], -+ texture_handles_u[tex])) { -+ return -EINVAL; - } -+ } - -- *(uint32_t *)(pkt_v + o + 4) = exec->uniforms_p; -- -- exec->uniforms_u += shader->uniforms_src_size; -- exec->uniforms_v += shader->uniforms_size; -- exec->uniforms_p += shader->uniforms_size; -- -- break; -+ *(uint32_t *)(pkt_v + o + 4) = exec->uniforms_p; - -- case RELOC_VBO: -- break; -- } -+ exec->uniforms_u += validated_shader->uniforms_src_size; -+ exec->uniforms_v += validated_shader->uniforms_size; -+ exec->uniforms_p += validated_shader->uniforms_size; - } - - for (i = 0; i < nr_attributes; i++) { -- struct drm_gem_cma_object *vbo = bo[nr_fixed_relocs + i]; -+ struct drm_gem_cma_object *vbo = -+ bo[ARRAY_SIZE(shader_reloc_offsets) + i]; - uint32_t o = 36 + i * 8; - uint32_t offset = *(uint32_t *)(pkt_u + o + 0); - uint32_t attr_size = *(uint8_t *)(pkt_u + o + 4) + 1; -@@ -933,9 +881,6 @@ validate_shader_rec(struct drm_device *d - } - - return 0; -- --fail: -- return -EINVAL; - } - - int -@@ -946,7 +891,7 @@ vc4_validate_shader_recs(struct drm_devi - int ret = 0; - - for (i = 0; i < exec->shader_state_count; i++) { -- ret = validate_shader_rec(dev, exec, &exec->shader_state[i]); -+ ret = validate_gl_shader_rec(dev, exec, &exec->shader_state[i]); - if (ret) - return ret; - } diff --git a/target/linux/brcm2708/patches-4.4/0121-Extend-clock-timeout-fix-modprobe-baudrate-parameter.patch b/target/linux/brcm2708/patches-4.4/0121-Extend-clock-timeout-fix-modprobe-baudrate-parameter.patch new file mode 100644 index 0000000000..18d02f2587 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0121-Extend-clock-timeout-fix-modprobe-baudrate-parameter.patch @@ -0,0 +1,108 @@ +From 127a32cdd6005a42f6b5b63de9d7db573d9de184 Mon Sep 17 00:00:00 2001 +From: Devon Fyson +Date: Wed, 30 Dec 2015 16:40:47 -0500 +Subject: [PATCH 121/232] Extend clock timeout, fix modprobe baudrate + parameter. + +Set the BSC_CLKT clock streching timeout to 35ms as per SMBus specs.\n- Increase priority of baudrate parameter passed to modprobe (in /etc/modprobe.d/*.conf or command line). Currently custom baudrates don't work because they are overridden by clock-frequency in the platform_device passed to the function. +--- + drivers/i2c/busses/i2c-bcm2708.c | 45 ++++++++++++++++++++++++++-------------- + 1 file changed, 29 insertions(+), 16 deletions(-) + +--- a/drivers/i2c/busses/i2c-bcm2708.c ++++ b/drivers/i2c/busses/i2c-bcm2708.c +@@ -71,7 +71,8 @@ + + #define DRV_NAME "bcm2708_i2c" + +-static unsigned int baudrate = CONFIG_I2C_BCM2708_BAUDRATE; ++static unsigned int baudrate_default = CONFIG_I2C_BCM2708_BAUDRATE; ++static unsigned int baudrate; + module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + MODULE_PARM_DESC(baudrate, "The I2C baudrate"); + +@@ -87,6 +88,7 @@ struct bcm2708_i2c { + int irq; + struct clk *clk; + u32 cdiv; ++ u32 clk_tout; + + struct completion done; + +@@ -126,7 +128,7 @@ static inline void bcm2708_bsc_fifo_fill + + static inline int bcm2708_bsc_setup(struct bcm2708_i2c *bi) + { +- u32 cdiv, s; ++ u32 cdiv, s, clk_tout; + u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1; + int wait_loops = I2C_WAIT_LOOP_COUNT; + +@@ -134,12 +136,14 @@ static inline int bcm2708_bsc_setup(stru + * Use the value that we cached in the probe. + */ + cdiv = bi->cdiv; ++ clk_tout = bi->clk_tout; + + if (bi->msg->flags & I2C_M_RD) + c |= BSC_C_INTR | BSC_C_READ; + else + c |= BSC_C_INTT; + ++ bcm2708_wr(bi, BSC_CLKT, clk_tout); + bcm2708_wr(bi, BSC_DIV, cdiv); + bcm2708_wr(bi, BSC_A, bi->msg->addr); + bcm2708_wr(bi, BSC_DLEN, bi->msg->len); +@@ -312,21 +316,24 @@ static int bcm2708_i2c_probe(struct plat + struct bcm2708_i2c *bi; + struct i2c_adapter *adap; + 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; ++ u32 cdiv, clk_tout; ++ ++ if (!baudrate) { ++ baudrate = baudrate_default; ++ 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"); + } +- 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); +@@ -417,7 +424,13 @@ static int bcm2708_i2c_probe(struct plat + cdiv = 0xffff; + baudrate = bus_hz / cdiv; + } ++ ++ clk_tout = 35/1000*baudrate; //35ms timeout as per SMBus specs. ++ if (clk_tout > 0xffff) ++ clk_tout = 0xffff; ++ + bi->cdiv = cdiv; ++ bi->clk_tout = clk_tout; + + dev_info(&pdev->dev, "BSC%d Controller at 0x%08lx (irq %d) (baudrate %d)\n", + pdev->id, (unsigned long)regs->start, irq, baudrate); diff --git a/target/linux/brcm2708/patches-4.4/0121-MMC-Do-not-use-mmc_debug-if-CONFIG_MMC_BCM2835-is-no.patch b/target/linux/brcm2708/patches-4.4/0121-MMC-Do-not-use-mmc_debug-if-CONFIG_MMC_BCM2835-is-no.patch deleted file mode 100644 index 5a0c685474..0000000000 --- a/target/linux/brcm2708/patches-4.4/0121-MMC-Do-not-use-mmc_debug-if-CONFIG_MMC_BCM2835-is-no.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 280bea89916813978b81811c2769411d438eb52f Mon Sep 17 00:00:00 2001 -From: janluca -Date: Sun, 27 Dec 2015 14:34:04 +0100 -Subject: [PATCH 121/170] MMC: Do not use mmc_debug if CONFIG_MMC_BCM2835 is - not set - -If CONFIG_MMC_BCM2835 was not set the compiling of the kernel failed -since mmc_debug was not defined but used in drivers/mmc/core/quirks.c. - -This patch add a ifdef-check for CONFIG_MMC_BCM2835 to the change of -commit 64d395457f793250d2e582eeb38cc3403b1db98c ---- - drivers/mmc/core/quirks.c | 4 ++++ - 1 file changed, 4 insertions(+) - ---- a/drivers/mmc/core/quirks.c -+++ b/drivers/mmc/core/quirks.c -@@ -53,7 +53,9 @@ static const struct mmc_fixup mmc_fixup_ - - void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table) - { -+#ifdef CONFIG_MMC_BCM2835 - extern unsigned mmc_debug; -+#endif - const struct mmc_fixup *f; - u64 rev = cid_rev_card(card); - -@@ -81,7 +83,9 @@ void mmc_fixup_device(struct mmc_card *c - /* 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). - */ -+#ifdef CONFIG_MMC_BCM2835 - if (mmc_debug & (1<<13)) - card->quirks |= MMC_QUIRK_BLK_NO_CMD23; -+#endif - } - EXPORT_SYMBOL(mmc_fixup_device); diff --git a/target/linux/brcm2708/patches-4.4/0122-Extend-clock-timeout-fix-modprobe-baudrate-parameter.patch b/target/linux/brcm2708/patches-4.4/0122-Extend-clock-timeout-fix-modprobe-baudrate-parameter.patch deleted file mode 100644 index 1059ae3445..0000000000 --- a/target/linux/brcm2708/patches-4.4/0122-Extend-clock-timeout-fix-modprobe-baudrate-parameter.patch +++ /dev/null @@ -1,108 +0,0 @@ -From d0390ae0ff774d8e7b5b4d5f38c33726354996bc Mon Sep 17 00:00:00 2001 -From: Devon Fyson -Date: Wed, 30 Dec 2015 16:40:47 -0500 -Subject: [PATCH 122/170] Extend clock timeout, fix modprobe baudrate - parameter. - -Set the BSC_CLKT clock streching timeout to 35ms as per SMBus specs.\n- Increase priority of baudrate parameter passed to modprobe (in /etc/modprobe.d/*.conf or command line). Currently custom baudrates don't work because they are overridden by clock-frequency in the platform_device passed to the function. ---- - drivers/i2c/busses/i2c-bcm2708.c | 45 ++++++++++++++++++++++++++-------------- - 1 file changed, 29 insertions(+), 16 deletions(-) - ---- a/drivers/i2c/busses/i2c-bcm2708.c -+++ b/drivers/i2c/busses/i2c-bcm2708.c -@@ -71,7 +71,8 @@ - - #define DRV_NAME "bcm2708_i2c" - --static unsigned int baudrate = CONFIG_I2C_BCM2708_BAUDRATE; -+static unsigned int baudrate_default = CONFIG_I2C_BCM2708_BAUDRATE; -+static unsigned int baudrate; - module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); - MODULE_PARM_DESC(baudrate, "The I2C baudrate"); - -@@ -87,6 +88,7 @@ struct bcm2708_i2c { - int irq; - struct clk *clk; - u32 cdiv; -+ u32 clk_tout; - - struct completion done; - -@@ -126,7 +128,7 @@ static inline void bcm2708_bsc_fifo_fill - - static inline int bcm2708_bsc_setup(struct bcm2708_i2c *bi) - { -- u32 cdiv, s; -+ u32 cdiv, s, clk_tout; - u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1; - int wait_loops = I2C_WAIT_LOOP_COUNT; - -@@ -134,12 +136,14 @@ static inline int bcm2708_bsc_setup(stru - * Use the value that we cached in the probe. - */ - cdiv = bi->cdiv; -+ clk_tout = bi->clk_tout; - - if (bi->msg->flags & I2C_M_RD) - c |= BSC_C_INTR | BSC_C_READ; - else - c |= BSC_C_INTT; - -+ bcm2708_wr(bi, BSC_CLKT, clk_tout); - bcm2708_wr(bi, BSC_DIV, cdiv); - bcm2708_wr(bi, BSC_A, bi->msg->addr); - bcm2708_wr(bi, BSC_DLEN, bi->msg->len); -@@ -312,21 +316,24 @@ static int bcm2708_i2c_probe(struct plat - struct bcm2708_i2c *bi; - struct i2c_adapter *adap; - 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; -+ u32 cdiv, clk_tout; -+ -+ if (!baudrate) { -+ baudrate = baudrate_default; -+ 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"); - } -- 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); -@@ -417,7 +424,13 @@ static int bcm2708_i2c_probe(struct plat - cdiv = 0xffff; - baudrate = bus_hz / cdiv; - } -+ -+ clk_tout = 35/1000*baudrate; //35ms timeout as per SMBus specs. -+ if (clk_tout > 0xffff) -+ clk_tout = 0xffff; -+ - bi->cdiv = cdiv; -+ bi->clk_tout = clk_tout; - - dev_info(&pdev->dev, "BSC%d Controller at 0x%08lx (irq %d) (baudrate %d)\n", - pdev->id, (unsigned long)regs->start, irq, baudrate); diff --git a/target/linux/brcm2708/patches-4.4/0122-bcm270x_dt-Add-dwc2-and-dwc-otg-overlays.patch b/target/linux/brcm2708/patches-4.4/0122-bcm270x_dt-Add-dwc2-and-dwc-otg-overlays.patch new file mode 100644 index 0000000000..230e7e6314 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0122-bcm270x_dt-Add-dwc2-and-dwc-otg-overlays.patch @@ -0,0 +1,110 @@ +From bde59419fd4b9dac732341a62159f01094dfe70d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= +Date: Thu, 31 Dec 2015 16:44:58 +0100 +Subject: [PATCH 122/232] bcm270x_dt: Add dwc2 and dwc-otg overlays + +--- + arch/arm/boot/dts/overlays/Makefile | 2 ++ + arch/arm/boot/dts/overlays/README | 21 +++++++++++++++++++ + arch/arm/boot/dts/overlays/dwc-otg-overlay.dts | 20 ++++++++++++++++++ + arch/arm/boot/dts/overlays/dwc2-overlay.dts | 29 ++++++++++++++++++++++++++ + 4 files changed, 72 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/dwc-otg-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/dwc2-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -15,6 +15,8 @@ endif + dtb-$(RPI_DT_OVERLAYS) += ads7846-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += at86rf233-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += bmp085_i2c-sensor-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += dwc2-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += dwc-otg-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += dht11-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += enc28j60-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += gpio-ir-overlay.dtb +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -198,6 +198,27 @@ Params: gpiopin GPIO co + (default 4) + + ++Name: dwc-otg ++Info: Selects the dwc_otg USB controller driver which has fiq support. This ++ is the default on all except the Pi Zero which defaults to dwc2. ++Load: dtoverlay=dwc-otg ++Params: ++ ++ ++Name: dwc2 ++Info: Selects the dwc2 USB controller driver ++Load: dtoverlay=dwc2,= ++Params: dr_mode Dual role mode: "host", "peripheral" or "otg" ++ ++ g-rx-fifo-size Size of rx fifo size in gadget mode ++ ++ g-np-tx-fifo-size Size of non-periodic tx fifo size in gadget ++ mode ++ ++ g-tx-fifo-size Size of periodic tx fifo per endpoint ++ (except ep0) in gadget mode ++ ++ + [ The ds1307-rtc overlay has been deleted. See i2c-rtc. ] + + +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/dwc-otg-overlay.dts +@@ -0,0 +1,20 @@ ++/dts-v1/; ++/plugin/; ++ ++/{ ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&usb>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ __overlay__ { ++ compatible = "brcm,bcm2708-usb"; ++ reg = <0x7e980000 0x10000>, ++ <0x7e006000 0x1000>; ++ interrupts = <2 0>, ++ <1 9>; ++ status = "okay"; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/dwc2-overlay.dts +@@ -0,0 +1,29 @@ ++/dts-v1/; ++/plugin/; ++ ++/{ ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&usb>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ __overlay__ { ++ compatible = "brcm,bcm2835-usb"; ++ reg = <0x7e980000 0x10000>; ++ interrupts = <1 9>; ++ dr_mode = "otg"; ++ g-np-tx-fifo-size = <32>; ++ g-rx-fifo-size = <256>; ++ g-tx-fifo-size = <256 128 128 64 64 64 32>; ++ status = "okay"; ++ }; ++ }; ++ ++ __overrides__ { ++ dr_mode = <&usb>, "dr_mode"; ++ g-np-tx-fifo-size = <&usb>,"g-np-tx-fifo-size:0"; ++ g-rx-fifo-size = <&usb>,"g-rx-fifo-size:0"; ++ g-tx-fifo-size = <&usb>,"g-tx-fifo-size:0"; ++ }; ++}; diff --git a/target/linux/brcm2708/patches-4.4/0123-BCM270X_DT-Add-the-sdtweak-overlay-for-tuning-sdhost.patch b/target/linux/brcm2708/patches-4.4/0123-BCM270X_DT-Add-the-sdtweak-overlay-for-tuning-sdhost.patch new file mode 100644 index 0000000000..91893c51e1 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0123-BCM270X_DT-Add-the-sdtweak-overlay-for-tuning-sdhost.patch @@ -0,0 +1,74 @@ +From 5d03b10c60e70fb5d316706a3777e46dc9dd748e Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 4 Jan 2016 14:42:17 +0000 +Subject: [PATCH 123/232] BCM270X_DT: Add the sdtweak overlay, for tuning + sdhost + +The sdhost overlay declares the sdhost interface and allows parameters +to be set. This is overkill for situations where the user just wants to +tweak the parameters of a pre-declared sdhost interface, so create an +sdtweak overlay that does just that. +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 14 ++++++++++++++ + arch/arm/boot/dts/overlays/sdtweak-overlay.dts | 21 +++++++++++++++++++++ + 3 files changed, 36 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/sdtweak-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -53,6 +53,7 @@ dtb-$(RPI_DT_OVERLAYS) += rpi-proto-over + dtb-$(RPI_DT_OVERLAYS) += rpi-sense-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += sdhost-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += sdio-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += sdtweak-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += smi-dev-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += smi-nand-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += smi-overlay.dtb +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -635,6 +635,20 @@ Params: overclock_50 Clock ( + (default on: polling once at boot-time) + + ++Name: sdtweak ++Info: Tunes the bcm2835-sdhost SD/MMC driver ++Load: dtoverlay=sdtweak,= ++Params: overclock_50 Clock (in MHz) to use when the MMC framework ++ requests 50MHz ++ ++ force_pio Disable DMA support (default off) ++ ++ pio_limit Number of blocks above which to use DMA ++ (default 1) ++ ++ debug Enable debug output (default off) ++ ++ + Name: smi + Info: Enables the Secondary Memory Interface peripheral. Uses GPIOs 2-25! + Load: dtoverlay=smi +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/sdtweak-overlay.dts +@@ -0,0 +1,21 @@ ++/dts-v1/; ++/plugin/; ++ ++/{ ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&sdhost>; ++ frag1: __overlay__ { ++ brcm,overclock-50 = <0>; ++ brcm,pio-limit = <1>; ++ }; ++ }; ++ ++ __overrides__ { ++ overclock_50 = <&frag1>,"brcm,overclock-50:0"; ++ force_pio = <&frag1>,"brcm,force-pio?"; ++ pio_limit = <&frag1>,"brcm,pio-limit:0"; ++ debug = <&frag1>,"brcm,debug?"; ++ }; ++}; diff --git a/target/linux/brcm2708/patches-4.4/0123-bcm270x_dt-Add-dwc2-and-dwc-otg-overlays.patch b/target/linux/brcm2708/patches-4.4/0123-bcm270x_dt-Add-dwc2-and-dwc-otg-overlays.patch deleted file mode 100644 index e4c8afa349..0000000000 --- a/target/linux/brcm2708/patches-4.4/0123-bcm270x_dt-Add-dwc2-and-dwc-otg-overlays.patch +++ /dev/null @@ -1,110 +0,0 @@ -From 62dca1937fc3acfe4b6471607b7a4d58c34e73b7 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= -Date: Thu, 31 Dec 2015 16:44:58 +0100 -Subject: [PATCH 123/170] bcm270x_dt: Add dwc2 and dwc-otg overlays - ---- - arch/arm/boot/dts/overlays/Makefile | 2 ++ - arch/arm/boot/dts/overlays/README | 21 +++++++++++++++++++ - arch/arm/boot/dts/overlays/dwc-otg-overlay.dts | 20 ++++++++++++++++++ - arch/arm/boot/dts/overlays/dwc2-overlay.dts | 29 ++++++++++++++++++++++++++ - 4 files changed, 72 insertions(+) - create mode 100644 arch/arm/boot/dts/overlays/dwc-otg-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/dwc2-overlay.dts - ---- a/arch/arm/boot/dts/overlays/Makefile -+++ b/arch/arm/boot/dts/overlays/Makefile -@@ -15,6 +15,8 @@ endif - dtb-$(RPI_DT_OVERLAYS) += ads7846-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += at86rf233-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += bmp085_i2c-sensor-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += dwc2-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += dwc-otg-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += dht11-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += enc28j60-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += gpio-ir-overlay.dtb ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -198,6 +198,27 @@ Params: gpiopin GPIO co - (default 4) - - -+Name: dwc-otg -+Info: Selects the dwc_otg USB controller driver which has fiq support. This -+ is the default on all except the Pi Zero which defaults to dwc2. -+Load: dtoverlay=dwc-otg -+Params: -+ -+ -+Name: dwc2 -+Info: Selects the dwc2 USB controller driver -+Load: dtoverlay=dwc2,= -+Params: dr_mode Dual role mode: "host", "peripheral" or "otg" -+ -+ g-rx-fifo-size Size of rx fifo size in gadget mode -+ -+ g-np-tx-fifo-size Size of non-periodic tx fifo size in gadget -+ mode -+ -+ g-tx-fifo-size Size of periodic tx fifo per endpoint -+ (except ep0) in gadget mode -+ -+ - [ The ds1307-rtc overlay has been deleted. See i2c-rtc. ] - - ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/dwc-otg-overlay.dts -@@ -0,0 +1,20 @@ -+/dts-v1/; -+/plugin/; -+ -+/{ -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&usb>; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ __overlay__ { -+ compatible = "brcm,bcm2708-usb"; -+ reg = <0x7e980000 0x10000>, -+ <0x7e006000 0x1000>; -+ interrupts = <2 0>, -+ <1 9>; -+ status = "okay"; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/dwc2-overlay.dts -@@ -0,0 +1,29 @@ -+/dts-v1/; -+/plugin/; -+ -+/{ -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&usb>; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ __overlay__ { -+ compatible = "brcm,bcm2835-usb"; -+ reg = <0x7e980000 0x10000>; -+ interrupts = <1 9>; -+ dr_mode = "otg"; -+ g-np-tx-fifo-size = <32>; -+ g-rx-fifo-size = <256>; -+ g-tx-fifo-size = <256 128 128 64 64 64 32>; -+ status = "okay"; -+ }; -+ }; -+ -+ __overrides__ { -+ dr_mode = <&usb>, "dr_mode"; -+ g-np-tx-fifo-size = <&usb>,"g-np-tx-fifo-size:0"; -+ g-rx-fifo-size = <&usb>,"g-rx-fifo-size:0"; -+ g-tx-fifo-size = <&usb>,"g-tx-fifo-size:0"; -+ }; -+}; diff --git a/target/linux/brcm2708/patches-4.4/0124-BCM270X_DT-Add-the-sdtweak-overlay-for-tuning-sdhost.patch b/target/linux/brcm2708/patches-4.4/0124-BCM270X_DT-Add-the-sdtweak-overlay-for-tuning-sdhost.patch deleted file mode 100644 index 584033d453..0000000000 --- a/target/linux/brcm2708/patches-4.4/0124-BCM270X_DT-Add-the-sdtweak-overlay-for-tuning-sdhost.patch +++ /dev/null @@ -1,74 +0,0 @@ -From 162dd00041afb2995f90a928ea80aeaa0d141ce5 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 4 Jan 2016 14:42:17 +0000 -Subject: [PATCH 124/170] BCM270X_DT: Add the sdtweak overlay, for tuning - sdhost - -The sdhost overlay declares the sdhost interface and allows parameters -to be set. This is overkill for situations where the user just wants to -tweak the parameters of a pre-declared sdhost interface, so create an -sdtweak overlay that does just that. ---- - arch/arm/boot/dts/overlays/Makefile | 1 + - arch/arm/boot/dts/overlays/README | 14 ++++++++++++++ - arch/arm/boot/dts/overlays/sdtweak-overlay.dts | 21 +++++++++++++++++++++ - 3 files changed, 36 insertions(+) - create mode 100644 arch/arm/boot/dts/overlays/sdtweak-overlay.dts - ---- a/arch/arm/boot/dts/overlays/Makefile -+++ b/arch/arm/boot/dts/overlays/Makefile -@@ -53,6 +53,7 @@ dtb-$(RPI_DT_OVERLAYS) += rpi-proto-over - dtb-$(RPI_DT_OVERLAYS) += rpi-sense-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += sdhost-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += sdio-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += sdtweak-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += smi-dev-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += smi-nand-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += smi-overlay.dtb ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -635,6 +635,20 @@ Params: overclock_50 Clock ( - (default on: polling once at boot-time) - - -+Name: sdtweak -+Info: Tunes the bcm2835-sdhost SD/MMC driver -+Load: dtoverlay=sdtweak,= -+Params: overclock_50 Clock (in MHz) to use when the MMC framework -+ requests 50MHz -+ -+ force_pio Disable DMA support (default off) -+ -+ pio_limit Number of blocks above which to use DMA -+ (default 1) -+ -+ debug Enable debug output (default off) -+ -+ - Name: smi - Info: Enables the Secondary Memory Interface peripheral. Uses GPIOs 2-25! - Load: dtoverlay=smi ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/sdtweak-overlay.dts -@@ -0,0 +1,21 @@ -+/dts-v1/; -+/plugin/; -+ -+/{ -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&sdhost>; -+ frag1: __overlay__ { -+ brcm,overclock-50 = <0>; -+ brcm,pio-limit = <1>; -+ }; -+ }; -+ -+ __overrides__ { -+ overclock_50 = <&frag1>,"brcm,overclock-50:0"; -+ force_pio = <&frag1>,"brcm,force-pio?"; -+ pio_limit = <&frag1>,"brcm,pio-limit:0"; -+ debug = <&frag1>,"brcm,debug?"; -+ }; -+}; diff --git a/target/linux/brcm2708/patches-4.4/0124-bcm2835-mmc-Don-t-override-bus-width-capabilities-fr.patch b/target/linux/brcm2708/patches-4.4/0124-bcm2835-mmc-Don-t-override-bus-width-capabilities-fr.patch new file mode 100644 index 0000000000..ca4d33560e --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0124-bcm2835-mmc-Don-t-override-bus-width-capabilities-fr.patch @@ -0,0 +1,24 @@ +From 17589e37dff9745d9c219973a77031905cb57973 Mon Sep 17 00:00:00 2001 +From: Andrew Litt +Date: Mon, 11 Jan 2016 07:54:21 +0000 +Subject: [PATCH 124/232] bcm2835-mmc: Don't override bus width capabilities + from devicetree + +Take out the force setting of the MMC_CAP_4_BIT_DATA host capability +so that the result read from devicetree via mmc_of_parse() is +preserved. +--- + drivers/mmc/host/bcm2835-mmc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/mmc/host/bcm2835-mmc.c ++++ b/drivers/mmc/host/bcm2835-mmc.c +@@ -1305,7 +1305,7 @@ static int bcm2835_mmc_add_host(struct b + /* 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; ++ MMC_CAP_MMC_HIGHSPEED; + + mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; + diff --git a/target/linux/brcm2708/patches-4.4/0125-SDIO-overlay-add-bus_width-parameter.patch b/target/linux/brcm2708/patches-4.4/0125-SDIO-overlay-add-bus_width-parameter.patch new file mode 100644 index 0000000000..c16a0027d5 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0125-SDIO-overlay-add-bus_width-parameter.patch @@ -0,0 +1,42 @@ +From c671b8fada8fe75583e25cd93f9a414c7024e613 Mon Sep 17 00:00:00 2001 +From: Andrew Litt +Date: Mon, 11 Jan 2016 07:55:54 +0000 +Subject: [PATCH 125/232] SDIO-overlay: add bus_width parameter + +Allow setting of the SDIO bus width capability of the bcm2835-mmc +host. This is helpful when only a 1 bit wide bus is connected +between host and device but both host and device advertise 4 bit +mode. +--- + arch/arm/boot/dts/overlays/README | 2 ++ + arch/arm/boot/dts/overlays/sdio-overlay.dts | 2 ++ + 2 files changed, 4 insertions(+) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -634,6 +634,8 @@ Params: overclock_50 Clock ( + poll_once Disable SDIO-device polling every second + (default on: polling once at boot-time) + ++ bus_width Set the SDIO host bus width (default 4 bits) ++ + + Name: sdtweak + Info: Tunes the bcm2835-sdhost SD/MMC driver +--- a/arch/arm/boot/dts/overlays/sdio-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sdio-overlay.dts +@@ -11,6 +11,7 @@ + pinctrl-names = "default"; + pinctrl-0 = <&sdio_pins>; + non-removable; ++ bus-width = <4>; + status = "okay"; + }; + }; +@@ -28,5 +29,6 @@ + + __overrides__ { + poll_once = <&sdio_mmc>,"non-removable?"; ++ bus_width = <&sdio_mmc>,"bus-width:0"; + }; + }; diff --git a/target/linux/brcm2708/patches-4.4/0125-bcm2835-mmc-Don-t-override-bus-width-capabilities-fr.patch b/target/linux/brcm2708/patches-4.4/0125-bcm2835-mmc-Don-t-override-bus-width-capabilities-fr.patch deleted file mode 100644 index 0413ece4ce..0000000000 --- a/target/linux/brcm2708/patches-4.4/0125-bcm2835-mmc-Don-t-override-bus-width-capabilities-fr.patch +++ /dev/null @@ -1,24 +0,0 @@ -From b07c427a7732a0fabcf521085e0fd61b0ef9047c Mon Sep 17 00:00:00 2001 -From: Andrew Litt -Date: Mon, 11 Jan 2016 07:54:21 +0000 -Subject: [PATCH 125/170] bcm2835-mmc: Don't override bus width capabilities - from devicetree - -Take out the force setting of the MMC_CAP_4_BIT_DATA host capability -so that the result read from devicetree via mmc_of_parse() is -preserved. ---- - drivers/mmc/host/bcm2835-mmc.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/drivers/mmc/host/bcm2835-mmc.c -+++ b/drivers/mmc/host/bcm2835-mmc.c -@@ -1305,7 +1305,7 @@ static int bcm2835_mmc_add_host(struct b - /* 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; -+ MMC_CAP_MMC_HIGHSPEED; - - mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; - diff --git a/target/linux/brcm2708/patches-4.4/0126-SDIO-overlay-add-bus_width-parameter.patch b/target/linux/brcm2708/patches-4.4/0126-SDIO-overlay-add-bus_width-parameter.patch deleted file mode 100644 index 0dcf03e3e5..0000000000 --- a/target/linux/brcm2708/patches-4.4/0126-SDIO-overlay-add-bus_width-parameter.patch +++ /dev/null @@ -1,42 +0,0 @@ -From f74652d026105ecc4a87b581a9c1c89a6e504d2c Mon Sep 17 00:00:00 2001 -From: Andrew Litt -Date: Mon, 11 Jan 2016 07:55:54 +0000 -Subject: [PATCH 126/170] SDIO-overlay: add bus_width parameter - -Allow setting of the SDIO bus width capability of the bcm2835-mmc -host. This is helpful when only a 1 bit wide bus is connected -between host and device but both host and device advertise 4 bit -mode. ---- - arch/arm/boot/dts/overlays/README | 2 ++ - arch/arm/boot/dts/overlays/sdio-overlay.dts | 2 ++ - 2 files changed, 4 insertions(+) - ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -634,6 +634,8 @@ Params: overclock_50 Clock ( - poll_once Disable SDIO-device polling every second - (default on: polling once at boot-time) - -+ bus_width Set the SDIO host bus width (default 4 bits) -+ - - Name: sdtweak - Info: Tunes the bcm2835-sdhost SD/MMC driver ---- a/arch/arm/boot/dts/overlays/sdio-overlay.dts -+++ b/arch/arm/boot/dts/overlays/sdio-overlay.dts -@@ -11,6 +11,7 @@ - pinctrl-names = "default"; - pinctrl-0 = <&sdio_pins>; - non-removable; -+ bus-width = <4>; - status = "okay"; - }; - }; -@@ -28,5 +29,6 @@ - - __overrides__ { - poll_once = <&sdio_mmc>,"non-removable?"; -+ bus_width = <&sdio_mmc>,"bus-width:0"; - }; - }; diff --git a/target/linux/brcm2708/patches-4.4/0126-bcm2835-extend-allowed-range-of-channels-and-sampler.patch b/target/linux/brcm2708/patches-4.4/0126-bcm2835-extend-allowed-range-of-channels-and-sampler.patch new file mode 100644 index 0000000000..20203c3b86 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0126-bcm2835-extend-allowed-range-of-channels-and-sampler.patch @@ -0,0 +1,37 @@ +From 688a5f0daa45e0a51b324707768d472e1d715c13 Mon Sep 17 00:00:00 2001 +From: wm4 +Date: Wed, 13 Jan 2016 19:41:45 +0100 +Subject: [PATCH 126/232] bcm2835: extend allowed range of channels and + samplerates + +Allow everything the videocore accepts. +--- + sound/arm/bcm2835-pcm.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/sound/arm/bcm2835-pcm.c ++++ b/sound/arm/bcm2835-pcm.c +@@ -26,9 +26,9 @@ static struct snd_pcm_hardware snd_bcm28 + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, +- .rate_max = 48000, ++ .rate_max = 192000, + .channels_min = 1, +- .channels_max = 2, ++ .channels_max = 8, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 1 * 1024, + .period_bytes_max = 128 * 1024, +@@ -43,9 +43,9 @@ static struct snd_pcm_hardware snd_bcm28 + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .rate_min = 44100, +- .rate_max = 48000, ++ .rate_max = 192000, + .channels_min = 2, +- .channels_max = 2, ++ .channels_max = 8, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 1 * 1024, + .period_bytes_max = 128 * 1024, diff --git a/target/linux/brcm2708/patches-4.4/0127-FIXUP-BCM270X_DT-random-HWRNG-dtparam-default-is-on.patch b/target/linux/brcm2708/patches-4.4/0127-FIXUP-BCM270X_DT-random-HWRNG-dtparam-default-is-on.patch deleted file mode 100644 index 86f381cf1f..0000000000 --- a/target/linux/brcm2708/patches-4.4/0127-FIXUP-BCM270X_DT-random-HWRNG-dtparam-default-is-on.patch +++ /dev/null @@ -1,21 +0,0 @@ -From 232754cb0ecd1967a8b72d6bd05467843d65a441 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Fri, 8 Jan 2016 13:42:06 +0000 -Subject: [PATCH 127/170] FIXUP BCM270X_DT: "random" (HWRNG) dtparam default is - on - ---- - arch/arm/boot/dts/overlays/README | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -110,7 +110,7 @@ Params: - (default "off") - - random Set to "on" to enable the hardware random -- number generator (default "off") -+ number generator (default "on") - - uart0 Set to "off" to disable uart0 (default "on") - diff --git a/target/linux/brcm2708/patches-4.4/0127-bcm2835-restrict-channels-rate-to-8-960000.patch b/target/linux/brcm2708/patches-4.4/0127-bcm2835-restrict-channels-rate-to-8-960000.patch new file mode 100644 index 0000000000..8b18cd6828 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0127-bcm2835-restrict-channels-rate-to-8-960000.patch @@ -0,0 +1,80 @@ +From 435962380099cafba4db3d3806b8e03cbcb0774e Mon Sep 17 00:00:00 2001 +From: wm4 +Date: Wed, 13 Jan 2016 19:42:18 +0100 +Subject: [PATCH 127/232] bcm2835: restrict channels*rate to 8*960000 + +This is required at least for SPDIF. If the bitrate goes above, +videocore will either resample the audio or corrupt it due to +underruns. Supposedly the hardware isn't designed to output +higher rates, but it can still resample it down to supported +rates. + +Some code is based on ac97_pcm.c. +--- + sound/arm/bcm2835-pcm.c | 41 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 41 insertions(+) + +--- a/sound/arm/bcm2835-pcm.c ++++ b/sound/arm/bcm2835-pcm.c +@@ -19,6 +19,9 @@ + + #include "bcm2835.h" + ++/* The hardware can not do much more num_channels*samplerate then this value */ ++#define MAX_COMBINED_RATE 768000 ++ + /* hardware definition */ + static struct snd_pcm_hardware snd_bcm2835_playback_hw = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | +@@ -107,6 +110,31 @@ static irqreturn_t bcm2835_playback_fifo + return IRQ_HANDLED; + } + ++ ++static int rate_hw_constraint_rate(struct snd_pcm_hw_params *params, ++ struct snd_pcm_hw_rule *rule) ++{ ++ struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); ++ struct snd_interval rates = { ++ .min = 8000, ++ .max = min(192000u, MAX_COMBINED_RATE / max(channels->min, 1u)), ++ }; ++ struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); ++ return snd_interval_refine(rate, &rates); ++} ++ ++static int rate_hw_constraint_channels(struct snd_pcm_hw_params *params, ++ struct snd_pcm_hw_rule *rule) ++{ ++ struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); ++ struct snd_interval channels_interval = { ++ .min = 1, ++ .max = min(8u, MAX_COMBINED_RATE / max(rate->min, 1u)), ++ }; ++ struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); ++ return snd_interval_refine(channels, &channels_interval); ++} ++ + /* open callback */ + static int snd_bcm2835_playback_open_generic( + struct snd_pcm_substream *substream, int spdif) +@@ -188,6 +216,19 @@ static int snd_bcm2835_playback_open_gen + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + 16); + ++ /* When playing PCM, pretend that we support the full range of channels ++ * and sample rates. The GPU can't output it, but is able to resample ++ * the data to a rate the hardware can handle it. This won't work with ++ * compressed data; the resampler would just destroy it. */ ++ if (spdif) { ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, ++ rate_hw_constraint_rate, NULL, ++ SNDRV_PCM_HW_PARAM_CHANNELS, -1); ++ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, ++ rate_hw_constraint_channels, NULL, ++ SNDRV_PCM_HW_PARAM_RATE, -1); ++ } ++ + chip->alsa_stream[idx] = alsa_stream; + + chip->opened |= (1 << idx); diff --git a/target/linux/brcm2708/patches-4.4/0128-bcm2835-extend-allowed-range-of-channels-and-sampler.patch b/target/linux/brcm2708/patches-4.4/0128-bcm2835-extend-allowed-range-of-channels-and-sampler.patch deleted file mode 100644 index 5fc9c99529..0000000000 --- a/target/linux/brcm2708/patches-4.4/0128-bcm2835-extend-allowed-range-of-channels-and-sampler.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 320bdec200197f074541e3999fa4b87889c5eb18 Mon Sep 17 00:00:00 2001 -From: wm4 -Date: Wed, 13 Jan 2016 19:41:45 +0100 -Subject: [PATCH 128/170] bcm2835: extend allowed range of channels and - samplerates - -Allow everything the videocore accepts. ---- - sound/arm/bcm2835-pcm.c | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - ---- a/sound/arm/bcm2835-pcm.c -+++ b/sound/arm/bcm2835-pcm.c -@@ -26,9 +26,9 @@ static struct snd_pcm_hardware snd_bcm28 - .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, - .rate_min = 8000, -- .rate_max = 48000, -+ .rate_max = 192000, - .channels_min = 1, -- .channels_max = 2, -+ .channels_max = 8, - .buffer_bytes_max = 128 * 1024, - .period_bytes_min = 1 * 1024, - .period_bytes_max = 128 * 1024, -@@ -43,9 +43,9 @@ static struct snd_pcm_hardware snd_bcm28 - .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000, - .rate_min = 44100, -- .rate_max = 48000, -+ .rate_max = 192000, - .channels_min = 2, -- .channels_max = 2, -+ .channels_max = 8, - .buffer_bytes_max = 128 * 1024, - .period_bytes_min = 1 * 1024, - .period_bytes_max = 128 * 1024, diff --git a/target/linux/brcm2708/patches-4.4/0128-rpi-update-vc_vchi_audioserv_defs.h.patch b/target/linux/brcm2708/patches-4.4/0128-rpi-update-vc_vchi_audioserv_defs.h.patch new file mode 100644 index 0000000000..94895524d8 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0128-rpi-update-vc_vchi_audioserv_defs.h.patch @@ -0,0 +1,66 @@ +From 64fa9f963dffab0145f7960a593422064bb0aa8d Mon Sep 17 00:00:00 2001 +From: wm4 +Date: Wed, 13 Jan 2016 19:42:48 +0100 +Subject: [PATCH 128/232] rpi: update vc_vchi_audioserv_defs.h + +Add audioserv 3 extensions. The changes were taken from the paste +linked here: + +https://github.com/raspberrypi/linux/pull/1166#issuecomment-151917067 +--- + sound/arm/vc_vchi_audioserv_defs.h | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +--- a/sound/arm/vc_vchi_audioserv_defs.h ++++ b/sound/arm/vc_vchi_audioserv_defs.h +@@ -16,7 +16,7 @@ + #define _VC_AUDIO_DEFS_H_ + + #define VC_AUDIOSERV_MIN_VER 1 +-#define VC_AUDIOSERV_VER 2 ++#define VC_AUDIOSERV_VER 3 + + // FourCC code used for VCHI connection + #define VC_AUDIO_SERVER_NAME MAKE_FOURCC("AUDS") +@@ -36,6 +36,7 @@ typedef enum { + VC_AUDIO_MSG_TYPE_START, // Configure audio + VC_AUDIO_MSG_TYPE_STOP, // Configure audio + VC_AUDIO_MSG_TYPE_WRITE, // Configure audio ++ VC_AUDIO_MSG_TYPE_LATENCY, // request latency in cycles + VC_AUDIO_MSG_TYPE_MAX + } VC_AUDIO_MSG_TYPE; + +@@ -44,6 +45,7 @@ typedef struct { + uint32_t channels; + uint32_t samplerate; + uint32_t bps; ++ uint32_t channelmap; + + } VC_AUDIO_CONFIG_T; + +@@ -84,6 +86,12 @@ typedef struct { + uint16_t max_packet; + } VC_AUDIO_WRITE_T; + ++// query latency in samples of sink ++typedef struct ++{ ++ uint32_t dummy; ++} VC_AUDIO_LATENCY_T; ++ + // Generic result for a request (VC->HOST) + typedef struct { + int32_t success; // Success value +@@ -108,9 +116,10 @@ typedef struct { + VC_AUDIO_START_T start; + VC_AUDIO_STOP_T stop; + VC_AUDIO_WRITE_T write; ++ VC_AUDIO_LATENCY_T latency; + VC_AUDIO_RESULT_T result; + VC_AUDIO_COMPLETE_T complete; + } u; + } VC_AUDIO_MSG_T; + +-#endif // _VC_AUDIO_DEFS_H_ ++#endif // _VC_AUDIO_DEFS_H_ +\ No newline at end of file diff --git a/target/linux/brcm2708/patches-4.4/0129-bcm2835-implement-channel-map-API.patch b/target/linux/brcm2708/patches-4.4/0129-bcm2835-implement-channel-map-API.patch new file mode 100644 index 0000000000..49fd87af8e --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0129-bcm2835-implement-channel-map-API.patch @@ -0,0 +1,421 @@ +From 5efba3f8c180c39609a8d40033ef92046ef0de75 Mon Sep 17 00:00:00 2001 +From: wm4 +Date: Wed, 13 Jan 2016 19:43:12 +0100 +Subject: [PATCH 129/232] bcm2835: implement channel map API + +Report all layouts supported by the HDMI protocol to userspace. +Make the videocore set the correct layout according to the +userspace request. + +Some code taken from patch_hdmi.c. In particular, the HDMI channel +layout table was copied without changes - with the idea in mind that +hopefully it can be shared one day. Or at least updating it will be +simpler. + +In my tests, everything appears to work, except when outputting +FL FR RL RR. Then my receiver outputs RL on both the RL and RR +speakers, while RR is never heard. +--- + sound/arm/bcm2835-ctl.c | 276 ++++++++++++++++++++++++++++++++++++++++++++++ + sound/arm/bcm2835-pcm.c | 22 +++- + sound/arm/bcm2835-vchiq.c | 13 +++ + sound/arm/bcm2835.h | 4 + + 4 files changed, 311 insertions(+), 4 deletions(-) + +--- a/sound/arm/bcm2835-ctl.c ++++ b/sound/arm/bcm2835-ctl.c +@@ -300,6 +300,281 @@ static struct snd_kcontrol_new snd_bcm28 + }, + }; + ++struct cea_channel_speaker_allocation { ++ int ca_index; ++ int speakers[8]; ++}; ++ ++#define FL SNDRV_CHMAP_FL ++#define FR SNDRV_CHMAP_FR ++#define RL SNDRV_CHMAP_RL ++#define RR SNDRV_CHMAP_RR ++#define LFE SNDRV_CHMAP_LFE ++#define FC SNDRV_CHMAP_FC ++#define RLC SNDRV_CHMAP_RLC ++#define RRC SNDRV_CHMAP_RRC ++#define RC SNDRV_CHMAP_RC ++#define FLC SNDRV_CHMAP_FLC ++#define FRC SNDRV_CHMAP_FRC ++#define FLH SNDRV_CHMAP_TFL ++#define FRH SNDRV_CHMAP_TFR ++#define FLW SNDRV_CHMAP_FLW ++#define FRW SNDRV_CHMAP_FRW ++#define TC SNDRV_CHMAP_TC ++#define FCH SNDRV_CHMAP_TFC ++ ++/* ++ * CEA-861 channel maps ++ * ++ * Stolen from sound/pci/hda/patch_hdmi.c ++ * (unlike the source, this uses SNDRV_* constants directly, as by the ++ * map_tables array in patch_hdmi.c) ++ * Unknown entries use 0, which unfortunately is SNDRV_CHMAP_UNKNOWN instead ++ * of SNDRV_CHMAP_NA. ++ */ ++static struct cea_channel_speaker_allocation channel_allocations[] = { ++/* channel: 7 6 5 4 3 2 1 0 */ ++{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, ++ /* 2.1 */ ++{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, ++ /* Dolby Surround */ ++{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, ++ /* surround40 */ ++{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, ++ /* surround41 */ ++{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, ++ /* surround50 */ ++{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, ++ /* surround51 */ ++{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, ++ /* 6.1 */ ++{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, ++ /* surround71 */ ++{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, ++ ++{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, ++{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, ++{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, ++{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, ++{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, ++{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, ++{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, ++{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, ++{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, ++{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, ++{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, ++{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, ++{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, ++{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, ++{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, ++{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, ++{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, ++{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, ++{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, ++{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, ++{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, ++{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, ++{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, ++{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, ++}; ++ ++static int snd_bcm2835_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, ++ unsigned int size, unsigned int __user *tlv) ++{ ++ unsigned int __user *dst; ++ int count = 0; ++ int i; ++ ++ if (size < 8) ++ return -ENOMEM; ++ if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) ++ return -EFAULT; ++ size -= 8; ++ dst = tlv + 2; ++ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { ++ struct cea_channel_speaker_allocation *ch = &channel_allocations[i]; ++ int num_chs = 0; ++ int chs_bytes; ++ int c; ++ ++ for (c = 0; c < 8; c++) { ++ if (ch->speakers[c]) ++ num_chs++; ++ } ++ ++ chs_bytes = num_chs * 4; ++ if (size < 8) ++ return -ENOMEM; ++ if (put_user(SNDRV_CTL_TLVT_CHMAP_FIXED, dst) || ++ put_user(chs_bytes, dst + 1)) ++ return -EFAULT; ++ dst += 2; ++ size -= 8; ++ count += 8; ++ if (size < chs_bytes) ++ return -ENOMEM; ++ size -= chs_bytes; ++ count += chs_bytes; ++ for (c = 0; c < 8; c++) { ++ int sp = ch->speakers[7 - c]; ++ if (sp) { ++ if (put_user(sp, dst)) ++ return -EFAULT; ++ dst++; ++ } ++ } ++ } ++ if (put_user(count, tlv + 1)) ++ return -EFAULT; ++ return 0; ++} ++ ++static int snd_bcm2835_chmap_ctl_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); ++ bcm2835_chip_t *chip = info->private_data; ++ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); ++ struct snd_pcm_substream *substream = snd_pcm_chmap_substream(info, idx); ++ struct cea_channel_speaker_allocation *ch = NULL; ++ int cur = 0; ++ int i; ++ ++ if (!substream || !substream->runtime) ++ return -ENODEV; ++ ++ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { ++ if (channel_allocations[i].ca_index == chip->cea_chmap) ++ ch = &channel_allocations[i]; ++ } ++ ++ /* If no layout was set yet, return a dummy. Apparently the userspace ++ * API will be confused if we don't. */ ++ if (!ch) ++ ch = &channel_allocations[0]; ++ ++ for (i = 0; i < 8; i++) { ++ if (ch->speakers[7 - i]) ++ ucontrol->value.integer.value[cur++] = ch->speakers[7 - i]; ++ } ++ while (cur < 8) ++ ucontrol->value.integer.value[cur++] = SNDRV_CHMAP_NA; ++ return 0; ++} ++ ++static int snd_bcm2835_chmap_ctl_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); ++ bcm2835_chip_t *chip = info->private_data; ++ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); ++ struct snd_pcm_substream *substream = snd_pcm_chmap_substream(info, idx); ++ int i, prepared = 0, cea_chmap = -1; ++ int remap[8]; ++ ++ if (!substream || !substream->runtime) ++ return -ENODEV; ++ ++ switch (substream->runtime->status->state) { ++ case SNDRV_PCM_STATE_OPEN: ++ case SNDRV_PCM_STATE_SETUP: ++ break; ++ case SNDRV_PCM_STATE_PREPARED: ++ prepared = 1; ++ break; ++ default: ++ return -EBUSY; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { ++ struct cea_channel_speaker_allocation *ch = &channel_allocations[i]; ++ int matches = 1; ++ int cur = 0; ++ int x; ++ memset(remap, 0, sizeof(remap)); ++ for (x = 0; x < substream->runtime->channels; x++) { ++ int sp = ucontrol->value.integer.value[x]; ++ while (cur < 8 && !ch->speakers[7 - cur]) ++ cur++; ++ if (cur >= 8) { ++ /* user has more channels than ch */ ++ matches = 0; ++ break; ++ } ++ if (ch->speakers[7 - cur] != sp) { ++ matches = 0; ++ break; ++ } ++ remap[x] = cur; ++ cur++; ++ } ++ for (x = cur; x < 8; x++) { ++ if (ch->speakers[7 - x]) { ++ /* ch has more channels than user */ ++ matches = 0; ++ break; ++ } ++ } ++ if (matches) { ++ cea_chmap = ch->ca_index; ++ break; ++ } ++ } ++ ++ if (cea_chmap < 0) ++ return -EINVAL; ++ ++ /* don't change the layout if another substream is active */ ++ if (chip->opened != (1 << substream->number) && chip->cea_chmap != cea_chmap) ++ return -EBUSY; /* unsure whether this is a good error code */ ++ ++ chip->cea_chmap = cea_chmap; ++ for (i = 0; i < 8; i++) ++ chip->map_channels[i] = remap[i]; ++ if (prepared) ++ snd_bcm2835_pcm_prepare_again(substream); ++ return 0; ++} ++ ++static int snd_bcm2835_add_chmap_ctl(bcm2835_chip_t * chip) ++{ ++ struct snd_pcm_chmap *chmap; ++ struct snd_kcontrol *kctl; ++ int err, i; ++ ++ err = snd_pcm_add_chmap_ctls(chip->pcm, ++ SNDRV_PCM_STREAM_PLAYBACK, ++ NULL, 8, 0, &chmap); ++ if (err < 0) ++ return err; ++ /* override handlers */ ++ chmap->private_data = chip; ++ kctl = chmap->kctl; ++ for (i = 0; i < kctl->count; i++) ++ kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; ++ kctl->get = snd_bcm2835_chmap_ctl_get; ++ kctl->put = snd_bcm2835_chmap_ctl_put; ++ kctl->tlv.c = snd_bcm2835_chmap_ctl_tlv; ++ return 0; ++} ++ + int snd_bcm2835_new_ctl(bcm2835_chip_t * chip) + { + int err; +@@ -313,6 +588,7 @@ int snd_bcm2835_new_ctl(bcm2835_chip_t * + if (err < 0) + return err; + } ++ snd_bcm2835_add_chmap_ctl(chip); + for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_spdif); idx++) { + err = snd_ctl_add(chip->card, + snd_ctl_new1(&snd_bcm2835_spdif[idx], chip)); +--- a/sound/arm/bcm2835-pcm.c ++++ b/sound/arm/bcm2835-pcm.c +@@ -231,6 +231,9 @@ static int snd_bcm2835_playback_open_gen + + chip->alsa_stream[idx] = alsa_stream; + ++ if (!chip->opened) ++ chip->cea_chmap = -1; ++ + chip->opened |= (1 << idx); + alsa_stream->open = 1; + alsa_stream->draining = 1; +@@ -341,8 +344,7 @@ static int snd_bcm2835_pcm_hw_free(struc + return snd_pcm_lib_free_pages(substream); + } + +-/* prepare callback */ +-static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream) ++int snd_bcm2835_pcm_prepare_again(struct snd_pcm_substream *substream) + { + bcm2835_chip_t *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; +@@ -350,8 +352,6 @@ static int snd_bcm2835_pcm_prepare(struc + int channels; + int err; + +- audio_info(" .. IN\n"); +- + /* notify the vchiq that it should enter spdif passthrough mode by + * setting channels=0 (see + * https://github.com/raspberrypi/linux/issues/528) */ +@@ -367,6 +367,20 @@ static int snd_bcm2835_pcm_prepare(struc + audio_error(" error setting hw params\n"); + } + ++ return err; ++} ++ ++/* prepare callback */ ++static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream) ++{ ++ bcm2835_chip_t *chip = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; ++ ++ audio_info(" .. IN\n"); ++ ++ snd_bcm2835_pcm_prepare_again(substream); ++ + bcm2835_audio_setup(alsa_stream); + + /* in preparation of the stream, set the controls (volume level) of the stream */ +--- a/sound/arm/bcm2835-vchiq.c ++++ b/sound/arm/bcm2835-vchiq.c +@@ -570,6 +570,8 @@ int bcm2835_audio_set_params(bcm2835_als + VC_AUDIO_MSG_T m; + AUDIO_INSTANCE_T *instance = alsa_stream->instance; + int32_t success; ++ uint32_t chmap_value; ++ int i; + int ret; + LOG_DBG(" .. IN\n"); + +@@ -593,10 +595,21 @@ int bcm2835_audio_set_params(bcm2835_als + + instance->result = -1; + ++ if (alsa_stream->chip->cea_chmap >= 0) { ++ chmap_value = (unsigned)alsa_stream->chip->cea_chmap << 24; ++ } else { ++ chmap_value = 0; /* force stereo */ ++ for (i = 0; i < 8; i++) ++ alsa_stream->chip->map_channels[i] = i; ++ } ++ for (i = 0; i < 8; i++) ++ chmap_value |= alsa_stream->chip->map_channels[i] << (i * 3); ++ + m.type = VC_AUDIO_MSG_TYPE_CONFIG; + m.u.config.channels = channels; + m.u.config.samplerate = samplerate; + m.u.config.bps = bps; ++ m.u.config.channelmap = chmap_value; + + /* Create the message available completion */ + init_completion(&instance->msg_avail_comp); +--- a/sound/arm/bcm2835.h ++++ b/sound/arm/bcm2835.h +@@ -107,6 +107,8 @@ typedef struct bcm2835_chip { + int old_volume; /* stores the volume value whist muted */ + int dest; + int mute; ++ int cea_chmap; /* currently requested Audio InfoFrame Data Byte 4 */ ++ int map_channels[8]; + + unsigned int opened; + unsigned int spdif_status; +@@ -149,6 +151,8 @@ int snd_bcm2835_new_ctl(bcm2835_chip_t * + int snd_bcm2835_new_pcm(bcm2835_chip_t * chip); + int snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip); + ++int snd_bcm2835_pcm_prepare_again(struct snd_pcm_substream *substream); ++ + int bcm2835_audio_open(bcm2835_alsa_stream_t * alsa_stream); + int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream); + int bcm2835_audio_set_params(bcm2835_alsa_stream_t * alsa_stream, diff --git a/target/linux/brcm2708/patches-4.4/0129-bcm2835-restrict-channels-rate-to-8-960000.patch b/target/linux/brcm2708/patches-4.4/0129-bcm2835-restrict-channels-rate-to-8-960000.patch deleted file mode 100644 index 29bd6ecd99..0000000000 --- a/target/linux/brcm2708/patches-4.4/0129-bcm2835-restrict-channels-rate-to-8-960000.patch +++ /dev/null @@ -1,80 +0,0 @@ -From 7e634c8f8fe70286e0b8b404494d3143aa7bc3fc Mon Sep 17 00:00:00 2001 -From: wm4 -Date: Wed, 13 Jan 2016 19:42:18 +0100 -Subject: [PATCH 129/170] bcm2835: restrict channels*rate to 8*960000 - -This is required at least for SPDIF. If the bitrate goes above, -videocore will either resample the audio or corrupt it due to -underruns. Supposedly the hardware isn't designed to output -higher rates, but it can still resample it down to supported -rates. - -Some code is based on ac97_pcm.c. ---- - sound/arm/bcm2835-pcm.c | 41 +++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 41 insertions(+) - ---- a/sound/arm/bcm2835-pcm.c -+++ b/sound/arm/bcm2835-pcm.c -@@ -19,6 +19,9 @@ - - #include "bcm2835.h" - -+/* The hardware can not do much more num_channels*samplerate then this value */ -+#define MAX_COMBINED_RATE 768000 -+ - /* hardware definition */ - static struct snd_pcm_hardware snd_bcm2835_playback_hw = { - .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | -@@ -107,6 +110,31 @@ static irqreturn_t bcm2835_playback_fifo - return IRQ_HANDLED; - } - -+ -+static int rate_hw_constraint_rate(struct snd_pcm_hw_params *params, -+ struct snd_pcm_hw_rule *rule) -+{ -+ struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); -+ struct snd_interval rates = { -+ .min = 8000, -+ .max = min(192000u, MAX_COMBINED_RATE / max(channels->min, 1u)), -+ }; -+ struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); -+ return snd_interval_refine(rate, &rates); -+} -+ -+static int rate_hw_constraint_channels(struct snd_pcm_hw_params *params, -+ struct snd_pcm_hw_rule *rule) -+{ -+ struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); -+ struct snd_interval channels_interval = { -+ .min = 1, -+ .max = min(8u, MAX_COMBINED_RATE / max(rate->min, 1u)), -+ }; -+ struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); -+ return snd_interval_refine(channels, &channels_interval); -+} -+ - /* open callback */ - static int snd_bcm2835_playback_open_generic( - struct snd_pcm_substream *substream, int spdif) -@@ -188,6 +216,19 @@ static int snd_bcm2835_playback_open_gen - snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - 16); - -+ /* When playing PCM, pretend that we support the full range of channels -+ * and sample rates. The GPU can't output it, but is able to resample -+ * the data to a rate the hardware can handle it. This won't work with -+ * compressed data; the resampler would just destroy it. */ -+ if (spdif) { -+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, -+ rate_hw_constraint_rate, NULL, -+ SNDRV_PCM_HW_PARAM_CHANNELS, -1); -+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, -+ rate_hw_constraint_channels, NULL, -+ SNDRV_PCM_HW_PARAM_RATE, -1); -+ } -+ - chip->alsa_stream[idx] = alsa_stream; - - chip->opened |= (1 << idx); diff --git a/target/linux/brcm2708/patches-4.4/0130-bcm2835-access-controls-under-the-audio-mutex.patch b/target/linux/brcm2708/patches-4.4/0130-bcm2835-access-controls-under-the-audio-mutex.patch new file mode 100644 index 0000000000..d497240f49 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0130-bcm2835-access-controls-under-the-audio-mutex.patch @@ -0,0 +1,237 @@ +From 26e1f66589860cfcc9a6473d705db890e1b8f6c1 Mon Sep 17 00:00:00 2001 +From: wm4 +Date: Wed, 13 Jan 2016 19:43:35 +0100 +Subject: [PATCH 130/232] bcm2835: access controls under the audio mutex + +I don't think the ALSA framework provides any kind of automatic +synchronization within the control callbacks. We most likely need +to ensure this manually, so add locking around all access to shared +mutable data. In particular, bcm2835_audio_set_ctls() should +probably always be called under our own audio lock. +--- + sound/arm/bcm2835-ctl.c | 74 +++++++++++++++++++++++++++++++++++++++++-------- + sound/arm/bcm2835-pcm.c | 4 +++ + 2 files changed, 66 insertions(+), 12 deletions(-) + +--- a/sound/arm/bcm2835-ctl.c ++++ b/sound/arm/bcm2835-ctl.c +@@ -94,6 +94,9 @@ static int snd_bcm2835_ctl_get(struct sn + { + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); + ++ if (mutex_lock_interruptible(&chip->audio_mutex)) ++ return -EINTR; ++ + BUG_ON(!chip && !(chip->avail_substreams & AVAIL_SUBSTREAMS_MASK)); + + if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) +@@ -103,6 +106,7 @@ static int snd_bcm2835_ctl_get(struct sn + else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) + ucontrol->value.integer.value[0] = chip->dest; + ++ mutex_unlock(&chip->audio_mutex); + return 0; + } + +@@ -112,11 +116,15 @@ static int snd_bcm2835_ctl_put(struct sn + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); + int changed = 0; + ++ if (mutex_lock_interruptible(&chip->audio_mutex)) ++ return -EINTR; ++ + if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) { + audio_info("Volume change attempted.. volume = %d new_volume = %d\n", chip->volume, (int)ucontrol->value.integer.value[0]); + if (chip->mute == CTRL_VOL_MUTE) { + /* changed = toggle_mute(chip, CTRL_VOL_UNMUTE); */ +- return 1; /* should return 0 to signify no change but the mixer takes this as the opposite sign (no idea why) */ ++ changed = 1; /* should return 0 to signify no change but the mixer takes this as the opposite sign (no idea why) */ ++ goto unlock; + } + if (changed + || (ucontrol->value.integer.value[0] != chip2alsa(chip->volume))) { +@@ -142,6 +150,8 @@ static int snd_bcm2835_ctl_put(struct sn + printk(KERN_ERR "Failed to set ALSA controls..\n"); + } + ++unlock: ++ mutex_unlock(&chip->audio_mutex); + return changed; + } + +@@ -198,10 +208,14 @@ static int snd_bcm2835_spdif_default_get + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); + int i; + ++ if (mutex_lock_interruptible(&chip->audio_mutex)) ++ return -EINTR; ++ + for (i = 0; i < 4; i++) + ucontrol->value.iec958.status[i] = + (chip->spdif_status >> (i * 8)) && 0xff; + ++ mutex_unlock(&chip->audio_mutex); + return 0; + } + +@@ -212,12 +226,16 @@ static int snd_bcm2835_spdif_default_put + unsigned int val = 0; + int i, change; + ++ if (mutex_lock_interruptible(&chip->audio_mutex)) ++ return -EINTR; ++ + for (i = 0; i < 4; i++) + val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); + + change = val != chip->spdif_status; + chip->spdif_status = val; + ++ mutex_unlock(&chip->audio_mutex); + return change; + } + +@@ -253,9 +271,14 @@ static int snd_bcm2835_spdif_stream_get( + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); + int i; + ++ if (mutex_lock_interruptible(&chip->audio_mutex)) ++ return -EINTR; ++ + for (i = 0; i < 4; i++) + ucontrol->value.iec958.status[i] = + (chip->spdif_status >> (i * 8)) & 0xff; ++ ++ mutex_unlock(&chip->audio_mutex); + return 0; + } + +@@ -266,11 +289,15 @@ static int snd_bcm2835_spdif_stream_put( + unsigned int val = 0; + int i, change; + ++ if (mutex_lock_interruptible(&chip->audio_mutex)) ++ return -EINTR; ++ + for (i = 0; i < 4; i++) + val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); + change = val != chip->spdif_status; + chip->spdif_status = val; + ++ mutex_unlock(&chip->audio_mutex); + return change; + } + +@@ -454,11 +481,17 @@ static int snd_bcm2835_chmap_ctl_get(str + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + struct snd_pcm_substream *substream = snd_pcm_chmap_substream(info, idx); + struct cea_channel_speaker_allocation *ch = NULL; ++ int res = 0; + int cur = 0; + int i; + +- if (!substream || !substream->runtime) +- return -ENODEV; ++ if (mutex_lock_interruptible(&chip->audio_mutex)) ++ return -EINTR; ++ ++ if (!substream || !substream->runtime) { ++ res = -ENODEV; ++ goto unlock; ++ } + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channel_allocations[i].ca_index == chip->cea_chmap) +@@ -476,7 +509,10 @@ static int snd_bcm2835_chmap_ctl_get(str + } + while (cur < 8) + ucontrol->value.integer.value[cur++] = SNDRV_CHMAP_NA; +- return 0; ++ ++unlock: ++ mutex_unlock(&chip->audio_mutex); ++ return res; + } + + static int snd_bcm2835_chmap_ctl_put(struct snd_kcontrol *kcontrol, +@@ -487,10 +523,16 @@ static int snd_bcm2835_chmap_ctl_put(str + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + struct snd_pcm_substream *substream = snd_pcm_chmap_substream(info, idx); + int i, prepared = 0, cea_chmap = -1; ++ int res = 0; + int remap[8]; + +- if (!substream || !substream->runtime) +- return -ENODEV; ++ if (mutex_lock_interruptible(&chip->audio_mutex)) ++ return -EINTR; ++ ++ if (!substream || !substream->runtime) { ++ res = -ENODEV; ++ goto unlock; ++ } + + switch (substream->runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: +@@ -500,7 +542,8 @@ static int snd_bcm2835_chmap_ctl_put(str + prepared = 1; + break; + default: +- return -EBUSY; ++ res = -EBUSY; ++ goto unlock; + } + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { +@@ -538,19 +581,26 @@ static int snd_bcm2835_chmap_ctl_put(str + } + } + +- if (cea_chmap < 0) +- return -EINVAL; ++ if (cea_chmap < 0) { ++ res = -EINVAL; ++ goto unlock; ++ } + + /* don't change the layout if another substream is active */ +- if (chip->opened != (1 << substream->number) && chip->cea_chmap != cea_chmap) +- return -EBUSY; /* unsure whether this is a good error code */ ++ if (chip->opened != (1 << substream->number) && chip->cea_chmap != cea_chmap) { ++ res = -EBUSY; /* unsure whether this is a good error code */ ++ goto unlock; ++ } + + chip->cea_chmap = cea_chmap; + for (i = 0; i < 8; i++) + chip->map_channels[i] = remap[i]; + if (prepared) + snd_bcm2835_pcm_prepare_again(substream); +- return 0; ++ ++unlock: ++ mutex_unlock(&chip->audio_mutex); ++ return res; + } + + static int snd_bcm2835_add_chmap_ctl(bcm2835_chip_t * chip) +--- a/sound/arm/bcm2835-pcm.c ++++ b/sound/arm/bcm2835-pcm.c +@@ -379,6 +379,9 @@ static int snd_bcm2835_pcm_prepare(struc + + audio_info(" .. IN\n"); + ++ if (mutex_lock_interruptible(&chip->audio_mutex)) ++ return -EINTR; ++ + snd_bcm2835_pcm_prepare_again(substream); + + bcm2835_audio_setup(alsa_stream); +@@ -401,6 +404,7 @@ static int snd_bcm2835_pcm_prepare(struc + alsa_stream->buffer_size, alsa_stream->period_size, + alsa_stream->pos, runtime->frame_bits); + ++ mutex_unlock(&chip->audio_mutex); + audio_info(" .. OUT\n"); + return 0; + } diff --git a/target/linux/brcm2708/patches-4.4/0130-rpi-update-vc_vchi_audioserv_defs.h.patch b/target/linux/brcm2708/patches-4.4/0130-rpi-update-vc_vchi_audioserv_defs.h.patch deleted file mode 100644 index 63f26aa931..0000000000 --- a/target/linux/brcm2708/patches-4.4/0130-rpi-update-vc_vchi_audioserv_defs.h.patch +++ /dev/null @@ -1,66 +0,0 @@ -From dcead34f063fc8c37ebfcfc9ab6696060c336bad Mon Sep 17 00:00:00 2001 -From: wm4 -Date: Wed, 13 Jan 2016 19:42:48 +0100 -Subject: [PATCH 130/170] rpi: update vc_vchi_audioserv_defs.h - -Add audioserv 3 extensions. The changes were taken from the paste -linked here: - -https://github.com/raspberrypi/linux/pull/1166#issuecomment-151917067 ---- - sound/arm/vc_vchi_audioserv_defs.h | 13 +++++++++++-- - 1 file changed, 11 insertions(+), 2 deletions(-) - ---- a/sound/arm/vc_vchi_audioserv_defs.h -+++ b/sound/arm/vc_vchi_audioserv_defs.h -@@ -16,7 +16,7 @@ - #define _VC_AUDIO_DEFS_H_ - - #define VC_AUDIOSERV_MIN_VER 1 --#define VC_AUDIOSERV_VER 2 -+#define VC_AUDIOSERV_VER 3 - - // FourCC code used for VCHI connection - #define VC_AUDIO_SERVER_NAME MAKE_FOURCC("AUDS") -@@ -36,6 +36,7 @@ typedef enum { - VC_AUDIO_MSG_TYPE_START, // Configure audio - VC_AUDIO_MSG_TYPE_STOP, // Configure audio - VC_AUDIO_MSG_TYPE_WRITE, // Configure audio -+ VC_AUDIO_MSG_TYPE_LATENCY, // request latency in cycles - VC_AUDIO_MSG_TYPE_MAX - } VC_AUDIO_MSG_TYPE; - -@@ -44,6 +45,7 @@ typedef struct { - uint32_t channels; - uint32_t samplerate; - uint32_t bps; -+ uint32_t channelmap; - - } VC_AUDIO_CONFIG_T; - -@@ -84,6 +86,12 @@ typedef struct { - uint16_t max_packet; - } VC_AUDIO_WRITE_T; - -+// query latency in samples of sink -+typedef struct -+{ -+ uint32_t dummy; -+} VC_AUDIO_LATENCY_T; -+ - // Generic result for a request (VC->HOST) - typedef struct { - int32_t success; // Success value -@@ -108,9 +116,10 @@ typedef struct { - VC_AUDIO_START_T start; - VC_AUDIO_STOP_T stop; - VC_AUDIO_WRITE_T write; -+ VC_AUDIO_LATENCY_T latency; - VC_AUDIO_RESULT_T result; - VC_AUDIO_COMPLETE_T complete; - } u; - } VC_AUDIO_MSG_T; - --#endif // _VC_AUDIO_DEFS_H_ -+#endif // _VC_AUDIO_DEFS_H_ -\ No newline at end of file diff --git a/target/linux/brcm2708/patches-4.4/0131-bcm2835-always-use-2-4-8-channels-for-multichannel-l.patch b/target/linux/brcm2708/patches-4.4/0131-bcm2835-always-use-2-4-8-channels-for-multichannel-l.patch new file mode 100644 index 0000000000..76dede73fa --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0131-bcm2835-always-use-2-4-8-channels-for-multichannel-l.patch @@ -0,0 +1,139 @@ +From 06931f74092d86087144f070b06a4444df8b444b Mon Sep 17 00:00:00 2001 +From: wm4 +Date: Wed, 13 Jan 2016 19:44:03 +0100 +Subject: [PATCH 131/232] bcm2835: always use 2/4/8 channels for multichannel + layouts + +Pad the unused channels with NA. This means userspace needs to write +additional, silent padding channels, which is not ideal, but better +than noise. + +Works around noise at the following channel counts: 3, 5, 6, 7 +--- + sound/arm/bcm2835-ctl.c | 89 +++++++++++++++++++++++++------------------------ + 1 file changed, 45 insertions(+), 44 deletions(-) + +--- a/sound/arm/bcm2835-ctl.c ++++ b/sound/arm/bcm2835-ctl.c +@@ -349,6 +349,7 @@ struct cea_channel_speaker_allocation { + #define FRW SNDRV_CHMAP_FRW + #define TC SNDRV_CHMAP_TC + #define FCH SNDRV_CHMAP_TFC ++#define NA SNDRV_CHMAP_NA + + /* + * CEA-861 channel maps +@@ -356,69 +357,69 @@ struct cea_channel_speaker_allocation { + * Stolen from sound/pci/hda/patch_hdmi.c + * (unlike the source, this uses SNDRV_* constants directly, as by the + * map_tables array in patch_hdmi.c) +- * Unknown entries use 0, which unfortunately is SNDRV_CHMAP_UNKNOWN instead +- * of SNDRV_CHMAP_NA. ++ * Entries which do not have a physical output channel use 0. Entries which ++ * require userspace to output silence use NA (SNDRV_CHMAP_NA). + */ + static struct cea_channel_speaker_allocation channel_allocations[] = { + /* channel: 7 6 5 4 3 2 1 0 */ + { .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, + /* 2.1 */ +-{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, ++{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, NA, LFE, FR, FL } }, + /* Dolby Surround */ +-{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, ++{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, NA, FR, FL } }, + /* surround40 */ +-{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, ++{ .ca_index = 0x08, .speakers = { NA, NA, RR, RL, NA, NA, FR, FL } }, + /* surround41 */ +-{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, ++{ .ca_index = 0x09, .speakers = { NA, NA, RR, RL, NA, LFE, FR, FL } }, + /* surround50 */ +-{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x0a, .speakers = { NA, NA, RR, RL, FC, NA, FR, FL } }, + /* surround51 */ +-{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x0b, .speakers = { NA, NA, RR, RL, FC, LFE, FR, FL } }, + /* 6.1 */ +-{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x0f, .speakers = { NA, RC, RR, RL, FC, LFE, FR, FL } }, + /* surround71 */ + { .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, + +-{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, +-{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, +-{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, +-{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, +-{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, +-{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, +-{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, +-{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, +-{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, +-{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, +-{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, +-{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, +-{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, +-{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, +-{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, +-{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, +-{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, +-{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, +-{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, +-{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, +-{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, +-{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x03, .speakers = { NA, NA, NA, NA, FC, LFE, FR, FL } }, ++{ .ca_index = 0x04, .speakers = { NA, NA, NA, RC, NA, NA, FR, FL } }, ++{ .ca_index = 0x05, .speakers = { NA, NA, NA, RC, NA, LFE, FR, FL } }, ++{ .ca_index = 0x06, .speakers = { NA, NA, NA, RC, FC, NA, FR, FL } }, ++{ .ca_index = 0x07, .speakers = { NA, NA, NA, RC, FC, LFE, FR, FL } }, ++{ .ca_index = 0x0c, .speakers = { NA, RC, RR, RL, NA, NA, FR, FL } }, ++{ .ca_index = 0x0d, .speakers = { NA, RC, RR, RL, NA, LFE, FR, FL } }, ++{ .ca_index = 0x0e, .speakers = { NA, RC, RR, RL, FC, NA, FR, FL } }, ++{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, NA, NA, FR, FL } }, ++{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, NA, LFE, FR, FL } }, ++{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, NA, FR, FL } }, ++{ .ca_index = 0x14, .speakers = { FRC, FLC, NA, NA, NA, NA, FR, FL } }, ++{ .ca_index = 0x15, .speakers = { FRC, FLC, NA, NA, NA, LFE, FR, FL } }, ++{ .ca_index = 0x16, .speakers = { FRC, FLC, NA, NA, FC, NA, FR, FL } }, ++{ .ca_index = 0x17, .speakers = { FRC, FLC, NA, NA, FC, LFE, FR, FL } }, ++{ .ca_index = 0x18, .speakers = { FRC, FLC, NA, RC, NA, NA, FR, FL } }, ++{ .ca_index = 0x19, .speakers = { FRC, FLC, NA, RC, NA, LFE, FR, FL } }, ++{ .ca_index = 0x1a, .speakers = { FRC, FLC, NA, RC, FC, NA, FR, FL } }, ++{ .ca_index = 0x1b, .speakers = { FRC, FLC, NA, RC, FC, LFE, FR, FL } }, ++{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, NA, NA, FR, FL } }, ++{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, NA, LFE, FR, FL } }, ++{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, NA, FR, FL } }, + { .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, +-{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, +-{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, +-{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, +-{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, +-{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, +-{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, +-{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, +-{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, +-{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x20, .speakers = { NA, FCH, RR, RL, FC, NA, FR, FL } }, ++{ .ca_index = 0x21, .speakers = { NA, FCH, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x22, .speakers = { TC, NA, RR, RL, FC, NA, FR, FL } }, ++{ .ca_index = 0x23, .speakers = { TC, NA, RR, RL, FC, LFE, FR, FL } }, ++{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, NA, NA, FR, FL } }, ++{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, NA, LFE, FR, FL } }, ++{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, NA, NA, FR, FL } }, ++{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, NA, LFE, FR, FL } }, ++{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, NA, FR, FL } }, + { .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, +-{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, NA, FR, FL } }, + { .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, +-{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, NA, FR, FL } }, + { .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, +-{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, NA, FR, FL } }, + { .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, +-{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, ++{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, NA, FR, FL } }, + { .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, + }; + diff --git a/target/linux/brcm2708/patches-4.4/0131-bcm2835-implement-channel-map-API.patch b/target/linux/brcm2708/patches-4.4/0131-bcm2835-implement-channel-map-API.patch deleted file mode 100644 index bdcdb8a558..0000000000 --- a/target/linux/brcm2708/patches-4.4/0131-bcm2835-implement-channel-map-API.patch +++ /dev/null @@ -1,421 +0,0 @@ -From 7c7d2db494fbf1fd0b014dab0bb4c5c740f90442 Mon Sep 17 00:00:00 2001 -From: wm4 -Date: Wed, 13 Jan 2016 19:43:12 +0100 -Subject: [PATCH 131/170] bcm2835: implement channel map API - -Report all layouts supported by the HDMI protocol to userspace. -Make the videocore set the correct layout according to the -userspace request. - -Some code taken from patch_hdmi.c. In particular, the HDMI channel -layout table was copied without changes - with the idea in mind that -hopefully it can be shared one day. Or at least updating it will be -simpler. - -In my tests, everything appears to work, except when outputting -FL FR RL RR. Then my receiver outputs RL on both the RL and RR -speakers, while RR is never heard. ---- - sound/arm/bcm2835-ctl.c | 276 ++++++++++++++++++++++++++++++++++++++++++++++ - sound/arm/bcm2835-pcm.c | 22 +++- - sound/arm/bcm2835-vchiq.c | 13 +++ - sound/arm/bcm2835.h | 4 + - 4 files changed, 311 insertions(+), 4 deletions(-) - ---- a/sound/arm/bcm2835-ctl.c -+++ b/sound/arm/bcm2835-ctl.c -@@ -300,6 +300,281 @@ static struct snd_kcontrol_new snd_bcm28 - }, - }; - -+struct cea_channel_speaker_allocation { -+ int ca_index; -+ int speakers[8]; -+}; -+ -+#define FL SNDRV_CHMAP_FL -+#define FR SNDRV_CHMAP_FR -+#define RL SNDRV_CHMAP_RL -+#define RR SNDRV_CHMAP_RR -+#define LFE SNDRV_CHMAP_LFE -+#define FC SNDRV_CHMAP_FC -+#define RLC SNDRV_CHMAP_RLC -+#define RRC SNDRV_CHMAP_RRC -+#define RC SNDRV_CHMAP_RC -+#define FLC SNDRV_CHMAP_FLC -+#define FRC SNDRV_CHMAP_FRC -+#define FLH SNDRV_CHMAP_TFL -+#define FRH SNDRV_CHMAP_TFR -+#define FLW SNDRV_CHMAP_FLW -+#define FRW SNDRV_CHMAP_FRW -+#define TC SNDRV_CHMAP_TC -+#define FCH SNDRV_CHMAP_TFC -+ -+/* -+ * CEA-861 channel maps -+ * -+ * Stolen from sound/pci/hda/patch_hdmi.c -+ * (unlike the source, this uses SNDRV_* constants directly, as by the -+ * map_tables array in patch_hdmi.c) -+ * Unknown entries use 0, which unfortunately is SNDRV_CHMAP_UNKNOWN instead -+ * of SNDRV_CHMAP_NA. -+ */ -+static struct cea_channel_speaker_allocation channel_allocations[] = { -+/* channel: 7 6 5 4 3 2 1 0 */ -+{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, -+ /* 2.1 */ -+{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, -+ /* Dolby Surround */ -+{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, -+ /* surround40 */ -+{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, -+ /* surround41 */ -+{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, -+ /* surround50 */ -+{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, -+ /* surround51 */ -+{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, -+ /* 6.1 */ -+{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, -+ /* surround71 */ -+{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, -+ -+{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, -+{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, -+{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, -+{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, -+{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, -+{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, -+{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, -+{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, -+{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, -+{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, -+{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, -+{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, -+{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, -+{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, -+{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, -+{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, -+{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, -+{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, -+{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, -+{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, -+{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, -+{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, -+{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, -+{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, -+{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, -+{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, -+{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, -+{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, -+{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, -+{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, -+{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, -+}; -+ -+static int snd_bcm2835_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, -+ unsigned int size, unsigned int __user *tlv) -+{ -+ unsigned int __user *dst; -+ int count = 0; -+ int i; -+ -+ if (size < 8) -+ return -ENOMEM; -+ if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) -+ return -EFAULT; -+ size -= 8; -+ dst = tlv + 2; -+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { -+ struct cea_channel_speaker_allocation *ch = &channel_allocations[i]; -+ int num_chs = 0; -+ int chs_bytes; -+ int c; -+ -+ for (c = 0; c < 8; c++) { -+ if (ch->speakers[c]) -+ num_chs++; -+ } -+ -+ chs_bytes = num_chs * 4; -+ if (size < 8) -+ return -ENOMEM; -+ if (put_user(SNDRV_CTL_TLVT_CHMAP_FIXED, dst) || -+ put_user(chs_bytes, dst + 1)) -+ return -EFAULT; -+ dst += 2; -+ size -= 8; -+ count += 8; -+ if (size < chs_bytes) -+ return -ENOMEM; -+ size -= chs_bytes; -+ count += chs_bytes; -+ for (c = 0; c < 8; c++) { -+ int sp = ch->speakers[7 - c]; -+ if (sp) { -+ if (put_user(sp, dst)) -+ return -EFAULT; -+ dst++; -+ } -+ } -+ } -+ if (put_user(count, tlv + 1)) -+ return -EFAULT; -+ return 0; -+} -+ -+static int snd_bcm2835_chmap_ctl_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); -+ bcm2835_chip_t *chip = info->private_data; -+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); -+ struct snd_pcm_substream *substream = snd_pcm_chmap_substream(info, idx); -+ struct cea_channel_speaker_allocation *ch = NULL; -+ int cur = 0; -+ int i; -+ -+ if (!substream || !substream->runtime) -+ return -ENODEV; -+ -+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { -+ if (channel_allocations[i].ca_index == chip->cea_chmap) -+ ch = &channel_allocations[i]; -+ } -+ -+ /* If no layout was set yet, return a dummy. Apparently the userspace -+ * API will be confused if we don't. */ -+ if (!ch) -+ ch = &channel_allocations[0]; -+ -+ for (i = 0; i < 8; i++) { -+ if (ch->speakers[7 - i]) -+ ucontrol->value.integer.value[cur++] = ch->speakers[7 - i]; -+ } -+ while (cur < 8) -+ ucontrol->value.integer.value[cur++] = SNDRV_CHMAP_NA; -+ return 0; -+} -+ -+static int snd_bcm2835_chmap_ctl_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); -+ bcm2835_chip_t *chip = info->private_data; -+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); -+ struct snd_pcm_substream *substream = snd_pcm_chmap_substream(info, idx); -+ int i, prepared = 0, cea_chmap = -1; -+ int remap[8]; -+ -+ if (!substream || !substream->runtime) -+ return -ENODEV; -+ -+ switch (substream->runtime->status->state) { -+ case SNDRV_PCM_STATE_OPEN: -+ case SNDRV_PCM_STATE_SETUP: -+ break; -+ case SNDRV_PCM_STATE_PREPARED: -+ prepared = 1; -+ break; -+ default: -+ return -EBUSY; -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { -+ struct cea_channel_speaker_allocation *ch = &channel_allocations[i]; -+ int matches = 1; -+ int cur = 0; -+ int x; -+ memset(remap, 0, sizeof(remap)); -+ for (x = 0; x < substream->runtime->channels; x++) { -+ int sp = ucontrol->value.integer.value[x]; -+ while (cur < 8 && !ch->speakers[7 - cur]) -+ cur++; -+ if (cur >= 8) { -+ /* user has more channels than ch */ -+ matches = 0; -+ break; -+ } -+ if (ch->speakers[7 - cur] != sp) { -+ matches = 0; -+ break; -+ } -+ remap[x] = cur; -+ cur++; -+ } -+ for (x = cur; x < 8; x++) { -+ if (ch->speakers[7 - x]) { -+ /* ch has more channels than user */ -+ matches = 0; -+ break; -+ } -+ } -+ if (matches) { -+ cea_chmap = ch->ca_index; -+ break; -+ } -+ } -+ -+ if (cea_chmap < 0) -+ return -EINVAL; -+ -+ /* don't change the layout if another substream is active */ -+ if (chip->opened != (1 << substream->number) && chip->cea_chmap != cea_chmap) -+ return -EBUSY; /* unsure whether this is a good error code */ -+ -+ chip->cea_chmap = cea_chmap; -+ for (i = 0; i < 8; i++) -+ chip->map_channels[i] = remap[i]; -+ if (prepared) -+ snd_bcm2835_pcm_prepare_again(substream); -+ return 0; -+} -+ -+static int snd_bcm2835_add_chmap_ctl(bcm2835_chip_t * chip) -+{ -+ struct snd_pcm_chmap *chmap; -+ struct snd_kcontrol *kctl; -+ int err, i; -+ -+ err = snd_pcm_add_chmap_ctls(chip->pcm, -+ SNDRV_PCM_STREAM_PLAYBACK, -+ NULL, 8, 0, &chmap); -+ if (err < 0) -+ return err; -+ /* override handlers */ -+ chmap->private_data = chip; -+ kctl = chmap->kctl; -+ for (i = 0; i < kctl->count; i++) -+ kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; -+ kctl->get = snd_bcm2835_chmap_ctl_get; -+ kctl->put = snd_bcm2835_chmap_ctl_put; -+ kctl->tlv.c = snd_bcm2835_chmap_ctl_tlv; -+ return 0; -+} -+ - int snd_bcm2835_new_ctl(bcm2835_chip_t * chip) - { - int err; -@@ -313,6 +588,7 @@ int snd_bcm2835_new_ctl(bcm2835_chip_t * - if (err < 0) - return err; - } -+ snd_bcm2835_add_chmap_ctl(chip); - for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_spdif); idx++) { - err = snd_ctl_add(chip->card, - snd_ctl_new1(&snd_bcm2835_spdif[idx], chip)); ---- a/sound/arm/bcm2835-pcm.c -+++ b/sound/arm/bcm2835-pcm.c -@@ -231,6 +231,9 @@ static int snd_bcm2835_playback_open_gen - - chip->alsa_stream[idx] = alsa_stream; - -+ if (!chip->opened) -+ chip->cea_chmap = -1; -+ - chip->opened |= (1 << idx); - alsa_stream->open = 1; - alsa_stream->draining = 1; -@@ -341,8 +344,7 @@ static int snd_bcm2835_pcm_hw_free(struc - return snd_pcm_lib_free_pages(substream); - } - --/* prepare callback */ --static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream) -+int snd_bcm2835_pcm_prepare_again(struct snd_pcm_substream *substream) - { - bcm2835_chip_t *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; -@@ -350,8 +352,6 @@ static int snd_bcm2835_pcm_prepare(struc - int channels; - int err; - -- audio_info(" .. IN\n"); -- - /* notify the vchiq that it should enter spdif passthrough mode by - * setting channels=0 (see - * https://github.com/raspberrypi/linux/issues/528) */ -@@ -367,6 +367,20 @@ static int snd_bcm2835_pcm_prepare(struc - audio_error(" error setting hw params\n"); - } - -+ return err; -+} -+ -+/* prepare callback */ -+static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream) -+{ -+ bcm2835_chip_t *chip = snd_pcm_substream_chip(substream); -+ struct snd_pcm_runtime *runtime = substream->runtime; -+ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; -+ -+ audio_info(" .. IN\n"); -+ -+ snd_bcm2835_pcm_prepare_again(substream); -+ - bcm2835_audio_setup(alsa_stream); - - /* in preparation of the stream, set the controls (volume level) of the stream */ ---- a/sound/arm/bcm2835-vchiq.c -+++ b/sound/arm/bcm2835-vchiq.c -@@ -570,6 +570,8 @@ int bcm2835_audio_set_params(bcm2835_als - VC_AUDIO_MSG_T m; - AUDIO_INSTANCE_T *instance = alsa_stream->instance; - int32_t success; -+ uint32_t chmap_value; -+ int i; - int ret; - LOG_DBG(" .. IN\n"); - -@@ -593,10 +595,21 @@ int bcm2835_audio_set_params(bcm2835_als - - instance->result = -1; - -+ if (alsa_stream->chip->cea_chmap >= 0) { -+ chmap_value = (unsigned)alsa_stream->chip->cea_chmap << 24; -+ } else { -+ chmap_value = 0; /* force stereo */ -+ for (i = 0; i < 8; i++) -+ alsa_stream->chip->map_channels[i] = i; -+ } -+ for (i = 0; i < 8; i++) -+ chmap_value |= alsa_stream->chip->map_channels[i] << (i * 3); -+ - m.type = VC_AUDIO_MSG_TYPE_CONFIG; - m.u.config.channels = channels; - m.u.config.samplerate = samplerate; - m.u.config.bps = bps; -+ m.u.config.channelmap = chmap_value; - - /* Create the message available completion */ - init_completion(&instance->msg_avail_comp); ---- a/sound/arm/bcm2835.h -+++ b/sound/arm/bcm2835.h -@@ -107,6 +107,8 @@ typedef struct bcm2835_chip { - int old_volume; /* stores the volume value whist muted */ - int dest; - int mute; -+ int cea_chmap; /* currently requested Audio InfoFrame Data Byte 4 */ -+ int map_channels[8]; - - unsigned int opened; - unsigned int spdif_status; -@@ -149,6 +151,8 @@ int snd_bcm2835_new_ctl(bcm2835_chip_t * - int snd_bcm2835_new_pcm(bcm2835_chip_t * chip); - int snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip); - -+int snd_bcm2835_pcm_prepare_again(struct snd_pcm_substream *substream); -+ - int bcm2835_audio_open(bcm2835_alsa_stream_t * alsa_stream); - int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream); - int bcm2835_audio_set_params(bcm2835_alsa_stream_t * alsa_stream, diff --git a/target/linux/brcm2708/patches-4.4/0132-bcm2835-access-controls-under-the-audio-mutex.patch b/target/linux/brcm2708/patches-4.4/0132-bcm2835-access-controls-under-the-audio-mutex.patch deleted file mode 100644 index d6d9cf297c..0000000000 --- a/target/linux/brcm2708/patches-4.4/0132-bcm2835-access-controls-under-the-audio-mutex.patch +++ /dev/null @@ -1,237 +0,0 @@ -From 2a64337156d0f84537e04338b2ebd89eb6ec1f09 Mon Sep 17 00:00:00 2001 -From: wm4 -Date: Wed, 13 Jan 2016 19:43:35 +0100 -Subject: [PATCH 132/170] bcm2835: access controls under the audio mutex - -I don't think the ALSA framework provides any kind of automatic -synchronization within the control callbacks. We most likely need -to ensure this manually, so add locking around all access to shared -mutable data. In particular, bcm2835_audio_set_ctls() should -probably always be called under our own audio lock. ---- - sound/arm/bcm2835-ctl.c | 74 +++++++++++++++++++++++++++++++++++++++++-------- - sound/arm/bcm2835-pcm.c | 4 +++ - 2 files changed, 66 insertions(+), 12 deletions(-) - ---- a/sound/arm/bcm2835-ctl.c -+++ b/sound/arm/bcm2835-ctl.c -@@ -94,6 +94,9 @@ static int snd_bcm2835_ctl_get(struct sn - { - struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); - -+ if (mutex_lock_interruptible(&chip->audio_mutex)) -+ return -EINTR; -+ - BUG_ON(!chip && !(chip->avail_substreams & AVAIL_SUBSTREAMS_MASK)); - - if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) -@@ -103,6 +106,7 @@ static int snd_bcm2835_ctl_get(struct sn - else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) - ucontrol->value.integer.value[0] = chip->dest; - -+ mutex_unlock(&chip->audio_mutex); - return 0; - } - -@@ -112,11 +116,15 @@ static int snd_bcm2835_ctl_put(struct sn - struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); - int changed = 0; - -+ if (mutex_lock_interruptible(&chip->audio_mutex)) -+ return -EINTR; -+ - if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) { - audio_info("Volume change attempted.. volume = %d new_volume = %d\n", chip->volume, (int)ucontrol->value.integer.value[0]); - if (chip->mute == CTRL_VOL_MUTE) { - /* changed = toggle_mute(chip, CTRL_VOL_UNMUTE); */ -- return 1; /* should return 0 to signify no change but the mixer takes this as the opposite sign (no idea why) */ -+ changed = 1; /* should return 0 to signify no change but the mixer takes this as the opposite sign (no idea why) */ -+ goto unlock; - } - if (changed - || (ucontrol->value.integer.value[0] != chip2alsa(chip->volume))) { -@@ -142,6 +150,8 @@ static int snd_bcm2835_ctl_put(struct sn - printk(KERN_ERR "Failed to set ALSA controls..\n"); - } - -+unlock: -+ mutex_unlock(&chip->audio_mutex); - return changed; - } - -@@ -198,10 +208,14 @@ static int snd_bcm2835_spdif_default_get - struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); - int i; - -+ if (mutex_lock_interruptible(&chip->audio_mutex)) -+ return -EINTR; -+ - for (i = 0; i < 4; i++) - ucontrol->value.iec958.status[i] = - (chip->spdif_status >> (i * 8)) && 0xff; - -+ mutex_unlock(&chip->audio_mutex); - return 0; - } - -@@ -212,12 +226,16 @@ static int snd_bcm2835_spdif_default_put - unsigned int val = 0; - int i, change; - -+ if (mutex_lock_interruptible(&chip->audio_mutex)) -+ return -EINTR; -+ - for (i = 0; i < 4; i++) - val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); - - change = val != chip->spdif_status; - chip->spdif_status = val; - -+ mutex_unlock(&chip->audio_mutex); - return change; - } - -@@ -253,9 +271,14 @@ static int snd_bcm2835_spdif_stream_get( - struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); - int i; - -+ if (mutex_lock_interruptible(&chip->audio_mutex)) -+ return -EINTR; -+ - for (i = 0; i < 4; i++) - ucontrol->value.iec958.status[i] = - (chip->spdif_status >> (i * 8)) & 0xff; -+ -+ mutex_unlock(&chip->audio_mutex); - return 0; - } - -@@ -266,11 +289,15 @@ static int snd_bcm2835_spdif_stream_put( - unsigned int val = 0; - int i, change; - -+ if (mutex_lock_interruptible(&chip->audio_mutex)) -+ return -EINTR; -+ - for (i = 0; i < 4; i++) - val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); - change = val != chip->spdif_status; - chip->spdif_status = val; - -+ mutex_unlock(&chip->audio_mutex); - return change; - } - -@@ -454,11 +481,17 @@ static int snd_bcm2835_chmap_ctl_get(str - unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - struct snd_pcm_substream *substream = snd_pcm_chmap_substream(info, idx); - struct cea_channel_speaker_allocation *ch = NULL; -+ int res = 0; - int cur = 0; - int i; - -- if (!substream || !substream->runtime) -- return -ENODEV; -+ if (mutex_lock_interruptible(&chip->audio_mutex)) -+ return -EINTR; -+ -+ if (!substream || !substream->runtime) { -+ res = -ENODEV; -+ goto unlock; -+ } - - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if (channel_allocations[i].ca_index == chip->cea_chmap) -@@ -476,7 +509,10 @@ static int snd_bcm2835_chmap_ctl_get(str - } - while (cur < 8) - ucontrol->value.integer.value[cur++] = SNDRV_CHMAP_NA; -- return 0; -+ -+unlock: -+ mutex_unlock(&chip->audio_mutex); -+ return res; - } - - static int snd_bcm2835_chmap_ctl_put(struct snd_kcontrol *kcontrol, -@@ -487,10 +523,16 @@ static int snd_bcm2835_chmap_ctl_put(str - unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - struct snd_pcm_substream *substream = snd_pcm_chmap_substream(info, idx); - int i, prepared = 0, cea_chmap = -1; -+ int res = 0; - int remap[8]; - -- if (!substream || !substream->runtime) -- return -ENODEV; -+ if (mutex_lock_interruptible(&chip->audio_mutex)) -+ return -EINTR; -+ -+ if (!substream || !substream->runtime) { -+ res = -ENODEV; -+ goto unlock; -+ } - - switch (substream->runtime->status->state) { - case SNDRV_PCM_STATE_OPEN: -@@ -500,7 +542,8 @@ static int snd_bcm2835_chmap_ctl_put(str - prepared = 1; - break; - default: -- return -EBUSY; -+ res = -EBUSY; -+ goto unlock; - } - - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { -@@ -538,19 +581,26 @@ static int snd_bcm2835_chmap_ctl_put(str - } - } - -- if (cea_chmap < 0) -- return -EINVAL; -+ if (cea_chmap < 0) { -+ res = -EINVAL; -+ goto unlock; -+ } - - /* don't change the layout if another substream is active */ -- if (chip->opened != (1 << substream->number) && chip->cea_chmap != cea_chmap) -- return -EBUSY; /* unsure whether this is a good error code */ -+ if (chip->opened != (1 << substream->number) && chip->cea_chmap != cea_chmap) { -+ res = -EBUSY; /* unsure whether this is a good error code */ -+ goto unlock; -+ } - - chip->cea_chmap = cea_chmap; - for (i = 0; i < 8; i++) - chip->map_channels[i] = remap[i]; - if (prepared) - snd_bcm2835_pcm_prepare_again(substream); -- return 0; -+ -+unlock: -+ mutex_unlock(&chip->audio_mutex); -+ return res; - } - - static int snd_bcm2835_add_chmap_ctl(bcm2835_chip_t * chip) ---- a/sound/arm/bcm2835-pcm.c -+++ b/sound/arm/bcm2835-pcm.c -@@ -379,6 +379,9 @@ static int snd_bcm2835_pcm_prepare(struc - - audio_info(" .. IN\n"); - -+ if (mutex_lock_interruptible(&chip->audio_mutex)) -+ return -EINTR; -+ - snd_bcm2835_pcm_prepare_again(substream); - - bcm2835_audio_setup(alsa_stream); -@@ -401,6 +404,7 @@ static int snd_bcm2835_pcm_prepare(struc - alsa_stream->buffer_size, alsa_stream->period_size, - alsa_stream->pos, runtime->frame_bits); - -+ mutex_unlock(&chip->audio_mutex); - audio_info(" .. OUT\n"); - return 0; - } diff --git a/target/linux/brcm2708/patches-4.4/0132-bcm2835-only-allow-stereo-if-analogue-jack-is-select.patch b/target/linux/brcm2708/patches-4.4/0132-bcm2835-only-allow-stereo-if-analogue-jack-is-select.patch new file mode 100644 index 0000000000..a7b790694b --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0132-bcm2835-only-allow-stereo-if-analogue-jack-is-select.patch @@ -0,0 +1,62 @@ +From 5f7049894f47b3836838cd68e29ee883179c80b3 Mon Sep 17 00:00:00 2001 +From: wm4 +Date: Wed, 13 Jan 2016 19:44:24 +0100 +Subject: [PATCH 132/232] bcm2835: only allow stereo if analogue jack is + selected + +Sending more than 2 channels to videocore while outputting to analogue +mysteriously outputs heavy artifacts. So just paint it over with a +hack: if analogue is explicitly selected as destination, do not +reporting support for anything other than stereo. + +I'm not sure how to deal with the auto case (destination 0). There's +probably way to retrieve this and even to listen to plug events, but +I didn't find one yet, and it's probably not worth the trouble. Just +don't use this setting, I guess. Unless you like noise. + +Changing the setting while an audio stream is active also doesn't +work properly. We could probably interrupt running streams by +returning ENODEV or using kernel hotplug stuff (maybe), but that +also doesn't seem worth the trouble. +--- + sound/arm/bcm2835-ctl.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +--- a/sound/arm/bcm2835-ctl.c ++++ b/sound/arm/bcm2835-ctl.c +@@ -423,9 +423,16 @@ static struct cea_channel_speaker_alloca + { .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, + }; + ++static int uses_analogue(bcm2835_chip_t *chip) ++{ ++ return chip->dest == 1; ++} ++ + static int snd_bcm2835_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv) + { ++ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); ++ bcm2835_chip_t *chip = info->private_data; + unsigned int __user *dst; + int count = 0; + int i; +@@ -442,6 +449,9 @@ static int snd_bcm2835_chmap_ctl_tlv(str + int chs_bytes; + int c; + ++ if (i > 0 && uses_analogue(chip)) ++ break; ++ + for (c = 0; c < 8; c++) { + if (ch->speakers[c]) + num_chs++; +@@ -552,6 +562,8 @@ static int snd_bcm2835_chmap_ctl_put(str + int matches = 1; + int cur = 0; + int x; ++ if (i > 0 && uses_analogue(chip)) ++ break; + memset(remap, 0, sizeof(remap)); + for (x = 0; x < substream->runtime->channels; x++) { + int sp = ucontrol->value.integer.value[x]; diff --git a/target/linux/brcm2708/patches-4.4/0133-bcm2835-always-use-2-4-8-channels-for-multichannel-l.patch b/target/linux/brcm2708/patches-4.4/0133-bcm2835-always-use-2-4-8-channels-for-multichannel-l.patch deleted file mode 100644 index 72304988ef..0000000000 --- a/target/linux/brcm2708/patches-4.4/0133-bcm2835-always-use-2-4-8-channels-for-multichannel-l.patch +++ /dev/null @@ -1,139 +0,0 @@ -From 6e781f631f7e23f7d88256d020d83950c9bf213a Mon Sep 17 00:00:00 2001 -From: wm4 -Date: Wed, 13 Jan 2016 19:44:03 +0100 -Subject: [PATCH 133/170] bcm2835: always use 2/4/8 channels for multichannel - layouts - -Pad the unused channels with NA. This means userspace needs to write -additional, silent padding channels, which is not ideal, but better -than noise. - -Works around noise at the following channel counts: 3, 5, 6, 7 ---- - sound/arm/bcm2835-ctl.c | 89 +++++++++++++++++++++++++------------------------ - 1 file changed, 45 insertions(+), 44 deletions(-) - ---- a/sound/arm/bcm2835-ctl.c -+++ b/sound/arm/bcm2835-ctl.c -@@ -349,6 +349,7 @@ struct cea_channel_speaker_allocation { - #define FRW SNDRV_CHMAP_FRW - #define TC SNDRV_CHMAP_TC - #define FCH SNDRV_CHMAP_TFC -+#define NA SNDRV_CHMAP_NA - - /* - * CEA-861 channel maps -@@ -356,69 +357,69 @@ struct cea_channel_speaker_allocation { - * Stolen from sound/pci/hda/patch_hdmi.c - * (unlike the source, this uses SNDRV_* constants directly, as by the - * map_tables array in patch_hdmi.c) -- * Unknown entries use 0, which unfortunately is SNDRV_CHMAP_UNKNOWN instead -- * of SNDRV_CHMAP_NA. -+ * Entries which do not have a physical output channel use 0. Entries which -+ * require userspace to output silence use NA (SNDRV_CHMAP_NA). - */ - static struct cea_channel_speaker_allocation channel_allocations[] = { - /* channel: 7 6 5 4 3 2 1 0 */ - { .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, - /* 2.1 */ --{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, -+{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, NA, LFE, FR, FL } }, - /* Dolby Surround */ --{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, -+{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, NA, FR, FL } }, - /* surround40 */ --{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, -+{ .ca_index = 0x08, .speakers = { NA, NA, RR, RL, NA, NA, FR, FL } }, - /* surround41 */ --{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, -+{ .ca_index = 0x09, .speakers = { NA, NA, RR, RL, NA, LFE, FR, FL } }, - /* surround50 */ --{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x0a, .speakers = { NA, NA, RR, RL, FC, NA, FR, FL } }, - /* surround51 */ --{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, -+{ .ca_index = 0x0b, .speakers = { NA, NA, RR, RL, FC, LFE, FR, FL } }, - /* 6.1 */ --{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, -+{ .ca_index = 0x0f, .speakers = { NA, RC, RR, RL, FC, LFE, FR, FL } }, - /* surround71 */ - { .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, - --{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, --{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, --{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, --{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, --{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, --{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, --{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, --{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, --{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, --{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, --{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, --{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, --{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, --{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, --{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, --{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, --{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, --{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, --{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, --{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, --{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, --{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x03, .speakers = { NA, NA, NA, NA, FC, LFE, FR, FL } }, -+{ .ca_index = 0x04, .speakers = { NA, NA, NA, RC, NA, NA, FR, FL } }, -+{ .ca_index = 0x05, .speakers = { NA, NA, NA, RC, NA, LFE, FR, FL } }, -+{ .ca_index = 0x06, .speakers = { NA, NA, NA, RC, FC, NA, FR, FL } }, -+{ .ca_index = 0x07, .speakers = { NA, NA, NA, RC, FC, LFE, FR, FL } }, -+{ .ca_index = 0x0c, .speakers = { NA, RC, RR, RL, NA, NA, FR, FL } }, -+{ .ca_index = 0x0d, .speakers = { NA, RC, RR, RL, NA, LFE, FR, FL } }, -+{ .ca_index = 0x0e, .speakers = { NA, RC, RR, RL, FC, NA, FR, FL } }, -+{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, NA, NA, FR, FL } }, -+{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, NA, LFE, FR, FL } }, -+{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, NA, FR, FL } }, -+{ .ca_index = 0x14, .speakers = { FRC, FLC, NA, NA, NA, NA, FR, FL } }, -+{ .ca_index = 0x15, .speakers = { FRC, FLC, NA, NA, NA, LFE, FR, FL } }, -+{ .ca_index = 0x16, .speakers = { FRC, FLC, NA, NA, FC, NA, FR, FL } }, -+{ .ca_index = 0x17, .speakers = { FRC, FLC, NA, NA, FC, LFE, FR, FL } }, -+{ .ca_index = 0x18, .speakers = { FRC, FLC, NA, RC, NA, NA, FR, FL } }, -+{ .ca_index = 0x19, .speakers = { FRC, FLC, NA, RC, NA, LFE, FR, FL } }, -+{ .ca_index = 0x1a, .speakers = { FRC, FLC, NA, RC, FC, NA, FR, FL } }, -+{ .ca_index = 0x1b, .speakers = { FRC, FLC, NA, RC, FC, LFE, FR, FL } }, -+{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, NA, NA, FR, FL } }, -+{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, NA, LFE, FR, FL } }, -+{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, NA, FR, FL } }, - { .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, --{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, --{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, --{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, --{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, --{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, --{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, --{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, --{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, --{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x20, .speakers = { NA, FCH, RR, RL, FC, NA, FR, FL } }, -+{ .ca_index = 0x21, .speakers = { NA, FCH, RR, RL, FC, LFE, FR, FL } }, -+{ .ca_index = 0x22, .speakers = { TC, NA, RR, RL, FC, NA, FR, FL } }, -+{ .ca_index = 0x23, .speakers = { TC, NA, RR, RL, FC, LFE, FR, FL } }, -+{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, NA, NA, FR, FL } }, -+{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, NA, LFE, FR, FL } }, -+{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, NA, NA, FR, FL } }, -+{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, NA, LFE, FR, FL } }, -+{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, NA, FR, FL } }, - { .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, --{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, NA, FR, FL } }, - { .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, --{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, NA, FR, FL } }, - { .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, --{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, NA, FR, FL } }, - { .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, --{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, NA, FR, FL } }, - { .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, - }; - diff --git a/target/linux/brcm2708/patches-4.4/0133-bcm2835-interpolate-audio-delay.patch b/target/linux/brcm2708/patches-4.4/0133-bcm2835-interpolate-audio-delay.patch new file mode 100644 index 0000000000..f08364bafe --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0133-bcm2835-interpolate-audio-delay.patch @@ -0,0 +1,90 @@ +From 83eca613d0eddd2c8299f114b8fe573ccaffdefc Mon Sep 17 00:00:00 2001 +From: wm4 +Date: Wed, 13 Jan 2016 19:44:47 +0100 +Subject: [PATCH 133/232] bcm2835: interpolate audio delay + +It appears the GPU only sends us a message all 10ms to update +the playback progress. Other than this, the playback position +(what SNDRV_PCM_IOCTL_DELAY will return) is not updated at all. +Userspace will see jitter up to 10ms in the audio position. + +Make this a bit nicer for userspace by interpolating the +position using the CPU clock. + +I'm not sure if setting snd_pcm_runtime.delay is the right +approach for this. Or if there is maybe an already existing +mechanism for position interpolation in the ALSA core. + +I only set SNDRV_PCM_INFO_BATCH because this appears to remove +at least one situation snd_pcm_runtime.delay is used, so I have +to worry less in which place I have to update this field, or +how it interacts with the rest of ALSA. + +In the future, it might be nice to use VC_AUDIO_MSG_TYPE_LATENCY. +One problem is that it requires sending a videocore message, and +waiting for a reply, which could make the implementation much +harder due to locking and synchronization requirements. +--- + sound/arm/bcm2835-pcm.c | 12 +++++++++++- + sound/arm/bcm2835.h | 1 + + 2 files changed, 12 insertions(+), 1 deletion(-) + +--- a/sound/arm/bcm2835-pcm.c ++++ b/sound/arm/bcm2835-pcm.c +@@ -25,7 +25,7 @@ + /* hardware definition */ + static struct snd_pcm_hardware snd_bcm2835_playback_hw = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | +- SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), ++ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, +@@ -99,6 +99,8 @@ static irqreturn_t bcm2835_playback_fifo + alsa_stream->pos %= alsa_stream->buffer_size; + } + ++ alsa_stream->interpolate_start = ktime_get_ns(); ++ + if (alsa_stream->substream) { + if (new_period) + snd_pcm_period_elapsed(alsa_stream->substream); +@@ -399,6 +401,7 @@ static int snd_bcm2835_pcm_prepare(struc + alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream); + alsa_stream->period_size = snd_pcm_lib_period_bytes(substream); + alsa_stream->pos = 0; ++ alsa_stream->interpolate_start = ktime_get_ns(); + + audio_debug("buffer_size=%d, period_size=%d pos=%d frame_bits=%d\n", + alsa_stream->buffer_size, alsa_stream->period_size, +@@ -495,6 +498,7 @@ snd_bcm2835_pcm_pointer(struct snd_pcm_s + { + struct snd_pcm_runtime *runtime = substream->runtime; + bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; ++ u64 now = ktime_get_ns(); + + audio_info(" .. IN\n"); + +@@ -503,6 +507,12 @@ snd_bcm2835_pcm_pointer(struct snd_pcm_s + frames_to_bytes(runtime, runtime->control->appl_ptr), + alsa_stream->pos); + ++ /* Give userspace better delay reporting by interpolating between GPU ++ * notifications, assuming audio speed is close enough to the clock ++ * used for ktime */ ++ if (alsa_stream->interpolate_start && alsa_stream->interpolate_start < now) ++ runtime->delay = -(int)div_u64((now - alsa_stream->interpolate_start) * runtime->rate, 1000000000); ++ + audio_info(" .. OUT\n"); + return snd_pcm_indirect_playback_pointer(substream, + &alsa_stream->pcm_indirect, +--- a/sound/arm/bcm2835.h ++++ b/sound/arm/bcm2835.h +@@ -137,6 +137,7 @@ typedef struct bcm2835_alsa_stream { + unsigned int pos; + unsigned int buffer_size; + unsigned int period_size; ++ u64 interpolate_start; + + uint32_t enable_fifo_irq; + irq_handler_t fifo_irq_handler; diff --git a/target/linux/brcm2708/patches-4.4/0134-bcm2835-only-allow-stereo-if-analogue-jack-is-select.patch b/target/linux/brcm2708/patches-4.4/0134-bcm2835-only-allow-stereo-if-analogue-jack-is-select.patch deleted file mode 100644 index 76a0e01ad1..0000000000 --- a/target/linux/brcm2708/patches-4.4/0134-bcm2835-only-allow-stereo-if-analogue-jack-is-select.patch +++ /dev/null @@ -1,62 +0,0 @@ -From aaa2a41f44a0fc141b9a983a5f9f30bb8b6dbc8b Mon Sep 17 00:00:00 2001 -From: wm4 -Date: Wed, 13 Jan 2016 19:44:24 +0100 -Subject: [PATCH 134/170] bcm2835: only allow stereo if analogue jack is - selected - -Sending more than 2 channels to videocore while outputting to analogue -mysteriously outputs heavy artifacts. So just paint it over with a -hack: if analogue is explicitly selected as destination, do not -reporting support for anything other than stereo. - -I'm not sure how to deal with the auto case (destination 0). There's -probably way to retrieve this and even to listen to plug events, but -I didn't find one yet, and it's probably not worth the trouble. Just -don't use this setting, I guess. Unless you like noise. - -Changing the setting while an audio stream is active also doesn't -work properly. We could probably interrupt running streams by -returning ENODEV or using kernel hotplug stuff (maybe), but that -also doesn't seem worth the trouble. ---- - sound/arm/bcm2835-ctl.c | 12 ++++++++++++ - 1 file changed, 12 insertions(+) - ---- a/sound/arm/bcm2835-ctl.c -+++ b/sound/arm/bcm2835-ctl.c -@@ -423,9 +423,16 @@ static struct cea_channel_speaker_alloca - { .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, - }; - -+static int uses_analogue(bcm2835_chip_t *chip) -+{ -+ return chip->dest == 1; -+} -+ - static int snd_bcm2835_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *tlv) - { -+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); -+ bcm2835_chip_t *chip = info->private_data; - unsigned int __user *dst; - int count = 0; - int i; -@@ -442,6 +449,9 @@ static int snd_bcm2835_chmap_ctl_tlv(str - int chs_bytes; - int c; - -+ if (i > 0 && uses_analogue(chip)) -+ break; -+ - for (c = 0; c < 8; c++) { - if (ch->speakers[c]) - num_chs++; -@@ -552,6 +562,8 @@ static int snd_bcm2835_chmap_ctl_put(str - int matches = 1; - int cur = 0; - int x; -+ if (i > 0 && uses_analogue(chip)) -+ break; - memset(remap, 0, sizeof(remap)); - for (x = 0; x < substream->runtime->channels; x++) { - int sp = ucontrol->value.integer.value[x]; diff --git a/target/linux/brcm2708/patches-4.4/0134-bcm2835-sdhost-Add-workaround-for-odd-behaviour-on-s.patch b/target/linux/brcm2708/patches-4.4/0134-bcm2835-sdhost-Add-workaround-for-odd-behaviour-on-s.patch new file mode 100644 index 0000000000..a7317b21c0 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0134-bcm2835-sdhost-Add-workaround-for-odd-behaviour-on-s.patch @@ -0,0 +1,137 @@ +From 22a9a698b6cbce7494ea14dce0e784e098c01ecb Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 19 Jan 2016 17:16:38 +0000 +Subject: [PATCH 134/232] bcm2835-sdhost: Add workaround for odd behaviour on + some cards + +For reasons not understood, the sdhost driver fails when reading +sectors very near the end of some SD cards. The problem could +be related to the similar issue that reading the final sector +of any card as part of a multiple read never completes, and the +workaround is an extension of the mechanism introduced to solve +that problem which ensures those sectors are always read singly. +--- + drivers/mmc/host/bcm2835-sdhost.c | 61 +++++++++++++++++++++++++++++++++------ + 1 file changed, 52 insertions(+), 9 deletions(-) + +--- a/drivers/mmc/host/bcm2835-sdhost.c ++++ b/drivers/mmc/host/bcm2835-sdhost.c +@@ -173,6 +173,9 @@ struct bcm2835_host { + u32 overclock_50; /* frequency to use when 50MHz is requested (in MHz) */ + u32 overclock; /* Current frequency if overclocked, else zero */ + u32 pio_limit; /* Maximum block count for PIO (0 = always DMA) */ ++ ++ u32 sectors; /* Cached card size in sectors */ ++ u32 single_read_sectors[8]; + }; + + +@@ -277,6 +280,9 @@ static void bcm2835_sdhost_reset_interna + { + u32 temp; + ++ if (host->debug) ++ pr_info("%s: reset\n", mmc_hostname(host->mmc)); ++ + bcm2835_sdhost_set_power(host, false); + + bcm2835_sdhost_write(host, 0, SDCMD); +@@ -299,6 +305,8 @@ static void bcm2835_sdhost_reset_interna + bcm2835_sdhost_set_power(host, true); + mdelay(10); + host->clock = 0; ++ host->sectors = 0; ++ host->single_read_sectors[0] = ~0; + bcm2835_sdhost_write(host, host->hcfg, SDHCFG); + bcm2835_sdhost_write(host, host->cdiv, SDCDIV); + mmiowb(); +@@ -309,8 +317,6 @@ static void bcm2835_sdhost_reset(struct + { + struct bcm2835_host *host = mmc_priv(mmc); + unsigned long flags; +- if (host->debug) +- pr_info("%s: reset\n", mmc_hostname(mmc)); + spin_lock_irqsave(&host->lock, flags); + + bcm2835_sdhost_reset_internal(host); +@@ -676,6 +682,32 @@ static void bcm2835_sdhost_prepare_data( + host->flush_fifo = 0; + host->data->bytes_xfered = 0; + ++ if (!host->sectors && host->mmc->card) ++ { ++ struct mmc_card *card = host->mmc->card; ++ if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) { ++ /* ++ * The EXT_CSD sector count is in number of 512 byte ++ * sectors. ++ */ ++ host->sectors = card->ext_csd.sectors; ++ pr_err("%s: using ext_csd!\n", mmc_hostname(host->mmc)); ++ } else { ++ /* ++ * The CSD capacity field is in units of read_blkbits. ++ * set_capacity takes units of 512 bytes. ++ */ ++ host->sectors = card->csd.capacity << ++ (card->csd.read_blkbits - 9); ++ } ++ host->single_read_sectors[0] = host->sectors - 65; ++ host->single_read_sectors[1] = host->sectors - 64; ++ host->single_read_sectors[2] = host->sectors - 33; ++ host->single_read_sectors[3] = host->sectors - 32; ++ host->single_read_sectors[4] = host->sectors - 1; ++ host->single_read_sectors[5] = ~0; /* Safety net */ ++ } ++ + host->use_dma = host->have_dma && (data->blocks > host->pio_limit); + if (!host->use_dma) { + int flags; +@@ -1246,6 +1278,10 @@ static u32 bcm2835_sdhost_block_irq(stru + + bcm2835_sdhost_finish_data(host); + } else { ++ /* Reset the timer */ ++ mod_timer(&host->pio_timer, ++ jiffies + host->pio_timeout); ++ + bcm2835_sdhost_transfer_pio(host); + + /* Reset the timer */ +@@ -1450,8 +1486,8 @@ void bcm2835_sdhost_set_clock(struct bcm + host->cdiv = div; + bcm2835_sdhost_write(host, host->cdiv, SDCDIV); + +- /* Set the timeout to 500ms */ +- bcm2835_sdhost_write(host, host->mmc->actual_clock/2, SDTOUT); ++ /* Set the timeout to 250ms */ ++ bcm2835_sdhost_write(host, host->mmc->actual_clock/4, SDTOUT); + + if (host->debug) + pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x (actual clock %d)\n", +@@ -1566,13 +1602,20 @@ static int bcm2835_sdhost_multi_io_quirk + reading the final sector of the card as part of a multiple read + problematic. Detect that case and shorten the read accordingly. + */ +- /* csd.capacity is in weird units - convert to sectors */ +- u32 card_sectors = (card->csd.capacity << (card->csd.read_blkbits - 9)); ++ struct bcm2835_host *host; ++ ++ host = mmc_priv(card->host); + +- if ((direction == MMC_DATA_READ) && +- ((blk_pos + blk_size) == card_sectors)) +- blk_size--; ++ if (direction == MMC_DATA_READ) ++ { ++ int i; ++ int sector; ++ for (i = 0; blk_pos > (sector = host->single_read_sectors[i]); i++) ++ continue; + ++ if ((blk_pos + blk_size) > sector) ++ blk_size = (blk_pos == sector) ? 1 : (sector - blk_pos); ++ } + return blk_size; + } + diff --git a/target/linux/brcm2708/patches-4.4/0135-bcm2835-interpolate-audio-delay.patch b/target/linux/brcm2708/patches-4.4/0135-bcm2835-interpolate-audio-delay.patch deleted file mode 100644 index 1c1724e3f6..0000000000 --- a/target/linux/brcm2708/patches-4.4/0135-bcm2835-interpolate-audio-delay.patch +++ /dev/null @@ -1,90 +0,0 @@ -From 2c967fa0f5b0d10c86c796098574ba09ffe66cd5 Mon Sep 17 00:00:00 2001 -From: wm4 -Date: Wed, 13 Jan 2016 19:44:47 +0100 -Subject: [PATCH 135/170] bcm2835: interpolate audio delay - -It appears the GPU only sends us a message all 10ms to update -the playback progress. Other than this, the playback position -(what SNDRV_PCM_IOCTL_DELAY will return) is not updated at all. -Userspace will see jitter up to 10ms in the audio position. - -Make this a bit nicer for userspace by interpolating the -position using the CPU clock. - -I'm not sure if setting snd_pcm_runtime.delay is the right -approach for this. Or if there is maybe an already existing -mechanism for position interpolation in the ALSA core. - -I only set SNDRV_PCM_INFO_BATCH because this appears to remove -at least one situation snd_pcm_runtime.delay is used, so I have -to worry less in which place I have to update this field, or -how it interacts with the rest of ALSA. - -In the future, it might be nice to use VC_AUDIO_MSG_TYPE_LATENCY. -One problem is that it requires sending a videocore message, and -waiting for a reply, which could make the implementation much -harder due to locking and synchronization requirements. ---- - sound/arm/bcm2835-pcm.c | 12 +++++++++++- - sound/arm/bcm2835.h | 1 + - 2 files changed, 12 insertions(+), 1 deletion(-) - ---- a/sound/arm/bcm2835-pcm.c -+++ b/sound/arm/bcm2835-pcm.c -@@ -25,7 +25,7 @@ - /* hardware definition */ - static struct snd_pcm_hardware snd_bcm2835_playback_hw = { - .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | -- SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), -+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH), - .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, - .rate_min = 8000, -@@ -99,6 +99,8 @@ static irqreturn_t bcm2835_playback_fifo - alsa_stream->pos %= alsa_stream->buffer_size; - } - -+ alsa_stream->interpolate_start = ktime_get_ns(); -+ - if (alsa_stream->substream) { - if (new_period) - snd_pcm_period_elapsed(alsa_stream->substream); -@@ -399,6 +401,7 @@ static int snd_bcm2835_pcm_prepare(struc - alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream); - alsa_stream->period_size = snd_pcm_lib_period_bytes(substream); - alsa_stream->pos = 0; -+ alsa_stream->interpolate_start = ktime_get_ns(); - - audio_debug("buffer_size=%d, period_size=%d pos=%d frame_bits=%d\n", - alsa_stream->buffer_size, alsa_stream->period_size, -@@ -495,6 +498,7 @@ snd_bcm2835_pcm_pointer(struct snd_pcm_s - { - struct snd_pcm_runtime *runtime = substream->runtime; - bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; -+ u64 now = ktime_get_ns(); - - audio_info(" .. IN\n"); - -@@ -503,6 +507,12 @@ snd_bcm2835_pcm_pointer(struct snd_pcm_s - frames_to_bytes(runtime, runtime->control->appl_ptr), - alsa_stream->pos); - -+ /* Give userspace better delay reporting by interpolating between GPU -+ * notifications, assuming audio speed is close enough to the clock -+ * used for ktime */ -+ if (alsa_stream->interpolate_start && alsa_stream->interpolate_start < now) -+ runtime->delay = -(int)div_u64((now - alsa_stream->interpolate_start) * runtime->rate, 1000000000); -+ - audio_info(" .. OUT\n"); - return snd_pcm_indirect_playback_pointer(substream, - &alsa_stream->pcm_indirect, ---- a/sound/arm/bcm2835.h -+++ b/sound/arm/bcm2835.h -@@ -137,6 +137,7 @@ typedef struct bcm2835_alsa_stream { - unsigned int pos; - unsigned int buffer_size; - unsigned int period_size; -+ u64 interpolate_start; - - uint32_t enable_fifo_irq; - irq_handler_t fifo_irq_handler; diff --git a/target/linux/brcm2708/patches-4.4/0135-bcm2835-sdhost-Add-debug_flags-dtparam.patch b/target/linux/brcm2708/patches-4.4/0135-bcm2835-sdhost-Add-debug_flags-dtparam.patch new file mode 100644 index 0000000000..36c3c50837 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0135-bcm2835-sdhost-Add-debug_flags-dtparam.patch @@ -0,0 +1,120 @@ +From d038f759d55f39c97e4232851e821b5e4b2fe7a2 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 22 Jan 2016 16:03:24 +0000 +Subject: [PATCH 135/232] bcm2835-sdhost: Add debug_flags dtparam + +Bit zero disables the single-read-sectors map: + +If the default MMC driver is bcm2835-mmc: + dtoverlay=sdhost,debug_flags=1 +If the default MMC driver is bcm2835-sdhost: + dtoverlay=sdtweak,debug_flags=1 +(although the sdhost overlay may also work, sdtweak is +less invasive and will work in more circumstances). + +Also revert the timeout change, just in case. +--- + arch/arm/boot/dts/overlays/sdhost-overlay.dts | 2 ++ + arch/arm/boot/dts/overlays/sdtweak-overlay.dts | 2 ++ + drivers/mmc/host/bcm2835-sdhost.c | 26 +++++++++++++++++++++----- + 3 files changed, 25 insertions(+), 5 deletions(-) + +--- a/arch/arm/boot/dts/overlays/sdhost-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sdhost-overlay.dts +@@ -16,6 +16,7 @@ + frag1: __overlay__ { + brcm,overclock-50 = <0>; + brcm,pio-limit = <1>; ++ brcm,debug-flags = <0>; + status = "okay"; + }; + }; +@@ -25,5 +26,6 @@ + force_pio = <&frag1>,"brcm,force-pio?"; + pio_limit = <&frag1>,"brcm,pio-limit:0"; + debug = <&frag1>,"brcm,debug?"; ++ debug_flags = <&frag1>,"brcm,debug-flags:0"; + }; + }; +--- a/arch/arm/boot/dts/overlays/sdtweak-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sdtweak-overlay.dts +@@ -9,6 +9,7 @@ + frag1: __overlay__ { + brcm,overclock-50 = <0>; + brcm,pio-limit = <1>; ++ brcm,debug-flags = <0>; + }; + }; + +@@ -17,5 +18,6 @@ + force_pio = <&frag1>,"brcm,force-pio?"; + pio_limit = <&frag1>,"brcm,pio-limit:0"; + debug = <&frag1>,"brcm,debug?"; ++ debug_flags = <&frag1>,"brcm,debug-flags:0"; + }; + }; +--- a/drivers/mmc/host/bcm2835-sdhost.c ++++ b/drivers/mmc/host/bcm2835-sdhost.c +@@ -174,6 +174,8 @@ struct bcm2835_host { + u32 overclock; /* Current frequency if overclocked, else zero */ + u32 pio_limit; /* Maximum block count for PIO (0 = always DMA) */ + ++ u32 debug_flags; ++ + u32 sectors; /* Cached card size in sectors */ + u32 single_read_sectors[8]; + }; +@@ -682,7 +684,7 @@ static void bcm2835_sdhost_prepare_data( + host->flush_fifo = 0; + host->data->bytes_xfered = 0; + +- if (!host->sectors && host->mmc->card) ++ if (!host->sectors && host->mmc->card && !(host->debug_flags & 1)) + { + struct mmc_card *card = host->mmc->card; + if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) { +@@ -1486,8 +1488,8 @@ void bcm2835_sdhost_set_clock(struct bcm + host->cdiv = div; + bcm2835_sdhost_write(host, host->cdiv, SDCDIV); + +- /* Set the timeout to 250ms */ +- bcm2835_sdhost_write(host, host->mmc->actual_clock/4, SDTOUT); ++ /* Set the timeout to 500ms */ ++ bcm2835_sdhost_write(host, host->mmc->actual_clock/2, SDTOUT); + + if (host->debug) + pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x (actual clock %d)\n", +@@ -1606,8 +1608,16 @@ static int bcm2835_sdhost_multi_io_quirk + + host = mmc_priv(card->host); + +- if (direction == MMC_DATA_READ) +- { ++ if (!host->sectors) { ++ /* csd.capacity is in weird units - convert to sectors */ ++ u32 card_sectors = (card->csd.capacity << (card->csd.read_blkbits - 9)); ++ if ((direction == MMC_DATA_READ) && ++ ((blk_pos + blk_size) == card_sectors)) ++ blk_size--; ++ return blk_size; ++ } ++ ++ if (direction == MMC_DATA_READ) { + int i; + int sector; + for (i = 0; blk_pos > (sector = host->single_read_sectors[i]); i++) +@@ -1838,8 +1848,14 @@ static int bcm2835_sdhost_probe(struct p + host->allow_dma = ALLOW_DMA && + !of_property_read_bool(node, "brcm,force-pio"); + host->debug = of_property_read_bool(node, "brcm,debug"); ++ of_property_read_u32(node, ++ "brcm,debug-flags", ++ &host->debug_flags); + } + ++ if (host->debug_flags) ++ dev_err(dev, "debug_flags=%x\n", host->debug_flags); ++ + if (host->allow_dma) { + if (node) { + host->dma_chan_tx = diff --git a/target/linux/brcm2708/patches-4.4/0136-BCM270X_DT-Add-sdio_overclock-parameter-to-sdio-over.patch b/target/linux/brcm2708/patches-4.4/0136-BCM270X_DT-Add-sdio_overclock-parameter-to-sdio-over.patch new file mode 100644 index 0000000000..6fc78efabc --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0136-BCM270X_DT-Add-sdio_overclock-parameter-to-sdio-over.patch @@ -0,0 +1,69 @@ +From 3f4c1f041954c21f9f06c7fe7df3d0e5711667e0 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 25 Jan 2016 09:12:06 +0000 +Subject: [PATCH 136/232] BCM270X_DT: Add sdio_overclock parameter to sdio + overlay + +The sdio_overclock parameter is like the overclock_50 parameter, i.e. +it sets an alternate frequency (in MHz) to use when the MMC framework +requests 50MHz, except that it applies to the SDIO bus. + +Be aware that the actual frequencies achievable are limited to even integer +divisions of 250MHz, and that the driver will round up to include fractions +(e.g. 62 will include 62.5) but then round down to the nearest frequency. +In other words, the chosen frequency is the highest possible that is less than +the parameter value + 1. In practise this means that 62 is the only sensible +value. + +Examples: + 250MHz/4 = 62.5MHz (sdio_overclock=62) + 250MHz/2 = 125MHz (sdio_overclock=125) # Too fast +--- + arch/arm/boot/dts/overlays/README | 9 ++++++--- + arch/arm/boot/dts/overlays/sdio-overlay.dts | 2 ++ + 2 files changed, 8 insertions(+), 3 deletions(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -53,8 +53,8 @@ have its contents deleted (or commented + Using Overlays + ============== + +-Overlays are loaded using the "dtoverlay" directive. As an example, consider the +-popular lirc-rpi module, the Linux Infrared Remote Control driver. In the ++Overlays are loaded using the "dtoverlay" directive. As an example, consider ++the popular lirc-rpi module, the Linux Infrared Remote Control driver. In the + pre-DT world this would be loaded from /etc/modules, with an explicit + "modprobe lirc-rpi" command, or programmatically by lircd. With DT enabled, + this becomes a line in config.txt: +@@ -621,9 +621,12 @@ Name: sdio + Info: Selects the bcm2835-sdhost SD/MMC driver, optionally with overclock, + and enables SDIO via GPIOs 22-27. + Load: dtoverlay=sdio,= +-Params: overclock_50 Clock (in MHz) to use when the MMC framework ++Params: overclock_50 SD Clock (in MHz) to use when the MMC framework + requests 50MHz + ++ sdio_overclock SDIO Clock (in MHz) to use when the MMC ++ framework requests 50MHz ++ + force_pio Disable DMA support (default off) + + pio_limit Number of blocks above which to use DMA +--- a/arch/arm/boot/dts/overlays/sdio-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sdio-overlay.dts +@@ -12,6 +12,7 @@ + pinctrl-0 = <&sdio_pins>; + non-removable; + bus-width = <4>; ++ brcm,overclock-50 = <0>; + status = "okay"; + }; + }; +@@ -30,5 +31,6 @@ + __overrides__ { + poll_once = <&sdio_mmc>,"non-removable?"; + bus_width = <&sdio_mmc>,"bus-width:0"; ++ sdio_overclock = <&sdio_mmc>,"brcm,overclock-50:0"; + }; + }; diff --git a/target/linux/brcm2708/patches-4.4/0136-bcm2835-sdhost-Add-workaround-for-odd-behaviour-on-s.patch b/target/linux/brcm2708/patches-4.4/0136-bcm2835-sdhost-Add-workaround-for-odd-behaviour-on-s.patch deleted file mode 100644 index dbe4bbfe5b..0000000000 --- a/target/linux/brcm2708/patches-4.4/0136-bcm2835-sdhost-Add-workaround-for-odd-behaviour-on-s.patch +++ /dev/null @@ -1,137 +0,0 @@ -From 205e27a7f94a5531764cc517ce43623361ca466c Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Tue, 19 Jan 2016 17:16:38 +0000 -Subject: [PATCH 136/170] bcm2835-sdhost: Add workaround for odd behaviour on - some cards - -For reasons not understood, the sdhost driver fails when reading -sectors very near the end of some SD cards. The problem could -be related to the similar issue that reading the final sector -of any card as part of a multiple read never completes, and the -workaround is an extension of the mechanism introduced to solve -that problem which ensures those sectors are always read singly. ---- - drivers/mmc/host/bcm2835-sdhost.c | 61 +++++++++++++++++++++++++++++++++------ - 1 file changed, 52 insertions(+), 9 deletions(-) - ---- a/drivers/mmc/host/bcm2835-sdhost.c -+++ b/drivers/mmc/host/bcm2835-sdhost.c -@@ -173,6 +173,9 @@ struct bcm2835_host { - u32 overclock_50; /* frequency to use when 50MHz is requested (in MHz) */ - u32 overclock; /* Current frequency if overclocked, else zero */ - u32 pio_limit; /* Maximum block count for PIO (0 = always DMA) */ -+ -+ u32 sectors; /* Cached card size in sectors */ -+ u32 single_read_sectors[8]; - }; - - -@@ -277,6 +280,9 @@ static void bcm2835_sdhost_reset_interna - { - u32 temp; - -+ if (host->debug) -+ pr_info("%s: reset\n", mmc_hostname(host->mmc)); -+ - bcm2835_sdhost_set_power(host, false); - - bcm2835_sdhost_write(host, 0, SDCMD); -@@ -299,6 +305,8 @@ static void bcm2835_sdhost_reset_interna - bcm2835_sdhost_set_power(host, true); - mdelay(10); - host->clock = 0; -+ host->sectors = 0; -+ host->single_read_sectors[0] = ~0; - bcm2835_sdhost_write(host, host->hcfg, SDHCFG); - bcm2835_sdhost_write(host, host->cdiv, SDCDIV); - mmiowb(); -@@ -309,8 +317,6 @@ static void bcm2835_sdhost_reset(struct - { - struct bcm2835_host *host = mmc_priv(mmc); - unsigned long flags; -- if (host->debug) -- pr_info("%s: reset\n", mmc_hostname(mmc)); - spin_lock_irqsave(&host->lock, flags); - - bcm2835_sdhost_reset_internal(host); -@@ -676,6 +682,32 @@ static void bcm2835_sdhost_prepare_data( - host->flush_fifo = 0; - host->data->bytes_xfered = 0; - -+ if (!host->sectors && host->mmc->card) -+ { -+ struct mmc_card *card = host->mmc->card; -+ if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) { -+ /* -+ * The EXT_CSD sector count is in number of 512 byte -+ * sectors. -+ */ -+ host->sectors = card->ext_csd.sectors; -+ pr_err("%s: using ext_csd!\n", mmc_hostname(host->mmc)); -+ } else { -+ /* -+ * The CSD capacity field is in units of read_blkbits. -+ * set_capacity takes units of 512 bytes. -+ */ -+ host->sectors = card->csd.capacity << -+ (card->csd.read_blkbits - 9); -+ } -+ host->single_read_sectors[0] = host->sectors - 65; -+ host->single_read_sectors[1] = host->sectors - 64; -+ host->single_read_sectors[2] = host->sectors - 33; -+ host->single_read_sectors[3] = host->sectors - 32; -+ host->single_read_sectors[4] = host->sectors - 1; -+ host->single_read_sectors[5] = ~0; /* Safety net */ -+ } -+ - host->use_dma = host->have_dma && (data->blocks > host->pio_limit); - if (!host->use_dma) { - int flags; -@@ -1246,6 +1278,10 @@ static u32 bcm2835_sdhost_block_irq(stru - - bcm2835_sdhost_finish_data(host); - } else { -+ /* Reset the timer */ -+ mod_timer(&host->pio_timer, -+ jiffies + host->pio_timeout); -+ - bcm2835_sdhost_transfer_pio(host); - - /* Reset the timer */ -@@ -1450,8 +1486,8 @@ void bcm2835_sdhost_set_clock(struct bcm - host->cdiv = div; - bcm2835_sdhost_write(host, host->cdiv, SDCDIV); - -- /* Set the timeout to 500ms */ -- bcm2835_sdhost_write(host, host->mmc->actual_clock/2, SDTOUT); -+ /* Set the timeout to 250ms */ -+ bcm2835_sdhost_write(host, host->mmc->actual_clock/4, SDTOUT); - - if (host->debug) - pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x (actual clock %d)\n", -@@ -1566,13 +1602,20 @@ static int bcm2835_sdhost_multi_io_quirk - reading the final sector of the card as part of a multiple read - problematic. Detect that case and shorten the read accordingly. - */ -- /* csd.capacity is in weird units - convert to sectors */ -- u32 card_sectors = (card->csd.capacity << (card->csd.read_blkbits - 9)); -+ struct bcm2835_host *host; -+ -+ host = mmc_priv(card->host); - -- if ((direction == MMC_DATA_READ) && -- ((blk_pos + blk_size) == card_sectors)) -- blk_size--; -+ if (direction == MMC_DATA_READ) -+ { -+ int i; -+ int sector; -+ for (i = 0; blk_pos > (sector = host->single_read_sectors[i]); i++) -+ continue; - -+ if ((blk_pos + blk_size) > sector) -+ blk_size = (blk_pos == sector) ? 1 : (sector - blk_pos); -+ } - return blk_size; - } - diff --git a/target/linux/brcm2708/patches-4.4/0137-bcm2835-sdhost-Add-debug_flags-dtparam.patch b/target/linux/brcm2708/patches-4.4/0137-bcm2835-sdhost-Add-debug_flags-dtparam.patch deleted file mode 100644 index cb56259066..0000000000 --- a/target/linux/brcm2708/patches-4.4/0137-bcm2835-sdhost-Add-debug_flags-dtparam.patch +++ /dev/null @@ -1,120 +0,0 @@ -From c5d24cba808118647d01a1869cf1c46d0658d78b Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Fri, 22 Jan 2016 16:03:24 +0000 -Subject: [PATCH 137/170] bcm2835-sdhost: Add debug_flags dtparam - -Bit zero disables the single-read-sectors map: - -If the default MMC driver is bcm2835-mmc: - dtoverlay=sdhost,debug_flags=1 -If the default MMC driver is bcm2835-sdhost: - dtoverlay=sdtweak,debug_flags=1 -(although the sdhost overlay may also work, sdtweak is -less invasive and will work in more circumstances). - -Also revert the timeout change, just in case. ---- - arch/arm/boot/dts/overlays/sdhost-overlay.dts | 2 ++ - arch/arm/boot/dts/overlays/sdtweak-overlay.dts | 2 ++ - drivers/mmc/host/bcm2835-sdhost.c | 26 +++++++++++++++++++++----- - 3 files changed, 25 insertions(+), 5 deletions(-) - ---- a/arch/arm/boot/dts/overlays/sdhost-overlay.dts -+++ b/arch/arm/boot/dts/overlays/sdhost-overlay.dts -@@ -16,6 +16,7 @@ - frag1: __overlay__ { - brcm,overclock-50 = <0>; - brcm,pio-limit = <1>; -+ brcm,debug-flags = <0>; - status = "okay"; - }; - }; -@@ -25,5 +26,6 @@ - force_pio = <&frag1>,"brcm,force-pio?"; - pio_limit = <&frag1>,"brcm,pio-limit:0"; - debug = <&frag1>,"brcm,debug?"; -+ debug_flags = <&frag1>,"brcm,debug-flags:0"; - }; - }; ---- a/arch/arm/boot/dts/overlays/sdtweak-overlay.dts -+++ b/arch/arm/boot/dts/overlays/sdtweak-overlay.dts -@@ -9,6 +9,7 @@ - frag1: __overlay__ { - brcm,overclock-50 = <0>; - brcm,pio-limit = <1>; -+ brcm,debug-flags = <0>; - }; - }; - -@@ -17,5 +18,6 @@ - force_pio = <&frag1>,"brcm,force-pio?"; - pio_limit = <&frag1>,"brcm,pio-limit:0"; - debug = <&frag1>,"brcm,debug?"; -+ debug_flags = <&frag1>,"brcm,debug-flags:0"; - }; - }; ---- a/drivers/mmc/host/bcm2835-sdhost.c -+++ b/drivers/mmc/host/bcm2835-sdhost.c -@@ -174,6 +174,8 @@ struct bcm2835_host { - u32 overclock; /* Current frequency if overclocked, else zero */ - u32 pio_limit; /* Maximum block count for PIO (0 = always DMA) */ - -+ u32 debug_flags; -+ - u32 sectors; /* Cached card size in sectors */ - u32 single_read_sectors[8]; - }; -@@ -682,7 +684,7 @@ static void bcm2835_sdhost_prepare_data( - host->flush_fifo = 0; - host->data->bytes_xfered = 0; - -- if (!host->sectors && host->mmc->card) -+ if (!host->sectors && host->mmc->card && !(host->debug_flags & 1)) - { - struct mmc_card *card = host->mmc->card; - if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) { -@@ -1486,8 +1488,8 @@ void bcm2835_sdhost_set_clock(struct bcm - host->cdiv = div; - bcm2835_sdhost_write(host, host->cdiv, SDCDIV); - -- /* Set the timeout to 250ms */ -- bcm2835_sdhost_write(host, host->mmc->actual_clock/4, SDTOUT); -+ /* Set the timeout to 500ms */ -+ bcm2835_sdhost_write(host, host->mmc->actual_clock/2, SDTOUT); - - if (host->debug) - pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x (actual clock %d)\n", -@@ -1606,8 +1608,16 @@ static int bcm2835_sdhost_multi_io_quirk - - host = mmc_priv(card->host); - -- if (direction == MMC_DATA_READ) -- { -+ if (!host->sectors) { -+ /* csd.capacity is in weird units - convert to sectors */ -+ u32 card_sectors = (card->csd.capacity << (card->csd.read_blkbits - 9)); -+ if ((direction == MMC_DATA_READ) && -+ ((blk_pos + blk_size) == card_sectors)) -+ blk_size--; -+ return blk_size; -+ } -+ -+ if (direction == MMC_DATA_READ) { - int i; - int sector; - for (i = 0; blk_pos > (sector = host->single_read_sectors[i]); i++) -@@ -1838,8 +1848,14 @@ static int bcm2835_sdhost_probe(struct p - host->allow_dma = ALLOW_DMA && - !of_property_read_bool(node, "brcm,force-pio"); - host->debug = of_property_read_bool(node, "brcm,debug"); -+ of_property_read_u32(node, -+ "brcm,debug-flags", -+ &host->debug_flags); - } - -+ if (host->debug_flags) -+ dev_err(dev, "debug_flags=%x\n", host->debug_flags); -+ - if (host->allow_dma) { - if (node) { - host->dma_chan_tx = diff --git a/target/linux/brcm2708/patches-4.4/0137-rtc-ds1307-add-support-for-the-DT-property-wakeup-so.patch b/target/linux/brcm2708/patches-4.4/0137-rtc-ds1307-add-support-for-the-DT-property-wakeup-so.patch new file mode 100644 index 0000000000..8bdbc78c0d --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0137-rtc-ds1307-add-support-for-the-DT-property-wakeup-so.patch @@ -0,0 +1,94 @@ +From 6f5d0a941a6a084c677a8843207a42f7b6d5a04d Mon Sep 17 00:00:00 2001 +From: Michael Lange +Date: Thu, 21 Jan 2016 18:10:16 +0100 +Subject: [PATCH 137/232] rtc: ds1307: add support for the DT property + 'wakeup-source' + +For RTC chips with no IRQ directly connected to the SoC, the RTC chip +can be forced as a wakeup source by stating that explicitly in +the device's .dts file using the "wakeup-source" boolean property. +This will guarantee the 'wakealarm' sysfs entry is available on the +device, if supported by the RTC. + +With these changes to the driver rtc-ds1307 and the necessary entries +in the .dts file, I get an working ds1337 RTC on the Witty Pi extension +board by UUGear for the Raspberry Pi. + +An example for the entry in the .dts file: + + rtc: ds1337@68 { + compatible = "dallas,ds1337"; + reg = <0x68>; + wakeup-source; + +If the "wakeup-source" property is set, do not request an IRQ. +Set also UIE mode to unsupported, to get a working 'hwclock' binary. + +Signed-off-by: Michael Lange +Signed-off-by: Alexandre Belloni +--- + drivers/rtc/rtc-ds1307.c | 29 +++++++++++++++++++++++++++-- + 1 file changed, 27 insertions(+), 2 deletions(-) + +--- a/drivers/rtc/rtc-ds1307.c ++++ b/drivers/rtc/rtc-ds1307.c +@@ -860,6 +860,7 @@ static int ds1307_probe(struct i2c_clien + struct chip_desc *chip = &chips[id->driver_data]; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + bool want_irq = false; ++ bool ds1307_can_wakeup_device = false; + unsigned char *buf; + struct ds1307_platform_data *pdata = dev_get_platdata(&client->dev); + irq_handler_t irq_handler = ds1307_irq; +@@ -907,6 +908,20 @@ static int ds1307_probe(struct i2c_clien + ds1307->write_block_data = ds1307_write_block_data; + } + ++#ifdef CONFIG_OF ++/* ++ * For devices with no IRQ directly connected to the SoC, the RTC chip ++ * can be forced as a wakeup source by stating that explicitly in ++ * the device's .dts file using the "wakeup-source" boolean property. ++ * If the "wakeup-source" property is set, don't request an IRQ. ++ * This will guarantee the 'wakealarm' sysfs entry is available on the device, ++ * if supported by the RTC. ++ */ ++ if (of_property_read_bool(client->dev.of_node, "wakeup-source")) { ++ ds1307_can_wakeup_device = true; ++ } ++#endif ++ + switch (ds1307->type) { + case ds_1337: + case ds_1339: +@@ -925,11 +940,13 @@ static int ds1307_probe(struct i2c_clien + ds1307->regs[0] &= ~DS1337_BIT_nEOSC; + + /* +- * Using IRQ? Disable the square wave and both alarms. ++ * Using IRQ or defined as wakeup-source? ++ * Disable the square wave and both alarms. + * For some variants, be sure alarms can trigger when we're + * running on Vbackup (BBSQI/BBSQW) + */ +- if (ds1307->client->irq > 0 && chip->alarm) { ++ if (chip->alarm && (ds1307->client->irq > 0 || ++ ds1307_can_wakeup_device)) { + ds1307->regs[0] |= DS1337_BIT_INTCN + | bbsqi_bitpos[ds1307->type]; + ds1307->regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE); +@@ -1144,6 +1161,14 @@ read_rtc: + return PTR_ERR(ds1307->rtc); + } + ++ if (ds1307_can_wakeup_device) { ++ /* Disable request for an IRQ */ ++ want_irq = false; ++ dev_info(&client->dev, "'wakeup-source' is set, request for an IRQ is disabled!\n"); ++ /* We cannot support UIE mode if we do not have an IRQ line */ ++ ds1307->rtc->uie_unsupported = 1; ++ } ++ + if (want_irq) { + err = devm_request_threaded_irq(&client->dev, + client->irq, NULL, irq_handler, diff --git a/target/linux/brcm2708/patches-4.4/0138-BCM270X_DT-Add-sdio_overclock-parameter-to-sdio-over.patch b/target/linux/brcm2708/patches-4.4/0138-BCM270X_DT-Add-sdio_overclock-parameter-to-sdio-over.patch deleted file mode 100644 index 8468f39ce8..0000000000 --- a/target/linux/brcm2708/patches-4.4/0138-BCM270X_DT-Add-sdio_overclock-parameter-to-sdio-over.patch +++ /dev/null @@ -1,69 +0,0 @@ -From 1a451a35b70fa9ed32f23a4ba925e5b2c2c3af78 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 25 Jan 2016 09:12:06 +0000 -Subject: [PATCH 138/170] BCM270X_DT: Add sdio_overclock parameter to sdio - overlay - -The sdio_overclock parameter is like the overclock_50 parameter, i.e. -it sets an alternate frequency (in MHz) to use when the MMC framework -requests 50MHz, except that it applies to the SDIO bus. - -Be aware that the actual frequencies achievable are limited to even integer -divisions of 250MHz, and that the driver will round up to include fractions -(e.g. 62 will include 62.5) but then round down to the nearest frequency. -In other words, the chosen frequency is the highest possible that is less than -the parameter value + 1. In practise this means that 62 is the only sensible -value. - -Examples: - 250MHz/4 = 62.5MHz (sdio_overclock=62) - 250MHz/2 = 125MHz (sdio_overclock=125) # Too fast ---- - arch/arm/boot/dts/overlays/README | 9 ++++++--- - arch/arm/boot/dts/overlays/sdio-overlay.dts | 2 ++ - 2 files changed, 8 insertions(+), 3 deletions(-) - ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -53,8 +53,8 @@ have its contents deleted (or commented - Using Overlays - ============== - --Overlays are loaded using the "dtoverlay" directive. As an example, consider the --popular lirc-rpi module, the Linux Infrared Remote Control driver. In the -+Overlays are loaded using the "dtoverlay" directive. As an example, consider -+the popular lirc-rpi module, the Linux Infrared Remote Control driver. In the - pre-DT world this would be loaded from /etc/modules, with an explicit - "modprobe lirc-rpi" command, or programmatically by lircd. With DT enabled, - this becomes a line in config.txt: -@@ -621,9 +621,12 @@ Name: sdio - Info: Selects the bcm2835-sdhost SD/MMC driver, optionally with overclock, - and enables SDIO via GPIOs 22-27. - Load: dtoverlay=sdio,= --Params: overclock_50 Clock (in MHz) to use when the MMC framework -+Params: overclock_50 SD Clock (in MHz) to use when the MMC framework - requests 50MHz - -+ sdio_overclock SDIO Clock (in MHz) to use when the MMC -+ framework requests 50MHz -+ - force_pio Disable DMA support (default off) - - pio_limit Number of blocks above which to use DMA ---- a/arch/arm/boot/dts/overlays/sdio-overlay.dts -+++ b/arch/arm/boot/dts/overlays/sdio-overlay.dts -@@ -12,6 +12,7 @@ - pinctrl-0 = <&sdio_pins>; - non-removable; - bus-width = <4>; -+ brcm,overclock-50 = <0>; - status = "okay"; - }; - }; -@@ -30,5 +31,6 @@ - __overrides__ { - poll_once = <&sdio_mmc>,"non-removable?"; - bus_width = <&sdio_mmc>,"bus-width:0"; -+ sdio_overclock = <&sdio_mmc>,"brcm,overclock-50:0"; - }; - }; diff --git a/target/linux/brcm2708/patches-4.4/0138-dt-overlay-add-wittypi-overlay.dts.patch b/target/linux/brcm2708/patches-4.4/0138-dt-overlay-add-wittypi-overlay.dts.patch new file mode 100644 index 0000000000..9faed43ce5 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0138-dt-overlay-add-wittypi-overlay.dts.patch @@ -0,0 +1,57 @@ +From 873bf69f1f6bf19f2a0c4cec884cb952b2a19e0c Mon Sep 17 00:00:00 2001 +From: vitalogy +Date: Tue, 19 Jan 2016 07:02:02 +0100 +Subject: [PATCH 138/232] dt-overlay: add wittypi-overlay.dts + +--- + arch/arm/boot/dts/overlays/wittypi-overlay.dts | 44 ++++++++++++++++++++++++++ + 1 file changed, 44 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/wittypi-overlay.dts + +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/wittypi-overlay.dts +@@ -0,0 +1,44 @@ ++/* ++ * Device Tree overlay for Witty Pi extension board by UUGear ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&leds>; ++ __overlay__ { ++ compatible = "gpio-leds"; ++ wittypi_led: wittypi_led { ++ label = "wittypi_led"; ++ linux,default-trigger = "default-on"; ++ gpios = <&gpio 17 0>; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&i2c1>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ rtc: ds1337@68 { ++ compatible = "dallas,ds1337"; ++ reg = <0x68>; ++ wakeup-source; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ led_gpio = <&wittypi_led>,"gpios:4"; ++ led_trigger = <&wittypi_led>,"linux,default-trigger"; ++ }; ++ ++}; diff --git a/target/linux/brcm2708/patches-4.4/0139-FIXUP-i2c_bcm2708-Don-t-change-module-baudrate-param.patch b/target/linux/brcm2708/patches-4.4/0139-FIXUP-i2c_bcm2708-Don-t-change-module-baudrate-param.patch new file mode 100644 index 0000000000..aa04f1bcbe --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0139-FIXUP-i2c_bcm2708-Don-t-change-module-baudrate-param.patch @@ -0,0 +1,99 @@ +From c74fc12557305b66537dc6e8ed3ab5d27c507de0 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 19 Jan 2016 16:28:05 +0000 +Subject: [PATCH 139/232] FIXUP i2c_bcm2708: Don't change module baudrate + parameter + +Overwriting the baudrate module parameter creates an apparent +forced baudrate for i2c busses after the first. Not only does this +override the baudrate from DT it also prevents the bus ID from +being initialised. + +Also fix whitespace errors. +--- + drivers/i2c/busses/i2c-bcm2708.c | 48 +++++++++++++++++++++------------------- + 1 file changed, 25 insertions(+), 23 deletions(-) + +--- a/drivers/i2c/busses/i2c-bcm2708.c ++++ b/drivers/i2c/busses/i2c-bcm2708.c +@@ -71,7 +71,6 @@ + + #define DRV_NAME "bcm2708_i2c" + +-static unsigned int baudrate_default = CONFIG_I2C_BCM2708_BAUDRATE; + static unsigned int baudrate; + module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + MODULE_PARM_DESC(baudrate, "The I2C baudrate"); +@@ -317,25 +316,28 @@ static int bcm2708_i2c_probe(struct plat + struct i2c_adapter *adap; + unsigned long bus_hz; + u32 cdiv, clk_tout; +- +- if (!baudrate) { +- baudrate = baudrate_default; +- 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"); ++ u32 baud; ++ ++ baud = CONFIG_I2C_BCM2708_BAUDRATE; ++ ++ 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)) ++ baud = bus_clk_rate; ++ else ++ dev_warn(&pdev->dev, ++ "Could not read clock-frequency property\n"); + } + ++ if (baudrate) ++ baud = baudrate; ++ + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(&pdev->dev, "could not get IO memory\n"); +@@ -419,21 +421,21 @@ static int bcm2708_i2c_probe(struct plat + } + + bus_hz = clk_get_rate(bi->clk); +- cdiv = bus_hz / baudrate; ++ cdiv = bus_hz / baud; + if (cdiv > 0xffff) { + cdiv = 0xffff; +- baudrate = bus_hz / cdiv; ++ baud = bus_hz / cdiv; + } +- +- clk_tout = 35/1000*baudrate; //35ms timeout as per SMBus specs. +- if (clk_tout > 0xffff) ++ ++ clk_tout = 35/1000*baud; //35ms timeout as per SMBus specs. ++ if (clk_tout > 0xffff) + clk_tout = 0xffff; + + bi->cdiv = cdiv; + bi->clk_tout = clk_tout; + + dev_info(&pdev->dev, "BSC%d Controller at 0x%08lx (irq %d) (baudrate %d)\n", +- pdev->id, (unsigned long)regs->start, irq, baudrate); ++ pdev->id, (unsigned long)regs->start, irq, baud); + + return 0; + diff --git a/target/linux/brcm2708/patches-4.4/0139-rtc-ds1307-add-support-for-the-DT-property-wakeup-so.patch b/target/linux/brcm2708/patches-4.4/0139-rtc-ds1307-add-support-for-the-DT-property-wakeup-so.patch deleted file mode 100644 index 741cdf57ea..0000000000 --- a/target/linux/brcm2708/patches-4.4/0139-rtc-ds1307-add-support-for-the-DT-property-wakeup-so.patch +++ /dev/null @@ -1,94 +0,0 @@ -From 49ff16d7af98e6191d6dd60ba75b711612e948ab Mon Sep 17 00:00:00 2001 -From: Michael Lange -Date: Thu, 21 Jan 2016 18:10:16 +0100 -Subject: [PATCH 139/170] rtc: ds1307: add support for the DT property - 'wakeup-source' - -For RTC chips with no IRQ directly connected to the SoC, the RTC chip -can be forced as a wakeup source by stating that explicitly in -the device's .dts file using the "wakeup-source" boolean property. -This will guarantee the 'wakealarm' sysfs entry is available on the -device, if supported by the RTC. - -With these changes to the driver rtc-ds1307 and the necessary entries -in the .dts file, I get an working ds1337 RTC on the Witty Pi extension -board by UUGear for the Raspberry Pi. - -An example for the entry in the .dts file: - - rtc: ds1337@68 { - compatible = "dallas,ds1337"; - reg = <0x68>; - wakeup-source; - -If the "wakeup-source" property is set, do not request an IRQ. -Set also UIE mode to unsupported, to get a working 'hwclock' binary. - -Signed-off-by: Michael Lange -Signed-off-by: Alexandre Belloni ---- - drivers/rtc/rtc-ds1307.c | 29 +++++++++++++++++++++++++++-- - 1 file changed, 27 insertions(+), 2 deletions(-) - ---- a/drivers/rtc/rtc-ds1307.c -+++ b/drivers/rtc/rtc-ds1307.c -@@ -860,6 +860,7 @@ static int ds1307_probe(struct i2c_clien - struct chip_desc *chip = &chips[id->driver_data]; - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - bool want_irq = false; -+ bool ds1307_can_wakeup_device = false; - unsigned char *buf; - struct ds1307_platform_data *pdata = dev_get_platdata(&client->dev); - irq_handler_t irq_handler = ds1307_irq; -@@ -907,6 +908,20 @@ static int ds1307_probe(struct i2c_clien - ds1307->write_block_data = ds1307_write_block_data; - } - -+#ifdef CONFIG_OF -+/* -+ * For devices with no IRQ directly connected to the SoC, the RTC chip -+ * can be forced as a wakeup source by stating that explicitly in -+ * the device's .dts file using the "wakeup-source" boolean property. -+ * If the "wakeup-source" property is set, don't request an IRQ. -+ * This will guarantee the 'wakealarm' sysfs entry is available on the device, -+ * if supported by the RTC. -+ */ -+ if (of_property_read_bool(client->dev.of_node, "wakeup-source")) { -+ ds1307_can_wakeup_device = true; -+ } -+#endif -+ - switch (ds1307->type) { - case ds_1337: - case ds_1339: -@@ -925,11 +940,13 @@ static int ds1307_probe(struct i2c_clien - ds1307->regs[0] &= ~DS1337_BIT_nEOSC; - - /* -- * Using IRQ? Disable the square wave and both alarms. -+ * Using IRQ or defined as wakeup-source? -+ * Disable the square wave and both alarms. - * For some variants, be sure alarms can trigger when we're - * running on Vbackup (BBSQI/BBSQW) - */ -- if (ds1307->client->irq > 0 && chip->alarm) { -+ if (chip->alarm && (ds1307->client->irq > 0 || -+ ds1307_can_wakeup_device)) { - ds1307->regs[0] |= DS1337_BIT_INTCN - | bbsqi_bitpos[ds1307->type]; - ds1307->regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE); -@@ -1144,6 +1161,14 @@ read_rtc: - return PTR_ERR(ds1307->rtc); - } - -+ if (ds1307_can_wakeup_device) { -+ /* Disable request for an IRQ */ -+ want_irq = false; -+ dev_info(&client->dev, "'wakeup-source' is set, request for an IRQ is disabled!\n"); -+ /* We cannot support UIE mode if we do not have an IRQ line */ -+ ds1307->rtc->uie_unsupported = 1; -+ } -+ - if (want_irq) { - err = devm_request_threaded_irq(&client->dev, - client->irq, NULL, irq_handler, diff --git a/target/linux/brcm2708/patches-4.4/0140-Allow-up-to-24dB-digital-gain-to-be-applied-when-usi.patch b/target/linux/brcm2708/patches-4.4/0140-Allow-up-to-24dB-digital-gain-to-be-applied-when-usi.patch new file mode 100644 index 0000000000..765dbc5586 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0140-Allow-up-to-24dB-digital-gain-to-be-applied-when-usi.patch @@ -0,0 +1,99 @@ +From 4593191b7df9439c295a93250a1f1d68d0938822 Mon Sep 17 00:00:00 2001 +From: Digital Dreamtime +Date: Thu, 4 Feb 2016 14:14:44 +0000 +Subject: [PATCH 140/232] Allow up to 24dB digital gain to be applied when + using IQAudIO DAC+ + +24db_digital_gain DT param can be used to specify that PCM512x +codec "Digital" volume control should not be limited to 0dB gain, +and if specified will allow the full 24dB gain. +--- + arch/arm/boot/dts/overlays/README | 17 +++++++++++++++-- + .../boot/dts/overlays/iqaudio-dacplus-overlay.dts | 6 +++++- + sound/soc/bcm/iqaudio-dac.c | 20 ++++++++++++++------ + 3 files changed, 34 insertions(+), 9 deletions(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -362,8 +362,21 @@ Params: + + Name: iqaudio-dacplus + Info: Configures the IQaudio DAC+ audio card +-Load: dtoverlay=iqaudio-dacplus +-Params: ++Load: dtoverlay=iqaudio-dacplus,= ++Params: 24db_digital_gain Allow gain to be applied via the PCM512x codec ++ Digital volume control. Enable with ++ "dtoverlay=iqaudio-dacplus,24db_digital_gain" ++ (The default behaviour is that the Digital ++ volume control is limited to a maximum of ++ 0dB. ie. it can attenuate but not provide ++ gain. For most users, this will be desired ++ as it will prevent clipping. By appending ++ the 24db_digital_gain parameter, the Digital ++ volume control will allow up to 24dB of ++ gain. If this parameter is enabled, it is the ++ responsibility of the user to ensure that ++ the Digital volume control is set to a value ++ that does not result in clipping/distortion!) + + + Name: lirc-rpi +--- a/arch/arm/boot/dts/overlays/iqaudio-dacplus-overlay.dts ++++ b/arch/arm/boot/dts/overlays/iqaudio-dacplus-overlay.dts +@@ -7,7 +7,7 @@ + + fragment@0 { + target = <&sound>; +- __overlay__ { ++ frag0: __overlay__ { + compatible = "iqaudio,iqaudio-dac"; + i2s-controller = <&i2s>; + status = "okay"; +@@ -36,4 +36,8 @@ + }; + }; + }; ++ ++ __overrides__ { ++ 24db_digital_gain = <&frag0>,"iqaudio,24db_digital_gain?"; ++ }; + }; +--- a/sound/soc/bcm/iqaudio-dac.c ++++ b/sound/soc/bcm/iqaudio-dac.c +@@ -23,14 +23,19 @@ + #include + #include + ++static bool digital_gain_0db_limit = true; ++ + static int snd_rpi_iqaudio_dac_init(struct snd_soc_pcm_runtime *rtd) + { +- int ret; +- struct snd_soc_card *card = rtd->card; +- +- ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207); +- if (ret < 0) +- dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); ++ if (digital_gain_0db_limit) ++ { ++ int ret; ++ struct snd_soc_card *card = rtd->card; ++ ++ ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207); ++ if (ret < 0) ++ dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); ++ } + + return 0; + } +@@ -94,6 +99,9 @@ static int snd_rpi_iqaudio_dac_probe(str + dai->platform_name = NULL; + dai->platform_of_node = i2s_node; + } ++ ++ digital_gain_0db_limit = !of_property_read_bool(pdev->dev.of_node, ++ "iqaudio,24db_digital_gain"); + } + + ret = snd_soc_register_card(&snd_rpi_iqaudio_dac); diff --git a/target/linux/brcm2708/patches-4.4/0140-dt-overlay-add-wittypi-overlay.dts.patch b/target/linux/brcm2708/patches-4.4/0140-dt-overlay-add-wittypi-overlay.dts.patch deleted file mode 100644 index b740c826f1..0000000000 --- a/target/linux/brcm2708/patches-4.4/0140-dt-overlay-add-wittypi-overlay.dts.patch +++ /dev/null @@ -1,57 +0,0 @@ -From e382e888be3f59b0ba3645809ae686947313e0c1 Mon Sep 17 00:00:00 2001 -From: vitalogy -Date: Tue, 19 Jan 2016 07:02:02 +0100 -Subject: [PATCH 140/170] dt-overlay: add wittypi-overlay.dts - ---- - arch/arm/boot/dts/overlays/wittypi-overlay.dts | 44 ++++++++++++++++++++++++++ - 1 file changed, 44 insertions(+) - create mode 100644 arch/arm/boot/dts/overlays/wittypi-overlay.dts - ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/wittypi-overlay.dts -@@ -0,0 +1,44 @@ -+/* -+ * Device Tree overlay for Witty Pi extension board by UUGear -+ * -+ */ -+ -+/dts-v1/; -+/plugin/; -+ -+/ { -+ -+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&leds>; -+ __overlay__ { -+ compatible = "gpio-leds"; -+ wittypi_led: wittypi_led { -+ label = "wittypi_led"; -+ linux,default-trigger = "default-on"; -+ gpios = <&gpio 17 0>; -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&i2c1>; -+ __overlay__ { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ rtc: ds1337@68 { -+ compatible = "dallas,ds1337"; -+ reg = <0x68>; -+ wakeup-source; -+ }; -+ }; -+ }; -+ -+ __overrides__ { -+ led_gpio = <&wittypi_led>,"gpios:4"; -+ led_trigger = <&wittypi_led>,"linux,default-trigger"; -+ }; -+ -+}; diff --git a/target/linux/brcm2708/patches-4.4/0141-BCM270X_DT-Disable-DMA-for-bcm2835-sdhost-on-Pi2.patch b/target/linux/brcm2708/patches-4.4/0141-BCM270X_DT-Disable-DMA-for-bcm2835-sdhost-on-Pi2.patch deleted file mode 100644 index b06b4786ce..0000000000 --- a/target/linux/brcm2708/patches-4.4/0141-BCM270X_DT-Disable-DMA-for-bcm2835-sdhost-on-Pi2.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 6dcfc3daab7c1c459b36261163b4e1dc15a6e965 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Wed, 3 Feb 2016 16:12:54 +0000 -Subject: [PATCH 141/170] BCM270X_DT: Disable DMA for bcm2835-sdhost on Pi2 - -This is an interim patch to verify that problems seen with -some cards can be worked around at the expense of an increased -CPU load by forcing PIO mode. - -Although this could have used the brcm,force-pio attribute, that -is a boolean (true if present, false if absent) and attributes -can't be deleted by overlays. Instead, make brcm,pio-limit -unfeasibly high instead to allow DMA to be re-enabled using the -pio_limit parameter of the sdhost or sdtweak overlays. ---- - arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 1 + - 1 file changed, 1 insertion(+) - ---- a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts -+++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts -@@ -43,6 +43,7 @@ - pinctrl-names = "default"; - pinctrl-0 = <&sdhost_pins>; - bus-width = <4>; -+ brcm,pio-limit = <0x7fffffff>; - status = "okay"; - }; - diff --git a/target/linux/brcm2708/patches-4.4/0141-Limit-PCM512x-Digital-gain-to-0dB-by-default-with-Hi.patch b/target/linux/brcm2708/patches-4.4/0141-Limit-PCM512x-Digital-gain-to-0dB-by-default-with-Hi.patch new file mode 100644 index 0000000000..cc28de89cb --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0141-Limit-PCM512x-Digital-gain-to-0dB-by-default-with-Hi.patch @@ -0,0 +1,98 @@ +From 6fda328fc5613d449641ad6701fe8180e70ec338 Mon Sep 17 00:00:00 2001 +From: Digital Dreamtime +Date: Thu, 4 Feb 2016 20:04:00 +0000 +Subject: [PATCH 141/232] Limit PCM512x "Digital" gain to 0dB by default with + HiFiBerry DAC+ + +24db_digital_gain DT param can be used to specify that PCM512x +codec "Digital" volume control should not be limited to 0dB gain, +and if specified will allow the full 24dB gain. +--- + arch/arm/boot/dts/overlays/README | 17 +++++++++++++++-- + .../arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts | 6 +++++- + sound/soc/bcm/hifiberry_dacplus.c | 14 ++++++++++++++ + 3 files changed, 34 insertions(+), 3 deletions(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -273,8 +273,21 @@ Params: + + Name: hifiberry-dacplus + Info: Configures the HifiBerry DAC+ audio card +-Load: dtoverlay=hifiberry-dacplus +-Params: ++Load: dtoverlay=hifiberry-dacplus,= ++Params: 24db_digital_gain Allow gain to be applied via the PCM512x codec ++ Digital volume control. Enable with ++ "dtoverlay=hifiberry-dacplus,24db_digital_gain" ++ (The default behaviour is that the Digital ++ volume control is limited to a maximum of ++ 0dB. ie. it can attenuate but not provide ++ gain. For most users, this will be desired ++ as it will prevent clipping. By appending ++ the 24dB_digital_gain parameter, the Digital ++ volume control will allow up to 24dB of ++ gain. If this parameter is enabled, it is the ++ responsibility of the user to ensure that ++ the Digital volume control is set to a value ++ that does not result in clipping/distortion!) + + + Name: hifiberry-digi +--- a/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts ++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts +@@ -17,7 +17,7 @@ + + fragment@1 { + target = <&sound>; +- __overlay__ { ++ frag1: __overlay__ { + compatible = "hifiberry,hifiberry-dacplus"; + i2s-controller = <&i2s>; + status = "okay"; +@@ -47,4 +47,8 @@ + }; + }; + }; ++ ++ __overrides__ { ++ 24db_digital_gain = <&frag1>,"hifiberry,24db_digital_gain?"; ++ }; + }; +--- a/sound/soc/bcm/hifiberry_dacplus.c ++++ b/sound/soc/bcm/hifiberry_dacplus.c +@@ -48,6 +48,7 @@ struct pcm512x_priv { + #define CLK_48EN_RATE 24576000UL + + static bool snd_rpi_hifiberry_is_dacpro; ++static bool digital_gain_0db_limit = true; + + static void snd_rpi_hifiberry_dacplus_select_clk(struct snd_soc_codec *codec, + int clk_id) +@@ -167,6 +168,16 @@ static int snd_rpi_hifiberry_dacplus_ini + snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02); + snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); + ++ if (digital_gain_0db_limit) ++ { ++ int ret; ++ struct snd_soc_card *card = rtd->card; ++ ++ ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207); ++ if (ret < 0) ++ dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); ++ } ++ + return 0; + } + +@@ -299,6 +310,9 @@ static int snd_rpi_hifiberry_dacplus_pro + dai->platform_name = NULL; + dai->platform_of_node = i2s_node; + } ++ ++ digital_gain_0db_limit = !of_property_read_bool( ++ pdev->dev.of_node, "hifiberry,24db_digital_gain"); + } + + ret = snd_soc_register_card(&snd_rpi_hifiberry_dacplus); diff --git a/target/linux/brcm2708/patches-4.4/0142-BCM270X_DT-Adjust-overlay-README-formatting.patch b/target/linux/brcm2708/patches-4.4/0142-BCM270X_DT-Adjust-overlay-README-formatting.patch new file mode 100644 index 0000000000..a9687a7c37 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0142-BCM270X_DT-Adjust-overlay-README-formatting.patch @@ -0,0 +1,729 @@ +From 1b3fd5b03546474795b7557a04c7c3c5cf162ecb Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 8 Feb 2016 09:46:33 +0000 +Subject: [PATCH 142/232] BCM270X_DT: Adjust overlay README formatting + +--- + arch/arm/boot/dts/overlays/README | 414 +++++++++++++++++++------------------- + 1 file changed, 207 insertions(+), 207 deletions(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -83,58 +83,58 @@ Name: + Info: Configures the base Raspberry Pi hardware + Load: + Params: +- audio Set to "on" to enable the onboard ALSA audio +- interface (default "off") ++ audio Set to "on" to enable the onboard ALSA audio ++ interface (default "off") + +- i2c_arm Set to "on" to enable the ARM's i2c interface +- (default "off") ++ i2c_arm Set to "on" to enable the ARM's i2c interface ++ (default "off") + +- i2c_vc Set to "on" to enable the i2c interface +- usually reserved for the VideoCore processor +- (default "off") ++ i2c_vc Set to "on" to enable the i2c interface ++ usually reserved for the VideoCore processor ++ (default "off") + +- i2c An alias for i2c_arm ++ i2c An alias for i2c_arm + +- i2c_arm_baudrate Set the baudrate of the ARM's i2c interface +- (default "100000") ++ i2c_arm_baudrate Set the baudrate of the ARM's i2c interface ++ (default "100000") + +- i2c_vc_baudrate Set the baudrate of the VideoCore i2c interface +- (default "100000") ++ i2c_vc_baudrate Set the baudrate of the VideoCore i2c interface ++ (default "100000") + +- i2c_baudrate An alias for i2c_arm_baudrate ++ i2c_baudrate An alias for i2c_arm_baudrate + +- i2s Set to "on" to enable the i2s interface +- (default "off") ++ i2s Set to "on" to enable the i2s interface ++ (default "off") + +- spi Set to "on" to enable the spi interfaces +- (default "off") ++ spi Set to "on" to enable the spi interfaces ++ (default "off") + +- random Set to "on" to enable the hardware random +- number generator (default "on") ++ random Set to "on" to enable the hardware random ++ number generator (default "on") + +- uart0 Set to "off" to disable uart0 (default "on") ++ uart0 Set to "off" to disable uart0 (default "on") + +- watchdog Set to "on" to enable the hardware watchdog +- (default "off") ++ watchdog Set to "on" to enable the hardware watchdog ++ (default "off") + +- act_led_trigger Choose which activity the LED tracks. +- Use "heartbeat" for a nice load indicator. +- (default "mmc") ++ act_led_trigger Choose which activity the LED tracks. ++ Use "heartbeat" for a nice load indicator. ++ (default "mmc") + +- act_led_activelow Set to "on" to invert the sense of the LED +- (default "off") ++ act_led_activelow Set to "on" to invert the sense of the LED ++ (default "off") + +- act_led_gpio Set which GPIO to use for the activity LED +- (in case you want to connect it to an external +- device) +- (default "16" on a non-Plus board, "47" on a +- Plus or Pi 2) ++ act_led_gpio Set which GPIO to use for the activity LED ++ (in case you want to connect it to an external ++ device) ++ (default "16" on a non-Plus board, "47" on a ++ Plus or Pi 2) + + pwr_led_trigger + pwr_led_activelow + pwr_led_gpio +- As for act_led_*, but using the PWR LED. +- Not available on Model A/B boards. ++ As for act_led_*, but using the PWR LED. ++ Not available on Model A/B boards. + + N.B. It is recommended to only enable those interfaces that are needed. + Leaving all interfaces enabled can lead to unwanted behaviour (i2c_vc +@@ -149,19 +149,19 @@ Params: + Name: ads7846 + Info: ADS7846 Touch controller + Load: dtoverlay=ads7846,= +-Params: cs SPI bus Chip Select (default 1) +- speed SPI bus speed (default 2MHz, max 3.25MHz) +- penirq GPIO used for PENIRQ. REQUIRED +- penirq_pull Set GPIO pull (default 0=none, 2=pullup) +- swapxy Swap x and y axis +- xmin Minimum value on the X axis (default 0) +- ymin Minimum value on the Y axis (default 0) +- xmax Maximum value on the X axis (default 4095) +- ymax Maximum value on the Y axis (default 4095) +- pmin Minimum reported pressure value (default 0) +- pmax Maximum reported pressure value (default 65535) +- xohms Touchpanel sensitivity (X-plate resistance) +- (default 400) ++Params: cs SPI bus Chip Select (default 1) ++ speed SPI bus speed (default 2MHz, max 3.25MHz) ++ penirq GPIO used for PENIRQ. REQUIRED ++ penirq_pull Set GPIO pull (default 0=none, 2=pullup) ++ swapxy Swap x and y axis ++ xmin Minimum value on the X axis (default 0) ++ ymin Minimum value on the Y axis (default 0) ++ xmax Maximum value on the X axis (default 4095) ++ ymax Maximum value on the Y axis (default 4095) ++ pmin Minimum reported pressure value (default 0) ++ pmax Maximum reported pressure value (default 65535) ++ xohms Touchpanel sensitivity (X-plate resistance) ++ (default 400) + + penirq is required and usually xohms (60-100) has to be set as well. + Apart from that, pmax (255) and swapxy are also common. +@@ -175,12 +175,12 @@ Name: at86rf233 + Info: Configures the Atmel AT86RF233 802.15.4 low-power WPAN transceiver, + connected to spi0.0 + Load: dtoverlay=at86rf233,= +-Params: interrupt GPIO used for INT (default 23) +- reset GPIO used for Reset (default 24) +- sleep GPIO used for Sleep (default 25) +- speed SPI bus speed in Hz (default 6000000) +- trim Fine tuning of the internal capacitance +- arrays (0=+0pF, 15=+4.5pF, default 15) ++Params: interrupt GPIO used for INT (default 23) ++ reset GPIO used for Reset (default 24) ++ sleep GPIO used for Sleep (default 25) ++ speed SPI bus speed in Hz (default 6000000) ++ trim Fine tuning of the internal capacitance ++ arrays (0=+0pF, 15=+4.5pF, default 15) + + + Name: bmp085_i2c-sensor +@@ -194,8 +194,8 @@ Name: dht11 + Info: Overlay for the DHT11/DHT21/DHT22 humidity/temperature sensors + Also sometimes found with the part number(s) AM230x. + Load: dtoverlay=dht11,= +-Params: gpiopin GPIO connected to the sensor's DATA output. +- (default 4) ++Params: gpiopin GPIO connected to the sensor's DATA output. ++ (default 4) + + + Name: dwc-otg +@@ -208,15 +208,15 @@ Params: + Name: dwc2 + Info: Selects the dwc2 USB controller driver + Load: dtoverlay=dwc2,= +-Params: dr_mode Dual role mode: "host", "peripheral" or "otg" ++Params: dr_mode Dual role mode: "host", "peripheral" or "otg" + +- g-rx-fifo-size Size of rx fifo size in gadget mode ++ g-rx-fifo-size Size of rx fifo size in gadget mode + +- g-np-tx-fifo-size Size of non-periodic tx fifo size in gadget +- mode ++ g-np-tx-fifo-size Size of non-periodic tx fifo size in gadget ++ mode + +- g-tx-fifo-size Size of periodic tx fifo per endpoint +- (except ep0) in gadget mode ++ g-tx-fifo-size Size of periodic tx fifo per endpoint ++ (except ep0) in gadget mode + + + [ The ds1307-rtc overlay has been deleted. See i2c-rtc. ] +@@ -225,9 +225,9 @@ Params: dr_mode Dual ro + Name: enc28j60 + Info: Overlay for the Microchip ENC28J60 Ethernet Controller (SPI) + Load: dtoverlay=enc28j60,= +-Params: int_pin GPIO used for INT (default 25) ++Params: int_pin GPIO used for INT (default 25) + +- speed SPI bus speed (default 12000000) ++ speed SPI bus speed (default 12000000) + + + Name: gpio-ir +@@ -237,26 +237,26 @@ Info: Use GPIO pin as rc-core style in + not required! The key mapping and other decoding parameters can be + configured by "ir-keytable" tool. + Load: dtoverlay=gpio-ir,= +-Params: gpio_pin Input pin number. Default is 18. ++Params: gpio_pin Input pin number. Default is 18. + +- gpio_pull Desired pull-up/down state (off, down, up) +- Default is "down". ++ gpio_pull Desired pull-up/down state (off, down, up) ++ Default is "down". + +- rc-map-name Default rc keymap (can also be changed by +- ir-keytable), defaults to "rc-rc6-mce" ++ rc-map-name Default rc keymap (can also be changed by ++ ir-keytable), defaults to "rc-rc6-mce" + + + Name: gpio-poweroff + Info: Drives a GPIO high or low on reboot + Load: dtoverlay=gpio-poweroff,= +-Params: gpiopin GPIO for signalling (default 26) ++Params: gpiopin GPIO for signalling (default 26) + +- active_low Set if the power control device requires a +- high->low transition to trigger a power-down. +- Note that this will require the support of a +- custom dt-blob.bin to prevent a power-down +- during the boot process, and that a reboot +- will also cause the pin to go low. ++ active_low Set if the power control device requires a ++ high->low transition to trigger a power-down. ++ Note that this will require the support of a ++ custom dt-blob.bin to prevent a power-down ++ during the boot process, and that a reboot ++ will also cause the pin to go low. + + + Name: hifiberry-amp +@@ -300,65 +300,65 @@ Name: hy28a + Info: HY28A - 2.8" TFT LCD Display Module by HAOYU Electronics + Default values match Texy's display shield + Load: dtoverlay=hy28a,= +-Params: speed Display SPI bus speed ++Params: speed Display SPI bus speed + +- rotate Display rotation {0,90,180,270} ++ rotate Display rotation {0,90,180,270} + +- fps Delay between frame updates ++ fps Delay between frame updates + +- debug Debug output level {0-7} ++ debug Debug output level {0-7} + +- xohms Touchpanel sensitivity (X-plate resistance) ++ xohms Touchpanel sensitivity (X-plate resistance) + +- resetgpio GPIO used to reset controller ++ resetgpio GPIO used to reset controller + +- ledgpio GPIO used to control backlight ++ ledgpio GPIO used to control backlight + + + Name: hy28b + Info: HY28B - 2.8" TFT LCD Display Module by HAOYU Electronics + Default values match Texy's display shield + Load: dtoverlay=hy28b,= +-Params: speed Display SPI bus speed ++Params: speed Display SPI bus speed + +- rotate Display rotation {0,90,180,270} ++ rotate Display rotation {0,90,180,270} + +- fps Delay between frame updates ++ fps Delay between frame updates + +- debug Debug output level {0-7} ++ debug Debug output level {0-7} + +- xohms Touchpanel sensitivity (X-plate resistance) ++ xohms Touchpanel sensitivity (X-plate resistance) + +- resetgpio GPIO used to reset controller ++ resetgpio GPIO used to reset controller + +- ledgpio GPIO used to control backlight ++ ledgpio GPIO used to control backlight + + + Name: i2c-gpio + Info: Adds support for software i2c controller on gpio pins + Load: dtoverlay=i2c-gpio,= +-Params: i2c_gpio_sda GPIO used for I2C data (default "23") ++Params: i2c_gpio_sda GPIO used for I2C data (default "23") + +- i2c_gpio_scl GPIO used for I2C clock (default "24") ++ i2c_gpio_scl GPIO used for I2C clock (default "24") + +- i2c_gpio_delay_us Clock delay in microseconds +- (default "2" = ~100kHz) ++ i2c_gpio_delay_us Clock delay in microseconds ++ (default "2" = ~100kHz) + + + Name: i2c-rtc + Info: Adds support for a number of I2C Real Time Clock devices + Load: dtoverlay=i2c-rtc,= +-Params: ds1307 Select the DS1307 device ++Params: ds1307 Select the DS1307 device + +- ds3231 Select the DS3231 device ++ ds3231 Select the DS3231 device + +- mcp7941x Select the MCP7941x device ++ mcp7941x Select the MCP7941x device + +- pcf2127 Select the PCF2127 device ++ pcf2127 Select the PCF2127 device + +- pcf8523 Select the PCF8523 device ++ pcf8523 Select the PCF8523 device + +- pcf8563 Select the PCF8563 device ++ pcf8563 Select the PCF8563 device + + + Name: i2s-mmap +@@ -396,70 +396,70 @@ Name: lirc-rpi + Info: Configures lirc-rpi (Linux Infrared Remote Control for Raspberry Pi) + Consult the module documentation for more details. + Load: dtoverlay=lirc-rpi,= +-Params: gpio_out_pin GPIO for output (default "17") ++Params: gpio_out_pin GPIO for output (default "17") + +- gpio_in_pin GPIO for input (default "18") ++ gpio_in_pin GPIO for input (default "18") + +- gpio_in_pull Pull up/down/off on the input pin +- (default "down") ++ gpio_in_pull Pull up/down/off on the input pin ++ (default "down") + +- sense Override the IR receive auto-detection logic: +- "0" = force active-high +- "1" = force active-low +- "-1" = use auto-detection +- (default "-1") ++ sense Override the IR receive auto-detection logic: ++ "0" = force active-high ++ "1" = force active-low ++ "-1" = use auto-detection ++ (default "-1") + +- softcarrier Turn the software carrier "on" or "off" +- (default "on") ++ softcarrier Turn the software carrier "on" or "off" ++ (default "on") + +- invert "on" = invert the output pin (default "off") ++ invert "on" = invert the output pin (default "off") + +- debug "on" = enable additional debug messages +- (default "off") ++ debug "on" = enable additional debug messages ++ (default "off") + + + Name: mcp2515-can0 + Info: Configures the MCP2515 CAN controller on spi0.0 + Load: dtoverlay=mcp2515-can0,= +-Params: oscillator Clock frequency for the CAN controller (Hz) ++Params: oscillator Clock frequency for the CAN controller (Hz) + +- spimaxfrequency Maximum SPI frequence (Hz) ++ spimaxfrequency Maximum SPI frequence (Hz) + +- interrupt GPIO for interrupt signal ++ interrupt GPIO for interrupt signal + + + Name: mcp2515-can1 + Info: Configures the MCP2515 CAN controller on spi0.1 + Load: dtoverlay=mcp2515-can1,= +-Params: oscillator Clock frequency for the CAN controller (Hz) ++Params: oscillator Clock frequency for the CAN controller (Hz) + +- spimaxfrequency Maximum SPI frequence (Hz) ++ spimaxfrequency Maximum SPI frequence (Hz) + +- interrupt GPIO for interrupt signal ++ interrupt GPIO for interrupt signal + + + Name: mmc + Info: Selects the bcm2835-mmc SD/MMC driver, optionally with overclock + Load: dtoverlay=mmc,= +-Params: overclock_50 Clock (in MHz) to use when the MMC framework +- requests 50MHz +- force_pio Disable DMA support ++Params: overclock_50 Clock (in MHz) to use when the MMC framework ++ requests 50MHz ++ force_pio Disable DMA support + + + Name: mz61581 + Info: MZ61581 display by Tontec + Load: dtoverlay=mz61581,= +-Params: speed Display SPI bus speed ++Params: speed Display SPI bus speed + +- rotate Display rotation {0,90,180,270} ++ rotate Display rotation {0,90,180,270} + +- fps Delay between frame updates ++ fps Delay between frame updates + +- txbuflen Transmit buffer length (default 32768) ++ txbuflen Transmit buffer length (default 32768) + +- debug Debug output level {0-7} ++ debug Debug output level {0-7} + +- xohms Touchpanel sensitivity (X-plate resistance) ++ xohms Touchpanel sensitivity (X-plate resistance) + + + [ The pcf2127-rtc overlay has been deleted. See i2c-rtc. ] +@@ -474,69 +474,69 @@ Params: speed Display + Name: piscreen + Info: PiScreen display by OzzMaker.com + Load: dtoverlay=piscreen,= +-Params: speed Display SPI bus speed ++Params: speed Display SPI bus speed + +- rotate Display rotation {0,90,180,270} ++ rotate Display rotation {0,90,180,270} + +- fps Delay between frame updates ++ fps Delay between frame updates + +- debug Debug output level {0-7} ++ debug Debug output level {0-7} + +- xohms Touchpanel sensitivity (X-plate resistance) ++ xohms Touchpanel sensitivity (X-plate resistance) + + + Name: piscreen2r + Info: PiScreen 2 with resistive TP display by OzzMaker.com + Load: dtoverlay=piscreen2r,= +-Params: speed Display SPI bus speed ++Params: speed Display SPI bus speed + +- rotate Display rotation {0,90,180,270} ++ rotate Display rotation {0,90,180,270} + +- fps Delay between frame updates ++ fps Delay between frame updates + +- debug Debug output level {0-7} ++ debug Debug output level {0-7} + +- xohms Touchpanel sensitivity (X-plate resistance) ++ xohms Touchpanel sensitivity (X-plate resistance) + + + Name: pitft28-capacitive + Info: Adafruit PiTFT 2.8" capacitive touch screen + Load: dtoverlay=pitft28-capacitive,= +-Params: speed Display SPI bus speed ++Params: speed Display SPI bus speed + +- rotate Display rotation {0,90,180,270} ++ rotate Display rotation {0,90,180,270} + +- fps Delay between frame updates ++ fps Delay between frame updates + +- debug Debug output level {0-7} ++ debug Debug output level {0-7} + +- touch-sizex Touchscreen size x (default 240) ++ touch-sizex Touchscreen size x (default 240) + +- touch-sizey Touchscreen size y (default 320) ++ touch-sizey Touchscreen size y (default 320) + +- touch-invx Touchscreen inverted x axis ++ touch-invx Touchscreen inverted x axis + +- touch-invy Touchscreen inverted y axis ++ touch-invy Touchscreen inverted y axis + +- touch-swapxy Touchscreen swapped x y axis ++ touch-swapxy Touchscreen swapped x y axis + + + Name: pitft28-resistive + Info: Adafruit PiTFT 2.8" resistive touch screen + Load: dtoverlay=pitft28-resistive,= +-Params: speed Display SPI bus speed ++Params: speed Display SPI bus speed + +- rotate Display rotation {0,90,180,270} ++ rotate Display rotation {0,90,180,270} + +- fps Delay between frame updates ++ fps Delay between frame updates + +- debug Debug output level {0-7} ++ debug Debug output level {0-7} + + + Name: pps-gpio + Info: Configures the pps-gpio (pulse-per-second time signal via GPIO). + Load: dtoverlay=pps-gpio,= +-Params: gpiopin Input GPIO (default "18") ++Params: gpiopin Input GPIO (default "18") + + + Name: pwm +@@ -553,9 +553,9 @@ Info: Configures a single PWM channel + 4) Currently the clock must have been enabled and configured + by other means. + Load: dtoverlay=pwm,= +-Params: pin Output pin (default 18) - see table +- func Pin function (default 2 = Alt5) - see above +- clock PWM clock frequency (informational) ++Params: pin Output pin (default 18) - see table ++ func Pin function (default 2 = Alt5) - see above ++ clock PWM clock frequency (informational) + + + Name: pwm-2chan +@@ -572,11 +572,11 @@ Info: Configures both PWM channels + 4) Currently the clock must have been enabled and configured + by other means. + Load: dtoverlay=pwm-2chan,= +-Params: pin Output pin (default 18) - see table +- pin2 Output pin for other channel (default 19) +- func Pin function (default 2 = Alt5) - see above +- func2 Function for pin2 (default 2 = Alt5) +- clock PWM clock frequency (informational) ++Params: pin Output pin (default 18) - see table ++ pin2 Output pin for other channel (default 19) ++ func Pin function (default 2 = Alt5) - see above ++ func2 Function for pin2 (default 2 = Alt5) ++ clock PWM clock frequency (informational) + + + Name: raspidac3 +@@ -600,15 +600,15 @@ Params: + Name: rpi-display + Info: RPi-Display - 2.8" Touch Display by Watterott + Load: dtoverlay=rpi-display,= +-Params: speed Display SPI bus speed ++Params: speed Display SPI bus speed + +- rotate Display rotation {0,90,180,270} ++ rotate Display rotation {0,90,180,270} + +- fps Delay between frame updates ++ fps Delay between frame updates + +- debug Debug output level {0-7} ++ debug Debug output level {0-7} + +- xohms Touchpanel sensitivity (X-plate resistance) ++ xohms Touchpanel sensitivity (X-plate resistance) + + + Name: rpi-ft5406 +@@ -632,52 +632,52 @@ Params: + Name: sdhost + Info: Selects the bcm2835-sdhost SD/MMC driver, optionally with overclock + Load: dtoverlay=sdhost,= +-Params: overclock_50 Clock (in MHz) to use when the MMC framework +- requests 50MHz ++Params: overclock_50 Clock (in MHz) to use when the MMC framework ++ requests 50MHz + +- force_pio Disable DMA support (default off) ++ force_pio Disable DMA support (default off) + +- pio_limit Number of blocks above which to use DMA +- (default 1) ++ pio_limit Number of blocks above which to use DMA ++ (default 1) + +- debug Enable debug output (default off) ++ debug Enable debug output (default off) + + + Name: sdio + Info: Selects the bcm2835-sdhost SD/MMC driver, optionally with overclock, + and enables SDIO via GPIOs 22-27. + Load: dtoverlay=sdio,= +-Params: overclock_50 SD Clock (in MHz) to use when the MMC framework +- requests 50MHz ++Params: overclock_50 SD Clock (in MHz) to use when the MMC framework ++ requests 50MHz + +- sdio_overclock SDIO Clock (in MHz) to use when the MMC +- framework requests 50MHz ++ sdio_overclock SDIO Clock (in MHz) to use when the MMC ++ framework requests 50MHz + +- force_pio Disable DMA support (default off) ++ force_pio Disable DMA support (default off) + +- pio_limit Number of blocks above which to use DMA +- (default 1) ++ pio_limit Number of blocks above which to use DMA ++ (default 1) + +- debug Enable debug output (default off) ++ debug Enable debug output (default off) + +- poll_once Disable SDIO-device polling every second +- (default on: polling once at boot-time) ++ poll_once Disable SDIO-device polling every second ++ (default on: polling once at boot-time) + +- bus_width Set the SDIO host bus width (default 4 bits) ++ bus_width Set the SDIO host bus width (default 4 bits) + + + Name: sdtweak + Info: Tunes the bcm2835-sdhost SD/MMC driver + Load: dtoverlay=sdtweak,= +-Params: overclock_50 Clock (in MHz) to use when the MMC framework +- requests 50MHz ++Params: overclock_50 Clock (in MHz) to use when the MMC framework ++ requests 50MHz + +- force_pio Disable DMA support (default off) ++ force_pio Disable DMA support (default off) + +- pio_limit Number of blocks above which to use DMA +- (default 1) ++ pio_limit Number of blocks above which to use DMA ++ (default 1) + +- debug Enable debug output (default off) ++ debug Enable debug output (default off) + + + Name: smi +@@ -708,25 +708,25 @@ Name: tinylcd35 + Info: 3.5" Color TFT Display by www.tinylcd.com + Options: Touch, RTC, keypad + Load: dtoverlay=tinylcd35,= +-Params: speed Display SPI bus speed ++Params: speed Display SPI bus speed + +- rotate Display rotation {0,90,180,270} ++ rotate Display rotation {0,90,180,270} + +- fps Delay between frame updates ++ fps Delay between frame updates + +- debug Debug output level {0-7} ++ debug Debug output level {0-7} + +- touch Enable touch panel ++ touch Enable touch panel + +- touchgpio Touch controller IRQ GPIO ++ touchgpio Touch controller IRQ GPIO + +- xohms Touchpanel: Resistance of X-plate in ohms ++ xohms Touchpanel: Resistance of X-plate in ohms + +- rtc-pcf PCF8563 Real Time Clock ++ rtc-pcf PCF8563 Real Time Clock + +- rtc-ds DS1307 Real Time Clock ++ rtc-ds DS1307 Real Time Clock + +- keypad Enable keypad ++ keypad Enable keypad + + Examples: + Display with touchpanel, PCF8563 RTC and keypad: +@@ -738,9 +738,9 @@ Params: speed Display + Name: uart1 + Info: Enable uart1 in place of uart0 + Load: dtoverlay=uart1,= +-Params: txd1_pin GPIO pin for TXD1 (14, 32 or 40 - default 14) ++Params: txd1_pin GPIO pin for TXD1 (14, 32 or 40 - default 14) + +- rxd1_pin GPIO pin for RXD1 (15, 33 or 41 - default 15) ++ rxd1_pin GPIO pin for RXD1 (15, 33 or 41 - default 15) + + + Name: vc4-kms-v3d +@@ -763,22 +763,22 @@ Name: w1-gpio + Info: Configures the w1-gpio Onewire interface module. + Use this overlay if you *don't* need a GPIO to drive an external pullup. + Load: dtoverlay=w1-gpio,= +-Params: gpiopin GPIO for I/O (default "4") ++Params: gpiopin GPIO for I/O (default "4") + +- pullup Non-zero, "on", or "y" to enable the parasitic +- power (2-wire, power-on-data) feature ++ pullup Non-zero, "on", or "y" to enable the parasitic ++ power (2-wire, power-on-data) feature + + + Name: w1-gpio-pullup + Info: Configures the w1-gpio Onewire interface module. + Use this overlay if you *do* need a GPIO to drive an external pullup. + Load: dtoverlay=w1-gpio-pullup,= +-Params: gpiopin GPIO for I/O (default "4") ++Params: gpiopin GPIO for I/O (default "4") + +- pullup Non-zero, "on", or "y" to enable the parasitic +- power (2-wire, power-on-data) feature ++ pullup Non-zero, "on", or "y" to enable the parasitic ++ power (2-wire, power-on-data) feature + +- extpullup GPIO for external pullup (default "5") ++ extpullup GPIO for external pullup (default "5") + + + Troubleshooting diff --git a/target/linux/brcm2708/patches-4.4/0142-FIXUP-i2c_bcm2708-Don-t-change-module-baudrate-param.patch b/target/linux/brcm2708/patches-4.4/0142-FIXUP-i2c_bcm2708-Don-t-change-module-baudrate-param.patch deleted file mode 100644 index b3ce9719ac..0000000000 --- a/target/linux/brcm2708/patches-4.4/0142-FIXUP-i2c_bcm2708-Don-t-change-module-baudrate-param.patch +++ /dev/null @@ -1,99 +0,0 @@ -From 7519a79f61a67792e238a118db58600fb5f60ec8 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Tue, 19 Jan 2016 16:28:05 +0000 -Subject: [PATCH 142/170] FIXUP i2c_bcm2708: Don't change module baudrate - parameter - -Overwriting the baudrate module parameter creates an apparent -forced baudrate for i2c busses after the first. Not only does this -override the baudrate from DT it also prevents the bus ID from -being initialised. - -Also fix whitespace errors. ---- - drivers/i2c/busses/i2c-bcm2708.c | 48 +++++++++++++++++++++------------------- - 1 file changed, 25 insertions(+), 23 deletions(-) - ---- a/drivers/i2c/busses/i2c-bcm2708.c -+++ b/drivers/i2c/busses/i2c-bcm2708.c -@@ -71,7 +71,6 @@ - - #define DRV_NAME "bcm2708_i2c" - --static unsigned int baudrate_default = CONFIG_I2C_BCM2708_BAUDRATE; - static unsigned int baudrate; - module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); - MODULE_PARM_DESC(baudrate, "The I2C baudrate"); -@@ -317,25 +316,28 @@ static int bcm2708_i2c_probe(struct plat - struct i2c_adapter *adap; - unsigned long bus_hz; - u32 cdiv, clk_tout; -- -- if (!baudrate) { -- baudrate = baudrate_default; -- 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"); -+ u32 baud; -+ -+ baud = CONFIG_I2C_BCM2708_BAUDRATE; -+ -+ 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)) -+ baud = bus_clk_rate; -+ else -+ dev_warn(&pdev->dev, -+ "Could not read clock-frequency property\n"); - } - -+ if (baudrate) -+ baud = baudrate; -+ - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!regs) { - dev_err(&pdev->dev, "could not get IO memory\n"); -@@ -419,21 +421,21 @@ static int bcm2708_i2c_probe(struct plat - } - - bus_hz = clk_get_rate(bi->clk); -- cdiv = bus_hz / baudrate; -+ cdiv = bus_hz / baud; - if (cdiv > 0xffff) { - cdiv = 0xffff; -- baudrate = bus_hz / cdiv; -+ baud = bus_hz / cdiv; - } -- -- clk_tout = 35/1000*baudrate; //35ms timeout as per SMBus specs. -- if (clk_tout > 0xffff) -+ -+ clk_tout = 35/1000*baud; //35ms timeout as per SMBus specs. -+ if (clk_tout > 0xffff) - clk_tout = 0xffff; - - bi->cdiv = cdiv; - bi->clk_tout = clk_tout; - - dev_info(&pdev->dev, "BSC%d Controller at 0x%08lx (irq %d) (baudrate %d)\n", -- pdev->id, (unsigned long)regs->start, irq, baudrate); -+ pdev->id, (unsigned long)regs->start, irq, baud); - - return 0; - diff --git a/target/linux/brcm2708/patches-4.4/0143-Allow-up-to-24dB-digital-gain-to-be-applied-when-usi.patch b/target/linux/brcm2708/patches-4.4/0143-Allow-up-to-24dB-digital-gain-to-be-applied-when-usi.patch deleted file mode 100644 index 646bb27845..0000000000 --- a/target/linux/brcm2708/patches-4.4/0143-Allow-up-to-24dB-digital-gain-to-be-applied-when-usi.patch +++ /dev/null @@ -1,99 +0,0 @@ -From f2d36d57d7e17b9f44ff914824df5b150eb9e3bd Mon Sep 17 00:00:00 2001 -From: Digital Dreamtime -Date: Thu, 4 Feb 2016 14:14:44 +0000 -Subject: [PATCH 143/170] Allow up to 24dB digital gain to be applied when - using IQAudIO DAC+ - -24db_digital_gain DT param can be used to specify that PCM512x -codec "Digital" volume control should not be limited to 0dB gain, -and if specified will allow the full 24dB gain. ---- - arch/arm/boot/dts/overlays/README | 17 +++++++++++++++-- - .../boot/dts/overlays/iqaudio-dacplus-overlay.dts | 6 +++++- - sound/soc/bcm/iqaudio-dac.c | 20 ++++++++++++++------ - 3 files changed, 34 insertions(+), 9 deletions(-) - ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -362,8 +362,21 @@ Params: - - Name: iqaudio-dacplus - Info: Configures the IQaudio DAC+ audio card --Load: dtoverlay=iqaudio-dacplus --Params: -+Load: dtoverlay=iqaudio-dacplus,= -+Params: 24db_digital_gain Allow gain to be applied via the PCM512x codec -+ Digital volume control. Enable with -+ "dtoverlay=iqaudio-dacplus,24db_digital_gain" -+ (The default behaviour is that the Digital -+ volume control is limited to a maximum of -+ 0dB. ie. it can attenuate but not provide -+ gain. For most users, this will be desired -+ as it will prevent clipping. By appending -+ the 24db_digital_gain parameter, the Digital -+ volume control will allow up to 24dB of -+ gain. If this parameter is enabled, it is the -+ responsibility of the user to ensure that -+ the Digital volume control is set to a value -+ that does not result in clipping/distortion!) - - - Name: lirc-rpi ---- a/arch/arm/boot/dts/overlays/iqaudio-dacplus-overlay.dts -+++ b/arch/arm/boot/dts/overlays/iqaudio-dacplus-overlay.dts -@@ -7,7 +7,7 @@ - - fragment@0 { - target = <&sound>; -- __overlay__ { -+ frag0: __overlay__ { - compatible = "iqaudio,iqaudio-dac"; - i2s-controller = <&i2s>; - status = "okay"; -@@ -36,4 +36,8 @@ - }; - }; - }; -+ -+ __overrides__ { -+ 24db_digital_gain = <&frag0>,"iqaudio,24db_digital_gain?"; -+ }; - }; ---- a/sound/soc/bcm/iqaudio-dac.c -+++ b/sound/soc/bcm/iqaudio-dac.c -@@ -23,14 +23,19 @@ - #include - #include - -+static bool digital_gain_0db_limit = true; -+ - static int snd_rpi_iqaudio_dac_init(struct snd_soc_pcm_runtime *rtd) - { -- int ret; -- struct snd_soc_card *card = rtd->card; -- -- ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207); -- if (ret < 0) -- dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); -+ if (digital_gain_0db_limit) -+ { -+ int ret; -+ struct snd_soc_card *card = rtd->card; -+ -+ ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207); -+ if (ret < 0) -+ dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); -+ } - - return 0; - } -@@ -94,6 +99,9 @@ static int snd_rpi_iqaudio_dac_probe(str - dai->platform_name = NULL; - dai->platform_of_node = i2s_node; - } -+ -+ digital_gain_0db_limit = !of_property_read_bool(pdev->dev.of_node, -+ "iqaudio,24db_digital_gain"); - } - - ret = snd_soc_register_card(&snd_rpi_iqaudio_dac); diff --git a/target/linux/brcm2708/patches-4.4/0143-pinctrl-bcm2835-Fix-cut-and-paste-error-in-pull-pars.patch b/target/linux/brcm2708/patches-4.4/0143-pinctrl-bcm2835-Fix-cut-and-paste-error-in-pull-pars.patch new file mode 100644 index 0000000000..5747eff99d --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0143-pinctrl-bcm2835-Fix-cut-and-paste-error-in-pull-pars.patch @@ -0,0 +1,25 @@ +From 5e3168449aa5a105df7043bfeb425b4dd929dea6 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 9 Feb 2016 09:52:13 +0000 +Subject: [PATCH 143/232] pinctrl-bcm2835: Fix cut-and-paste error in "pull" + parsing + +The DT bindings for pinctrl-bcm2835 allow both the function and pull +to contain either one entry or one per pin. However, an error in the +DT parsing can cause failures if the number of pulls differs from the +number of functions. +--- + drivers/pinctrl/bcm/pinctrl-bcm2835.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c ++++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c +@@ -802,7 +802,7 @@ static int bcm2835_pctl_dt_node_to_map(s + } + if (num_pulls) { + err = of_property_read_u32_index(np, "brcm,pull", +- (num_funcs > 1) ? i : 0, &pull); ++ (num_pulls > 1) ? i : 0, &pull); + if (err) + goto out; + err = bcm2835_pctl_dt_node_to_map_pull(pc, np, pin, diff --git a/target/linux/brcm2708/patches-4.4/0144-Limit-PCM512x-Digital-gain-to-0dB-by-default-with-Hi.patch b/target/linux/brcm2708/patches-4.4/0144-Limit-PCM512x-Digital-gain-to-0dB-by-default-with-Hi.patch deleted file mode 100644 index 1633cfe6ed..0000000000 --- a/target/linux/brcm2708/patches-4.4/0144-Limit-PCM512x-Digital-gain-to-0dB-by-default-with-Hi.patch +++ /dev/null @@ -1,98 +0,0 @@ -From 8a189f18de820c6facd00ba2db97ba7c4a75156f Mon Sep 17 00:00:00 2001 -From: Digital Dreamtime -Date: Thu, 4 Feb 2016 20:04:00 +0000 -Subject: [PATCH 144/170] Limit PCM512x "Digital" gain to 0dB by default with - HiFiBerry DAC+ - -24db_digital_gain DT param can be used to specify that PCM512x -codec "Digital" volume control should not be limited to 0dB gain, -and if specified will allow the full 24dB gain. ---- - arch/arm/boot/dts/overlays/README | 17 +++++++++++++++-- - .../arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts | 6 +++++- - sound/soc/bcm/hifiberry_dacplus.c | 14 ++++++++++++++ - 3 files changed, 34 insertions(+), 3 deletions(-) - ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -273,8 +273,21 @@ Params: - - Name: hifiberry-dacplus - Info: Configures the HifiBerry DAC+ audio card --Load: dtoverlay=hifiberry-dacplus --Params: -+Load: dtoverlay=hifiberry-dacplus,= -+Params: 24db_digital_gain Allow gain to be applied via the PCM512x codec -+ Digital volume control. Enable with -+ "dtoverlay=hifiberry-dacplus,24db_digital_gain" -+ (The default behaviour is that the Digital -+ volume control is limited to a maximum of -+ 0dB. ie. it can attenuate but not provide -+ gain. For most users, this will be desired -+ as it will prevent clipping. By appending -+ the 24dB_digital_gain parameter, the Digital -+ volume control will allow up to 24dB of -+ gain. If this parameter is enabled, it is the -+ responsibility of the user to ensure that -+ the Digital volume control is set to a value -+ that does not result in clipping/distortion!) - - - Name: hifiberry-digi ---- a/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts -+++ b/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts -@@ -17,7 +17,7 @@ - - fragment@1 { - target = <&sound>; -- __overlay__ { -+ frag1: __overlay__ { - compatible = "hifiberry,hifiberry-dacplus"; - i2s-controller = <&i2s>; - status = "okay"; -@@ -47,4 +47,8 @@ - }; - }; - }; -+ -+ __overrides__ { -+ 24db_digital_gain = <&frag1>,"hifiberry,24db_digital_gain?"; -+ }; - }; ---- a/sound/soc/bcm/hifiberry_dacplus.c -+++ b/sound/soc/bcm/hifiberry_dacplus.c -@@ -48,6 +48,7 @@ struct pcm512x_priv { - #define CLK_48EN_RATE 24576000UL - - static bool snd_rpi_hifiberry_is_dacpro; -+static bool digital_gain_0db_limit = true; - - static void snd_rpi_hifiberry_dacplus_select_clk(struct snd_soc_codec *codec, - int clk_id) -@@ -167,6 +168,16 @@ static int snd_rpi_hifiberry_dacplus_ini - snd_soc_update_bits(codec, PCM512x_GPIO_OUTPUT_4, 0x0f, 0x02); - snd_soc_update_bits(codec, PCM512x_GPIO_CONTROL_1, 0x08, 0x08); - -+ if (digital_gain_0db_limit) -+ { -+ int ret; -+ struct snd_soc_card *card = rtd->card; -+ -+ ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207); -+ if (ret < 0) -+ dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); -+ } -+ - return 0; - } - -@@ -299,6 +310,9 @@ static int snd_rpi_hifiberry_dacplus_pro - dai->platform_name = NULL; - dai->platform_of_node = i2s_node; - } -+ -+ digital_gain_0db_limit = !of_property_read_bool( -+ pdev->dev.of_node, "hifiberry,24db_digital_gain"); - } - - ret = snd_soc_register_card(&snd_rpi_hifiberry_dacplus); diff --git a/target/linux/brcm2708/patches-4.4/0144-bcm2835-sdhost-Major-revision.patch b/target/linux/brcm2708/patches-4.4/0144-bcm2835-sdhost-Major-revision.patch new file mode 100644 index 0000000000..b67a279551 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0144-bcm2835-sdhost-Major-revision.patch @@ -0,0 +1,2070 @@ +From 60425f3b85682fa3378cb6aeecce15a50384b7e2 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 11 Feb 2016 16:51:01 +0000 +Subject: [PATCH 144/232] bcm2835-sdhost: Major revision + +This is a significant revision of the bcm2835-sdhost driver. It +improves on the original in a number of ways: + +1) Through the use of CMD23 for reads it appears to avoid problems + reading some sectors on certain high speed cards. +2) Better atomicity to prevent crashes. +3) Higher performance. +4) Activity logging included, for easier diagnosis in the event + of a problem. + +Signed-off-by: Phil Elwell +--- + drivers/mmc/host/bcm2835-sdhost.c | 1284 ++++++++++++++++++++----------------- + 1 file changed, 686 insertions(+), 598 deletions(-) + +--- a/drivers/mmc/host/bcm2835-sdhost.c ++++ b/drivers/mmc/host/bcm2835-sdhost.c +@@ -2,7 +2,7 @@ + * BCM2835 SD host driver. + * + * Author: Phil Elwell +- * Copyright 2015 ++ * Copyright (C) 2015-2016 Raspberry Pi (Trading) Ltd. + * + * Based on + * mmc-bcm2835.c by Gellert Weisz +@@ -24,12 +24,13 @@ + * along with this program. If not, see . + */ + +-#define SAFE_READ_THRESHOLD 4 +-#define SAFE_WRITE_THRESHOLD 4 +-#define ALLOW_DMA 1 +-#define ALLOW_CMD23 0 +-#define ALLOW_FAST 1 +-#define USE_BLOCK_IRQ 1 ++#define FIFO_READ_THRESHOLD 4 ++#define FIFO_WRITE_THRESHOLD 4 ++#define ALLOW_CMD23_READ 1 ++#define ALLOW_CMD23_WRITE 0 ++#define ENABLE_LOG 1 ++#define SDDATA_FIFO_PIO_BURST 8 ++#define CMD_DALLY_US 1 + + #include + #include +@@ -48,6 +49,7 @@ + #include + #include + #include ++#include + + #define DRIVER_NAME "sdhost-bcm2835" + +@@ -110,6 +112,28 @@ + #define SDEDM_READ_THRESHOLD_SHIFT 14 + #define SDEDM_THRESHOLD_MASK 0x1f + ++#define SDEDM_FSM_MASK 0xf ++#define SDEDM_FSM_IDENTMODE 0x0 ++#define SDEDM_FSM_DATAMODE 0x1 ++#define SDEDM_FSM_READDATA 0x2 ++#define SDEDM_FSM_WRITEDATA 0x3 ++#define SDEDM_FSM_READWAIT 0x4 ++#define SDEDM_FSM_READCRC 0x5 ++#define SDEDM_FSM_WRITECRC 0x6 ++#define SDEDM_FSM_WRITEWAIT1 0x7 ++#define SDEDM_FSM_POWERDOWN 0x8 ++#define SDEDM_FSM_POWERUP 0x9 ++#define SDEDM_FSM_WRITESTART1 0xa ++#define SDEDM_FSM_WRITESTART2 0xb ++#define SDEDM_FSM_GENPULSES 0xc ++#define SDEDM_FSM_WRITEWAIT2 0xd ++#define SDEDM_FSM_STARTPOWDOWN 0xf ++ ++#define SDDATA_FIFO_WORDS 16 ++ ++#define USE_CMD23_FLAGS ((ALLOW_CMD23_READ * MMC_DATA_READ) | \ ++ (ALLOW_CMD23_WRITE * MMC_DATA_WRITE)) ++ + #define MHZ 1000000 + + +@@ -131,15 +155,17 @@ struct bcm2835_host { + + struct tasklet_struct finish_tasklet; /* Tasklet structures */ + +- struct timer_list timer; /* Timer for timeouts */ ++ struct work_struct cmd_wait_wq; /* Workqueue function */ + +- struct timer_list pio_timer; /* PIO error detection timer */ ++ 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 cmd_quick_poll_retries; ++ u32 ns_per_fifo_word; + + /* cached registers */ + u32 hcfg; +@@ -154,16 +180,21 @@ struct bcm2835_host { + + unsigned int use_busy:1; /* Wait for busy interrupt */ + +- unsigned int debug:1; /* Enable debug output */ ++ unsigned int use_sbc:1; /* Send CMD23 */ + +- u32 thread_isr; ++ unsigned int debug:1; /* Enable debug output */ + + /*DMA part*/ + struct dma_chan *dma_chan_rx; /* DMA channel for reads */ + struct dma_chan *dma_chan_tx; /* DMA channel for writes */ ++ struct dma_chan *dma_chan; /* Channel in used */ ++ struct dma_async_tx_descriptor *dma_desc; ++ u32 dma_dir; ++ u32 drain_words; ++ struct page *drain_page; ++ u32 drain_offset; + + bool allow_dma; +- bool have_dma; + bool use_dma; + /*end of DMA part*/ + +@@ -173,13 +204,98 @@ struct bcm2835_host { + u32 overclock_50; /* frequency to use when 50MHz is requested (in MHz) */ + u32 overclock; /* Current frequency if overclocked, else zero */ + u32 pio_limit; /* Maximum block count for PIO (0 = always DMA) */ ++}; + +- u32 debug_flags; ++#if ENABLE_LOG + +- u32 sectors; /* Cached card size in sectors */ +- u32 single_read_sectors[8]; ++struct log_entry_struct { ++ char event[4]; ++ u32 timestamp; ++ u32 param1; ++ u32 param2; + }; + ++typedef struct log_entry_struct LOG_ENTRY_T; ++ ++LOG_ENTRY_T *sdhost_log_buf; ++dma_addr_t sdhost_log_addr; ++static u32 sdhost_log_idx; ++static spinlock_t log_lock; ++static void __iomem *timer_base; ++ ++#define LOG_ENTRIES (256*1) ++#define LOG_SIZE (sizeof(LOG_ENTRY_T)*LOG_ENTRIES) ++ ++static void log_init(u32 bus_to_phys) ++{ ++ spin_lock_init(&log_lock); ++ sdhost_log_buf = dma_zalloc_coherent(NULL, LOG_SIZE, &sdhost_log_addr, ++ GFP_KERNEL); ++ if (sdhost_log_buf) { ++ pr_err("sdhost: log_buf @ %p (%x)\n", ++ sdhost_log_buf, sdhost_log_addr); ++ timer_base = ioremap_nocache(bus_to_phys + 0x7e003000, SZ_4K); ++ if (!timer_base) ++ pr_err("sdhost: failed to remap timer\n"); ++ } ++ else ++ pr_err("sdhost: failed to allocate log buf\n"); ++} ++ ++static void log_event_impl(const char *event, u32 param1, u32 param2) ++{ ++ if (sdhost_log_buf) { ++ LOG_ENTRY_T *entry; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&log_lock, flags); ++ ++ entry = sdhost_log_buf + sdhost_log_idx; ++ memcpy(entry->event, event, 4); ++ entry->timestamp = (readl(timer_base + 4) & 0x3fffffff) + ++ (smp_processor_id()<<30); ++ entry->param1 = param1; ++ entry->param2 = param2; ++ sdhost_log_idx = (sdhost_log_idx + 1) % LOG_ENTRIES; ++ ++ spin_unlock_irqrestore(&log_lock, flags); ++ } ++} ++ ++static void log_dump(void) ++{ ++ if (sdhost_log_buf) { ++ LOG_ENTRY_T *entry; ++ unsigned long flags; ++ int idx; ++ ++ spin_lock_irqsave(&log_lock, flags); ++ ++ idx = sdhost_log_idx; ++ do { ++ entry = sdhost_log_buf + idx; ++ if (entry->event[0] != '\0') ++ pr_err("[%08x] %.4s %x %x\n", ++ entry->timestamp, ++ entry->event, ++ entry->param1, ++ entry->param2); ++ idx = (idx + 1) % LOG_ENTRIES; ++ } while (idx != sdhost_log_idx); ++ ++ spin_unlock_irqrestore(&log_lock, flags); ++ } ++} ++ ++#define log_event(event, param1, param2) log_event_impl(event, param1, param2) ++ ++#else ++ ++#define log_init(x) (void)0 ++#define log_event(event, param1, param2) (void)0 ++#define log_dump() (void)0 ++ ++#endif + + static inline void bcm2835_sdhost_write(struct bcm2835_host *host, u32 val, int reg) + { +@@ -201,7 +317,7 @@ static void bcm2835_sdhost_dumpcmd(struc + const char *label) + { + if (cmd) +- pr_info("%s:%c%s op %d arg 0x%x flags 0x%x - resp %08x %08x %08x %08x, err %d\n", ++ pr_err("%s:%c%s op %d arg 0x%x flags 0x%x - resp %08x %08x %08x %08x, err %d\n", + mmc_hostname(host->mmc), + (cmd == host->cmd) ? '>' : ' ', + label, cmd->opcode, cmd->arg, cmd->flags, +@@ -211,73 +327,74 @@ static void bcm2835_sdhost_dumpcmd(struc + + static void bcm2835_sdhost_dumpregs(struct bcm2835_host *host) + { +- bcm2835_sdhost_dumpcmd(host, host->mrq->sbc, "sbc"); +- bcm2835_sdhost_dumpcmd(host, host->mrq->cmd, "cmd"); +- if (host->mrq->data) +- pr_err("%s: data blocks %x blksz %x - err %d\n", +- mmc_hostname(host->mmc), +- host->mrq->data->blocks, +- host->mrq->data->blksz, +- host->mrq->data->error); +- bcm2835_sdhost_dumpcmd(host, host->mrq->stop, "stop"); ++ if (host->mrq) ++ { ++ bcm2835_sdhost_dumpcmd(host, host->mrq->sbc, "sbc"); ++ bcm2835_sdhost_dumpcmd(host, host->mrq->cmd, "cmd"); ++ if (host->mrq->data) ++ pr_err("%s: data blocks %x blksz %x - err %d\n", ++ mmc_hostname(host->mmc), ++ host->mrq->data->blocks, ++ host->mrq->data->blksz, ++ host->mrq->data->error); ++ bcm2835_sdhost_dumpcmd(host, host->mrq->stop, "stop"); ++ } + +- pr_info("%s: =========== REGISTER DUMP ===========\n", ++ pr_err("%s: =========== REGISTER DUMP ===========\n", + mmc_hostname(host->mmc)); + +- pr_info("%s: SDCMD 0x%08x\n", ++ pr_err("%s: SDCMD 0x%08x\n", + mmc_hostname(host->mmc), + bcm2835_sdhost_read(host, SDCMD)); +- pr_info("%s: SDARG 0x%08x\n", ++ pr_err("%s: SDARG 0x%08x\n", + mmc_hostname(host->mmc), + bcm2835_sdhost_read(host, SDARG)); +- pr_info("%s: SDTOUT 0x%08x\n", ++ pr_err("%s: SDTOUT 0x%08x\n", + mmc_hostname(host->mmc), + bcm2835_sdhost_read(host, SDTOUT)); +- pr_info("%s: SDCDIV 0x%08x\n", ++ pr_err("%s: SDCDIV 0x%08x\n", + mmc_hostname(host->mmc), + bcm2835_sdhost_read(host, SDCDIV)); +- pr_info("%s: SDRSP0 0x%08x\n", ++ pr_err("%s: SDRSP0 0x%08x\n", + mmc_hostname(host->mmc), + bcm2835_sdhost_read(host, SDRSP0)); +- pr_info("%s: SDRSP1 0x%08x\n", ++ pr_err("%s: SDRSP1 0x%08x\n", + mmc_hostname(host->mmc), + bcm2835_sdhost_read(host, SDRSP1)); +- pr_info("%s: SDRSP2 0x%08x\n", ++ pr_err("%s: SDRSP2 0x%08x\n", + mmc_hostname(host->mmc), + bcm2835_sdhost_read(host, SDRSP2)); +- pr_info("%s: SDRSP3 0x%08x\n", ++ pr_err("%s: SDRSP3 0x%08x\n", + mmc_hostname(host->mmc), + bcm2835_sdhost_read(host, SDRSP3)); +- pr_info("%s: SDHSTS 0x%08x\n", ++ pr_err("%s: SDHSTS 0x%08x\n", + mmc_hostname(host->mmc), + bcm2835_sdhost_read(host, SDHSTS)); +- pr_info("%s: SDVDD 0x%08x\n", ++ pr_err("%s: SDVDD 0x%08x\n", + mmc_hostname(host->mmc), + bcm2835_sdhost_read(host, SDVDD)); +- pr_info("%s: SDEDM 0x%08x\n", ++ pr_err("%s: SDEDM 0x%08x\n", + mmc_hostname(host->mmc), + bcm2835_sdhost_read(host, SDEDM)); +- pr_info("%s: SDHCFG 0x%08x\n", ++ pr_err("%s: SDHCFG 0x%08x\n", + mmc_hostname(host->mmc), + bcm2835_sdhost_read(host, SDHCFG)); +- pr_info("%s: SDHBCT 0x%08x\n", ++ pr_err("%s: SDHBCT 0x%08x\n", + mmc_hostname(host->mmc), + bcm2835_sdhost_read(host, SDHBCT)); +- pr_info("%s: SDHBLC 0x%08x\n", ++ pr_err("%s: SDHBLC 0x%08x\n", + mmc_hostname(host->mmc), + bcm2835_sdhost_read(host, SDHBLC)); + +- pr_info("%s: ===========================================\n", ++ pr_err("%s: ===========================================\n", + mmc_hostname(host->mmc)); + } + +- + static void bcm2835_sdhost_set_power(struct bcm2835_host *host, bool on) + { + bcm2835_sdhost_write(host, on ? 1 : 0, SDVDD); + } + +- + static void bcm2835_sdhost_reset_internal(struct bcm2835_host *host) + { + u32 temp; +@@ -300,26 +417,24 @@ static void bcm2835_sdhost_reset_interna + temp = bcm2835_sdhost_read(host, SDEDM); + temp &= ~((SDEDM_THRESHOLD_MASK<clock = 0; +- host->sectors = 0; +- host->single_read_sectors[0] = ~0; + bcm2835_sdhost_write(host, host->hcfg, SDHCFG); + bcm2835_sdhost_write(host, host->cdiv, SDCDIV); + mmiowb(); + } + +- + static void bcm2835_sdhost_reset(struct mmc_host *mmc) + { + struct bcm2835_host *host = mmc_priv(mmc); + unsigned long flags; + spin_lock_irqsave(&host->lock, flags); ++ log_event("RST<", 0, 0); + + bcm2835_sdhost_reset_internal(host); + +@@ -344,82 +459,48 @@ static void bcm2835_sdhost_init(struct b + } + } + +-static bool bcm2835_sdhost_is_write_complete(struct bcm2835_host *host) ++static void bcm2835_sdhost_wait_transfer_complete(struct bcm2835_host *host) + { +- bool write_complete = ((bcm2835_sdhost_read(host, SDEDM) & 0xf) == 1); ++ int timediff; ++ u32 alternate_idle; ++ u32 edm; + +- if (!write_complete) { +- /* Request an IRQ for the last block */ +- host->hcfg |= SDHCFG_BLOCK_IRPT_EN; +- bcm2835_sdhost_write(host, host->hcfg, SDHCFG); +- if ((bcm2835_sdhost_read(host, SDEDM) & 0xf) == 1) { +- /* The write has now completed. Disable the interrupt +- and clear the status flag */ +- host->hcfg &= ~SDHCFG_BLOCK_IRPT_EN; +- bcm2835_sdhost_write(host, host->hcfg, SDHCFG); +- bcm2835_sdhost_write(host, SDHSTS_BLOCK_IRPT, SDHSTS); +- write_complete = true; +- } +- } ++ alternate_idle = (host->mrq->data->flags & MMC_DATA_READ) ? ++ SDEDM_FSM_READWAIT : SDEDM_FSM_WRITESTART1; + +- return write_complete; +-} ++ edm = bcm2835_sdhost_read(host, SDEDM); + +-static void bcm2835_sdhost_wait_write_complete(struct bcm2835_host *host) +-{ +- int timediff; +-#ifdef DEBUG +- static struct timeval start_time; +- static int max_stall_time = 0; +- static int total_stall_time = 0; +- struct timeval before, after; +- +- do_gettimeofday(&before); +- if (max_stall_time == 0) +- start_time = before; +-#endif ++ log_event("WTC<", edm, 0); + + timediff = 0; + + while (1) { +- u32 edm = bcm2835_sdhost_read(host, SDEDM); +- if ((edm & 0xf) == 1) ++ u32 fsm = edm & SDEDM_FSM_MASK; ++ if ((fsm == SDEDM_FSM_IDENTMODE) || ++ (fsm == SDEDM_FSM_DATAMODE)) + break; +- timediff++; +- if (timediff > 5000000) { +-#ifdef DEBUG +- do_gettimeofday(&after); +- timediff = (after.tv_sec - before.tv_sec)*1000000 + +- (after.tv_usec - before.tv_usec); ++ if (fsm == alternate_idle) { ++ bcm2835_sdhost_write(host, ++ edm | SDEDM_FORCE_DATA_MODE, ++ SDEDM); ++ break; ++ } + +- pr_err(" wait_write_complete - still waiting after %dus\n", +- timediff); +-#else +- pr_err(" wait_write_complete - still waiting after %d retries\n", ++ timediff++; ++ if (timediff == 100000) { ++ pr_err("%s: wait_transfer_complete - still waiting after %d retries\n", ++ mmc_hostname(host->mmc), + timediff); +-#endif ++ log_dump(); + bcm2835_sdhost_dumpregs(host); +- host->data->error = -ETIMEDOUT; ++ host->mrq->data->error = -ETIMEDOUT; ++ log_event("WTC!", edm, 0); + return; + } ++ cpu_relax(); ++ edm = bcm2835_sdhost_read(host, SDEDM); + } +- +-#ifdef DEBUG +- do_gettimeofday(&after); +- timediff = (after.tv_sec - before.tv_sec)*1000000 + (after.tv_usec - before.tv_usec); +- +- total_stall_time += timediff; +- if (timediff > max_stall_time) +- max_stall_time = timediff; +- +- if ((after.tv_sec - start_time.tv_sec) > 10) { +- pr_debug(" wait_write_complete - max wait %dus, total %dus\n", +- max_stall_time, total_stall_time); +- start_time = after; +- max_stall_time = 0; +- total_stall_time = 0; +- } +-#endif ++ log_event("WTC>", edm, 0); + } + + static void bcm2835_sdhost_finish_data(struct bcm2835_host *host); +@@ -427,65 +508,44 @@ static void bcm2835_sdhost_finish_data(s + static void bcm2835_sdhost_dma_complete(void *param) + { + struct bcm2835_host *host = param; +- struct dma_chan *dma_chan; ++ struct mmc_data *data = host->data; + unsigned long flags; +- u32 dir_data; + + spin_lock_irqsave(&host->lock, flags); ++ log_event("DMA<", (u32)host->data, bcm2835_sdhost_read(host, SDHSTS)); ++ log_event("DMA ", bcm2835_sdhost_read(host, SDCMD), ++ bcm2835_sdhost_read(host, SDEDM)); + +- if (host->data) { +- bool write_complete; +- if (USE_BLOCK_IRQ) +- write_complete = bcm2835_sdhost_is_write_complete(host); +- else { +- bcm2835_sdhost_wait_write_complete(host); +- write_complete = true; +- } +- pr_debug("dma_complete() - write_complete=%d\n", +- write_complete); +- +- if (write_complete || (host->data->flags & MMC_DATA_READ)) +- { +- if (write_complete) { +- dma_chan = host->dma_chan_tx; +- dir_data = DMA_TO_DEVICE; +- } else { +- 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); ++ if (host->dma_chan) { ++ dma_unmap_sg(host->dma_chan->device->dev, ++ data->sg, data->sg_len, ++ host->dma_dir); + +- bcm2835_sdhost_finish_data(host); +- } ++ host->dma_chan = NULL; + } + +- spin_unlock_irqrestore(&host->lock, flags); +-} ++ if (host->drain_words) { ++ void *page; ++ u32 *buf; + +-static bool data_transfer_wait(struct bcm2835_host *host) +-{ +- unsigned long timeout = 1000000; +- while (timeout) +- { +- u32 sdhsts = bcm2835_sdhost_read(host, SDHSTS); +- if (sdhsts & SDHSTS_DATA_FLAG) { +- bcm2835_sdhost_write(host, SDHSTS_DATA_FLAG, SDHSTS); +- break; ++ page = kmap_atomic(host->drain_page); ++ buf = page + host->drain_offset; ++ ++ while (host->drain_words) { ++ u32 edm = bcm2835_sdhost_read(host, SDEDM); ++ if ((edm >> 4) & 0x1f) ++ *(buf++) = bcm2835_sdhost_read(host, ++ SDDATA); ++ host->drain_words--; + } +- timeout--; +- } +- if (timeout == 0) { +- pr_err("%s: Data %s timeout\n", +- mmc_hostname(host->mmc), +- (host->data->flags & MMC_DATA_READ) ? "read" : "write"); +- bcm2835_sdhost_dumpregs(host); +- host->data->error = -ETIMEDOUT; +- return false; ++ ++ kunmap_atomic(page); + } +- return true; ++ ++ bcm2835_sdhost_finish_data(host); ++ ++ log_event("DMA>", (u32)host->data, 0); ++ spin_unlock_irqrestore(&host->lock, flags); + } + + static void bcm2835_sdhost_read_block_pio(struct bcm2835_host *host) +@@ -493,32 +553,83 @@ static void bcm2835_sdhost_read_block_pi + unsigned long flags; + size_t blksize, len; + u32 *buf; ++ unsigned long wait_max; + + blksize = host->data->blksz; + ++ wait_max = jiffies + msecs_to_jiffies(host->pio_timeout); ++ + local_irq_save(flags); + + while (blksize) { +- if (!sg_miter_next(&host->sg_miter)) +- BUG(); ++ int copy_words; ++ u32 hsts = 0; ++ ++ if (!sg_miter_next(&host->sg_miter)) { ++ host->data->error = -EINVAL; ++ break; ++ } + + len = min(host->sg_miter.length, blksize); +- BUG_ON(len % 4); ++ if (len % 4) { ++ host->data->error = -EINVAL; ++ break; ++ } + + blksize -= len; + host->sg_miter.consumed = len; + + buf = (u32 *)host->sg_miter.addr; + +- while (len) { +- if (!data_transfer_wait(host)) +- break; ++ copy_words = len/4; ++ ++ while (copy_words) { ++ int burst_words, words; ++ u32 edm; ++ ++ burst_words = SDDATA_FIFO_PIO_BURST; ++ if (burst_words > copy_words) ++ burst_words = copy_words; ++ edm = bcm2835_sdhost_read(host, SDEDM); ++ words = ((edm >> 4) & 0x1f); ++ ++ if (words < burst_words) { ++ int fsm_state = (edm & SDEDM_FSM_MASK); ++ if ((fsm_state != SDEDM_FSM_READDATA) && ++ (fsm_state != SDEDM_FSM_READWAIT) && ++ (fsm_state != SDEDM_FSM_READCRC)) { ++ hsts = bcm2835_sdhost_read(host, ++ SDHSTS); ++ pr_err("%s: fsm %x, hsts %x\n", ++ mmc_hostname(host->mmc), ++ fsm_state, hsts); ++ if (hsts & SDHSTS_ERROR_MASK) ++ break; ++ } ++ ++ if (time_after(jiffies, wait_max)) { ++ pr_err("%s: PIO read timeout - EDM %x\n", ++ mmc_hostname(host->mmc), ++ edm); ++ hsts = SDHSTS_REW_TIME_OUT; ++ break; ++ } ++ ndelay((burst_words - words) * ++ host->ns_per_fifo_word); ++ continue; ++ } else if (words > copy_words) { ++ words = copy_words; ++ } ++ ++ copy_words -= words; + +- *(buf++) = bcm2835_sdhost_read(host, SDDATA); +- len -= 4; ++ while (words) { ++ *(buf++) = bcm2835_sdhost_read(host, SDDATA); ++ words--; ++ } + } + +- if (host->data->error) ++ if (hsts & SDHSTS_ERROR_MASK) + break; + } + +@@ -532,32 +643,83 @@ static void bcm2835_sdhost_write_block_p + unsigned long flags; + size_t blksize, len; + u32 *buf; ++ unsigned long wait_max; + + blksize = host->data->blksz; + ++ wait_max = jiffies + msecs_to_jiffies(host->pio_timeout); ++ + local_irq_save(flags); + + while (blksize) { +- if (!sg_miter_next(&host->sg_miter)) +- BUG(); ++ int copy_words; ++ u32 hsts = 0; ++ ++ if (!sg_miter_next(&host->sg_miter)) { ++ host->data->error = -EINVAL; ++ break; ++ } + + len = min(host->sg_miter.length, blksize); +- BUG_ON(len % 4); ++ if (len % 4) { ++ host->data->error = -EINVAL; ++ break; ++ } + + blksize -= len; + host->sg_miter.consumed = len; + +- buf = host->sg_miter.addr; ++ buf = (u32 *)host->sg_miter.addr; + +- while (len) { +- if (!data_transfer_wait(host)) +- break; ++ copy_words = len/4; ++ ++ while (copy_words) { ++ int burst_words, words; ++ u32 edm; ++ ++ burst_words = SDDATA_FIFO_PIO_BURST; ++ if (burst_words > copy_words) ++ burst_words = copy_words; ++ edm = bcm2835_sdhost_read(host, SDEDM); ++ words = SDDATA_FIFO_WORDS - ((edm >> 4) & 0x1f); ++ ++ if (words < burst_words) { ++ int fsm_state = (edm & SDEDM_FSM_MASK); ++ if ((fsm_state != SDEDM_FSM_WRITEDATA) && ++ (fsm_state != SDEDM_FSM_WRITESTART1) && ++ (fsm_state != SDEDM_FSM_WRITESTART2)) { ++ hsts = bcm2835_sdhost_read(host, ++ SDHSTS); ++ pr_err("%s: fsm %x, hsts %x\n", ++ mmc_hostname(host->mmc), ++ fsm_state, hsts); ++ if (hsts & SDHSTS_ERROR_MASK) ++ break; ++ } + +- bcm2835_sdhost_write(host, *(buf++), SDDATA); +- len -= 4; ++ if (time_after(jiffies, wait_max)) { ++ pr_err("%s: PIO write timeout - EDM %x\n", ++ mmc_hostname(host->mmc), ++ edm); ++ hsts = SDHSTS_REW_TIME_OUT; ++ break; ++ } ++ ndelay((burst_words - words) * ++ host->ns_per_fifo_word); ++ continue; ++ } else if (words > copy_words) { ++ words = copy_words; ++ } ++ ++ copy_words -= words; ++ ++ while (words) { ++ bcm2835_sdhost_write(host, *(buf++), SDDATA); ++ words--; ++ } + } + +- if (host->data->error) ++ if (hsts & SDHSTS_ERROR_MASK) + break; + } + +@@ -566,12 +728,12 @@ static void bcm2835_sdhost_write_block_p + local_irq_restore(flags); + } + +- + static void bcm2835_sdhost_transfer_pio(struct bcm2835_host *host) + { + u32 sdhsts; + bool is_read; + BUG_ON(!host->data); ++ log_event("XFP<", (u32)host->data, host->blocks); + + is_read = (host->data->flags & MMC_DATA_READ) != 0; + if (is_read) +@@ -595,28 +757,21 @@ static void bcm2835_sdhost_transfer_pio( + is_read ? "read" : "write", + sdhsts); + host->data->error = -ETIMEDOUT; +- } else if (!is_read && !host->data->error) { +- /* Start a timer in case a transfer error occurs because +- there is no error interrupt */ +- mod_timer(&host->pio_timer, jiffies + host->pio_timeout); + } ++ log_event("XFP>", (u32)host->data, host->blocks); + } + +- +-static void bcm2835_sdhost_transfer_dma(struct bcm2835_host *host) ++static void bcm2835_sdhost_prepare_dma(struct bcm2835_host *host, ++ struct mmc_data *data) + { +- u32 len, dir_data, dir_slave; ++ int len, dir_data, dir_slave; + struct dma_async_tx_descriptor *desc = NULL; + struct dma_chan *dma_chan; + +- pr_debug("bcm2835_sdhost_transfer_dma()\n"); +- +- WARN_ON(!host->data); ++ log_event("PRD<", (u32)data, 0); ++ pr_debug("bcm2835_sdhost_prepare_dma()\n"); + +- if (!host->data) +- return; +- +- if (host->data->flags & MMC_DATA_READ) { ++ if (data->flags & MMC_DATA_READ) { + dma_chan = host->dma_chan_rx; + dir_data = DMA_FROM_DEVICE; + dir_slave = DMA_DEV_TO_MEM; +@@ -625,35 +780,71 @@ static void bcm2835_sdhost_transfer_dma( + dir_data = DMA_TO_DEVICE; + dir_slave = DMA_MEM_TO_DEV; + } ++ log_event("PRD1", (u32)dma_chan, 0); + + BUG_ON(!dma_chan->device); + BUG_ON(!dma_chan->device->dev); +- BUG_ON(!host->data->sg); ++ BUG_ON(!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, ++ /* The block doesn't manage the FIFO DREQs properly for multi-block ++ transfers, so don't attempt to DMA the final few words. ++ Unfortunately this requires the final sg entry to be trimmed. ++ N.B. This code demands that the overspill is contained in ++ a single sg entry. ++ */ ++ ++ host->drain_words = 0; ++ if ((data->blocks > 1) && (dir_data == DMA_FROM_DEVICE)) { ++ struct scatterlist *sg; ++ u32 len; ++ int i; ++ ++ len = min((u32)(FIFO_READ_THRESHOLD - 1) * 4, ++ (u32)data->blocks * data->blksz); ++ ++ for_each_sg(data->sg, sg, data->sg_len, i) { ++ if (sg_is_last(sg)) { ++ BUG_ON(sg->length < len); ++ sg->length -= len; ++ host->drain_page = (struct page *)sg->page_link; ++ host->drain_offset = sg->offset + sg->length; ++ } ++ } ++ host->drain_words = len/4; ++ } ++ ++ len = dma_map_sg(dma_chan->device->dev, data->sg, data->sg_len, ++ dir_data); ++ ++ log_event("PRD2", len, 0); ++ if (len > 0) ++ desc = dmaengine_prep_slave_sg(dma_chan, 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"); +- } ++ log_event("PRD3", (u32)desc, 0); ++ + if (desc) { + desc->callback = bcm2835_sdhost_dma_complete; + desc->callback_param = host; +- dmaengine_submit(desc); +- dma_async_issue_pending(dma_chan); ++ host->dma_desc = desc; ++ host->dma_chan = dma_chan; ++ host->dma_dir = dir_data; + } +- ++ log_event("PDM>", (u32)data, 0); + } + ++static void bcm2835_sdhost_start_dma(struct bcm2835_host *host) ++{ ++ log_event("SDMA", (u32)host->data, (u32)host->dma_chan); ++ dmaengine_submit(host->dma_desc); ++ dma_async_issue_pending(host->dma_chan); ++} + + static void bcm2835_sdhost_set_transfer_irqs(struct bcm2835_host *host) + { + u32 all_irqs = SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN | + SDHCFG_BUSY_IRPT_EN; +- if (host->use_dma) ++ if (host->dma_desc) + host->hcfg = (host->hcfg & ~all_irqs) | + SDHCFG_BUSY_IRPT_EN; + else +@@ -664,13 +855,13 @@ static void bcm2835_sdhost_set_transfer_ + bcm2835_sdhost_write(host, host->hcfg, SDHCFG); + } + +- + static void bcm2835_sdhost_prepare_data(struct bcm2835_host *host, struct mmc_command *cmd) + { + struct mmc_data *data = cmd->data; + + WARN_ON(host->data); + ++ host->data = data; + if (!data) + return; + +@@ -679,46 +870,19 @@ static void bcm2835_sdhost_prepare_data( + BUG_ON(data->blksz > host->mmc->max_blk_size); + BUG_ON(data->blocks > 65535); + +- host->data = data; + host->data_complete = 0; + host->flush_fifo = 0; + host->data->bytes_xfered = 0; + +- if (!host->sectors && host->mmc->card && !(host->debug_flags & 1)) +- { +- struct mmc_card *card = host->mmc->card; +- if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) { +- /* +- * The EXT_CSD sector count is in number of 512 byte +- * sectors. +- */ +- host->sectors = card->ext_csd.sectors; +- pr_err("%s: using ext_csd!\n", mmc_hostname(host->mmc)); +- } else { +- /* +- * The CSD capacity field is in units of read_blkbits. +- * set_capacity takes units of 512 bytes. +- */ +- host->sectors = card->csd.capacity << +- (card->csd.read_blkbits - 9); +- } +- host->single_read_sectors[0] = host->sectors - 65; +- host->single_read_sectors[1] = host->sectors - 64; +- host->single_read_sectors[2] = host->sectors - 33; +- host->single_read_sectors[3] = host->sectors - 32; +- host->single_read_sectors[4] = host->sectors - 1; +- host->single_read_sectors[5] = ~0; /* Safety net */ +- } + +- host->use_dma = host->have_dma && (data->blocks > host->pio_limit); +- if (!host->use_dma) { ++ if (!host->dma_desc) { ++ /* Use PIO */ + int flags; + +- flags = SG_MITER_ATOMIC; + if (data->flags & MMC_DATA_READ) +- flags |= SG_MITER_TO_SG; ++ flags = SG_MITER_TO_SG; + else +- flags |= SG_MITER_FROM_SG; ++ flags = SG_MITER_FROM_SG; + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); + host->blocks = data->blocks; + } +@@ -726,19 +890,20 @@ static void bcm2835_sdhost_prepare_data( + bcm2835_sdhost_set_transfer_irqs(host); + + bcm2835_sdhost_write(host, data->blksz, SDHBCT); +- bcm2835_sdhost_write(host, host->use_dma ? data->blocks : 0, SDHBLC); ++ bcm2835_sdhost_write(host, data->blocks, SDHBLC); + + BUG_ON(!host->data); + } + +- +-void bcm2835_sdhost_send_command(struct bcm2835_host *host, struct mmc_command *cmd) ++bool bcm2835_sdhost_send_command(struct bcm2835_host *host, ++ struct mmc_command *cmd) + { + u32 sdcmd, sdhsts; + unsigned long timeout; + int delay; + + WARN_ON(host->cmd); ++ log_event("CMD<", cmd->opcode, cmd->arg); + + if (cmd->data) + pr_debug("%s: send_command %d 0x%x " +@@ -761,9 +926,9 @@ void bcm2835_sdhost_send_command(struct + pr_err("%s: previous command never completed.\n", + mmc_hostname(host->mmc)); + bcm2835_sdhost_dumpregs(host); +- cmd->error = -EIO; ++ cmd->error = -EILSEQ; + tasklet_schedule(&host->finish_tasklet); +- return; ++ return false; + } + timeout--; + udelay(10); +@@ -791,23 +956,24 @@ void bcm2835_sdhost_send_command(struct + if (sdhsts & SDHSTS_ERROR_MASK) + bcm2835_sdhost_write(host, sdhsts, SDHSTS); + +- bcm2835_sdhost_prepare_data(host, cmd); +- +- bcm2835_sdhost_write(host, cmd->arg, SDARG); +- + 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 false; + } + ++ bcm2835_sdhost_prepare_data(host, cmd); ++ ++ bcm2835_sdhost_write(host, cmd->arg, SDARG); ++ + sdcmd = cmd->opcode & SDCMD_CMD_MASK; + +- if (!(cmd->flags & MMC_RSP_PRESENT)) ++ host->use_busy = 0; ++ if (!(cmd->flags & MMC_RSP_PRESENT)) { + sdcmd |= SDCMD_NO_RESPONSE; +- else { ++ } else { + if (cmd->flags & MMC_RSP_136) + sdcmd |= SDCMD_LONG_RESPONSE; + if (cmd->flags & MMC_RSP_BUSY) { +@@ -817,6 +983,7 @@ void bcm2835_sdhost_send_command(struct + } + + if (cmd->data) { ++ log_event("CMDD", cmd->data->blocks, cmd->data->blksz); + if (host->delay_after_stop) { + struct timeval now; + int time_since_stop; +@@ -839,10 +1006,12 @@ void bcm2835_sdhost_send_command(struct + } + + bcm2835_sdhost_write(host, sdcmd | SDCMD_NEW_FLAG, SDCMD); +-} + ++ return true; ++} + +-static void bcm2835_sdhost_finish_command(struct bcm2835_host *host); ++static void bcm2835_sdhost_finish_command(struct bcm2835_host *host, ++ unsigned long *irq_flags); + static void bcm2835_sdhost_transfer_complete(struct bcm2835_host *host); + + static void bcm2835_sdhost_finish_data(struct bcm2835_host *host) +@@ -852,6 +1021,7 @@ static void bcm2835_sdhost_finish_data(s + data = host->data; + BUG_ON(!data); + ++ log_event("FDA<", (u32)host->mrq, (u32)host->cmd); + pr_debug("finish_data(error %d, stop %d, sbc %d)\n", + data->error, data->stop ? 1 : 0, + host->mrq->sbc ? 1 : 0); +@@ -859,10 +1029,7 @@ static void bcm2835_sdhost_finish_data(s + host->hcfg &= ~(SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN); + bcm2835_sdhost_write(host, host->hcfg, SDHCFG); + +- if (data->error) { +- data->bytes_xfered = 0; +- } else +- data->bytes_xfered = data->blksz * data->blocks; ++ data->bytes_xfered = data->error ? 0 : (data->blksz * data->blocks); + + host->data_complete = 1; + +@@ -877,9 +1044,9 @@ static void bcm2835_sdhost_finish_data(s + } + else + bcm2835_sdhost_transfer_complete(host); ++ log_event("FDA>", (u32)host->mrq, (u32)host->cmd); + } + +- + static void bcm2835_sdhost_transfer_complete(struct bcm2835_host *host) + { + struct mmc_data *data; +@@ -891,6 +1058,7 @@ static void bcm2835_sdhost_transfer_comp + data = host->data; + host->data = NULL; + ++ log_event("TCM<", (u32)data, data->error); + pr_debug("transfer_complete(error %d, stop %d)\n", + data->error, data->stop ? 1 : 0); + +@@ -899,88 +1067,114 @@ static void bcm2835_sdhost_transfer_comp + * a) open-ended multiblock transfer (no CMD23) + * b) error in multiblock transfer + */ +- if (data->stop && +- (data->error || +- !host->mrq->sbc)) { +- host->flush_fifo = 1; +- bcm2835_sdhost_send_command(host, data->stop); +- if (host->delay_after_stop) +- do_gettimeofday(&host->stop_time); +- if (!host->use_busy) +- bcm2835_sdhost_finish_command(host); ++ if (host->mrq->stop && (data->error || !host->use_sbc)) { ++ if (bcm2835_sdhost_send_command(host, host->mrq->stop)) { ++ /* No busy, so poll for completion */ ++ if (!host->use_busy) ++ bcm2835_sdhost_finish_command(host, NULL); ++ ++ if (host->delay_after_stop) ++ do_gettimeofday(&host->stop_time); ++ } + } else { ++ bcm2835_sdhost_wait_transfer_complete(host); + tasklet_schedule(&host->finish_tasklet); + } ++ log_event("TCM>", (u32)data, 0); + } + +-static void bcm2835_sdhost_finish_command(struct bcm2835_host *host) ++/* If irq_flags is valid, the caller is in a thread context and is allowed ++ to sleep */ ++static void bcm2835_sdhost_finish_command(struct bcm2835_host *host, ++ unsigned long *irq_flags) + { + u32 sdcmd; +- unsigned long timeout; ++ u32 retries; + #ifdef DEBUG + struct timeval before, after; + int timediff = 0; + #endif + ++ log_event("FCM<", (u32)host->mrq, (u32)host->cmd); + pr_debug("finish_command(%x)\n", bcm2835_sdhost_read(host, SDCMD)); + + BUG_ON(!host->cmd || !host->mrq); + +-#ifdef DEBUG +- do_gettimeofday(&before); +-#endif +- /* Wait max 100 ms */ +- timeout = 10000; ++ /* Poll quickly at first */ ++ ++ retries = host->cmd_quick_poll_retries; ++ if (!retries) { ++ /* Work out how many polls take 1us by timing 10us */ ++ struct timeval start, now; ++ int us_diff; ++ ++ retries = 1; ++ do { ++ int i; ++ ++ retries *= 2; ++ ++ do_gettimeofday(&start); ++ ++ for (i = 0; i < retries; i++) { ++ cpu_relax(); ++ sdcmd = bcm2835_sdhost_read(host, SDCMD); ++ } ++ ++ do_gettimeofday(&now); ++ us_diff = (now.tv_sec - start.tv_sec) * 1000000 + ++ (now.tv_usec - start.tv_usec); ++ } while (us_diff < 10); ++ ++ host->cmd_quick_poll_retries = ((retries * us_diff + 9)*CMD_DALLY_US)/10 + 1; ++ retries = 1; // We've already waited long enough this time ++ } ++ ++ retries = host->cmd_quick_poll_retries; + for (sdcmd = bcm2835_sdhost_read(host, SDCMD); +- (sdcmd & SDCMD_NEW_FLAG) && timeout; +- timeout--) { +- if (host->flush_fifo) { +- while (bcm2835_sdhost_read(host, SDHSTS) & +- SDHSTS_DATA_FLAG) +- (void)bcm2835_sdhost_read(host, SDDATA); +- } +- udelay(10); ++ (sdcmd & SDCMD_NEW_FLAG) && !(sdcmd & SDCMD_FAIL_FLAG) && retries; ++ retries--) { ++ cpu_relax(); + sdcmd = bcm2835_sdhost_read(host, SDCMD); + } +-#ifdef DEBUG +- do_gettimeofday(&after); +- timediff = (after.tv_sec - before.tv_sec)*1000000 + +- (after.tv_usec - before.tv_usec); + +- pr_debug(" finish_command - waited %dus\n", timediff); +-#endif ++ if (!retries) { ++ unsigned long wait_max; ++ ++ if (!irq_flags) { ++ /* Schedule the work */ ++ log_event("CWWQ", 0, 0); ++ schedule_work(&host->cmd_wait_wq); ++ return; ++ } ++ ++ /* Wait max 100 ms */ ++ wait_max = jiffies + msecs_to_jiffies(100); ++ while (time_before(jiffies, wait_max)) { ++ spin_unlock_irqrestore(&host->lock, *irq_flags); ++ usleep_range(1, 10); ++ spin_lock_irqsave(&host->lock, *irq_flags); ++ sdcmd = bcm2835_sdhost_read(host, SDCMD); ++ if (!(sdcmd & SDCMD_NEW_FLAG) || ++ (sdcmd & SDCMD_FAIL_FLAG)) ++ break; ++ } ++ } + +- if (timeout == 0) { ++ /* Check for errors */ ++ if (sdcmd & SDCMD_NEW_FLAG) { + pr_err("%s: command never completed.\n", + mmc_hostname(host->mmc)); + bcm2835_sdhost_dumpregs(host); + host->cmd->error = -EIO; + tasklet_schedule(&host->finish_tasklet); + return; +- } +- +- if (host->flush_fifo) { +- for (timeout = 100; +- (bcm2835_sdhost_read(host, SDHSTS) & SDHSTS_DATA_FLAG) && timeout; +- timeout--) { +- (void)bcm2835_sdhost_read(host, SDDATA); +- } +- host->flush_fifo = 0; +- if (timeout == 0) { +- pr_err("%s: FIFO never drained.\n", +- mmc_hostname(host->mmc)); +- bcm2835_sdhost_dumpregs(host); +- host->cmd->error = -EIO; +- tasklet_schedule(&host->finish_tasklet); +- return; +- } +- } +- +- /* Check for errors */ +- if (sdcmd & SDCMD_FAIL_FLAG) +- { ++ } else if (sdcmd & SDCMD_FAIL_FLAG) { + u32 sdhsts = bcm2835_sdhost_read(host, SDHSTS); + ++ /* Clear the errors */ ++ bcm2835_sdhost_write(host, SDHSTS_ERROR_MASK, SDHSTS); ++ + if (host->debug) + pr_info("%s: error detected - CMD %x, HSTS %03x, EDM %x\n", + mmc_hostname(host->mmc), sdcmd, sdhsts, +@@ -1003,7 +1197,7 @@ static void bcm2835_sdhost_finish_comman + mmc_hostname(host->mmc), + host->cmd->opcode); + bcm2835_sdhost_dumpregs(host); +- host->cmd->error = -EIO; ++ host->cmd->error = -EILSEQ; + } + tasklet_schedule(&host->finish_tasklet); + return; +@@ -1018,31 +1212,31 @@ static void bcm2835_sdhost_finish_comman + pr_debug("%s: finish_command %08x %08x %08x %08x\n", + mmc_hostname(host->mmc), + host->cmd->resp[0], host->cmd->resp[1], host->cmd->resp[2], host->cmd->resp[3]); ++ log_event("RSP ", host->cmd->resp[0], host->cmd->resp[1]); + } else { + host->cmd->resp[0] = bcm2835_sdhost_read(host, SDRSP0); + pr_debug("%s: finish_command %08x\n", + mmc_hostname(host->mmc), + host->cmd->resp[0]); ++ log_event("RSP ", host->cmd->resp[0], 0); + } + } + +- host->cmd->error = 0; +- + if (host->cmd == host->mrq->sbc) { + /* Finished CMD23, now send actual command. */ + host->cmd = NULL; +- bcm2835_sdhost_send_command(host, host->mrq->cmd); ++ if (bcm2835_sdhost_send_command(host, host->mrq->cmd)) { ++ if (host->data && host->dma_desc) ++ /* DMA transfer starts now, PIO starts after irq */ ++ bcm2835_sdhost_start_dma(host); + +- if (host->cmd->data && host->use_dma) +- /* DMA transfer starts now, PIO starts after irq */ +- bcm2835_sdhost_transfer_dma(host); +- +- if (!host->use_busy) +- bcm2835_sdhost_finish_command(host); +- } else if (host->cmd == host->mrq->stop) ++ if (!host->use_busy) ++ bcm2835_sdhost_finish_command(host, NULL); ++ } ++ } else if (host->cmd == host->mrq->stop) { + /* Finished CMD12 */ + tasklet_schedule(&host->finish_tasklet); +- else { ++ } else { + /* Processed actual command. */ + host->cmd = NULL; + if (!host->data) +@@ -1050,6 +1244,7 @@ static void bcm2835_sdhost_finish_comman + else if (host->data_complete) + bcm2835_sdhost_transfer_complete(host); + } ++ log_event("FCM>", (u32)host->mrq, (u32)host->cmd); + } + + static void bcm2835_sdhost_timeout(unsigned long data) +@@ -1060,10 +1255,12 @@ static void bcm2835_sdhost_timeout(unsig + host = (struct bcm2835_host *)data; + + spin_lock_irqsave(&host->lock, flags); ++ log_event("TIM<", 0, 0); + + if (host->mrq) { + pr_err("%s: timeout waiting for hardware interrupt.\n", + mmc_hostname(host->mmc)); ++ log_dump(); + bcm2835_sdhost_dumpregs(host); + + if (host->data) { +@@ -1084,74 +1281,15 @@ static void bcm2835_sdhost_timeout(unsig + spin_unlock_irqrestore(&host->lock, flags); + } + +-static void bcm2835_sdhost_pio_timeout(unsigned long data) +-{ +- struct bcm2835_host *host; +- unsigned long flags; +- +- host = (struct bcm2835_host *)data; +- +- spin_lock_irqsave(&host->lock, flags); +- +- if (host->data) { +- u32 sdhsts = bcm2835_sdhost_read(host, SDHSTS); +- +- if (sdhsts & SDHSTS_REW_TIME_OUT) { +- pr_err("%s: transfer timeout\n", +- mmc_hostname(host->mmc)); +- if (host->debug) +- bcm2835_sdhost_dumpregs(host); +- } else { +- pr_err("%s: unexpected transfer timeout\n", +- mmc_hostname(host->mmc)); +- bcm2835_sdhost_dumpregs(host); +- } +- +- bcm2835_sdhost_write(host, SDHSTS_TRANSFER_ERROR_MASK, +- SDHSTS); +- +- host->data->error = -ETIMEDOUT; +- +- bcm2835_sdhost_finish_data(host); +- } +- +- mmiowb(); +- spin_unlock_irqrestore(&host->lock, flags); +-} +- +-static void bcm2835_sdhost_enable_sdio_irq_nolock(struct bcm2835_host *host, int enable) +-{ +- if (enable) +- host->hcfg |= SDHCFG_SDIO_IRPT_EN; +- else +- host->hcfg &= ~SDHCFG_SDIO_IRPT_EN; +- bcm2835_sdhost_write(host, host->hcfg, SDHCFG); +- mmiowb(); +-} +- +-static void bcm2835_sdhost_enable_sdio_irq(struct mmc_host *mmc, int enable) +-{ +- struct bcm2835_host *host = mmc_priv(mmc); +- unsigned long flags; +- +- pr_debug("%s: enable_sdio_irq(%d)\n", mmc_hostname(mmc), enable); +- spin_lock_irqsave(&host->lock, flags); +- bcm2835_sdhost_enable_sdio_irq_nolock(host, enable); +- spin_unlock_irqrestore(&host->lock, flags); +-} +- +-static u32 bcm2835_sdhost_busy_irq(struct bcm2835_host *host, u32 intmask) ++static void bcm2835_sdhost_busy_irq(struct bcm2835_host *host, u32 intmask) + { +- const u32 handled = (SDHSTS_REW_TIME_OUT | SDHSTS_CMD_TIME_OUT | +- SDHSTS_CRC16_ERROR | SDHSTS_CRC7_ERROR | +- SDHSTS_FIFO_ERROR); +- ++ log_event("IRQB", (u32)host->cmd, intmask); + if (!host->cmd) { + pr_err("%s: got command busy interrupt 0x%08x even " + "though no command operation was in progress.\n", + mmc_hostname(host->mmc), (unsigned)intmask); + bcm2835_sdhost_dumpregs(host); +- return 0; ++ return; + } + + if (!host->use_busy) { +@@ -1159,7 +1297,7 @@ static u32 bcm2835_sdhost_busy_irq(struc + "though not expecting one.\n", + mmc_hostname(host->mmc), (unsigned)intmask); + bcm2835_sdhost_dumpregs(host); +- return 0; ++ return; + } + host->use_busy = 0; + +@@ -1182,28 +1320,23 @@ static u32 bcm2835_sdhost_busy_irq(struc + } else if (intmask & SDHSTS_CMD_TIME_OUT) + host->cmd->error = -ETIMEDOUT; + ++ log_dump(); + bcm2835_sdhost_dumpregs(host); +- tasklet_schedule(&host->finish_tasklet); + } + else +- bcm2835_sdhost_finish_command(host); +- +- return handled; ++ bcm2835_sdhost_finish_command(host, NULL); + } + +-static u32 bcm2835_sdhost_data_irq(struct bcm2835_host *host, u32 intmask) ++static void bcm2835_sdhost_data_irq(struct bcm2835_host *host, u32 intmask) + { +- const u32 handled = (SDHSTS_REW_TIME_OUT | +- SDHSTS_CRC16_ERROR | +- SDHSTS_FIFO_ERROR); +- + /* There are no dedicated data/space available interrupt + status bits, so it is necessary to use the single shared + data/space available FIFO status bits. It is therefore not + an error to get here when there is no data transfer in + progress. */ ++ log_event("IRQD", (u32)host->data, intmask); + if (!host->data) +- return 0; ++ return; + + if (intmask & (SDHSTS_CRC16_ERROR | + SDHSTS_FIFO_ERROR | +@@ -1214,46 +1347,37 @@ static u32 bcm2835_sdhost_data_irq(struc + else + host->data->error = -ETIMEDOUT; + +- bcm2835_sdhost_dumpregs(host); +- tasklet_schedule(&host->finish_tasklet); +- return handled; ++ if (host->debug) { ++ log_dump(); ++ bcm2835_sdhost_dumpregs(host); ++ } + } + +- /* Use the block interrupt for writes after the first block */ +- if (host->data->flags & MMC_DATA_WRITE) { ++ if (host->data->error) { ++ bcm2835_sdhost_finish_data(host); ++ } else if (host->data->flags & MMC_DATA_WRITE) { ++ /* Use the block interrupt for writes after the first block */ + host->hcfg &= ~(SDHCFG_DATA_IRPT_EN); + host->hcfg |= SDHCFG_BLOCK_IRPT_EN; + bcm2835_sdhost_write(host, host->hcfg, SDHCFG); +- if (host->data->error) +- bcm2835_sdhost_finish_data(host); +- else +- bcm2835_sdhost_transfer_pio(host); ++ bcm2835_sdhost_transfer_pio(host); + } else { +- if (!host->data->error) { +- bcm2835_sdhost_transfer_pio(host); +- host->blocks--; +- } ++ bcm2835_sdhost_transfer_pio(host); ++ host->blocks--; + if ((host->blocks == 0) || host->data->error) + bcm2835_sdhost_finish_data(host); + } +- +- return handled; + } + +-static u32 bcm2835_sdhost_block_irq(struct bcm2835_host *host, u32 intmask) ++static void bcm2835_sdhost_block_irq(struct bcm2835_host *host, u32 intmask) + { +- struct dma_chan *dma_chan; +- u32 dir_data; +- const u32 handled = (SDHSTS_REW_TIME_OUT | +- SDHSTS_CRC16_ERROR | +- SDHSTS_FIFO_ERROR); +- ++ log_event("IRQK", (u32)host->data, intmask); + if (!host->data) { + pr_err("%s: got block interrupt 0x%08x even " + "though no data operation was in progress.\n", + mmc_hostname(host->mmc), (unsigned)intmask); + bcm2835_sdhost_dumpregs(host); +- return handled; ++ return; + } + + if (intmask & (SDHSTS_CRC16_ERROR | +@@ -1265,149 +1389,69 @@ static u32 bcm2835_sdhost_block_irq(stru + else + host->data->error = -ETIMEDOUT; + +- if (host->debug) ++ if (host->debug) { ++ log_dump(); + bcm2835_sdhost_dumpregs(host); +- tasklet_schedule(&host->finish_tasklet); +- return handled; ++ } + } + +- if (!host->use_dma) { ++ if (!host->dma_desc) { + BUG_ON(!host->blocks); +- host->blocks--; +- if ((host->blocks == 0) || host->data->error) { +- /* Cancel the timer */ +- del_timer(&host->pio_timer); +- ++ if (host->data->error || (--host->blocks == 0)) { + bcm2835_sdhost_finish_data(host); + } else { +- /* Reset the timer */ +- mod_timer(&host->pio_timer, +- jiffies + host->pio_timeout); +- + bcm2835_sdhost_transfer_pio(host); +- +- /* Reset the timer */ +- mod_timer(&host->pio_timer, +- jiffies + host->pio_timeout); + } + } else if (host->data->flags & MMC_DATA_WRITE) { +- 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_sdhost_finish_data(host); + } +- +- return handled; + } + +- + static irqreturn_t bcm2835_sdhost_irq(int irq, void *dev_id) + { + irqreturn_t result = IRQ_NONE; + struct bcm2835_host *host = dev_id; +- u32 unexpected = 0, early = 0; +- int loops = 0; ++ u32 intmask; + + spin_lock(&host->lock); + +- for (loops = 0; loops < 1; loops++) { +- u32 intmask, handled; +- +- intmask = bcm2835_sdhost_read(host, SDHSTS); +- handled = intmask & (SDHSTS_BUSY_IRPT | +- SDHSTS_BLOCK_IRPT | +- SDHSTS_SDIO_IRPT | +- SDHSTS_DATA_FLAG); +- if ((handled == SDHSTS_DATA_FLAG) && +- (loops == 0) && !host->data) { +- pr_err("%s: sdhost_irq data interrupt 0x%08x even " +- "though no data operation was in progress.\n", +- mmc_hostname(host->mmc), +- (unsigned)intmask); +- +- bcm2835_sdhost_dumpregs(host); +- } +- +- if (!handled) +- break; ++ intmask = bcm2835_sdhost_read(host, SDHSTS); ++ log_event("IRQ<", intmask, 0); + +- if (loops) +- early |= handled; ++ bcm2835_sdhost_write(host, ++ SDHSTS_BUSY_IRPT | ++ SDHSTS_BLOCK_IRPT | ++ SDHSTS_SDIO_IRPT | ++ SDHSTS_DATA_FLAG, ++ SDHSTS); + ++ if (intmask & SDHSTS_BLOCK_IRPT) { ++ bcm2835_sdhost_block_irq(host, intmask); + result = IRQ_HANDLED; ++ } + +- /* Clear all interrupts and notifications */ +- bcm2835_sdhost_write(host, intmask, SDHSTS); +- +- if (intmask & SDHSTS_BUSY_IRPT) +- handled |= bcm2835_sdhost_busy_irq(host, intmask); +- +- /* There is no true data interrupt status bit, so it is +- necessary to qualify the data flag with the interrupt +- enable bit */ +- if ((intmask & SDHSTS_DATA_FLAG) && +- (host->hcfg & SDHCFG_DATA_IRPT_EN)) +- handled |= bcm2835_sdhost_data_irq(host, intmask); +- +- if (intmask & SDHSTS_BLOCK_IRPT) +- handled |= bcm2835_sdhost_block_irq(host, intmask); +- +- if (intmask & SDHSTS_SDIO_IRPT) { +- bcm2835_sdhost_enable_sdio_irq_nolock(host, false); +- host->thread_isr |= SDHSTS_SDIO_IRPT; +- result = IRQ_WAKE_THREAD; +- } ++ if (intmask & SDHSTS_BUSY_IRPT) { ++ bcm2835_sdhost_busy_irq(host, intmask); ++ result = IRQ_HANDLED; ++ } + +- unexpected |= (intmask & ~handled); ++ /* There is no true data interrupt status bit, so it is ++ necessary to qualify the data flag with the interrupt ++ enable bit */ ++ if ((intmask & SDHSTS_DATA_FLAG) && ++ (host->hcfg & SDHCFG_DATA_IRPT_EN)) { ++ bcm2835_sdhost_data_irq(host, intmask); ++ result = IRQ_HANDLED; + } + + mmiowb(); + ++ log_event("IRQ>", bcm2835_sdhost_read(host, SDHSTS), 0); + spin_unlock(&host->lock); + +- if (early) +- pr_debug("%s: early %x (loops %d)\n", +- mmc_hostname(host->mmc), early, loops); +- +- if (unexpected) { +- pr_err("%s: unexpected interrupt 0x%08x.\n", +- mmc_hostname(host->mmc), unexpected); +- bcm2835_sdhost_dumpregs(host); +- } +- + return result; + } + +-static irqreturn_t bcm2835_sdhost_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 & SDHSTS_SDIO_IRPT) { +- sdio_run_irqs(host->mmc); +- +-/* Is this necessary? Why re-enable an interrupt which is enabled? +- spin_lock_irqsave(&host->lock, flags); +- if (host->flags & SDHSTS_SDIO_IRPT_ENABLED) +- bcm2835_sdhost_enable_sdio_irq_nolock(host, true); +- spin_unlock_irqrestore(&host->lock, flags); +-*/ +- } +- +- return isr ? IRQ_HANDLED : IRQ_NONE; +-} +- +- +- + void bcm2835_sdhost_set_clock(struct bcm2835_host *host, unsigned int clock) + { + int div = 0; /* Initialized for compiler warning */ +@@ -1417,9 +1461,8 @@ void bcm2835_sdhost_set_clock(struct bcm + pr_info("%s: set_clock(%d)\n", mmc_hostname(host->mmc), clock); + + if ((host->overclock_50 > 50) && +- (clock == 50*MHZ)) { ++ (clock == 50*MHZ)) + clock = host->overclock_50 * MHZ + (MHZ - 1); +- } + + /* The SDCDIV register has 11 bits, and holds (div - 2). + But in data mode the max is 50MHz wihout a minimum, and only the +@@ -1466,6 +1509,11 @@ void bcm2835_sdhost_set_clock(struct bcm + clock = host->max_clk / (div + 2); + host->mmc->actual_clock = clock; + ++ /* Calibrate some delays */ ++ ++ host->ns_per_fifo_word = (1000000000/clock) * ++ ((host->mmc->caps & MMC_CAP_4_BIT_DATA) ? 8 : 32); ++ + if (clock > input_clock) { + /* Save the closest value, to make it easier + to reduce in the event of error */ +@@ -1501,6 +1549,7 @@ static void bcm2835_sdhost_request(struc + { + struct bcm2835_host *host; + unsigned long flags; ++ u32 edm, fsm; + + host = mmc_priv(mmc); + +@@ -1521,6 +1570,8 @@ static void bcm2835_sdhost_request(struc + } + + /* Reset the error statuses in case this is a retry */ ++ if (mrq->sbc) ++ mrq->sbc->error = 0; + if (mrq->cmd) + mrq->cmd->error = 0; + if (mrq->data) +@@ -1536,28 +1587,58 @@ static void bcm2835_sdhost_request(struc + return; + } + ++ if (host->use_dma && mrq->data && ++ (mrq->data->blocks > host->pio_limit)) ++ bcm2835_sdhost_prepare_dma(host, mrq->data); ++ + spin_lock_irqsave(&host->lock, flags); + + WARN_ON(host->mrq != NULL); +- + host->mrq = mrq; + +- if (mrq->sbc) +- bcm2835_sdhost_send_command(host, mrq->sbc); +- else +- bcm2835_sdhost_send_command(host, mrq->cmd); ++ edm = bcm2835_sdhost_read(host, SDEDM); ++ fsm = edm & SDEDM_FSM_MASK; + +- mmiowb(); +- spin_unlock_irqrestore(&host->lock, flags); ++ log_event("REQ<", (u32)mrq, edm); ++ if ((fsm != SDEDM_FSM_IDENTMODE) && ++ (fsm != SDEDM_FSM_DATAMODE)) { ++ pr_err("%s: previous command (%d) not complete (EDM %x)\n", ++ mmc_hostname(host->mmc), ++ bcm2835_sdhost_read(host, SDCMD) & SDCMD_CMD_MASK, ++ edm); ++ log_event("REQ!", (u32)mrq, edm); ++ log_dump(); ++ bcm2835_sdhost_dumpregs(host); ++ mrq->cmd->error = -EILSEQ; ++ tasklet_schedule(&host->finish_tasklet); ++ mmiowb(); ++ spin_unlock_irqrestore(&host->lock, flags); ++ return; ++ } ++ ++ host->use_sbc = !!mrq->sbc && ++ (host->mrq->data->flags & USE_CMD23_FLAGS); ++ if (host->use_sbc) { ++ if (bcm2835_sdhost_send_command(host, mrq->sbc)) { ++ if (!host->use_busy) ++ bcm2835_sdhost_finish_command(host, &flags); ++ } ++ } else if (bcm2835_sdhost_send_command(host, mrq->cmd)) { ++ if (host->data && host->dma_desc) ++ /* DMA transfer starts now, PIO starts after irq */ ++ bcm2835_sdhost_start_dma(host); + +- if (!mrq->sbc && mrq->cmd->data && host->use_dma) +- /* DMA transfer starts now, PIO starts after irq */ +- bcm2835_sdhost_transfer_dma(host); ++ if (!host->use_busy) ++ bcm2835_sdhost_finish_command(host, &flags); ++ } + +- if (!host->use_busy) +- bcm2835_sdhost_finish_command(host); +-} ++ log_event("CMD ", (u32)mrq->cmd->opcode, ++ mrq->data ? (u32)mrq->data->blksz : 0); ++ mmiowb(); + ++ log_event("REQ>", (u32)mrq, 0); ++ spin_unlock_irqrestore(&host->lock, flags); ++} + + static void bcm2835_sdhost_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) + { +@@ -1574,6 +1655,8 @@ static void bcm2835_sdhost_set_ios(struc + + spin_lock_irqsave(&host->lock, flags); + ++ log_event("IOS<", ios->clock, 0); ++ + if (!ios->clock || ios->clock != host->clock) { + bcm2835_sdhost_set_clock(host, ios->clock); + host->clock = ios->clock; +@@ -1596,59 +1679,53 @@ static void bcm2835_sdhost_set_ios(struc + spin_unlock_irqrestore(&host->lock, flags); + } + +-static int bcm2835_sdhost_multi_io_quirk(struct mmc_card *card, +- unsigned int direction, +- u32 blk_pos, int blk_size) +-{ +- /* There is a bug in the host controller hardware that makes +- reading the final sector of the card as part of a multiple read +- problematic. Detect that case and shorten the read accordingly. +- */ ++static struct mmc_host_ops bcm2835_sdhost_ops = { ++ .request = bcm2835_sdhost_request, ++ .set_ios = bcm2835_sdhost_set_ios, ++ .hw_reset = bcm2835_sdhost_reset, ++}; ++ ++static void bcm2835_sdhost_cmd_wait_work(struct work_struct *work) ++{ + struct bcm2835_host *host; ++ unsigned long flags; + +- host = mmc_priv(card->host); ++ host = container_of(work, struct bcm2835_host, cmd_wait_wq); + +- if (!host->sectors) { +- /* csd.capacity is in weird units - convert to sectors */ +- u32 card_sectors = (card->csd.capacity << (card->csd.read_blkbits - 9)); +- if ((direction == MMC_DATA_READ) && +- ((blk_pos + blk_size) == card_sectors)) +- blk_size--; +- return blk_size; +- } ++ spin_lock_irqsave(&host->lock, flags); + +- if (direction == MMC_DATA_READ) { +- int i; +- int sector; +- for (i = 0; blk_pos > (sector = host->single_read_sectors[i]); i++) +- continue; ++ log_event("CWK<", (u32)host->cmd, (u32)host->mrq); + +- if ((blk_pos + blk_size) > sector) +- blk_size = (blk_pos == sector) ? 1 : (sector - blk_pos); ++ /* ++ * 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; + } +- return blk_size; +-} + ++ bcm2835_sdhost_finish_command(host, &flags); + +-static struct mmc_host_ops bcm2835_sdhost_ops = { +- .request = bcm2835_sdhost_request, +- .set_ios = bcm2835_sdhost_set_ios, +- .enable_sdio_irq = bcm2835_sdhost_enable_sdio_irq, +- .hw_reset = bcm2835_sdhost_reset, +- .multi_io_quirk = bcm2835_sdhost_multi_io_quirk, +-}; ++ mmiowb(); ++ ++ log_event("CWK>", (u32)host->cmd, 0); + ++ spin_unlock_irqrestore(&host->lock, flags); ++} + + static void bcm2835_sdhost_tasklet_finish(unsigned long param) + { + struct bcm2835_host *host; + unsigned long flags; + struct mmc_request *mrq; ++ struct dma_chan *terminate_chan = NULL; + + host = (struct bcm2835_host *)param; + + spin_lock_irqsave(&host->lock, flags); + ++ log_event("TSK<", (u32)host->mrq, 0); + /* + * If this tasklet gets rescheduled while running, it will + * be run again afterwards but without any active request. +@@ -1683,11 +1760,23 @@ static void bcm2835_sdhost_tasklet_finis + + mmiowb(); + ++ host->dma_desc = NULL; ++ terminate_chan = host->dma_chan; ++ host->dma_chan = NULL; ++ + spin_unlock_irqrestore(&host->lock, flags); +- mmc_request_done(host->mmc, mrq); +-} + ++ if (terminate_chan) ++ { ++ int err = dmaengine_terminate_all(terminate_chan); ++ if (err) ++ pr_err("%s: failed to terminate DMA (%d)\n", ++ mmc_hostname(host->mmc), err); ++ } + ++ mmc_request_done(host->mmc, mrq); ++ log_event("TSK>", (u32)mrq, 0); ++} + + int bcm2835_sdhost_add_host(struct bcm2835_host *host) + { +@@ -1709,10 +1798,10 @@ int bcm2835_sdhost_add_host(struct bcm28 + mmc->f_max, mmc->f_min, mmc->max_busy_timeout); + + /* host controller capabilities */ +- mmc->caps |= /* MMC_CAP_SDIO_IRQ |*/ MMC_CAP_4_BIT_DATA | ++ mmc->caps |= + MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | + MMC_CAP_NEEDS_POLL | MMC_CAP_HW_RESET | MMC_CAP_ERASE | +- (ALLOW_CMD23 * MMC_CAP_CMD23); ++ ((ALLOW_CMD23_READ|ALLOW_CMD23_WRITE) * MMC_CAP_CMD23); + + spin_lock_init(&host->lock); + +@@ -1722,9 +1811,9 @@ int bcm2835_sdhost_add_host(struct bcm28 + pr_err("%s: unable to initialise DMA channels. " + "Falling back to PIO\n", + mmc_hostname(mmc)); +- host->have_dma = false; ++ host->use_dma = false; + } else { +- host->have_dma = true; ++ host->use_dma = true; + + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; +@@ -1741,7 +1830,7 @@ int bcm2835_sdhost_add_host(struct bcm28 + ret = dmaengine_slave_config(host->dma_chan_rx, &cfg); + } + } else { +- host->have_dma = false; ++ host->use_dma = false; + } + + mmc->max_segs = 128; +@@ -1756,16 +1845,15 @@ int bcm2835_sdhost_add_host(struct bcm28 + tasklet_init(&host->finish_tasklet, + bcm2835_sdhost_tasklet_finish, (unsigned long)host); + +- setup_timer(&host->timer, bcm2835_sdhost_timeout, +- (unsigned long)host); ++ INIT_WORK(&host->cmd_wait_wq, bcm2835_sdhost_cmd_wait_work); + +- setup_timer(&host->pio_timer, bcm2835_sdhost_pio_timeout, ++ setup_timer(&host->timer, bcm2835_sdhost_timeout, + (unsigned long)host); + + bcm2835_sdhost_init(host, 0); +- ret = request_threaded_irq(host->irq, bcm2835_sdhost_irq, +- bcm2835_sdhost_thread_irq, +- IRQF_SHARED, mmc_hostname(mmc), host); ++ ++ ret = request_irq(host->irq, bcm2835_sdhost_irq, 0 /*IRQF_SHARED*/, ++ mmc_hostname(mmc), host); + if (ret) { + pr_err("%s: failed to request IRQ %d: %d\n", + mmc_hostname(mmc), host->irq, ret); +@@ -1776,11 +1864,11 @@ int bcm2835_sdhost_add_host(struct bcm28 + mmc_add_host(mmc); + + pio_limit_string[0] = '\0'; +- if (host->have_dma && (host->pio_limit > 0)) ++ if (host->use_dma && (host->pio_limit > 0)) + sprintf(pio_limit_string, " (>%d)", host->pio_limit); + pr_info("%s: %s loaded - DMA %s%s\n", + mmc_hostname(mmc), DRIVER_NAME, +- host->have_dma ? "enabled" : "disabled", ++ host->use_dma ? "enabled" : "disabled", + pio_limit_string); + + return 0; +@@ -1810,8 +1898,11 @@ static int bcm2835_sdhost_probe(struct p + mmc->ops = &bcm2835_sdhost_ops; + host = mmc_priv(mmc); + host->mmc = mmc; ++ host->cmd_quick_poll_retries = 0; + host->pio_timeout = msecs_to_jiffies(500); ++ host->pio_limit = 1; + host->max_delay = 1; /* Warn if over 1ms */ ++ host->allow_dma = 1; + spin_lock_init(&host->lock); + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +@@ -1827,13 +1918,12 @@ static int bcm2835_sdhost_probe(struct p + return -ENODEV; + } + host->bus_addr = be32_to_cpup(addr); ++ log_init(iomem->start - host->bus_addr); + pr_debug(" - ioaddr %lx, iomem->start %lx, bus_addr %lx\n", + (unsigned long)host->ioaddr, + (unsigned long)iomem->start, + (unsigned long)host->bus_addr); + +- host->allow_dma = ALLOW_DMA; +- + if (node) { + /* Read any custom properties */ + of_property_read_u32(node, +@@ -1845,16 +1935,17 @@ static int bcm2835_sdhost_probe(struct p + of_property_read_u32(node, + "brcm,pio-limit", + &host->pio_limit); +- host->allow_dma = ALLOW_DMA && ++ host->allow_dma = + !of_property_read_bool(node, "brcm,force-pio"); + host->debug = of_property_read_bool(node, "brcm,debug"); +- of_property_read_u32(node, +- "brcm,debug-flags", +- &host->debug_flags); + } + +- if (host->debug_flags) +- dev_err(dev, "debug_flags=%x\n", host->debug_flags); ++ host->dma_chan = NULL; ++ host->dma_desc = NULL; ++ ++ /* Formally recognise the other way of disabling DMA */ ++ if (host->pio_limit == 0x7fffffff) ++ host->allow_dma = false; + + if (host->allow_dma) { + if (node) { +@@ -1940,15 +2031,12 @@ static int bcm2835_sdhost_remove(struct + return 0; + } + +- + static const struct of_device_id bcm2835_sdhost_match[] = { + { .compatible = "brcm,bcm2835-sdhost" }, + { } + }; + MODULE_DEVICE_TABLE(of, bcm2835_sdhost_match); + +- +- + static struct platform_driver bcm2835_sdhost_driver = { + .probe = bcm2835_sdhost_probe, + .remove = bcm2835_sdhost_remove, diff --git a/target/linux/brcm2708/patches-4.4/0145-BCM270X_DT-Add-dtparams-for-the-SD-interface.patch b/target/linux/brcm2708/patches-4.4/0145-BCM270X_DT-Add-dtparams-for-the-SD-interface.patch new file mode 100644 index 0000000000..c3a0816a87 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0145-BCM270X_DT-Add-dtparams-for-the-SD-interface.patch @@ -0,0 +1,235 @@ +From 177ef127e03243ede5b1b00032d5f9fdce94673b Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 12 Feb 2016 15:38:00 +0000 +Subject: [PATCH 145/232] BCM270X_DT: Add dtparams for the SD interface + +Add new base dtparams sd_overclock, sd_force_pio, sd_pio_limit +and sd_debug. +--- + arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 4 ++++ + arch/arm/boot/dts/bcm2708-rpi-b.dts | 4 ++++ + arch/arm/boot/dts/bcm2708-rpi-cm.dts | 1 - + arch/arm/boot/dts/bcm2708-rpi-cm.dtsi | 13 +++++++++++++ + arch/arm/boot/dts/bcm2708_common.dtsi | 2 ++ + arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 4 ++++ + arch/arm/boot/dts/overlays/README | 11 ++++++++++- + arch/arm/boot/dts/overlays/mmc-overlay.dts | 1 - + arch/arm/boot/dts/overlays/sdhost-overlay.dts | 27 +++++++++++++------------- + arch/arm/boot/dts/overlays/sdtweak-overlay.dts | 14 ++++++------- + 10 files changed, 58 insertions(+), 23 deletions(-) + +--- a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts +@@ -141,5 +141,9 @@ + audio = <&audio>,"status"; + watchdog = <&watchdog>,"status"; + random = <&random>,"status"; ++ sd_overclock = <&sdhost>,"brcm,overclock-50:0"; ++ sd_force_pio = <&sdhost>,"brcm,force-pio?"; ++ sd_pio_limit = <&sdhost>,"brcm,pio-limit:0"; ++ sd_debug = <&sdhost>,"brcm,debug"; + }; + }; +--- a/arch/arm/boot/dts/bcm2708-rpi-b.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts +@@ -131,5 +131,9 @@ + audio = <&audio>,"status"; + watchdog = <&watchdog>,"status"; + random = <&random>,"status"; ++ sd_overclock = <&sdhost>,"brcm,overclock-50:0"; ++ sd_force_pio = <&sdhost>,"brcm,force-pio?"; ++ sd_pio_limit = <&sdhost>,"brcm,pio-limit:0"; ++ sd_debug = <&sdhost>,"brcm,debug"; + }; + }; +--- a/arch/arm/boot/dts/bcm2708-rpi-cm.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-cm.dts +@@ -97,6 +97,5 @@ + i2c0_baudrate = <&i2c0>,"clock-frequency:0"; + i2c1_baudrate = <&i2c1>,"clock-frequency:0"; + i2c2_baudrate = <&i2c2>,"clock-frequency:0"; +- core_freq = <&clk_core>,"clock-frequency:0"; + }; + }; +--- a/arch/arm/boot/dts/bcm2708-rpi-cm.dtsi ++++ b/arch/arm/boot/dts/bcm2708-rpi-cm.dtsi +@@ -7,6 +7,13 @@ + }; + }; + ++&gpio { ++ sdhost_pins: sdhost_pins { ++ brcm,pins = <48 49 50 51 52 53>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++}; ++ + &leds { + act_led: act { + label = "led0"; +@@ -29,6 +36,8 @@ + + / { + __overrides__ { ++ core_freq = <&clk_core>,"clock-frequency:0"; ++ + act_led_gpio = <&act_led>,"gpios:4"; + act_led_activelow = <&act_led>,"gpios:8"; + act_led_trigger = <&act_led>,"linux,default-trigger"; +@@ -36,5 +45,9 @@ + audio = <&audio>,"status"; + watchdog = <&watchdog>,"status"; + random = <&random>,"status"; ++ sd_overclock = <&sdhost>,"brcm,overclock-50:0"; ++ sd_force_pio = <&sdhost>,"brcm,force-pio?"; ++ sd_pio_limit = <&sdhost>,"brcm,pio-limit:0"; ++ sd_debug = <&sdhost>,"brcm,debug"; + }; + }; +--- a/arch/arm/boot/dts/bcm2708_common.dtsi ++++ b/arch/arm/boot/dts/bcm2708_common.dtsi +@@ -135,6 +135,7 @@ + dmas = <&dma 13>, + <&dma 13>; + dma-names = "tx", "rx"; ++ brcm,overclock-50 = <0>; + brcm,pio-limit = <1>; + status = "disabled"; + }; +@@ -203,6 +204,7 @@ + dmas = <&dma 11>, + <&dma 11>; + dma-names = "tx", "rx"; ++ brcm,overclock-50 = <0>; + status = "disabled"; + }; + +--- a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts ++++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts +@@ -141,5 +141,9 @@ + audio = <&audio>,"status"; + watchdog = <&watchdog>,"status"; + random = <&random>,"status"; ++ sd_overclock = <&sdhost>,"brcm,overclock-50:0"; ++ sd_force_pio = <&sdhost>,"brcm,force-pio?"; ++ sd_pio_limit = <&sdhost>,"brcm,pio-limit:0"; ++ sd_debug = <&sdhost>,"brcm,debug"; + }; + }; +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -112,6 +112,16 @@ Params: + random Set to "on" to enable the hardware random + number generator (default "on") + ++ sd_overclock Clock (in MHz) to use when the MMC framework ++ requests 50MHz ++ ++ sd_force_pio Disable DMA support for SD driver (default off) ++ ++ sd_pio_limit Number of blocks above which to use DMA for ++ SD card (default 1) ++ ++ sd_debug Enable debug output from SD driver (default off) ++ + uart0 Set to "off" to disable uart0 (default "on") + + watchdog Set to "on" to enable the hardware watchdog +@@ -443,7 +453,6 @@ Info: Selects the bcm2835-mmc SD/MMC d + Load: dtoverlay=mmc,= + Params: overclock_50 Clock (in MHz) to use when the MMC framework + requests 50MHz +- force_pio Disable DMA support + + + Name: mz61581 +--- a/arch/arm/boot/dts/overlays/mmc-overlay.dts ++++ b/arch/arm/boot/dts/overlays/mmc-overlay.dts +@@ -34,6 +34,5 @@ + + __overrides__ { + overclock_50 = <&frag0>,"brcm,overclock-50:0"; +- force_pio = <&frag0>,"brcm,force-pio?"; + }; + }; +--- a/arch/arm/boot/dts/overlays/sdhost-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sdhost-overlay.dts +@@ -1,19 +1,14 @@ + /dts-v1/; + /plugin/; + ++/* Provide backwards compatible aliases for the old sdhost dtparams. */ ++ + /{ + compatible = "brcm,bcm2708"; + + fragment@0 { +- target = <&mmc>; +- __overlay__ { +- status = "disabled"; +- }; +- }; +- +- fragment@1 { + target = <&sdhost>; +- frag1: __overlay__ { ++ frag0: __overlay__ { + brcm,overclock-50 = <0>; + brcm,pio-limit = <1>; + brcm,debug-flags = <0>; +@@ -21,11 +16,17 @@ + }; + }; + ++ fragment@1 { ++ target = <&mmc>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ + __overrides__ { +- overclock_50 = <&frag1>,"brcm,overclock-50:0"; +- force_pio = <&frag1>,"brcm,force-pio?"; +- pio_limit = <&frag1>,"brcm,pio-limit:0"; +- debug = <&frag1>,"brcm,debug?"; +- debug_flags = <&frag1>,"brcm,debug-flags:0"; ++ overclock_50 = <&frag0>,"brcm,overclock-50:0"; ++ force_pio = <&frag0>,"brcm,force-pio?"; ++ pio_limit = <&frag0>,"brcm,pio-limit:0"; ++ debug = <&frag0>,"brcm,debug?"; + }; + }; +--- a/arch/arm/boot/dts/overlays/sdtweak-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sdtweak-overlay.dts +@@ -1,23 +1,23 @@ + /dts-v1/; + /plugin/; + ++/* Provide backwards compatible aliases for the old sdhost dtparams. */ ++ + /{ + compatible = "brcm,bcm2708"; + + fragment@0 { + target = <&sdhost>; +- frag1: __overlay__ { ++ frag0: __overlay__ { + brcm,overclock-50 = <0>; + brcm,pio-limit = <1>; +- brcm,debug-flags = <0>; + }; + }; + + __overrides__ { +- overclock_50 = <&frag1>,"brcm,overclock-50:0"; +- force_pio = <&frag1>,"brcm,force-pio?"; +- pio_limit = <&frag1>,"brcm,pio-limit:0"; +- debug = <&frag1>,"brcm,debug?"; +- debug_flags = <&frag1>,"brcm,debug-flags:0"; ++ overclock_50 = <&frag0>,"brcm,overclock-50:0"; ++ force_pio = <&frag0>,"brcm,force-pio?"; ++ pio_limit = <&frag0>,"brcm,pio-limit:0"; ++ debug = <&frag0>,"brcm,debug?"; + }; + }; diff --git a/target/linux/brcm2708/patches-4.4/0145-BCM270X_DT-Adjust-overlay-README-formatting.patch b/target/linux/brcm2708/patches-4.4/0145-BCM270X_DT-Adjust-overlay-README-formatting.patch deleted file mode 100644 index 24084e5f48..0000000000 --- a/target/linux/brcm2708/patches-4.4/0145-BCM270X_DT-Adjust-overlay-README-formatting.patch +++ /dev/null @@ -1,746 +0,0 @@ -From 14ba431031afa218c0db43a7c20fe54916f8d357 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 8 Feb 2016 09:46:33 +0000 -Subject: [PATCH 145/170] BCM270X_DT: Adjust overlay README formatting - ---- - arch/arm/boot/dts/overlays/README | 420 +++++++++++++++++++------------------- - 1 file changed, 210 insertions(+), 210 deletions(-) - ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -83,58 +83,58 @@ Name: - Info: Configures the base Raspberry Pi hardware - Load: - Params: -- audio Set to "on" to enable the onboard ALSA audio -- interface (default "off") -+ audio Set to "on" to enable the onboard ALSA audio -+ interface (default "off") - -- i2c_arm Set to "on" to enable the ARM's i2c interface -- (default "off") -+ i2c_arm Set to "on" to enable the ARM's i2c interface -+ (default "off") - -- i2c_vc Set to "on" to enable the i2c interface -- usually reserved for the VideoCore processor -- (default "off") -+ i2c_vc Set to "on" to enable the i2c interface -+ usually reserved for the VideoCore processor -+ (default "off") - -- i2c An alias for i2c_arm -+ i2c An alias for i2c_arm - -- i2c_arm_baudrate Set the baudrate of the ARM's i2c interface -- (default "100000") -+ i2c_arm_baudrate Set the baudrate of the ARM's i2c interface -+ (default "100000") - -- i2c_vc_baudrate Set the baudrate of the VideoCore i2c interface -- (default "100000") -+ i2c_vc_baudrate Set the baudrate of the VideoCore i2c interface -+ (default "100000") - -- i2c_baudrate An alias for i2c_arm_baudrate -+ i2c_baudrate An alias for i2c_arm_baudrate - -- i2s Set to "on" to enable the i2s interface -- (default "off") -+ i2s Set to "on" to enable the i2s interface -+ (default "off") - -- spi Set to "on" to enable the spi interfaces -- (default "off") -+ spi Set to "on" to enable the spi interfaces -+ (default "off") - -- random Set to "on" to enable the hardware random -- number generator (default "on") -+ random Set to "on" to enable the hardware random -+ number generator (default "on") - -- uart0 Set to "off" to disable uart0 (default "on") -+ uart0 Set to "off" to disable uart0 (default "on") - -- watchdog Set to "on" to enable the hardware watchdog -- (default "off") -+ watchdog Set to "on" to enable the hardware watchdog -+ (default "off") - -- act_led_trigger Choose which activity the LED tracks. -- Use "heartbeat" for a nice load indicator. -- (default "mmc") -+ act_led_trigger Choose which activity the LED tracks. -+ Use "heartbeat" for a nice load indicator. -+ (default "mmc") - -- act_led_activelow Set to "on" to invert the sense of the LED -- (default "off") -+ act_led_activelow Set to "on" to invert the sense of the LED -+ (default "off") - -- act_led_gpio Set which GPIO to use for the activity LED -- (in case you want to connect it to an external -- device) -- (default "16" on a non-Plus board, "47" on a -- Plus or Pi 2) -+ act_led_gpio Set which GPIO to use for the activity LED -+ (in case you want to connect it to an external -+ device) -+ (default "16" on a non-Plus board, "47" on a -+ Plus or Pi 2) - - pwr_led_trigger - pwr_led_activelow - pwr_led_gpio -- As for act_led_*, but using the PWR LED. -- Not available on Model A/B boards. -+ As for act_led_*, but using the PWR LED. -+ Not available on Model A/B boards. - - N.B. It is recommended to only enable those interfaces that are needed. - Leaving all interfaces enabled can lead to unwanted behaviour (i2c_vc -@@ -149,19 +149,19 @@ Params: - Name: ads7846 - Info: ADS7846 Touch controller - Load: dtoverlay=ads7846,= --Params: cs SPI bus Chip Select (default 1) -- speed SPI bus speed (default 2MHz, max 3.25MHz) -- penirq GPIO used for PENIRQ. REQUIRED -- penirq_pull Set GPIO pull (default 0=none, 2=pullup) -- swapxy Swap x and y axis -- xmin Minimum value on the X axis (default 0) -- ymin Minimum value on the Y axis (default 0) -- xmax Maximum value on the X axis (default 4095) -- ymax Maximum value on the Y axis (default 4095) -- pmin Minimum reported pressure value (default 0) -- pmax Maximum reported pressure value (default 65535) -- xohms Touchpanel sensitivity (X-plate resistance) -- (default 400) -+Params: cs SPI bus Chip Select (default 1) -+ speed SPI bus speed (default 2MHz, max 3.25MHz) -+ penirq GPIO used for PENIRQ. REQUIRED -+ penirq_pull Set GPIO pull (default 0=none, 2=pullup) -+ swapxy Swap x and y axis -+ xmin Minimum value on the X axis (default 0) -+ ymin Minimum value on the Y axis (default 0) -+ xmax Maximum value on the X axis (default 4095) -+ ymax Maximum value on the Y axis (default 4095) -+ pmin Minimum reported pressure value (default 0) -+ pmax Maximum reported pressure value (default 65535) -+ xohms Touchpanel sensitivity (X-plate resistance) -+ (default 400) - - penirq is required and usually xohms (60-100) has to be set as well. - Apart from that, pmax (255) and swapxy are also common. -@@ -175,12 +175,12 @@ Name: at86rf233 - Info: Configures the Atmel AT86RF233 802.15.4 low-power WPAN transceiver, - connected to spi0.0 - Load: dtoverlay=at86rf233,= --Params: interrupt GPIO used for INT (default 23) -- reset GPIO used for Reset (default 24) -- sleep GPIO used for Sleep (default 25) -- speed SPI bus speed in Hz (default 6000000) -- trim Fine tuning of the internal capacitance -- arrays (0=+0pF, 15=+4.5pF, default 15) -+Params: interrupt GPIO used for INT (default 23) -+ reset GPIO used for Reset (default 24) -+ sleep GPIO used for Sleep (default 25) -+ speed SPI bus speed in Hz (default 6000000) -+ trim Fine tuning of the internal capacitance -+ arrays (0=+0pF, 15=+4.5pF, default 15) - - - Name: bmp085_i2c-sensor -@@ -194,8 +194,8 @@ Name: dht11 - Info: Overlay for the DHT11/DHT21/DHT22 humidity/temperature sensors - Also sometimes found with the part number(s) AM230x. - Load: dtoverlay=dht11,= --Params: gpiopin GPIO connected to the sensor's DATA output. -- (default 4) -+Params: gpiopin GPIO connected to the sensor's DATA output. -+ (default 4) - - - Name: dwc-otg -@@ -208,15 +208,15 @@ Params: - Name: dwc2 - Info: Selects the dwc2 USB controller driver - Load: dtoverlay=dwc2,= --Params: dr_mode Dual role mode: "host", "peripheral" or "otg" -+Params: dr_mode Dual role mode: "host", "peripheral" or "otg" - -- g-rx-fifo-size Size of rx fifo size in gadget mode -+ g-rx-fifo-size Size of rx fifo size in gadget mode - -- g-np-tx-fifo-size Size of non-periodic tx fifo size in gadget -- mode -+ g-np-tx-fifo-size Size of non-periodic tx fifo size in gadget -+ mode - -- g-tx-fifo-size Size of periodic tx fifo per endpoint -- (except ep0) in gadget mode -+ g-tx-fifo-size Size of periodic tx fifo per endpoint -+ (except ep0) in gadget mode - - - [ The ds1307-rtc overlay has been deleted. See i2c-rtc. ] -@@ -225,9 +225,9 @@ Params: dr_mode Dual ro - Name: enc28j60 - Info: Overlay for the Microchip ENC28J60 Ethernet Controller (SPI) - Load: dtoverlay=enc28j60,= --Params: int_pin GPIO used for INT (default 25) -+Params: int_pin GPIO used for INT (default 25) - -- speed SPI bus speed (default 12000000) -+ speed SPI bus speed (default 12000000) - - - Name: gpio-ir -@@ -237,26 +237,26 @@ Info: Use GPIO pin as rc-core style in - not required! The key mapping and other decoding parameters can be - configured by "ir-keytable" tool. - Load: dtoverlay=gpio-ir,= --Params: gpio_pin Input pin number. Default is 18. -+Params: gpio_pin Input pin number. Default is 18. - -- gpio_pull Desired pull-up/down state (off, down, up) -- Default is "down". -+ gpio_pull Desired pull-up/down state (off, down, up) -+ Default is "down". - -- rc-map-name Default rc keymap (can also be changed by -- ir-keytable), defaults to "rc-rc6-mce" -+ rc-map-name Default rc keymap (can also be changed by -+ ir-keytable), defaults to "rc-rc6-mce" - - - Name: gpio-poweroff - Info: Drives a GPIO high or low on reboot - Load: dtoverlay=gpio-poweroff,= --Params: gpiopin GPIO for signalling (default 26) -+Params: gpiopin GPIO for signalling (default 26) - -- active_low Set if the power control device requires a -- high->low transition to trigger a power-down. -- Note that this will require the support of a -- custom dt-blob.bin to prevent a power-down -- during the boot process, and that a reboot -- will also cause the pin to go low. -+ active_low Set if the power control device requires a -+ high->low transition to trigger a power-down. -+ Note that this will require the support of a -+ custom dt-blob.bin to prevent a power-down -+ during the boot process, and that a reboot -+ will also cause the pin to go low. - - - Name: hifiberry-amp -@@ -300,65 +300,65 @@ Name: hy28a - Info: HY28A - 2.8" TFT LCD Display Module by HAOYU Electronics - Default values match Texy's display shield - Load: dtoverlay=hy28a,= --Params: speed Display SPI bus speed -+Params: speed Display SPI bus speed - -- rotate Display rotation {0,90,180,270} -+ rotate Display rotation {0,90,180,270} - -- fps Delay between frame updates -+ fps Delay between frame updates - -- debug Debug output level {0-7} -+ debug Debug output level {0-7} - -- xohms Touchpanel sensitivity (X-plate resistance) -+ xohms Touchpanel sensitivity (X-plate resistance) - -- resetgpio GPIO used to reset controller -+ resetgpio GPIO used to reset controller - -- ledgpio GPIO used to control backlight -+ ledgpio GPIO used to control backlight - - - Name: hy28b - Info: HY28B - 2.8" TFT LCD Display Module by HAOYU Electronics - Default values match Texy's display shield - Load: dtoverlay=hy28b,= --Params: speed Display SPI bus speed -+Params: speed Display SPI bus speed - -- rotate Display rotation {0,90,180,270} -+ rotate Display rotation {0,90,180,270} - -- fps Delay between frame updates -+ fps Delay between frame updates - -- debug Debug output level {0-7} -+ debug Debug output level {0-7} - -- xohms Touchpanel sensitivity (X-plate resistance) -+ xohms Touchpanel sensitivity (X-plate resistance) - -- resetgpio GPIO used to reset controller -+ resetgpio GPIO used to reset controller - -- ledgpio GPIO used to control backlight -+ ledgpio GPIO used to control backlight - - - Name: i2c-gpio - Info: Adds support for software i2c controller on gpio pins - Load: dtoverlay=i2c-gpio,= --Params: i2c_gpio_sda GPIO used for I2C data (default "23") -+Params: i2c_gpio_sda GPIO used for I2C data (default "23") - -- i2c_gpio_scl GPIO used for I2C clock (default "24") -+ i2c_gpio_scl GPIO used for I2C clock (default "24") - -- i2c_gpio_delay_us Clock delay in microseconds -- (default "2" = ~100kHz) -+ i2c_gpio_delay_us Clock delay in microseconds -+ (default "2" = ~100kHz) - - - Name: i2c-rtc - Info: Adds support for a number of I2C Real Time Clock devices - Load: dtoverlay=i2c-rtc,= --Params: ds1307 Select the DS1307 device -+Params: ds1307 Select the DS1307 device - -- ds3231 Select the DS3231 device -+ ds3231 Select the DS3231 device - -- mcp7941x Select the MCP7941x device -+ mcp7941x Select the MCP7941x device - -- pcf2127 Select the PCF2127 device -+ pcf2127 Select the PCF2127 device - -- pcf8523 Select the PCF8523 device -+ pcf8523 Select the PCF8523 device - -- pcf8563 Select the PCF8563 device -+ pcf8563 Select the PCF8563 device - - - Name: i2s-mmap -@@ -396,70 +396,70 @@ Name: lirc-rpi - Info: Configures lirc-rpi (Linux Infrared Remote Control for Raspberry Pi) - Consult the module documentation for more details. - Load: dtoverlay=lirc-rpi,= --Params: gpio_out_pin GPIO for output (default "17") -+Params: gpio_out_pin GPIO for output (default "17") - -- gpio_in_pin GPIO for input (default "18") -+ gpio_in_pin GPIO for input (default "18") - -- gpio_in_pull Pull up/down/off on the input pin -- (default "down") -+ gpio_in_pull Pull up/down/off on the input pin -+ (default "down") - -- sense Override the IR receive auto-detection logic: -- "0" = force active-high -- "1" = force active-low -- "-1" = use auto-detection -- (default "-1") -+ sense Override the IR receive auto-detection logic: -+ "0" = force active-high -+ "1" = force active-low -+ "-1" = use auto-detection -+ (default "-1") - -- softcarrier Turn the software carrier "on" or "off" -- (default "on") -+ softcarrier Turn the software carrier "on" or "off" -+ (default "on") - -- invert "on" = invert the output pin (default "off") -+ invert "on" = invert the output pin (default "off") - -- debug "on" = enable additional debug messages -- (default "off") -+ debug "on" = enable additional debug messages -+ (default "off") - - - Name: mcp2515-can0 - Info: Configures the MCP2515 CAN controller on spi0.0 - Load: dtoverlay=mcp2515-can0,= --Params: oscillator Clock frequency for the CAN controller (Hz) -+Params: oscillator Clock frequency for the CAN controller (Hz) - -- spimaxfrequency Maximum SPI frequence (Hz) -+ spimaxfrequency Maximum SPI frequence (Hz) - -- interrupt GPIO for interrupt signal -+ interrupt GPIO for interrupt signal - - - Name: mcp2515-can1 - Info: Configures the MCP2515 CAN controller on spi0.1 - Load: dtoverlay=mcp2515-can1,= --Params: oscillator Clock frequency for the CAN controller (Hz) -+Params: oscillator Clock frequency for the CAN controller (Hz) - -- spimaxfrequency Maximum SPI frequence (Hz) -+ spimaxfrequency Maximum SPI frequence (Hz) - -- interrupt GPIO for interrupt signal -+ interrupt GPIO for interrupt signal - - - Name: mmc - Info: Selects the bcm2835-mmc SD/MMC driver, optionally with overclock - Load: dtoverlay=mmc,= --Params: overclock_50 Clock (in MHz) to use when the MMC framework -- requests 50MHz -- force_pio Disable DMA support -+Params: overclock_50 Clock (in MHz) to use when the MMC framework -+ requests 50MHz -+ force_pio Disable DMA support - - - Name: mz61581 - Info: MZ61581 display by Tontec - Load: dtoverlay=mz61581,= --Params: speed Display SPI bus speed -+Params: speed Display SPI bus speed - -- rotate Display rotation {0,90,180,270} -+ rotate Display rotation {0,90,180,270} - -- fps Delay between frame updates -+ fps Delay between frame updates - -- txbuflen Transmit buffer length (default 32768) -+ txbuflen Transmit buffer length (default 32768) - -- debug Debug output level {0-7} -+ debug Debug output level {0-7} - -- xohms Touchpanel sensitivity (X-plate resistance) -+ xohms Touchpanel sensitivity (X-plate resistance) - - - [ The pcf2127-rtc overlay has been deleted. See i2c-rtc. ] -@@ -474,69 +474,69 @@ Params: speed Display - Name: piscreen - Info: PiScreen display by OzzMaker.com - Load: dtoverlay=piscreen,= --Params: speed Display SPI bus speed -+Params: speed Display SPI bus speed - -- rotate Display rotation {0,90,180,270} -+ rotate Display rotation {0,90,180,270} - -- fps Delay between frame updates -+ fps Delay between frame updates - -- debug Debug output level {0-7} -+ debug Debug output level {0-7} - -- xohms Touchpanel sensitivity (X-plate resistance) -+ xohms Touchpanel sensitivity (X-plate resistance) - - - Name: piscreen2r - Info: PiScreen 2 with resistive TP display by OzzMaker.com - Load: dtoverlay=piscreen2r,= --Params: speed Display SPI bus speed -+Params: speed Display SPI bus speed - -- rotate Display rotation {0,90,180,270} -+ rotate Display rotation {0,90,180,270} - -- fps Delay between frame updates -+ fps Delay between frame updates - -- debug Debug output level {0-7} -+ debug Debug output level {0-7} - -- xohms Touchpanel sensitivity (X-plate resistance) -+ xohms Touchpanel sensitivity (X-plate resistance) - - - Name: pitft28-capacitive - Info: Adafruit PiTFT 2.8" capacitive touch screen - Load: dtoverlay=pitft28-capacitive,= --Params: speed Display SPI bus speed -+Params: speed Display SPI bus speed - -- rotate Display rotation {0,90,180,270} -+ rotate Display rotation {0,90,180,270} - -- fps Delay between frame updates -+ fps Delay between frame updates - -- debug Debug output level {0-7} -+ debug Debug output level {0-7} - -- touch-sizex Touchscreen size x (default 240) -+ touch-sizex Touchscreen size x (default 240) - -- touch-sizey Touchscreen size y (default 320) -+ touch-sizey Touchscreen size y (default 320) - -- touch-invx Touchscreen inverted x axis -+ touch-invx Touchscreen inverted x axis - -- touch-invy Touchscreen inverted y axis -+ touch-invy Touchscreen inverted y axis - -- touch-swapxy Touchscreen swapped x y axis -+ touch-swapxy Touchscreen swapped x y axis - - - Name: pitft28-resistive - Info: Adafruit PiTFT 2.8" resistive touch screen - Load: dtoverlay=pitft28-resistive,= --Params: speed Display SPI bus speed -+Params: speed Display SPI bus speed - -- rotate Display rotation {0,90,180,270} -+ rotate Display rotation {0,90,180,270} - -- fps Delay between frame updates -+ fps Delay between frame updates - -- debug Debug output level {0-7} -+ debug Debug output level {0-7} - - - Name: pps-gpio - Info: Configures the pps-gpio (pulse-per-second time signal via GPIO). - Load: dtoverlay=pps-gpio,= --Params: gpiopin Input GPIO (default "18") -+Params: gpiopin Input GPIO (default "18") - - - Name: pwm -@@ -550,12 +550,12 @@ Info: Configures a single PWM channel - Pins 12 and 13 might be better choices on an A+, B+ or Pi2. - 2) The onboard analogue audio output uses both PWM channels. - 3) So be careful mixing audio and PWM. -- 4) Currently the clock must have been enabled and configured -+ 4) Currently the clockmust have been enabled and configured - by other means. - Load: dtoverlay=pwm,= --Params: pin Output pin (default 18) - see table -- func Pin function (default 2 = Alt5) - see above -- clock PWM clock frequency (informational) -+Params: pin Output pin (default 18) - see table -+ func Pin function (default 2 = Alt5) - see above -+ clock PWM clock frequency (informational) - - - Name: pwm-2chan -@@ -569,14 +569,14 @@ Info: Configures both PWM channels - Pins 12 and 13 might be better choices on an A+, B+ or Pi2. - 2) The onboard analogue audio output uses both PWM channels. - 3) So be careful mixing audio and PWM. -- 4) Currently the clock must have been enabled and configured -+ 4) Currently the clockmust have been enabled and configured - by other means. - Load: dtoverlay=pwm-2chan,= --Params: pin Output pin (default 18) - see table -- pin2 Output pin for other channel (default 19) -- func Pin function (default 2 = Alt5) - see above -- func2 Function for pin2 (default 2 = Alt5) -- clock PWM clock frequency (informational) -+Params: pin Output pin (default 18) - see table -+ pin2 Output pin for other channel (default 19) -+ func Pin function (default 2 = Alt5) - see above -+ func2 Function for pin2 (default 2 = Alt5) -+ clock PWM clock frequency (informational) - - - Name: raspidac3 -@@ -600,15 +600,15 @@ Params: - Name: rpi-display - Info: RPi-Display - 2.8" Touch Display by Watterott - Load: dtoverlay=rpi-display,= --Params: speed Display SPI bus speed -+Params: speed Display SPI bus speed - -- rotate Display rotation {0,90,180,270} -+ rotate Display rotation {0,90,180,270} - -- fps Delay between frame updates -+ fps Delay between frame updates - -- debug Debug output level {0-7} -+ debug Debug output level {0-7} - -- xohms Touchpanel sensitivity (X-plate resistance) -+ xohms Touchpanel sensitivity (X-plate resistance) - - - Name: rpi-ft5406 -@@ -632,52 +632,52 @@ Params: - Name: sdhost - Info: Selects the bcm2835-sdhost SD/MMC driver, optionally with overclock - Load: dtoverlay=sdhost,= --Params: overclock_50 Clock (in MHz) to use when the MMC framework -- requests 50MHz -+Params: overclock_50 Clock (in MHz) to use when the MMC framework -+ requests 50MHz - -- force_pio Disable DMA support (default off) -+ force_pio Disable DMA support (default off) - -- pio_limit Number of blocks above which to use DMA -- (default 1) -+ pio_limit Number of blocks above which to use DMA -+ (default 1) - -- debug Enable debug output (default off) -+ debug Enable debug output (default off) - - - Name: sdio - Info: Selects the bcm2835-sdhost SD/MMC driver, optionally with overclock, - and enables SDIO via GPIOs 22-27. - Load: dtoverlay=sdio,= --Params: overclock_50 SD Clock (in MHz) to use when the MMC framework -- requests 50MHz -+Params: overclock_50 SD Clock (in MHz) to use when the MMC framework -+ requests 50MHz - -- sdio_overclock SDIO Clock (in MHz) to use when the MMC -- framework requests 50MHz -+ sdio_overclock SDIO Clock (in MHz) to use when the MMC -+ framework requests 50MHz - -- force_pio Disable DMA support (default off) -+ force_pio Disable DMA support (default off) - -- pio_limit Number of blocks above which to use DMA -- (default 1) -+ pio_limit Number of blocks above which to use DMA -+ (default 1) - -- debug Enable debug output (default off) -+ debug Enable debug output (default off) - -- poll_once Disable SDIO-device polling every second -- (default on: polling once at boot-time) -+ poll_once Disable SDIO-device polling every second -+ (default on: polling once at boot-time) - -- bus_width Set the SDIO host bus width (default 4 bits) -+ bus_width Set the SDIO host bus width (default 4 bits) - - - Name: sdtweak - Info: Tunes the bcm2835-sdhost SD/MMC driver - Load: dtoverlay=sdtweak,= --Params: overclock_50 Clock (in MHz) to use when the MMC framework -- requests 50MHz -+Params: overclock_50 Clock (in MHz) to use when the MMC framework -+ requests 50MHz - -- force_pio Disable DMA support (default off) -+ force_pio Disable DMA support (default off) - -- pio_limit Number of blocks above which to use DMA -- (default 1) -+ pio_limit Number of blocks above which to use DMA -+ (default 1) - -- debug Enable debug output (default off) -+ debug Enable debug output (default off) - - - Name: smi -@@ -708,25 +708,25 @@ Name: tinylcd35 - Info: 3.5" Color TFT Display by www.tinylcd.com - Options: Touch, RTC, keypad - Load: dtoverlay=tinylcd35,= --Params: speed Display SPI bus speed -+Params: speed Display SPI bus speed - -- rotate Display rotation {0,90,180,270} -+ rotate Display rotation {0,90,180,270} - -- fps Delay between frame updates -+ fps Delay between frame updates - -- debug Debug output level {0-7} -+ debug Debug output level {0-7} - -- touch Enable touch panel -+ touch Enable touch panel - -- touchgpio Touch controller IRQ GPIO -+ touchgpio Touch controller IRQ GPIO - -- xohms Touchpanel: Resistance of X-plate in ohms -+ xohms Touchpanel: Resistance of X-plate in ohms - -- rtc-pcf PCF8563 Real Time Clock -+ rtc-pcf PCF8563 Real Time Clock - -- rtc-ds DS1307 Real Time Clock -+ rtc-ds DS1307 Real Time Clock - -- keypad Enable keypad -+ keypad Enable keypad - - Examples: - Display with touchpanel, PCF8563 RTC and keypad: -@@ -738,9 +738,9 @@ Params: speed Display - Name: uart1 - Info: Enable uart1 in place of uart0 - Load: dtoverlay=uart1,= --Params: txd1_pin GPIO pin for TXD1 (14, 32 or 40 - default 14) -+Params: txd1_pin GPIO pin for TXD1 (14, 32 or 40 - default 14) - -- rxd1_pin GPIO pin for RXD1 (15, 33 or 41 - default 15) -+ rxd1_pin GPIO pin for RXD1 (15, 33 or 41 - default 15) - - - Name: vc4-kms-v3d -@@ -753,7 +753,7 @@ Params: - - Name: vga666 - Info: Overlay for the Fen Logic VGA666 board -- This uses GPIOs 2-21 (so no I2C), and activates the output 2-3 seconds -+ This uses GPIOs 2-21 (sono I2C), and activates the output 2-3 seconds - after the kernel has started. - Load: dtoverlay=vga666 - Params: -@@ -763,22 +763,22 @@ Name: w1-gpio - Info: Configures the w1-gpio Onewire interface module. - Use this overlay if you *don't* need a GPIO to drive an external pullup. - Load: dtoverlay=w1-gpio,= --Params: gpiopin GPIO for I/O (default "4") -+Params: gpiopin GPIO for I/O (default "4") - -- pullup Non-zero, "on", or "y" to enable the parasitic -- power (2-wire, power-on-data) feature -+ pullup Non-zero, "on", or "y" to enable the parasitic -+ power (2-wire, power-on-data) feature - - - Name: w1-gpio-pullup - Info: Configures the w1-gpio Onewire interface module. - Use this overlay if you *do* need a GPIO to drive an external pullup. - Load: dtoverlay=w1-gpio-pullup,= --Params: gpiopin GPIO for I/O (default "4") -+Params: gpiopin GPIO for I/O (default "4") - -- pullup Non-zero, "on", or "y" to enable the parasitic -- power (2-wire, power-on-data) feature -+ pullup Non-zero, "on", or "y" to enable the parasitic -+ power (2-wire, power-on-data) feature - -- extpullup GPIO for external pullup (default "5") -+ extpullup GPIO for external pullup (default "5") - - - Troubleshooting diff --git a/target/linux/brcm2708/patches-4.4/0146-FIXUP-Overlay-README-Restore-spaces-deleted-in-error.patch b/target/linux/brcm2708/patches-4.4/0146-FIXUP-Overlay-README-Restore-spaces-deleted-in-error.patch deleted file mode 100644 index 70a9fc7718..0000000000 --- a/target/linux/brcm2708/patches-4.4/0146-FIXUP-Overlay-README-Restore-spaces-deleted-in-error.patch +++ /dev/null @@ -1,39 +0,0 @@ -From d32795d2be55748552ddacd9b1e47efc61264a98 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 8 Feb 2016 12:53:44 +0000 -Subject: [PATCH 146/170] FIXUP: Overlay README - Restore spaces deleted in - error - ---- - arch/arm/boot/dts/overlays/README | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -550,7 +550,7 @@ Info: Configures a single PWM channel - Pins 12 and 13 might be better choices on an A+, B+ or Pi2. - 2) The onboard analogue audio output uses both PWM channels. - 3) So be careful mixing audio and PWM. -- 4) Currently the clockmust have been enabled and configured -+ 4) Currently the clock must have been enabled and configured - by other means. - Load: dtoverlay=pwm,= - Params: pin Output pin (default 18) - see table -@@ -569,7 +569,7 @@ Info: Configures both PWM channels - Pins 12 and 13 might be better choices on an A+, B+ or Pi2. - 2) The onboard analogue audio output uses both PWM channels. - 3) So be careful mixing audio and PWM. -- 4) Currently the clockmust have been enabled and configured -+ 4) Currently the clock must have been enabled and configured - by other means. - Load: dtoverlay=pwm-2chan,= - Params: pin Output pin (default 18) - see table -@@ -753,7 +753,7 @@ Params: - - Name: vga666 - Info: Overlay for the Fen Logic VGA666 board -- This uses GPIOs 2-21 (sono I2C), and activates the output 2-3 seconds -+ This uses GPIOs 2-21 (so no I2C), and activates the output 2-3 seconds - after the kernel has started. - Load: dtoverlay=vga666 - Params: diff --git a/target/linux/brcm2708/patches-4.4/0146-dcw_otg-trim-xfer-length-when-buffer-larger-than-all.patch b/target/linux/brcm2708/patches-4.4/0146-dcw_otg-trim-xfer-length-when-buffer-larger-than-all.patch new file mode 100644 index 0000000000..519264bb0f --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0146-dcw_otg-trim-xfer-length-when-buffer-larger-than-all.patch @@ -0,0 +1,37 @@ +From 63ff907f6e6ab3b93535dc2b5251b7b41eaf7ec2 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Fri, 12 Feb 2016 14:50:25 +0000 +Subject: [PATCH 146/232] dcw_otg: trim xfer length when buffer larger than + allocated size is received + +--- + drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c ++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c +@@ -737,6 +737,11 @@ static int update_urb_state_xfer_comp(dw + DWC_OTG_HC_XFER_COMPLETE, + &short_read); + ++ if (urb->actual_length + xfer_length > urb->length) { ++ DWC_WARN("%s(): trimming xfer length\n", __func__); ++ xfer_length = urb->length - urb->actual_length; ++ } ++ + /* 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, +@@ -1423,6 +1428,12 @@ static void update_urb_state_xfer_intr(d + { + uint32_t bytes_transferred = get_actual_xfer_length(hc, hc_regs, qtd, + halt_status, NULL); ++ ++ if (urb->actual_length + bytes_transferred > urb->length) { ++ DWC_WARN("%s(): trimming xfer length\n", __func__); ++ bytes_transferred = urb->length - urb->actual_length; ++ } ++ + /* non DWORD-aligned buffer case handling. */ + if (hc->align_buff && bytes_transferred && hc->ep_is_in) { + dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf, diff --git a/target/linux/brcm2708/patches-4.4/0147-bcm2835-sdhost-Restore-ATOMIC-flag-to-PIO-sg-mapping.patch b/target/linux/brcm2708/patches-4.4/0147-bcm2835-sdhost-Restore-ATOMIC-flag-to-PIO-sg-mapping.patch new file mode 100644 index 0000000000..dd41138d1d --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0147-bcm2835-sdhost-Restore-ATOMIC-flag-to-PIO-sg-mapping.patch @@ -0,0 +1,32 @@ +From ebb44753d0310c6adb03a9b448a4038c3e37e6e0 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 15 Feb 2016 10:00:27 +0000 +Subject: [PATCH 147/232] bcm2835-sdhost: Restore ATOMIC flag to PIO sg mapping + +Allocation problems have been seen in a wireless driver, and +this is the only change which might have been responsible. +--- + drivers/mmc/host/bcm2835-sdhost.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +--- a/drivers/mmc/host/bcm2835-sdhost.c ++++ b/drivers/mmc/host/bcm2835-sdhost.c +@@ -874,15 +874,14 @@ static void bcm2835_sdhost_prepare_data( + host->flush_fifo = 0; + host->data->bytes_xfered = 0; + +- + if (!host->dma_desc) { + /* Use PIO */ +- int flags; ++ int flags = SG_MITER_ATOMIC; + + if (data->flags & MMC_DATA_READ) +- flags = SG_MITER_TO_SG; ++ flags |= SG_MITER_TO_SG; + else +- flags = SG_MITER_FROM_SG; ++ flags |= SG_MITER_FROM_SG; + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); + host->blocks = data->blocks; + } diff --git a/target/linux/brcm2708/patches-4.4/0148-Revert-BCM270X_DT-Disable-DMA-for-bcm2835-sdhost-on-.patch b/target/linux/brcm2708/patches-4.4/0148-Revert-BCM270X_DT-Disable-DMA-for-bcm2835-sdhost-on-.patch deleted file mode 100644 index e71eda57b4..0000000000 --- a/target/linux/brcm2708/patches-4.4/0148-Revert-BCM270X_DT-Disable-DMA-for-bcm2835-sdhost-on-.patch +++ /dev/null @@ -1,21 +0,0 @@ -From 14e35b8cca5bf1f6223ebc6cc4d527645076a3a3 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Thu, 11 Feb 2016 17:30:49 +0000 -Subject: [PATCH 148/170] Revert "BCM270X_DT: Disable DMA for bcm2835-sdhost on - Pi2" - -This reverts commit 9c1adcc07a40f762fd4ac580f07646784de135e1. ---- - arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 1 - - 1 file changed, 1 deletion(-) - ---- a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts -+++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts -@@ -43,7 +43,6 @@ - pinctrl-names = "default"; - pinctrl-0 = <&sdhost_pins>; - bus-width = <4>; -- brcm,pio-limit = <0x7fffffff>; - status = "okay"; - }; - diff --git a/target/linux/brcm2708/patches-4.4/0148-Updated-smsc95xx-driver-to-check-for-a-valid-MAC-add.patch b/target/linux/brcm2708/patches-4.4/0148-Updated-smsc95xx-driver-to-check-for-a-valid-MAC-add.patch new file mode 100644 index 0000000000..a87c94bc80 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0148-Updated-smsc95xx-driver-to-check-for-a-valid-MAC-add.patch @@ -0,0 +1,40 @@ +From 55e8519aa4c86b37f1933e8e53c6e97372d61871 Mon Sep 17 00:00:00 2001 +From: Craig Roberts +Date: Tue, 16 Feb 2016 10:03:42 +0000 +Subject: [PATCH 148/232] Updated smsc95xx driver to check for a valid MAC + address in eeprom before using smsc95xx.macaddr parameter passed on command + line. + +The built-in RPi adaptor will still get a MAC address based on the parameter passed on the command line as the RPi hardware does not have an eeprom, +however usb->ethernet adaptors using the same driver should have an eeprom with MAC address as part of their hardware and therefore will use this +meaning they don't end up with the same MAC address as the built-in RPi adaptor. +--- + drivers/net/usb/smsc95xx.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/drivers/net/usb/smsc95xx.c ++++ b/drivers/net/usb/smsc95xx.c +@@ -817,10 +817,6 @@ static int smsc95xx_is_macaddr_param(str + + 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) { +@@ -831,7 +827,11 @@ static void smsc95xx_init_mac_address(st + } + } + +- /* no eeprom, or eeprom values are invalid. generate random MAC */ ++ /* Check module parameters */ ++ if (smsc95xx_is_macaddr_param(dev, dev->net->dev_addr)) ++ return; ++ ++ /* no eeprom, or eeprom values are invalid, and no module parameter specified to set MAC. Generate random MAC */ + eth_hw_addr_random(dev->net); + netif_dbg(dev, ifup, dev->net, "MAC address set to eth_random_addr\n"); + } diff --git a/target/linux/brcm2708/patches-4.4/0149-bcm2835-sdhost-Major-revision.patch b/target/linux/brcm2708/patches-4.4/0149-bcm2835-sdhost-Major-revision.patch deleted file mode 100644 index c7772cbc0c..0000000000 --- a/target/linux/brcm2708/patches-4.4/0149-bcm2835-sdhost-Major-revision.patch +++ /dev/null @@ -1,2070 +0,0 @@ -From 5a117bd925d13a305d94eeb28919dedeaa9be17d Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Thu, 11 Feb 2016 16:51:01 +0000 -Subject: [PATCH 149/170] bcm2835-sdhost: Major revision - -This is a significant revision of the bcm2835-sdhost driver. It -improves on the original in a number of ways: - -1) Through the use of CMD23 for reads it appears to avoid problems - reading some sectors on certain high speed cards. -2) Better atomicity to prevent crashes. -3) Higher performance. -4) Activity logging included, for easier diagnosis in the event - of a problem. - -Signed-off-by: Phil Elwell ---- - drivers/mmc/host/bcm2835-sdhost.c | 1284 ++++++++++++++++++++----------------- - 1 file changed, 686 insertions(+), 598 deletions(-) - ---- a/drivers/mmc/host/bcm2835-sdhost.c -+++ b/drivers/mmc/host/bcm2835-sdhost.c -@@ -2,7 +2,7 @@ - * BCM2835 SD host driver. - * - * Author: Phil Elwell -- * Copyright 2015 -+ * Copyright (C) 2015-2016 Raspberry Pi (Trading) Ltd. - * - * Based on - * mmc-bcm2835.c by Gellert Weisz -@@ -24,12 +24,13 @@ - * along with this program. If not, see . - */ - --#define SAFE_READ_THRESHOLD 4 --#define SAFE_WRITE_THRESHOLD 4 --#define ALLOW_DMA 1 --#define ALLOW_CMD23 0 --#define ALLOW_FAST 1 --#define USE_BLOCK_IRQ 1 -+#define FIFO_READ_THRESHOLD 4 -+#define FIFO_WRITE_THRESHOLD 4 -+#define ALLOW_CMD23_READ 1 -+#define ALLOW_CMD23_WRITE 0 -+#define ENABLE_LOG 1 -+#define SDDATA_FIFO_PIO_BURST 8 -+#define CMD_DALLY_US 1 - - #include - #include -@@ -48,6 +49,7 @@ - #include - #include - #include -+#include - - #define DRIVER_NAME "sdhost-bcm2835" - -@@ -110,6 +112,28 @@ - #define SDEDM_READ_THRESHOLD_SHIFT 14 - #define SDEDM_THRESHOLD_MASK 0x1f - -+#define SDEDM_FSM_MASK 0xf -+#define SDEDM_FSM_IDENTMODE 0x0 -+#define SDEDM_FSM_DATAMODE 0x1 -+#define SDEDM_FSM_READDATA 0x2 -+#define SDEDM_FSM_WRITEDATA 0x3 -+#define SDEDM_FSM_READWAIT 0x4 -+#define SDEDM_FSM_READCRC 0x5 -+#define SDEDM_FSM_WRITECRC 0x6 -+#define SDEDM_FSM_WRITEWAIT1 0x7 -+#define SDEDM_FSM_POWERDOWN 0x8 -+#define SDEDM_FSM_POWERUP 0x9 -+#define SDEDM_FSM_WRITESTART1 0xa -+#define SDEDM_FSM_WRITESTART2 0xb -+#define SDEDM_FSM_GENPULSES 0xc -+#define SDEDM_FSM_WRITEWAIT2 0xd -+#define SDEDM_FSM_STARTPOWDOWN 0xf -+ -+#define SDDATA_FIFO_WORDS 16 -+ -+#define USE_CMD23_FLAGS ((ALLOW_CMD23_READ * MMC_DATA_READ) | \ -+ (ALLOW_CMD23_WRITE * MMC_DATA_WRITE)) -+ - #define MHZ 1000000 - - -@@ -131,15 +155,17 @@ struct bcm2835_host { - - struct tasklet_struct finish_tasklet; /* Tasklet structures */ - -- struct timer_list timer; /* Timer for timeouts */ -+ struct work_struct cmd_wait_wq; /* Workqueue function */ - -- struct timer_list pio_timer; /* PIO error detection timer */ -+ 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 cmd_quick_poll_retries; -+ u32 ns_per_fifo_word; - - /* cached registers */ - u32 hcfg; -@@ -154,16 +180,21 @@ struct bcm2835_host { - - unsigned int use_busy:1; /* Wait for busy interrupt */ - -- unsigned int debug:1; /* Enable debug output */ -+ unsigned int use_sbc:1; /* Send CMD23 */ - -- u32 thread_isr; -+ unsigned int debug:1; /* Enable debug output */ - - /*DMA part*/ - struct dma_chan *dma_chan_rx; /* DMA channel for reads */ - struct dma_chan *dma_chan_tx; /* DMA channel for writes */ -+ struct dma_chan *dma_chan; /* Channel in used */ -+ struct dma_async_tx_descriptor *dma_desc; -+ u32 dma_dir; -+ u32 drain_words; -+ struct page *drain_page; -+ u32 drain_offset; - - bool allow_dma; -- bool have_dma; - bool use_dma; - /*end of DMA part*/ - -@@ -173,13 +204,98 @@ struct bcm2835_host { - u32 overclock_50; /* frequency to use when 50MHz is requested (in MHz) */ - u32 overclock; /* Current frequency if overclocked, else zero */ - u32 pio_limit; /* Maximum block count for PIO (0 = always DMA) */ -+}; - -- u32 debug_flags; -+#if ENABLE_LOG - -- u32 sectors; /* Cached card size in sectors */ -- u32 single_read_sectors[8]; -+struct log_entry_struct { -+ char event[4]; -+ u32 timestamp; -+ u32 param1; -+ u32 param2; - }; - -+typedef struct log_entry_struct LOG_ENTRY_T; -+ -+LOG_ENTRY_T *sdhost_log_buf; -+dma_addr_t sdhost_log_addr; -+static u32 sdhost_log_idx; -+static spinlock_t log_lock; -+static void __iomem *timer_base; -+ -+#define LOG_ENTRIES (256*1) -+#define LOG_SIZE (sizeof(LOG_ENTRY_T)*LOG_ENTRIES) -+ -+static void log_init(u32 bus_to_phys) -+{ -+ spin_lock_init(&log_lock); -+ sdhost_log_buf = dma_zalloc_coherent(NULL, LOG_SIZE, &sdhost_log_addr, -+ GFP_KERNEL); -+ if (sdhost_log_buf) { -+ pr_err("sdhost: log_buf @ %p (%x)\n", -+ sdhost_log_buf, sdhost_log_addr); -+ timer_base = ioremap_nocache(bus_to_phys + 0x7e003000, SZ_4K); -+ if (!timer_base) -+ pr_err("sdhost: failed to remap timer\n"); -+ } -+ else -+ pr_err("sdhost: failed to allocate log buf\n"); -+} -+ -+static void log_event_impl(const char *event, u32 param1, u32 param2) -+{ -+ if (sdhost_log_buf) { -+ LOG_ENTRY_T *entry; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&log_lock, flags); -+ -+ entry = sdhost_log_buf + sdhost_log_idx; -+ memcpy(entry->event, event, 4); -+ entry->timestamp = (readl(timer_base + 4) & 0x3fffffff) + -+ (smp_processor_id()<<30); -+ entry->param1 = param1; -+ entry->param2 = param2; -+ sdhost_log_idx = (sdhost_log_idx + 1) % LOG_ENTRIES; -+ -+ spin_unlock_irqrestore(&log_lock, flags); -+ } -+} -+ -+static void log_dump(void) -+{ -+ if (sdhost_log_buf) { -+ LOG_ENTRY_T *entry; -+ unsigned long flags; -+ int idx; -+ -+ spin_lock_irqsave(&log_lock, flags); -+ -+ idx = sdhost_log_idx; -+ do { -+ entry = sdhost_log_buf + idx; -+ if (entry->event[0] != '\0') -+ pr_err("[%08x] %.4s %x %x\n", -+ entry->timestamp, -+ entry->event, -+ entry->param1, -+ entry->param2); -+ idx = (idx + 1) % LOG_ENTRIES; -+ } while (idx != sdhost_log_idx); -+ -+ spin_unlock_irqrestore(&log_lock, flags); -+ } -+} -+ -+#define log_event(event, param1, param2) log_event_impl(event, param1, param2) -+ -+#else -+ -+#define log_init(x) (void)0 -+#define log_event(event, param1, param2) (void)0 -+#define log_dump() (void)0 -+ -+#endif - - static inline void bcm2835_sdhost_write(struct bcm2835_host *host, u32 val, int reg) - { -@@ -201,7 +317,7 @@ static void bcm2835_sdhost_dumpcmd(struc - const char *label) - { - if (cmd) -- pr_info("%s:%c%s op %d arg 0x%x flags 0x%x - resp %08x %08x %08x %08x, err %d\n", -+ pr_err("%s:%c%s op %d arg 0x%x flags 0x%x - resp %08x %08x %08x %08x, err %d\n", - mmc_hostname(host->mmc), - (cmd == host->cmd) ? '>' : ' ', - label, cmd->opcode, cmd->arg, cmd->flags, -@@ -211,73 +327,74 @@ static void bcm2835_sdhost_dumpcmd(struc - - static void bcm2835_sdhost_dumpregs(struct bcm2835_host *host) - { -- bcm2835_sdhost_dumpcmd(host, host->mrq->sbc, "sbc"); -- bcm2835_sdhost_dumpcmd(host, host->mrq->cmd, "cmd"); -- if (host->mrq->data) -- pr_err("%s: data blocks %x blksz %x - err %d\n", -- mmc_hostname(host->mmc), -- host->mrq->data->blocks, -- host->mrq->data->blksz, -- host->mrq->data->error); -- bcm2835_sdhost_dumpcmd(host, host->mrq->stop, "stop"); -+ if (host->mrq) -+ { -+ bcm2835_sdhost_dumpcmd(host, host->mrq->sbc, "sbc"); -+ bcm2835_sdhost_dumpcmd(host, host->mrq->cmd, "cmd"); -+ if (host->mrq->data) -+ pr_err("%s: data blocks %x blksz %x - err %d\n", -+ mmc_hostname(host->mmc), -+ host->mrq->data->blocks, -+ host->mrq->data->blksz, -+ host->mrq->data->error); -+ bcm2835_sdhost_dumpcmd(host, host->mrq->stop, "stop"); -+ } - -- pr_info("%s: =========== REGISTER DUMP ===========\n", -+ pr_err("%s: =========== REGISTER DUMP ===========\n", - mmc_hostname(host->mmc)); - -- pr_info("%s: SDCMD 0x%08x\n", -+ pr_err("%s: SDCMD 0x%08x\n", - mmc_hostname(host->mmc), - bcm2835_sdhost_read(host, SDCMD)); -- pr_info("%s: SDARG 0x%08x\n", -+ pr_err("%s: SDARG 0x%08x\n", - mmc_hostname(host->mmc), - bcm2835_sdhost_read(host, SDARG)); -- pr_info("%s: SDTOUT 0x%08x\n", -+ pr_err("%s: SDTOUT 0x%08x\n", - mmc_hostname(host->mmc), - bcm2835_sdhost_read(host, SDTOUT)); -- pr_info("%s: SDCDIV 0x%08x\n", -+ pr_err("%s: SDCDIV 0x%08x\n", - mmc_hostname(host->mmc), - bcm2835_sdhost_read(host, SDCDIV)); -- pr_info("%s: SDRSP0 0x%08x\n", -+ pr_err("%s: SDRSP0 0x%08x\n", - mmc_hostname(host->mmc), - bcm2835_sdhost_read(host, SDRSP0)); -- pr_info("%s: SDRSP1 0x%08x\n", -+ pr_err("%s: SDRSP1 0x%08x\n", - mmc_hostname(host->mmc), - bcm2835_sdhost_read(host, SDRSP1)); -- pr_info("%s: SDRSP2 0x%08x\n", -+ pr_err("%s: SDRSP2 0x%08x\n", - mmc_hostname(host->mmc), - bcm2835_sdhost_read(host, SDRSP2)); -- pr_info("%s: SDRSP3 0x%08x\n", -+ pr_err("%s: SDRSP3 0x%08x\n", - mmc_hostname(host->mmc), - bcm2835_sdhost_read(host, SDRSP3)); -- pr_info("%s: SDHSTS 0x%08x\n", -+ pr_err("%s: SDHSTS 0x%08x\n", - mmc_hostname(host->mmc), - bcm2835_sdhost_read(host, SDHSTS)); -- pr_info("%s: SDVDD 0x%08x\n", -+ pr_err("%s: SDVDD 0x%08x\n", - mmc_hostname(host->mmc), - bcm2835_sdhost_read(host, SDVDD)); -- pr_info("%s: SDEDM 0x%08x\n", -+ pr_err("%s: SDEDM 0x%08x\n", - mmc_hostname(host->mmc), - bcm2835_sdhost_read(host, SDEDM)); -- pr_info("%s: SDHCFG 0x%08x\n", -+ pr_err("%s: SDHCFG 0x%08x\n", - mmc_hostname(host->mmc), - bcm2835_sdhost_read(host, SDHCFG)); -- pr_info("%s: SDHBCT 0x%08x\n", -+ pr_err("%s: SDHBCT 0x%08x\n", - mmc_hostname(host->mmc), - bcm2835_sdhost_read(host, SDHBCT)); -- pr_info("%s: SDHBLC 0x%08x\n", -+ pr_err("%s: SDHBLC 0x%08x\n", - mmc_hostname(host->mmc), - bcm2835_sdhost_read(host, SDHBLC)); - -- pr_info("%s: ===========================================\n", -+ pr_err("%s: ===========================================\n", - mmc_hostname(host->mmc)); - } - -- - static void bcm2835_sdhost_set_power(struct bcm2835_host *host, bool on) - { - bcm2835_sdhost_write(host, on ? 1 : 0, SDVDD); - } - -- - static void bcm2835_sdhost_reset_internal(struct bcm2835_host *host) - { - u32 temp; -@@ -300,26 +417,24 @@ static void bcm2835_sdhost_reset_interna - temp = bcm2835_sdhost_read(host, SDEDM); - temp &= ~((SDEDM_THRESHOLD_MASK<clock = 0; -- host->sectors = 0; -- host->single_read_sectors[0] = ~0; - bcm2835_sdhost_write(host, host->hcfg, SDHCFG); - bcm2835_sdhost_write(host, host->cdiv, SDCDIV); - mmiowb(); - } - -- - static void bcm2835_sdhost_reset(struct mmc_host *mmc) - { - struct bcm2835_host *host = mmc_priv(mmc); - unsigned long flags; - spin_lock_irqsave(&host->lock, flags); -+ log_event("RST<", 0, 0); - - bcm2835_sdhost_reset_internal(host); - -@@ -344,82 +459,48 @@ static void bcm2835_sdhost_init(struct b - } - } - --static bool bcm2835_sdhost_is_write_complete(struct bcm2835_host *host) -+static void bcm2835_sdhost_wait_transfer_complete(struct bcm2835_host *host) - { -- bool write_complete = ((bcm2835_sdhost_read(host, SDEDM) & 0xf) == 1); -+ int timediff; -+ u32 alternate_idle; -+ u32 edm; - -- if (!write_complete) { -- /* Request an IRQ for the last block */ -- host->hcfg |= SDHCFG_BLOCK_IRPT_EN; -- bcm2835_sdhost_write(host, host->hcfg, SDHCFG); -- if ((bcm2835_sdhost_read(host, SDEDM) & 0xf) == 1) { -- /* The write has now completed. Disable the interrupt -- and clear the status flag */ -- host->hcfg &= ~SDHCFG_BLOCK_IRPT_EN; -- bcm2835_sdhost_write(host, host->hcfg, SDHCFG); -- bcm2835_sdhost_write(host, SDHSTS_BLOCK_IRPT, SDHSTS); -- write_complete = true; -- } -- } -+ alternate_idle = (host->mrq->data->flags & MMC_DATA_READ) ? -+ SDEDM_FSM_READWAIT : SDEDM_FSM_WRITESTART1; - -- return write_complete; --} -+ edm = bcm2835_sdhost_read(host, SDEDM); - --static void bcm2835_sdhost_wait_write_complete(struct bcm2835_host *host) --{ -- int timediff; --#ifdef DEBUG -- static struct timeval start_time; -- static int max_stall_time = 0; -- static int total_stall_time = 0; -- struct timeval before, after; -- -- do_gettimeofday(&before); -- if (max_stall_time == 0) -- start_time = before; --#endif -+ log_event("WTC<", edm, 0); - - timediff = 0; - - while (1) { -- u32 edm = bcm2835_sdhost_read(host, SDEDM); -- if ((edm & 0xf) == 1) -+ u32 fsm = edm & SDEDM_FSM_MASK; -+ if ((fsm == SDEDM_FSM_IDENTMODE) || -+ (fsm == SDEDM_FSM_DATAMODE)) - break; -- timediff++; -- if (timediff > 5000000) { --#ifdef DEBUG -- do_gettimeofday(&after); -- timediff = (after.tv_sec - before.tv_sec)*1000000 + -- (after.tv_usec - before.tv_usec); -+ if (fsm == alternate_idle) { -+ bcm2835_sdhost_write(host, -+ edm | SDEDM_FORCE_DATA_MODE, -+ SDEDM); -+ break; -+ } - -- pr_err(" wait_write_complete - still waiting after %dus\n", -- timediff); --#else -- pr_err(" wait_write_complete - still waiting after %d retries\n", -+ timediff++; -+ if (timediff == 100000) { -+ pr_err("%s: wait_transfer_complete - still waiting after %d retries\n", -+ mmc_hostname(host->mmc), - timediff); --#endif -+ log_dump(); - bcm2835_sdhost_dumpregs(host); -- host->data->error = -ETIMEDOUT; -+ host->mrq->data->error = -ETIMEDOUT; -+ log_event("WTC!", edm, 0); - return; - } -+ cpu_relax(); -+ edm = bcm2835_sdhost_read(host, SDEDM); - } -- --#ifdef DEBUG -- do_gettimeofday(&after); -- timediff = (after.tv_sec - before.tv_sec)*1000000 + (after.tv_usec - before.tv_usec); -- -- total_stall_time += timediff; -- if (timediff > max_stall_time) -- max_stall_time = timediff; -- -- if ((after.tv_sec - start_time.tv_sec) > 10) { -- pr_debug(" wait_write_complete - max wait %dus, total %dus\n", -- max_stall_time, total_stall_time); -- start_time = after; -- max_stall_time = 0; -- total_stall_time = 0; -- } --#endif -+ log_event("WTC>", edm, 0); - } - - static void bcm2835_sdhost_finish_data(struct bcm2835_host *host); -@@ -427,65 +508,44 @@ static void bcm2835_sdhost_finish_data(s - static void bcm2835_sdhost_dma_complete(void *param) - { - struct bcm2835_host *host = param; -- struct dma_chan *dma_chan; -+ struct mmc_data *data = host->data; - unsigned long flags; -- u32 dir_data; - - spin_lock_irqsave(&host->lock, flags); -+ log_event("DMA<", (u32)host->data, bcm2835_sdhost_read(host, SDHSTS)); -+ log_event("DMA ", bcm2835_sdhost_read(host, SDCMD), -+ bcm2835_sdhost_read(host, SDEDM)); - -- if (host->data) { -- bool write_complete; -- if (USE_BLOCK_IRQ) -- write_complete = bcm2835_sdhost_is_write_complete(host); -- else { -- bcm2835_sdhost_wait_write_complete(host); -- write_complete = true; -- } -- pr_debug("dma_complete() - write_complete=%d\n", -- write_complete); -- -- if (write_complete || (host->data->flags & MMC_DATA_READ)) -- { -- if (write_complete) { -- dma_chan = host->dma_chan_tx; -- dir_data = DMA_TO_DEVICE; -- } else { -- 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); -+ if (host->dma_chan) { -+ dma_unmap_sg(host->dma_chan->device->dev, -+ data->sg, data->sg_len, -+ host->dma_dir); - -- bcm2835_sdhost_finish_data(host); -- } -+ host->dma_chan = NULL; - } - -- spin_unlock_irqrestore(&host->lock, flags); --} -+ if (host->drain_words) { -+ void *page; -+ u32 *buf; - --static bool data_transfer_wait(struct bcm2835_host *host) --{ -- unsigned long timeout = 1000000; -- while (timeout) -- { -- u32 sdhsts = bcm2835_sdhost_read(host, SDHSTS); -- if (sdhsts & SDHSTS_DATA_FLAG) { -- bcm2835_sdhost_write(host, SDHSTS_DATA_FLAG, SDHSTS); -- break; -+ page = kmap_atomic(host->drain_page); -+ buf = page + host->drain_offset; -+ -+ while (host->drain_words) { -+ u32 edm = bcm2835_sdhost_read(host, SDEDM); -+ if ((edm >> 4) & 0x1f) -+ *(buf++) = bcm2835_sdhost_read(host, -+ SDDATA); -+ host->drain_words--; - } -- timeout--; -- } -- if (timeout == 0) { -- pr_err("%s: Data %s timeout\n", -- mmc_hostname(host->mmc), -- (host->data->flags & MMC_DATA_READ) ? "read" : "write"); -- bcm2835_sdhost_dumpregs(host); -- host->data->error = -ETIMEDOUT; -- return false; -+ -+ kunmap_atomic(page); - } -- return true; -+ -+ bcm2835_sdhost_finish_data(host); -+ -+ log_event("DMA>", (u32)host->data, 0); -+ spin_unlock_irqrestore(&host->lock, flags); - } - - static void bcm2835_sdhost_read_block_pio(struct bcm2835_host *host) -@@ -493,32 +553,83 @@ static void bcm2835_sdhost_read_block_pi - unsigned long flags; - size_t blksize, len; - u32 *buf; -+ unsigned long wait_max; - - blksize = host->data->blksz; - -+ wait_max = jiffies + msecs_to_jiffies(host->pio_timeout); -+ - local_irq_save(flags); - - while (blksize) { -- if (!sg_miter_next(&host->sg_miter)) -- BUG(); -+ int copy_words; -+ u32 hsts = 0; -+ -+ if (!sg_miter_next(&host->sg_miter)) { -+ host->data->error = -EINVAL; -+ break; -+ } - - len = min(host->sg_miter.length, blksize); -- BUG_ON(len % 4); -+ if (len % 4) { -+ host->data->error = -EINVAL; -+ break; -+ } - - blksize -= len; - host->sg_miter.consumed = len; - - buf = (u32 *)host->sg_miter.addr; - -- while (len) { -- if (!data_transfer_wait(host)) -- break; -+ copy_words = len/4; -+ -+ while (copy_words) { -+ int burst_words, words; -+ u32 edm; -+ -+ burst_words = SDDATA_FIFO_PIO_BURST; -+ if (burst_words > copy_words) -+ burst_words = copy_words; -+ edm = bcm2835_sdhost_read(host, SDEDM); -+ words = ((edm >> 4) & 0x1f); -+ -+ if (words < burst_words) { -+ int fsm_state = (edm & SDEDM_FSM_MASK); -+ if ((fsm_state != SDEDM_FSM_READDATA) && -+ (fsm_state != SDEDM_FSM_READWAIT) && -+ (fsm_state != SDEDM_FSM_READCRC)) { -+ hsts = bcm2835_sdhost_read(host, -+ SDHSTS); -+ pr_err("%s: fsm %x, hsts %x\n", -+ mmc_hostname(host->mmc), -+ fsm_state, hsts); -+ if (hsts & SDHSTS_ERROR_MASK) -+ break; -+ } -+ -+ if (time_after(jiffies, wait_max)) { -+ pr_err("%s: PIO read timeout - EDM %x\n", -+ mmc_hostname(host->mmc), -+ edm); -+ hsts = SDHSTS_REW_TIME_OUT; -+ break; -+ } -+ ndelay((burst_words - words) * -+ host->ns_per_fifo_word); -+ continue; -+ } else if (words > copy_words) { -+ words = copy_words; -+ } -+ -+ copy_words -= words; - -- *(buf++) = bcm2835_sdhost_read(host, SDDATA); -- len -= 4; -+ while (words) { -+ *(buf++) = bcm2835_sdhost_read(host, SDDATA); -+ words--; -+ } - } - -- if (host->data->error) -+ if (hsts & SDHSTS_ERROR_MASK) - break; - } - -@@ -532,32 +643,83 @@ static void bcm2835_sdhost_write_block_p - unsigned long flags; - size_t blksize, len; - u32 *buf; -+ unsigned long wait_max; - - blksize = host->data->blksz; - -+ wait_max = jiffies + msecs_to_jiffies(host->pio_timeout); -+ - local_irq_save(flags); - - while (blksize) { -- if (!sg_miter_next(&host->sg_miter)) -- BUG(); -+ int copy_words; -+ u32 hsts = 0; -+ -+ if (!sg_miter_next(&host->sg_miter)) { -+ host->data->error = -EINVAL; -+ break; -+ } - - len = min(host->sg_miter.length, blksize); -- BUG_ON(len % 4); -+ if (len % 4) { -+ host->data->error = -EINVAL; -+ break; -+ } - - blksize -= len; - host->sg_miter.consumed = len; - -- buf = host->sg_miter.addr; -+ buf = (u32 *)host->sg_miter.addr; - -- while (len) { -- if (!data_transfer_wait(host)) -- break; -+ copy_words = len/4; -+ -+ while (copy_words) { -+ int burst_words, words; -+ u32 edm; -+ -+ burst_words = SDDATA_FIFO_PIO_BURST; -+ if (burst_words > copy_words) -+ burst_words = copy_words; -+ edm = bcm2835_sdhost_read(host, SDEDM); -+ words = SDDATA_FIFO_WORDS - ((edm >> 4) & 0x1f); -+ -+ if (words < burst_words) { -+ int fsm_state = (edm & SDEDM_FSM_MASK); -+ if ((fsm_state != SDEDM_FSM_WRITEDATA) && -+ (fsm_state != SDEDM_FSM_WRITESTART1) && -+ (fsm_state != SDEDM_FSM_WRITESTART2)) { -+ hsts = bcm2835_sdhost_read(host, -+ SDHSTS); -+ pr_err("%s: fsm %x, hsts %x\n", -+ mmc_hostname(host->mmc), -+ fsm_state, hsts); -+ if (hsts & SDHSTS_ERROR_MASK) -+ break; -+ } - -- bcm2835_sdhost_write(host, *(buf++), SDDATA); -- len -= 4; -+ if (time_after(jiffies, wait_max)) { -+ pr_err("%s: PIO write timeout - EDM %x\n", -+ mmc_hostname(host->mmc), -+ edm); -+ hsts = SDHSTS_REW_TIME_OUT; -+ break; -+ } -+ ndelay((burst_words - words) * -+ host->ns_per_fifo_word); -+ continue; -+ } else if (words > copy_words) { -+ words = copy_words; -+ } -+ -+ copy_words -= words; -+ -+ while (words) { -+ bcm2835_sdhost_write(host, *(buf++), SDDATA); -+ words--; -+ } - } - -- if (host->data->error) -+ if (hsts & SDHSTS_ERROR_MASK) - break; - } - -@@ -566,12 +728,12 @@ static void bcm2835_sdhost_write_block_p - local_irq_restore(flags); - } - -- - static void bcm2835_sdhost_transfer_pio(struct bcm2835_host *host) - { - u32 sdhsts; - bool is_read; - BUG_ON(!host->data); -+ log_event("XFP<", (u32)host->data, host->blocks); - - is_read = (host->data->flags & MMC_DATA_READ) != 0; - if (is_read) -@@ -595,28 +757,21 @@ static void bcm2835_sdhost_transfer_pio( - is_read ? "read" : "write", - sdhsts); - host->data->error = -ETIMEDOUT; -- } else if (!is_read && !host->data->error) { -- /* Start a timer in case a transfer error occurs because -- there is no error interrupt */ -- mod_timer(&host->pio_timer, jiffies + host->pio_timeout); - } -+ log_event("XFP>", (u32)host->data, host->blocks); - } - -- --static void bcm2835_sdhost_transfer_dma(struct bcm2835_host *host) -+static void bcm2835_sdhost_prepare_dma(struct bcm2835_host *host, -+ struct mmc_data *data) - { -- u32 len, dir_data, dir_slave; -+ int len, dir_data, dir_slave; - struct dma_async_tx_descriptor *desc = NULL; - struct dma_chan *dma_chan; - -- pr_debug("bcm2835_sdhost_transfer_dma()\n"); -- -- WARN_ON(!host->data); -+ log_event("PRD<", (u32)data, 0); -+ pr_debug("bcm2835_sdhost_prepare_dma()\n"); - -- if (!host->data) -- return; -- -- if (host->data->flags & MMC_DATA_READ) { -+ if (data->flags & MMC_DATA_READ) { - dma_chan = host->dma_chan_rx; - dir_data = DMA_FROM_DEVICE; - dir_slave = DMA_DEV_TO_MEM; -@@ -625,35 +780,71 @@ static void bcm2835_sdhost_transfer_dma( - dir_data = DMA_TO_DEVICE; - dir_slave = DMA_MEM_TO_DEV; - } -+ log_event("PRD1", (u32)dma_chan, 0); - - BUG_ON(!dma_chan->device); - BUG_ON(!dma_chan->device->dev); -- BUG_ON(!host->data->sg); -+ BUG_ON(!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, -+ /* The block doesn't manage the FIFO DREQs properly for multi-block -+ transfers, so don't attempt to DMA the final few words. -+ Unfortunately this requires the final sg entry to be trimmed. -+ N.B. This code demands that the overspill is contained in -+ a single sg entry. -+ */ -+ -+ host->drain_words = 0; -+ if ((data->blocks > 1) && (dir_data == DMA_FROM_DEVICE)) { -+ struct scatterlist *sg; -+ u32 len; -+ int i; -+ -+ len = min((u32)(FIFO_READ_THRESHOLD - 1) * 4, -+ (u32)data->blocks * data->blksz); -+ -+ for_each_sg(data->sg, sg, data->sg_len, i) { -+ if (sg_is_last(sg)) { -+ BUG_ON(sg->length < len); -+ sg->length -= len; -+ host->drain_page = (struct page *)sg->page_link; -+ host->drain_offset = sg->offset + sg->length; -+ } -+ } -+ host->drain_words = len/4; -+ } -+ -+ len = dma_map_sg(dma_chan->device->dev, data->sg, data->sg_len, -+ dir_data); -+ -+ log_event("PRD2", len, 0); -+ if (len > 0) -+ desc = dmaengine_prep_slave_sg(dma_chan, 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"); -- } -+ log_event("PRD3", (u32)desc, 0); -+ - if (desc) { - desc->callback = bcm2835_sdhost_dma_complete; - desc->callback_param = host; -- dmaengine_submit(desc); -- dma_async_issue_pending(dma_chan); -+ host->dma_desc = desc; -+ host->dma_chan = dma_chan; -+ host->dma_dir = dir_data; - } -- -+ log_event("PDM>", (u32)data, 0); - } - -+static void bcm2835_sdhost_start_dma(struct bcm2835_host *host) -+{ -+ log_event("SDMA", (u32)host->data, (u32)host->dma_chan); -+ dmaengine_submit(host->dma_desc); -+ dma_async_issue_pending(host->dma_chan); -+} - - static void bcm2835_sdhost_set_transfer_irqs(struct bcm2835_host *host) - { - u32 all_irqs = SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN | - SDHCFG_BUSY_IRPT_EN; -- if (host->use_dma) -+ if (host->dma_desc) - host->hcfg = (host->hcfg & ~all_irqs) | - SDHCFG_BUSY_IRPT_EN; - else -@@ -664,13 +855,13 @@ static void bcm2835_sdhost_set_transfer_ - bcm2835_sdhost_write(host, host->hcfg, SDHCFG); - } - -- - static void bcm2835_sdhost_prepare_data(struct bcm2835_host *host, struct mmc_command *cmd) - { - struct mmc_data *data = cmd->data; - - WARN_ON(host->data); - -+ host->data = data; - if (!data) - return; - -@@ -679,46 +870,19 @@ static void bcm2835_sdhost_prepare_data( - BUG_ON(data->blksz > host->mmc->max_blk_size); - BUG_ON(data->blocks > 65535); - -- host->data = data; - host->data_complete = 0; - host->flush_fifo = 0; - host->data->bytes_xfered = 0; - -- if (!host->sectors && host->mmc->card && !(host->debug_flags & 1)) -- { -- struct mmc_card *card = host->mmc->card; -- if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) { -- /* -- * The EXT_CSD sector count is in number of 512 byte -- * sectors. -- */ -- host->sectors = card->ext_csd.sectors; -- pr_err("%s: using ext_csd!\n", mmc_hostname(host->mmc)); -- } else { -- /* -- * The CSD capacity field is in units of read_blkbits. -- * set_capacity takes units of 512 bytes. -- */ -- host->sectors = card->csd.capacity << -- (card->csd.read_blkbits - 9); -- } -- host->single_read_sectors[0] = host->sectors - 65; -- host->single_read_sectors[1] = host->sectors - 64; -- host->single_read_sectors[2] = host->sectors - 33; -- host->single_read_sectors[3] = host->sectors - 32; -- host->single_read_sectors[4] = host->sectors - 1; -- host->single_read_sectors[5] = ~0; /* Safety net */ -- } - -- host->use_dma = host->have_dma && (data->blocks > host->pio_limit); -- if (!host->use_dma) { -+ if (!host->dma_desc) { -+ /* Use PIO */ - int flags; - -- flags = SG_MITER_ATOMIC; - if (data->flags & MMC_DATA_READ) -- flags |= SG_MITER_TO_SG; -+ flags = SG_MITER_TO_SG; - else -- flags |= SG_MITER_FROM_SG; -+ flags = SG_MITER_FROM_SG; - sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); - host->blocks = data->blocks; - } -@@ -726,19 +890,20 @@ static void bcm2835_sdhost_prepare_data( - bcm2835_sdhost_set_transfer_irqs(host); - - bcm2835_sdhost_write(host, data->blksz, SDHBCT); -- bcm2835_sdhost_write(host, host->use_dma ? data->blocks : 0, SDHBLC); -+ bcm2835_sdhost_write(host, data->blocks, SDHBLC); - - BUG_ON(!host->data); - } - -- --void bcm2835_sdhost_send_command(struct bcm2835_host *host, struct mmc_command *cmd) -+bool bcm2835_sdhost_send_command(struct bcm2835_host *host, -+ struct mmc_command *cmd) - { - u32 sdcmd, sdhsts; - unsigned long timeout; - int delay; - - WARN_ON(host->cmd); -+ log_event("CMD<", cmd->opcode, cmd->arg); - - if (cmd->data) - pr_debug("%s: send_command %d 0x%x " -@@ -761,9 +926,9 @@ void bcm2835_sdhost_send_command(struct - pr_err("%s: previous command never completed.\n", - mmc_hostname(host->mmc)); - bcm2835_sdhost_dumpregs(host); -- cmd->error = -EIO; -+ cmd->error = -EILSEQ; - tasklet_schedule(&host->finish_tasklet); -- return; -+ return false; - } - timeout--; - udelay(10); -@@ -791,23 +956,24 @@ void bcm2835_sdhost_send_command(struct - if (sdhsts & SDHSTS_ERROR_MASK) - bcm2835_sdhost_write(host, sdhsts, SDHSTS); - -- bcm2835_sdhost_prepare_data(host, cmd); -- -- bcm2835_sdhost_write(host, cmd->arg, SDARG); -- - 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 false; - } - -+ bcm2835_sdhost_prepare_data(host, cmd); -+ -+ bcm2835_sdhost_write(host, cmd->arg, SDARG); -+ - sdcmd = cmd->opcode & SDCMD_CMD_MASK; - -- if (!(cmd->flags & MMC_RSP_PRESENT)) -+ host->use_busy = 0; -+ if (!(cmd->flags & MMC_RSP_PRESENT)) { - sdcmd |= SDCMD_NO_RESPONSE; -- else { -+ } else { - if (cmd->flags & MMC_RSP_136) - sdcmd |= SDCMD_LONG_RESPONSE; - if (cmd->flags & MMC_RSP_BUSY) { -@@ -817,6 +983,7 @@ void bcm2835_sdhost_send_command(struct - } - - if (cmd->data) { -+ log_event("CMDD", cmd->data->blocks, cmd->data->blksz); - if (host->delay_after_stop) { - struct timeval now; - int time_since_stop; -@@ -839,10 +1006,12 @@ void bcm2835_sdhost_send_command(struct - } - - bcm2835_sdhost_write(host, sdcmd | SDCMD_NEW_FLAG, SDCMD); --} - -+ return true; -+} - --static void bcm2835_sdhost_finish_command(struct bcm2835_host *host); -+static void bcm2835_sdhost_finish_command(struct bcm2835_host *host, -+ unsigned long *irq_flags); - static void bcm2835_sdhost_transfer_complete(struct bcm2835_host *host); - - static void bcm2835_sdhost_finish_data(struct bcm2835_host *host) -@@ -852,6 +1021,7 @@ static void bcm2835_sdhost_finish_data(s - data = host->data; - BUG_ON(!data); - -+ log_event("FDA<", (u32)host->mrq, (u32)host->cmd); - pr_debug("finish_data(error %d, stop %d, sbc %d)\n", - data->error, data->stop ? 1 : 0, - host->mrq->sbc ? 1 : 0); -@@ -859,10 +1029,7 @@ static void bcm2835_sdhost_finish_data(s - host->hcfg &= ~(SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN); - bcm2835_sdhost_write(host, host->hcfg, SDHCFG); - -- if (data->error) { -- data->bytes_xfered = 0; -- } else -- data->bytes_xfered = data->blksz * data->blocks; -+ data->bytes_xfered = data->error ? 0 : (data->blksz * data->blocks); - - host->data_complete = 1; - -@@ -877,9 +1044,9 @@ static void bcm2835_sdhost_finish_data(s - } - else - bcm2835_sdhost_transfer_complete(host); -+ log_event("FDA>", (u32)host->mrq, (u32)host->cmd); - } - -- - static void bcm2835_sdhost_transfer_complete(struct bcm2835_host *host) - { - struct mmc_data *data; -@@ -891,6 +1058,7 @@ static void bcm2835_sdhost_transfer_comp - data = host->data; - host->data = NULL; - -+ log_event("TCM<", (u32)data, data->error); - pr_debug("transfer_complete(error %d, stop %d)\n", - data->error, data->stop ? 1 : 0); - -@@ -899,88 +1067,114 @@ static void bcm2835_sdhost_transfer_comp - * a) open-ended multiblock transfer (no CMD23) - * b) error in multiblock transfer - */ -- if (data->stop && -- (data->error || -- !host->mrq->sbc)) { -- host->flush_fifo = 1; -- bcm2835_sdhost_send_command(host, data->stop); -- if (host->delay_after_stop) -- do_gettimeofday(&host->stop_time); -- if (!host->use_busy) -- bcm2835_sdhost_finish_command(host); -+ if (host->mrq->stop && (data->error || !host->use_sbc)) { -+ if (bcm2835_sdhost_send_command(host, host->mrq->stop)) { -+ /* No busy, so poll for completion */ -+ if (!host->use_busy) -+ bcm2835_sdhost_finish_command(host, NULL); -+ -+ if (host->delay_after_stop) -+ do_gettimeofday(&host->stop_time); -+ } - } else { -+ bcm2835_sdhost_wait_transfer_complete(host); - tasklet_schedule(&host->finish_tasklet); - } -+ log_event("TCM>", (u32)data, 0); - } - --static void bcm2835_sdhost_finish_command(struct bcm2835_host *host) -+/* If irq_flags is valid, the caller is in a thread context and is allowed -+ to sleep */ -+static void bcm2835_sdhost_finish_command(struct bcm2835_host *host, -+ unsigned long *irq_flags) - { - u32 sdcmd; -- unsigned long timeout; -+ u32 retries; - #ifdef DEBUG - struct timeval before, after; - int timediff = 0; - #endif - -+ log_event("FCM<", (u32)host->mrq, (u32)host->cmd); - pr_debug("finish_command(%x)\n", bcm2835_sdhost_read(host, SDCMD)); - - BUG_ON(!host->cmd || !host->mrq); - --#ifdef DEBUG -- do_gettimeofday(&before); --#endif -- /* Wait max 100 ms */ -- timeout = 10000; -+ /* Poll quickly at first */ -+ -+ retries = host->cmd_quick_poll_retries; -+ if (!retries) { -+ /* Work out how many polls take 1us by timing 10us */ -+ struct timeval start, now; -+ int us_diff; -+ -+ retries = 1; -+ do { -+ int i; -+ -+ retries *= 2; -+ -+ do_gettimeofday(&start); -+ -+ for (i = 0; i < retries; i++) { -+ cpu_relax(); -+ sdcmd = bcm2835_sdhost_read(host, SDCMD); -+ } -+ -+ do_gettimeofday(&now); -+ us_diff = (now.tv_sec - start.tv_sec) * 1000000 + -+ (now.tv_usec - start.tv_usec); -+ } while (us_diff < 10); -+ -+ host->cmd_quick_poll_retries = ((retries * us_diff + 9)*CMD_DALLY_US)/10 + 1; -+ retries = 1; // We've already waited long enough this time -+ } -+ -+ retries = host->cmd_quick_poll_retries; - for (sdcmd = bcm2835_sdhost_read(host, SDCMD); -- (sdcmd & SDCMD_NEW_FLAG) && timeout; -- timeout--) { -- if (host->flush_fifo) { -- while (bcm2835_sdhost_read(host, SDHSTS) & -- SDHSTS_DATA_FLAG) -- (void)bcm2835_sdhost_read(host, SDDATA); -- } -- udelay(10); -+ (sdcmd & SDCMD_NEW_FLAG) && !(sdcmd & SDCMD_FAIL_FLAG) && retries; -+ retries--) { -+ cpu_relax(); - sdcmd = bcm2835_sdhost_read(host, SDCMD); - } --#ifdef DEBUG -- do_gettimeofday(&after); -- timediff = (after.tv_sec - before.tv_sec)*1000000 + -- (after.tv_usec - before.tv_usec); - -- pr_debug(" finish_command - waited %dus\n", timediff); --#endif -+ if (!retries) { -+ unsigned long wait_max; -+ -+ if (!irq_flags) { -+ /* Schedule the work */ -+ log_event("CWWQ", 0, 0); -+ schedule_work(&host->cmd_wait_wq); -+ return; -+ } -+ -+ /* Wait max 100 ms */ -+ wait_max = jiffies + msecs_to_jiffies(100); -+ while (time_before(jiffies, wait_max)) { -+ spin_unlock_irqrestore(&host->lock, *irq_flags); -+ usleep_range(1, 10); -+ spin_lock_irqsave(&host->lock, *irq_flags); -+ sdcmd = bcm2835_sdhost_read(host, SDCMD); -+ if (!(sdcmd & SDCMD_NEW_FLAG) || -+ (sdcmd & SDCMD_FAIL_FLAG)) -+ break; -+ } -+ } - -- if (timeout == 0) { -+ /* Check for errors */ -+ if (sdcmd & SDCMD_NEW_FLAG) { - pr_err("%s: command never completed.\n", - mmc_hostname(host->mmc)); - bcm2835_sdhost_dumpregs(host); - host->cmd->error = -EIO; - tasklet_schedule(&host->finish_tasklet); - return; -- } -- -- if (host->flush_fifo) { -- for (timeout = 100; -- (bcm2835_sdhost_read(host, SDHSTS) & SDHSTS_DATA_FLAG) && timeout; -- timeout--) { -- (void)bcm2835_sdhost_read(host, SDDATA); -- } -- host->flush_fifo = 0; -- if (timeout == 0) { -- pr_err("%s: FIFO never drained.\n", -- mmc_hostname(host->mmc)); -- bcm2835_sdhost_dumpregs(host); -- host->cmd->error = -EIO; -- tasklet_schedule(&host->finish_tasklet); -- return; -- } -- } -- -- /* Check for errors */ -- if (sdcmd & SDCMD_FAIL_FLAG) -- { -+ } else if (sdcmd & SDCMD_FAIL_FLAG) { - u32 sdhsts = bcm2835_sdhost_read(host, SDHSTS); - -+ /* Clear the errors */ -+ bcm2835_sdhost_write(host, SDHSTS_ERROR_MASK, SDHSTS); -+ - if (host->debug) - pr_info("%s: error detected - CMD %x, HSTS %03x, EDM %x\n", - mmc_hostname(host->mmc), sdcmd, sdhsts, -@@ -1003,7 +1197,7 @@ static void bcm2835_sdhost_finish_comman - mmc_hostname(host->mmc), - host->cmd->opcode); - bcm2835_sdhost_dumpregs(host); -- host->cmd->error = -EIO; -+ host->cmd->error = -EILSEQ; - } - tasklet_schedule(&host->finish_tasklet); - return; -@@ -1018,31 +1212,31 @@ static void bcm2835_sdhost_finish_comman - pr_debug("%s: finish_command %08x %08x %08x %08x\n", - mmc_hostname(host->mmc), - host->cmd->resp[0], host->cmd->resp[1], host->cmd->resp[2], host->cmd->resp[3]); -+ log_event("RSP ", host->cmd->resp[0], host->cmd->resp[1]); - } else { - host->cmd->resp[0] = bcm2835_sdhost_read(host, SDRSP0); - pr_debug("%s: finish_command %08x\n", - mmc_hostname(host->mmc), - host->cmd->resp[0]); -+ log_event("RSP ", host->cmd->resp[0], 0); - } - } - -- host->cmd->error = 0; -- - if (host->cmd == host->mrq->sbc) { - /* Finished CMD23, now send actual command. */ - host->cmd = NULL; -- bcm2835_sdhost_send_command(host, host->mrq->cmd); -+ if (bcm2835_sdhost_send_command(host, host->mrq->cmd)) { -+ if (host->data && host->dma_desc) -+ /* DMA transfer starts now, PIO starts after irq */ -+ bcm2835_sdhost_start_dma(host); - -- if (host->cmd->data && host->use_dma) -- /* DMA transfer starts now, PIO starts after irq */ -- bcm2835_sdhost_transfer_dma(host); -- -- if (!host->use_busy) -- bcm2835_sdhost_finish_command(host); -- } else if (host->cmd == host->mrq->stop) -+ if (!host->use_busy) -+ bcm2835_sdhost_finish_command(host, NULL); -+ } -+ } else if (host->cmd == host->mrq->stop) { - /* Finished CMD12 */ - tasklet_schedule(&host->finish_tasklet); -- else { -+ } else { - /* Processed actual command. */ - host->cmd = NULL; - if (!host->data) -@@ -1050,6 +1244,7 @@ static void bcm2835_sdhost_finish_comman - else if (host->data_complete) - bcm2835_sdhost_transfer_complete(host); - } -+ log_event("FCM>", (u32)host->mrq, (u32)host->cmd); - } - - static void bcm2835_sdhost_timeout(unsigned long data) -@@ -1060,10 +1255,12 @@ static void bcm2835_sdhost_timeout(unsig - host = (struct bcm2835_host *)data; - - spin_lock_irqsave(&host->lock, flags); -+ log_event("TIM<", 0, 0); - - if (host->mrq) { - pr_err("%s: timeout waiting for hardware interrupt.\n", - mmc_hostname(host->mmc)); -+ log_dump(); - bcm2835_sdhost_dumpregs(host); - - if (host->data) { -@@ -1084,74 +1281,15 @@ static void bcm2835_sdhost_timeout(unsig - spin_unlock_irqrestore(&host->lock, flags); - } - --static void bcm2835_sdhost_pio_timeout(unsigned long data) --{ -- struct bcm2835_host *host; -- unsigned long flags; -- -- host = (struct bcm2835_host *)data; -- -- spin_lock_irqsave(&host->lock, flags); -- -- if (host->data) { -- u32 sdhsts = bcm2835_sdhost_read(host, SDHSTS); -- -- if (sdhsts & SDHSTS_REW_TIME_OUT) { -- pr_err("%s: transfer timeout\n", -- mmc_hostname(host->mmc)); -- if (host->debug) -- bcm2835_sdhost_dumpregs(host); -- } else { -- pr_err("%s: unexpected transfer timeout\n", -- mmc_hostname(host->mmc)); -- bcm2835_sdhost_dumpregs(host); -- } -- -- bcm2835_sdhost_write(host, SDHSTS_TRANSFER_ERROR_MASK, -- SDHSTS); -- -- host->data->error = -ETIMEDOUT; -- -- bcm2835_sdhost_finish_data(host); -- } -- -- mmiowb(); -- spin_unlock_irqrestore(&host->lock, flags); --} -- --static void bcm2835_sdhost_enable_sdio_irq_nolock(struct bcm2835_host *host, int enable) --{ -- if (enable) -- host->hcfg |= SDHCFG_SDIO_IRPT_EN; -- else -- host->hcfg &= ~SDHCFG_SDIO_IRPT_EN; -- bcm2835_sdhost_write(host, host->hcfg, SDHCFG); -- mmiowb(); --} -- --static void bcm2835_sdhost_enable_sdio_irq(struct mmc_host *mmc, int enable) --{ -- struct bcm2835_host *host = mmc_priv(mmc); -- unsigned long flags; -- -- pr_debug("%s: enable_sdio_irq(%d)\n", mmc_hostname(mmc), enable); -- spin_lock_irqsave(&host->lock, flags); -- bcm2835_sdhost_enable_sdio_irq_nolock(host, enable); -- spin_unlock_irqrestore(&host->lock, flags); --} -- --static u32 bcm2835_sdhost_busy_irq(struct bcm2835_host *host, u32 intmask) -+static void bcm2835_sdhost_busy_irq(struct bcm2835_host *host, u32 intmask) - { -- const u32 handled = (SDHSTS_REW_TIME_OUT | SDHSTS_CMD_TIME_OUT | -- SDHSTS_CRC16_ERROR | SDHSTS_CRC7_ERROR | -- SDHSTS_FIFO_ERROR); -- -+ log_event("IRQB", (u32)host->cmd, intmask); - if (!host->cmd) { - pr_err("%s: got command busy interrupt 0x%08x even " - "though no command operation was in progress.\n", - mmc_hostname(host->mmc), (unsigned)intmask); - bcm2835_sdhost_dumpregs(host); -- return 0; -+ return; - } - - if (!host->use_busy) { -@@ -1159,7 +1297,7 @@ static u32 bcm2835_sdhost_busy_irq(struc - "though not expecting one.\n", - mmc_hostname(host->mmc), (unsigned)intmask); - bcm2835_sdhost_dumpregs(host); -- return 0; -+ return; - } - host->use_busy = 0; - -@@ -1182,28 +1320,23 @@ static u32 bcm2835_sdhost_busy_irq(struc - } else if (intmask & SDHSTS_CMD_TIME_OUT) - host->cmd->error = -ETIMEDOUT; - -+ log_dump(); - bcm2835_sdhost_dumpregs(host); -- tasklet_schedule(&host->finish_tasklet); - } - else -- bcm2835_sdhost_finish_command(host); -- -- return handled; -+ bcm2835_sdhost_finish_command(host, NULL); - } - --static u32 bcm2835_sdhost_data_irq(struct bcm2835_host *host, u32 intmask) -+static void bcm2835_sdhost_data_irq(struct bcm2835_host *host, u32 intmask) - { -- const u32 handled = (SDHSTS_REW_TIME_OUT | -- SDHSTS_CRC16_ERROR | -- SDHSTS_FIFO_ERROR); -- - /* There are no dedicated data/space available interrupt - status bits, so it is necessary to use the single shared - data/space available FIFO status bits. It is therefore not - an error to get here when there is no data transfer in - progress. */ -+ log_event("IRQD", (u32)host->data, intmask); - if (!host->data) -- return 0; -+ return; - - if (intmask & (SDHSTS_CRC16_ERROR | - SDHSTS_FIFO_ERROR | -@@ -1214,46 +1347,37 @@ static u32 bcm2835_sdhost_data_irq(struc - else - host->data->error = -ETIMEDOUT; - -- bcm2835_sdhost_dumpregs(host); -- tasklet_schedule(&host->finish_tasklet); -- return handled; -+ if (host->debug) { -+ log_dump(); -+ bcm2835_sdhost_dumpregs(host); -+ } - } - -- /* Use the block interrupt for writes after the first block */ -- if (host->data->flags & MMC_DATA_WRITE) { -+ if (host->data->error) { -+ bcm2835_sdhost_finish_data(host); -+ } else if (host->data->flags & MMC_DATA_WRITE) { -+ /* Use the block interrupt for writes after the first block */ - host->hcfg &= ~(SDHCFG_DATA_IRPT_EN); - host->hcfg |= SDHCFG_BLOCK_IRPT_EN; - bcm2835_sdhost_write(host, host->hcfg, SDHCFG); -- if (host->data->error) -- bcm2835_sdhost_finish_data(host); -- else -- bcm2835_sdhost_transfer_pio(host); -+ bcm2835_sdhost_transfer_pio(host); - } else { -- if (!host->data->error) { -- bcm2835_sdhost_transfer_pio(host); -- host->blocks--; -- } -+ bcm2835_sdhost_transfer_pio(host); -+ host->blocks--; - if ((host->blocks == 0) || host->data->error) - bcm2835_sdhost_finish_data(host); - } -- -- return handled; - } - --static u32 bcm2835_sdhost_block_irq(struct bcm2835_host *host, u32 intmask) -+static void bcm2835_sdhost_block_irq(struct bcm2835_host *host, u32 intmask) - { -- struct dma_chan *dma_chan; -- u32 dir_data; -- const u32 handled = (SDHSTS_REW_TIME_OUT | -- SDHSTS_CRC16_ERROR | -- SDHSTS_FIFO_ERROR); -- -+ log_event("IRQK", (u32)host->data, intmask); - if (!host->data) { - pr_err("%s: got block interrupt 0x%08x even " - "though no data operation was in progress.\n", - mmc_hostname(host->mmc), (unsigned)intmask); - bcm2835_sdhost_dumpregs(host); -- return handled; -+ return; - } - - if (intmask & (SDHSTS_CRC16_ERROR | -@@ -1265,149 +1389,69 @@ static u32 bcm2835_sdhost_block_irq(stru - else - host->data->error = -ETIMEDOUT; - -- if (host->debug) -+ if (host->debug) { -+ log_dump(); - bcm2835_sdhost_dumpregs(host); -- tasklet_schedule(&host->finish_tasklet); -- return handled; -+ } - } - -- if (!host->use_dma) { -+ if (!host->dma_desc) { - BUG_ON(!host->blocks); -- host->blocks--; -- if ((host->blocks == 0) || host->data->error) { -- /* Cancel the timer */ -- del_timer(&host->pio_timer); -- -+ if (host->data->error || (--host->blocks == 0)) { - bcm2835_sdhost_finish_data(host); - } else { -- /* Reset the timer */ -- mod_timer(&host->pio_timer, -- jiffies + host->pio_timeout); -- - bcm2835_sdhost_transfer_pio(host); -- -- /* Reset the timer */ -- mod_timer(&host->pio_timer, -- jiffies + host->pio_timeout); - } - } else if (host->data->flags & MMC_DATA_WRITE) { -- 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_sdhost_finish_data(host); - } -- -- return handled; - } - -- - static irqreturn_t bcm2835_sdhost_irq(int irq, void *dev_id) - { - irqreturn_t result = IRQ_NONE; - struct bcm2835_host *host = dev_id; -- u32 unexpected = 0, early = 0; -- int loops = 0; -+ u32 intmask; - - spin_lock(&host->lock); - -- for (loops = 0; loops < 1; loops++) { -- u32 intmask, handled; -- -- intmask = bcm2835_sdhost_read(host, SDHSTS); -- handled = intmask & (SDHSTS_BUSY_IRPT | -- SDHSTS_BLOCK_IRPT | -- SDHSTS_SDIO_IRPT | -- SDHSTS_DATA_FLAG); -- if ((handled == SDHSTS_DATA_FLAG) && -- (loops == 0) && !host->data) { -- pr_err("%s: sdhost_irq data interrupt 0x%08x even " -- "though no data operation was in progress.\n", -- mmc_hostname(host->mmc), -- (unsigned)intmask); -- -- bcm2835_sdhost_dumpregs(host); -- } -- -- if (!handled) -- break; -+ intmask = bcm2835_sdhost_read(host, SDHSTS); -+ log_event("IRQ<", intmask, 0); - -- if (loops) -- early |= handled; -+ bcm2835_sdhost_write(host, -+ SDHSTS_BUSY_IRPT | -+ SDHSTS_BLOCK_IRPT | -+ SDHSTS_SDIO_IRPT | -+ SDHSTS_DATA_FLAG, -+ SDHSTS); - -+ if (intmask & SDHSTS_BLOCK_IRPT) { -+ bcm2835_sdhost_block_irq(host, intmask); - result = IRQ_HANDLED; -+ } - -- /* Clear all interrupts and notifications */ -- bcm2835_sdhost_write(host, intmask, SDHSTS); -- -- if (intmask & SDHSTS_BUSY_IRPT) -- handled |= bcm2835_sdhost_busy_irq(host, intmask); -- -- /* There is no true data interrupt status bit, so it is -- necessary to qualify the data flag with the interrupt -- enable bit */ -- if ((intmask & SDHSTS_DATA_FLAG) && -- (host->hcfg & SDHCFG_DATA_IRPT_EN)) -- handled |= bcm2835_sdhost_data_irq(host, intmask); -- -- if (intmask & SDHSTS_BLOCK_IRPT) -- handled |= bcm2835_sdhost_block_irq(host, intmask); -- -- if (intmask & SDHSTS_SDIO_IRPT) { -- bcm2835_sdhost_enable_sdio_irq_nolock(host, false); -- host->thread_isr |= SDHSTS_SDIO_IRPT; -- result = IRQ_WAKE_THREAD; -- } -+ if (intmask & SDHSTS_BUSY_IRPT) { -+ bcm2835_sdhost_busy_irq(host, intmask); -+ result = IRQ_HANDLED; -+ } - -- unexpected |= (intmask & ~handled); -+ /* There is no true data interrupt status bit, so it is -+ necessary to qualify the data flag with the interrupt -+ enable bit */ -+ if ((intmask & SDHSTS_DATA_FLAG) && -+ (host->hcfg & SDHCFG_DATA_IRPT_EN)) { -+ bcm2835_sdhost_data_irq(host, intmask); -+ result = IRQ_HANDLED; - } - - mmiowb(); - -+ log_event("IRQ>", bcm2835_sdhost_read(host, SDHSTS), 0); - spin_unlock(&host->lock); - -- if (early) -- pr_debug("%s: early %x (loops %d)\n", -- mmc_hostname(host->mmc), early, loops); -- -- if (unexpected) { -- pr_err("%s: unexpected interrupt 0x%08x.\n", -- mmc_hostname(host->mmc), unexpected); -- bcm2835_sdhost_dumpregs(host); -- } -- - return result; - } - --static irqreturn_t bcm2835_sdhost_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 & SDHSTS_SDIO_IRPT) { -- sdio_run_irqs(host->mmc); -- --/* Is this necessary? Why re-enable an interrupt which is enabled? -- spin_lock_irqsave(&host->lock, flags); -- if (host->flags & SDHSTS_SDIO_IRPT_ENABLED) -- bcm2835_sdhost_enable_sdio_irq_nolock(host, true); -- spin_unlock_irqrestore(&host->lock, flags); --*/ -- } -- -- return isr ? IRQ_HANDLED : IRQ_NONE; --} -- -- -- - void bcm2835_sdhost_set_clock(struct bcm2835_host *host, unsigned int clock) - { - int div = 0; /* Initialized for compiler warning */ -@@ -1417,9 +1461,8 @@ void bcm2835_sdhost_set_clock(struct bcm - pr_info("%s: set_clock(%d)\n", mmc_hostname(host->mmc), clock); - - if ((host->overclock_50 > 50) && -- (clock == 50*MHZ)) { -+ (clock == 50*MHZ)) - clock = host->overclock_50 * MHZ + (MHZ - 1); -- } - - /* The SDCDIV register has 11 bits, and holds (div - 2). - But in data mode the max is 50MHz wihout a minimum, and only the -@@ -1466,6 +1509,11 @@ void bcm2835_sdhost_set_clock(struct bcm - clock = host->max_clk / (div + 2); - host->mmc->actual_clock = clock; - -+ /* Calibrate some delays */ -+ -+ host->ns_per_fifo_word = (1000000000/clock) * -+ ((host->mmc->caps & MMC_CAP_4_BIT_DATA) ? 8 : 32); -+ - if (clock > input_clock) { - /* Save the closest value, to make it easier - to reduce in the event of error */ -@@ -1501,6 +1549,7 @@ static void bcm2835_sdhost_request(struc - { - struct bcm2835_host *host; - unsigned long flags; -+ u32 edm, fsm; - - host = mmc_priv(mmc); - -@@ -1521,6 +1570,8 @@ static void bcm2835_sdhost_request(struc - } - - /* Reset the error statuses in case this is a retry */ -+ if (mrq->sbc) -+ mrq->sbc->error = 0; - if (mrq->cmd) - mrq->cmd->error = 0; - if (mrq->data) -@@ -1536,28 +1587,58 @@ static void bcm2835_sdhost_request(struc - return; - } - -+ if (host->use_dma && mrq->data && -+ (mrq->data->blocks > host->pio_limit)) -+ bcm2835_sdhost_prepare_dma(host, mrq->data); -+ - spin_lock_irqsave(&host->lock, flags); - - WARN_ON(host->mrq != NULL); -- - host->mrq = mrq; - -- if (mrq->sbc) -- bcm2835_sdhost_send_command(host, mrq->sbc); -- else -- bcm2835_sdhost_send_command(host, mrq->cmd); -+ edm = bcm2835_sdhost_read(host, SDEDM); -+ fsm = edm & SDEDM_FSM_MASK; - -- mmiowb(); -- spin_unlock_irqrestore(&host->lock, flags); -+ log_event("REQ<", (u32)mrq, edm); -+ if ((fsm != SDEDM_FSM_IDENTMODE) && -+ (fsm != SDEDM_FSM_DATAMODE)) { -+ pr_err("%s: previous command (%d) not complete (EDM %x)\n", -+ mmc_hostname(host->mmc), -+ bcm2835_sdhost_read(host, SDCMD) & SDCMD_CMD_MASK, -+ edm); -+ log_event("REQ!", (u32)mrq, edm); -+ log_dump(); -+ bcm2835_sdhost_dumpregs(host); -+ mrq->cmd->error = -EILSEQ; -+ tasklet_schedule(&host->finish_tasklet); -+ mmiowb(); -+ spin_unlock_irqrestore(&host->lock, flags); -+ return; -+ } -+ -+ host->use_sbc = !!mrq->sbc && -+ (host->mrq->data->flags & USE_CMD23_FLAGS); -+ if (host->use_sbc) { -+ if (bcm2835_sdhost_send_command(host, mrq->sbc)) { -+ if (!host->use_busy) -+ bcm2835_sdhost_finish_command(host, &flags); -+ } -+ } else if (bcm2835_sdhost_send_command(host, mrq->cmd)) { -+ if (host->data && host->dma_desc) -+ /* DMA transfer starts now, PIO starts after irq */ -+ bcm2835_sdhost_start_dma(host); - -- if (!mrq->sbc && mrq->cmd->data && host->use_dma) -- /* DMA transfer starts now, PIO starts after irq */ -- bcm2835_sdhost_transfer_dma(host); -+ if (!host->use_busy) -+ bcm2835_sdhost_finish_command(host, &flags); -+ } - -- if (!host->use_busy) -- bcm2835_sdhost_finish_command(host); --} -+ log_event("CMD ", (u32)mrq->cmd->opcode, -+ mrq->data ? (u32)mrq->data->blksz : 0); -+ mmiowb(); - -+ log_event("REQ>", (u32)mrq, 0); -+ spin_unlock_irqrestore(&host->lock, flags); -+} - - static void bcm2835_sdhost_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) - { -@@ -1574,6 +1655,8 @@ static void bcm2835_sdhost_set_ios(struc - - spin_lock_irqsave(&host->lock, flags); - -+ log_event("IOS<", ios->clock, 0); -+ - if (!ios->clock || ios->clock != host->clock) { - bcm2835_sdhost_set_clock(host, ios->clock); - host->clock = ios->clock; -@@ -1596,59 +1679,53 @@ static void bcm2835_sdhost_set_ios(struc - spin_unlock_irqrestore(&host->lock, flags); - } - --static int bcm2835_sdhost_multi_io_quirk(struct mmc_card *card, -- unsigned int direction, -- u32 blk_pos, int blk_size) --{ -- /* There is a bug in the host controller hardware that makes -- reading the final sector of the card as part of a multiple read -- problematic. Detect that case and shorten the read accordingly. -- */ -+static struct mmc_host_ops bcm2835_sdhost_ops = { -+ .request = bcm2835_sdhost_request, -+ .set_ios = bcm2835_sdhost_set_ios, -+ .hw_reset = bcm2835_sdhost_reset, -+}; -+ -+static void bcm2835_sdhost_cmd_wait_work(struct work_struct *work) -+{ - struct bcm2835_host *host; -+ unsigned long flags; - -- host = mmc_priv(card->host); -+ host = container_of(work, struct bcm2835_host, cmd_wait_wq); - -- if (!host->sectors) { -- /* csd.capacity is in weird units - convert to sectors */ -- u32 card_sectors = (card->csd.capacity << (card->csd.read_blkbits - 9)); -- if ((direction == MMC_DATA_READ) && -- ((blk_pos + blk_size) == card_sectors)) -- blk_size--; -- return blk_size; -- } -+ spin_lock_irqsave(&host->lock, flags); - -- if (direction == MMC_DATA_READ) { -- int i; -- int sector; -- for (i = 0; blk_pos > (sector = host->single_read_sectors[i]); i++) -- continue; -+ log_event("CWK<", (u32)host->cmd, (u32)host->mrq); - -- if ((blk_pos + blk_size) > sector) -- blk_size = (blk_pos == sector) ? 1 : (sector - blk_pos); -+ /* -+ * 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; - } -- return blk_size; --} - -+ bcm2835_sdhost_finish_command(host, &flags); - --static struct mmc_host_ops bcm2835_sdhost_ops = { -- .request = bcm2835_sdhost_request, -- .set_ios = bcm2835_sdhost_set_ios, -- .enable_sdio_irq = bcm2835_sdhost_enable_sdio_irq, -- .hw_reset = bcm2835_sdhost_reset, -- .multi_io_quirk = bcm2835_sdhost_multi_io_quirk, --}; -+ mmiowb(); -+ -+ log_event("CWK>", (u32)host->cmd, 0); - -+ spin_unlock_irqrestore(&host->lock, flags); -+} - - static void bcm2835_sdhost_tasklet_finish(unsigned long param) - { - struct bcm2835_host *host; - unsigned long flags; - struct mmc_request *mrq; -+ struct dma_chan *terminate_chan = NULL; - - host = (struct bcm2835_host *)param; - - spin_lock_irqsave(&host->lock, flags); - -+ log_event("TSK<", (u32)host->mrq, 0); - /* - * If this tasklet gets rescheduled while running, it will - * be run again afterwards but without any active request. -@@ -1683,11 +1760,23 @@ static void bcm2835_sdhost_tasklet_finis - - mmiowb(); - -+ host->dma_desc = NULL; -+ terminate_chan = host->dma_chan; -+ host->dma_chan = NULL; -+ - spin_unlock_irqrestore(&host->lock, flags); -- mmc_request_done(host->mmc, mrq); --} - -+ if (terminate_chan) -+ { -+ int err = dmaengine_terminate_all(terminate_chan); -+ if (err) -+ pr_err("%s: failed to terminate DMA (%d)\n", -+ mmc_hostname(host->mmc), err); -+ } - -+ mmc_request_done(host->mmc, mrq); -+ log_event("TSK>", (u32)mrq, 0); -+} - - int bcm2835_sdhost_add_host(struct bcm2835_host *host) - { -@@ -1709,10 +1798,10 @@ int bcm2835_sdhost_add_host(struct bcm28 - mmc->f_max, mmc->f_min, mmc->max_busy_timeout); - - /* host controller capabilities */ -- mmc->caps |= /* MMC_CAP_SDIO_IRQ |*/ MMC_CAP_4_BIT_DATA | -+ mmc->caps |= - MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | - MMC_CAP_NEEDS_POLL | MMC_CAP_HW_RESET | MMC_CAP_ERASE | -- (ALLOW_CMD23 * MMC_CAP_CMD23); -+ ((ALLOW_CMD23_READ|ALLOW_CMD23_WRITE) * MMC_CAP_CMD23); - - spin_lock_init(&host->lock); - -@@ -1722,9 +1811,9 @@ int bcm2835_sdhost_add_host(struct bcm28 - pr_err("%s: unable to initialise DMA channels. " - "Falling back to PIO\n", - mmc_hostname(mmc)); -- host->have_dma = false; -+ host->use_dma = false; - } else { -- host->have_dma = true; -+ host->use_dma = true; - - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; -@@ -1741,7 +1830,7 @@ int bcm2835_sdhost_add_host(struct bcm28 - ret = dmaengine_slave_config(host->dma_chan_rx, &cfg); - } - } else { -- host->have_dma = false; -+ host->use_dma = false; - } - - mmc->max_segs = 128; -@@ -1756,16 +1845,15 @@ int bcm2835_sdhost_add_host(struct bcm28 - tasklet_init(&host->finish_tasklet, - bcm2835_sdhost_tasklet_finish, (unsigned long)host); - -- setup_timer(&host->timer, bcm2835_sdhost_timeout, -- (unsigned long)host); -+ INIT_WORK(&host->cmd_wait_wq, bcm2835_sdhost_cmd_wait_work); - -- setup_timer(&host->pio_timer, bcm2835_sdhost_pio_timeout, -+ setup_timer(&host->timer, bcm2835_sdhost_timeout, - (unsigned long)host); - - bcm2835_sdhost_init(host, 0); -- ret = request_threaded_irq(host->irq, bcm2835_sdhost_irq, -- bcm2835_sdhost_thread_irq, -- IRQF_SHARED, mmc_hostname(mmc), host); -+ -+ ret = request_irq(host->irq, bcm2835_sdhost_irq, 0 /*IRQF_SHARED*/, -+ mmc_hostname(mmc), host); - if (ret) { - pr_err("%s: failed to request IRQ %d: %d\n", - mmc_hostname(mmc), host->irq, ret); -@@ -1776,11 +1864,11 @@ int bcm2835_sdhost_add_host(struct bcm28 - mmc_add_host(mmc); - - pio_limit_string[0] = '\0'; -- if (host->have_dma && (host->pio_limit > 0)) -+ if (host->use_dma && (host->pio_limit > 0)) - sprintf(pio_limit_string, " (>%d)", host->pio_limit); - pr_info("%s: %s loaded - DMA %s%s\n", - mmc_hostname(mmc), DRIVER_NAME, -- host->have_dma ? "enabled" : "disabled", -+ host->use_dma ? "enabled" : "disabled", - pio_limit_string); - - return 0; -@@ -1810,8 +1898,11 @@ static int bcm2835_sdhost_probe(struct p - mmc->ops = &bcm2835_sdhost_ops; - host = mmc_priv(mmc); - host->mmc = mmc; -+ host->cmd_quick_poll_retries = 0; - host->pio_timeout = msecs_to_jiffies(500); -+ host->pio_limit = 1; - host->max_delay = 1; /* Warn if over 1ms */ -+ host->allow_dma = 1; - spin_lock_init(&host->lock); - - iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); -@@ -1827,13 +1918,12 @@ static int bcm2835_sdhost_probe(struct p - return -ENODEV; - } - host->bus_addr = be32_to_cpup(addr); -+ log_init(iomem->start - host->bus_addr); - pr_debug(" - ioaddr %lx, iomem->start %lx, bus_addr %lx\n", - (unsigned long)host->ioaddr, - (unsigned long)iomem->start, - (unsigned long)host->bus_addr); - -- host->allow_dma = ALLOW_DMA; -- - if (node) { - /* Read any custom properties */ - of_property_read_u32(node, -@@ -1845,16 +1935,17 @@ static int bcm2835_sdhost_probe(struct p - of_property_read_u32(node, - "brcm,pio-limit", - &host->pio_limit); -- host->allow_dma = ALLOW_DMA && -+ host->allow_dma = - !of_property_read_bool(node, "brcm,force-pio"); - host->debug = of_property_read_bool(node, "brcm,debug"); -- of_property_read_u32(node, -- "brcm,debug-flags", -- &host->debug_flags); - } - -- if (host->debug_flags) -- dev_err(dev, "debug_flags=%x\n", host->debug_flags); -+ host->dma_chan = NULL; -+ host->dma_desc = NULL; -+ -+ /* Formally recognise the other way of disabling DMA */ -+ if (host->pio_limit == 0x7fffffff) -+ host->allow_dma = false; - - if (host->allow_dma) { - if (node) { -@@ -1940,15 +2031,12 @@ static int bcm2835_sdhost_remove(struct - return 0; - } - -- - static const struct of_device_id bcm2835_sdhost_match[] = { - { .compatible = "brcm,bcm2835-sdhost" }, - { } - }; - MODULE_DEVICE_TABLE(of, bcm2835_sdhost_match); - -- -- - static struct platform_driver bcm2835_sdhost_driver = { - .probe = bcm2835_sdhost_probe, - .remove = bcm2835_sdhost_remove, diff --git a/target/linux/brcm2708/patches-4.4/0149-dcw_otg-Make-trimming-messages-less-noisy.patch b/target/linux/brcm2708/patches-4.4/0149-dcw_otg-Make-trimming-messages-less-noisy.patch new file mode 100644 index 0000000000..e689d4016e --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0149-dcw_otg-Make-trimming-messages-less-noisy.patch @@ -0,0 +1,31 @@ +From 3f4cd273dcc578b48783a23fb2cbef50e7fd151d Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 17 Feb 2016 19:02:31 +0000 +Subject: [PATCH 149/232] dcw_otg: Make trimming messages less noisy + +--- + drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c ++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c +@@ -738,7 +738,8 @@ static int update_urb_state_xfer_comp(dw + &short_read); + + if (urb->actual_length + xfer_length > urb->length) { +- DWC_WARN("%s(): trimming xfer length\n", __func__); ++ printk_once(KERN_DEBUG "dwc_otg: DEVICE:%03d : %s:%d:trimming xfer length\n", ++ hc->dev_addr, __func__, __LINE__); + xfer_length = urb->length - urb->actual_length; + } + +@@ -1430,7 +1431,8 @@ static void update_urb_state_xfer_intr(d + halt_status, NULL); + + if (urb->actual_length + bytes_transferred > urb->length) { +- DWC_WARN("%s(): trimming xfer length\n", __func__); ++ printk_once(KERN_DEBUG "dwc_otg: DEVICE:%03d : %s:%d:trimming xfer length\n", ++ hc->dev_addr, __func__, __LINE__); + bytes_transferred = urb->length - urb->actual_length; + } + diff --git a/target/linux/brcm2708/patches-4.4/0150-BCM270X_DT-Add-dtparams-for-the-SD-interface.patch b/target/linux/brcm2708/patches-4.4/0150-BCM270X_DT-Add-dtparams-for-the-SD-interface.patch deleted file mode 100644 index f4f3d6cc5f..0000000000 --- a/target/linux/brcm2708/patches-4.4/0150-BCM270X_DT-Add-dtparams-for-the-SD-interface.patch +++ /dev/null @@ -1,235 +0,0 @@ -From d15e041e838bd735a7ed3eaa5dbff785213baf6d Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Fri, 12 Feb 2016 15:38:00 +0000 -Subject: [PATCH 150/170] BCM270X_DT: Add dtparams for the SD interface - -Add new base dtparams sd_overclock, sd_force_pio, sd_pio_limit -and sd_debug. ---- - arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 4 ++++ - arch/arm/boot/dts/bcm2708-rpi-b.dts | 4 ++++ - arch/arm/boot/dts/bcm2708-rpi-cm.dts | 1 - - arch/arm/boot/dts/bcm2708-rpi-cm.dtsi | 13 +++++++++++++ - arch/arm/boot/dts/bcm2708_common.dtsi | 2 ++ - arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 4 ++++ - arch/arm/boot/dts/overlays/README | 11 ++++++++++- - arch/arm/boot/dts/overlays/mmc-overlay.dts | 1 - - arch/arm/boot/dts/overlays/sdhost-overlay.dts | 27 +++++++++++++------------- - arch/arm/boot/dts/overlays/sdtweak-overlay.dts | 14 ++++++------- - 10 files changed, 58 insertions(+), 23 deletions(-) - ---- a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts -+++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts -@@ -141,5 +141,9 @@ - audio = <&audio>,"status"; - watchdog = <&watchdog>,"status"; - random = <&random>,"status"; -+ sd_overclock = <&sdhost>,"brcm,overclock-50:0"; -+ sd_force_pio = <&sdhost>,"brcm,force-pio?"; -+ sd_pio_limit = <&sdhost>,"brcm,pio-limit:0"; -+ sd_debug = <&sdhost>,"brcm,debug"; - }; - }; ---- a/arch/arm/boot/dts/bcm2708-rpi-b.dts -+++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts -@@ -131,5 +131,9 @@ - audio = <&audio>,"status"; - watchdog = <&watchdog>,"status"; - random = <&random>,"status"; -+ sd_overclock = <&sdhost>,"brcm,overclock-50:0"; -+ sd_force_pio = <&sdhost>,"brcm,force-pio?"; -+ sd_pio_limit = <&sdhost>,"brcm,pio-limit:0"; -+ sd_debug = <&sdhost>,"brcm,debug"; - }; - }; ---- a/arch/arm/boot/dts/bcm2708-rpi-cm.dts -+++ b/arch/arm/boot/dts/bcm2708-rpi-cm.dts -@@ -97,6 +97,5 @@ - i2c0_baudrate = <&i2c0>,"clock-frequency:0"; - i2c1_baudrate = <&i2c1>,"clock-frequency:0"; - i2c2_baudrate = <&i2c2>,"clock-frequency:0"; -- core_freq = <&clk_core>,"clock-frequency:0"; - }; - }; ---- a/arch/arm/boot/dts/bcm2708-rpi-cm.dtsi -+++ b/arch/arm/boot/dts/bcm2708-rpi-cm.dtsi -@@ -7,6 +7,13 @@ - }; - }; - -+&gpio { -+ sdhost_pins: sdhost_pins { -+ brcm,pins = <48 49 50 51 52 53>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+}; -+ - &leds { - act_led: act { - label = "led0"; -@@ -29,6 +36,8 @@ - - / { - __overrides__ { -+ core_freq = <&clk_core>,"clock-frequency:0"; -+ - act_led_gpio = <&act_led>,"gpios:4"; - act_led_activelow = <&act_led>,"gpios:8"; - act_led_trigger = <&act_led>,"linux,default-trigger"; -@@ -36,5 +45,9 @@ - audio = <&audio>,"status"; - watchdog = <&watchdog>,"status"; - random = <&random>,"status"; -+ sd_overclock = <&sdhost>,"brcm,overclock-50:0"; -+ sd_force_pio = <&sdhost>,"brcm,force-pio?"; -+ sd_pio_limit = <&sdhost>,"brcm,pio-limit:0"; -+ sd_debug = <&sdhost>,"brcm,debug"; - }; - }; ---- a/arch/arm/boot/dts/bcm2708_common.dtsi -+++ b/arch/arm/boot/dts/bcm2708_common.dtsi -@@ -135,6 +135,7 @@ - dmas = <&dma 13>, - <&dma 13>; - dma-names = "tx", "rx"; -+ brcm,overclock-50 = <0>; - brcm,pio-limit = <1>; - status = "disabled"; - }; -@@ -203,6 +204,7 @@ - dmas = <&dma 11>, - <&dma 11>; - dma-names = "tx", "rx"; -+ brcm,overclock-50 = <0>; - status = "disabled"; - }; - ---- a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts -+++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts -@@ -141,5 +141,9 @@ - audio = <&audio>,"status"; - watchdog = <&watchdog>,"status"; - random = <&random>,"status"; -+ sd_overclock = <&sdhost>,"brcm,overclock-50:0"; -+ sd_force_pio = <&sdhost>,"brcm,force-pio?"; -+ sd_pio_limit = <&sdhost>,"brcm,pio-limit:0"; -+ sd_debug = <&sdhost>,"brcm,debug"; - }; - }; ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -112,6 +112,16 @@ Params: - random Set to "on" to enable the hardware random - number generator (default "on") - -+ sd_overclock Clock (in MHz) to use when the MMC framework -+ requests 50MHz -+ -+ sd_force_pio Disable DMA support for SD driver (default off) -+ -+ sd_pio_limit Number of blocks above which to use DMA for -+ SD card (default 1) -+ -+ sd_debug Enable debug output from SD driver (default off) -+ - uart0 Set to "off" to disable uart0 (default "on") - - watchdog Set to "on" to enable the hardware watchdog -@@ -443,7 +453,6 @@ Info: Selects the bcm2835-mmc SD/MMC d - Load: dtoverlay=mmc,= - Params: overclock_50 Clock (in MHz) to use when the MMC framework - requests 50MHz -- force_pio Disable DMA support - - - Name: mz61581 ---- a/arch/arm/boot/dts/overlays/mmc-overlay.dts -+++ b/arch/arm/boot/dts/overlays/mmc-overlay.dts -@@ -34,6 +34,5 @@ - - __overrides__ { - overclock_50 = <&frag0>,"brcm,overclock-50:0"; -- force_pio = <&frag0>,"brcm,force-pio?"; - }; - }; ---- a/arch/arm/boot/dts/overlays/sdhost-overlay.dts -+++ b/arch/arm/boot/dts/overlays/sdhost-overlay.dts -@@ -1,19 +1,14 @@ - /dts-v1/; - /plugin/; - -+/* Provide backwards compatible aliases for the old sdhost dtparams. */ -+ - /{ - compatible = "brcm,bcm2708"; - - fragment@0 { -- target = <&mmc>; -- __overlay__ { -- status = "disabled"; -- }; -- }; -- -- fragment@1 { - target = <&sdhost>; -- frag1: __overlay__ { -+ frag0: __overlay__ { - brcm,overclock-50 = <0>; - brcm,pio-limit = <1>; - brcm,debug-flags = <0>; -@@ -21,11 +16,17 @@ - }; - }; - -+ fragment@1 { -+ target = <&mmc>; -+ __overlay__ { -+ status = "disabled"; -+ }; -+ }; -+ - __overrides__ { -- overclock_50 = <&frag1>,"brcm,overclock-50:0"; -- force_pio = <&frag1>,"brcm,force-pio?"; -- pio_limit = <&frag1>,"brcm,pio-limit:0"; -- debug = <&frag1>,"brcm,debug?"; -- debug_flags = <&frag1>,"brcm,debug-flags:0"; -+ overclock_50 = <&frag0>,"brcm,overclock-50:0"; -+ force_pio = <&frag0>,"brcm,force-pio?"; -+ pio_limit = <&frag0>,"brcm,pio-limit:0"; -+ debug = <&frag0>,"brcm,debug?"; - }; - }; ---- a/arch/arm/boot/dts/overlays/sdtweak-overlay.dts -+++ b/arch/arm/boot/dts/overlays/sdtweak-overlay.dts -@@ -1,23 +1,23 @@ - /dts-v1/; - /plugin/; - -+/* Provide backwards compatible aliases for the old sdhost dtparams. */ -+ - /{ - compatible = "brcm,bcm2708"; - - fragment@0 { - target = <&sdhost>; -- frag1: __overlay__ { -+ frag0: __overlay__ { - brcm,overclock-50 = <0>; - brcm,pio-limit = <1>; -- brcm,debug-flags = <0>; - }; - }; - - __overrides__ { -- overclock_50 = <&frag1>,"brcm,overclock-50:0"; -- force_pio = <&frag1>,"brcm,force-pio?"; -- pio_limit = <&frag1>,"brcm,pio-limit:0"; -- debug = <&frag1>,"brcm,debug?"; -- debug_flags = <&frag1>,"brcm,debug-flags:0"; -+ overclock_50 = <&frag0>,"brcm,overclock-50:0"; -+ force_pio = <&frag0>,"brcm,force-pio?"; -+ pio_limit = <&frag0>,"brcm,pio-limit:0"; -+ debug = <&frag0>,"brcm,debug?"; - }; - }; diff --git a/target/linux/brcm2708/patches-4.4/0150-BCM270X_DT-at86rf233-overlay-drop-to-3MHz.patch b/target/linux/brcm2708/patches-4.4/0150-BCM270X_DT-at86rf233-overlay-drop-to-3MHz.patch new file mode 100644 index 0000000000..4e150a1a16 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0150-BCM270X_DT-at86rf233-overlay-drop-to-3MHz.patch @@ -0,0 +1,36 @@ +From f7f0e8b831ae66fc19c1f9457975d0f8b31ea601 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 18 Feb 2016 15:28:14 +0000 +Subject: [PATCH 150/232] BCM270X_DT: at86rf233 overlay - drop to 3MHz + +The consensus is that 6MHz is too fast, but that 3MHz is OK. + +See: https://github.com/raspberrypi/linux/issues/1294 + https://github.com/raspberrypi/linux/issues/1151 +--- + arch/arm/boot/dts/overlays/README | 2 +- + arch/arm/boot/dts/overlays/at86rf233-overlay.dts | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -188,7 +188,7 @@ Load: dtoverlay=at86rf233,=; /* active high */ + reset-gpio = <&gpio 24 1>; + sleep-gpio = <&gpio 25 1>; +- spi-max-frequency = <6000000>; ++ spi-max-frequency = <3000000>; + xtal-trim = /bits/ 8 <0xf>; + }; + }; diff --git a/target/linux/brcm2708/patches-4.4/0151-bcm2835-sdhost-Downgrade-log-message-status.patch b/target/linux/brcm2708/patches-4.4/0151-bcm2835-sdhost-Downgrade-log-message-status.patch new file mode 100644 index 0000000000..fdb8cb8387 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0151-bcm2835-sdhost-Downgrade-log-message-status.patch @@ -0,0 +1,22 @@ +From 76667f779c11557d7ebc84130619c25f3c1642af Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 19 Feb 2016 12:04:48 +0000 +Subject: [PATCH 151/232] bcm2835-sdhost: Downgrade log message status + +--- + drivers/mmc/host/bcm2835-sdhost.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/mmc/host/bcm2835-sdhost.c ++++ b/drivers/mmc/host/bcm2835-sdhost.c +@@ -232,8 +232,8 @@ static void log_init(u32 bus_to_phys) + sdhost_log_buf = dma_zalloc_coherent(NULL, LOG_SIZE, &sdhost_log_addr, + GFP_KERNEL); + if (sdhost_log_buf) { +- pr_err("sdhost: log_buf @ %p (%x)\n", +- sdhost_log_buf, sdhost_log_addr); ++ pr_info("sdhost: log_buf @ %p (%x)\n", ++ sdhost_log_buf, sdhost_log_addr); + timer_base = ioremap_nocache(bus_to_phys + 0x7e003000, SZ_4K); + if (!timer_base) + pr_err("sdhost: failed to remap timer\n"); diff --git a/target/linux/brcm2708/patches-4.4/0151-dcw_otg-trim-xfer-length-when-buffer-larger-than-all.patch b/target/linux/brcm2708/patches-4.4/0151-dcw_otg-trim-xfer-length-when-buffer-larger-than-all.patch deleted file mode 100644 index 203e97b8e9..0000000000 --- a/target/linux/brcm2708/patches-4.4/0151-dcw_otg-trim-xfer-length-when-buffer-larger-than-all.patch +++ /dev/null @@ -1,37 +0,0 @@ -From bc7400cadff6bf33451b124eac2e8bf80ebb0197 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Fri, 12 Feb 2016 14:50:25 +0000 -Subject: [PATCH 151/170] dcw_otg: trim xfer length when buffer larger than - allocated size is received - ---- - drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 11 +++++++++++ - 1 file changed, 11 insertions(+) - ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -@@ -737,6 +737,11 @@ static int update_urb_state_xfer_comp(dw - DWC_OTG_HC_XFER_COMPLETE, - &short_read); - -+ if (urb->actual_length + xfer_length > urb->length) { -+ DWC_WARN("%s(): trimming xfer length\n", __func__); -+ xfer_length = urb->length - urb->actual_length; -+ } -+ - /* 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, -@@ -1423,6 +1428,12 @@ static void update_urb_state_xfer_intr(d - { - uint32_t bytes_transferred = get_actual_xfer_length(hc, hc_regs, qtd, - halt_status, NULL); -+ -+ if (urb->actual_length + bytes_transferred > urb->length) { -+ DWC_WARN("%s(): trimming xfer length\n", __func__); -+ bytes_transferred = urb->length - urb->actual_length; -+ } -+ - /* non DWORD-aligned buffer case handling. */ - if (hc->align_buff && bytes_transferred && hc->ep_is_in) { - dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf, diff --git a/target/linux/brcm2708/patches-4.4/0152-bcm2835-sdhost-Restore-ATOMIC-flag-to-PIO-sg-mapping.patch b/target/linux/brcm2708/patches-4.4/0152-bcm2835-sdhost-Restore-ATOMIC-flag-to-PIO-sg-mapping.patch deleted file mode 100644 index 48aca151dc..0000000000 --- a/target/linux/brcm2708/patches-4.4/0152-bcm2835-sdhost-Restore-ATOMIC-flag-to-PIO-sg-mapping.patch +++ /dev/null @@ -1,32 +0,0 @@ -From f395ff998f2bde846499b1a0ef84519989dc2d4e Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 15 Feb 2016 10:00:27 +0000 -Subject: [PATCH 152/170] bcm2835-sdhost: Restore ATOMIC flag to PIO sg mapping - -Allocation problems have been seen in a wireless driver, and -this is the only change which might have been responsible. ---- - drivers/mmc/host/bcm2835-sdhost.c | 7 +++---- - 1 file changed, 3 insertions(+), 4 deletions(-) - ---- a/drivers/mmc/host/bcm2835-sdhost.c -+++ b/drivers/mmc/host/bcm2835-sdhost.c -@@ -874,15 +874,14 @@ static void bcm2835_sdhost_prepare_data( - host->flush_fifo = 0; - host->data->bytes_xfered = 0; - -- - if (!host->dma_desc) { - /* Use PIO */ -- int flags; -+ int flags = SG_MITER_ATOMIC; - - if (data->flags & MMC_DATA_READ) -- flags = SG_MITER_TO_SG; -+ flags |= SG_MITER_TO_SG; - else -- flags = SG_MITER_FROM_SG; -+ flags |= SG_MITER_FROM_SG; - sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); - host->blocks = data->blocks; - } diff --git a/target/linux/brcm2708/patches-4.4/0152-config-Enable-HCI-over-UARTs.patch b/target/linux/brcm2708/patches-4.4/0152-config-Enable-HCI-over-UARTs.patch new file mode 100644 index 0000000000..9390c357a9 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0152-config-Enable-HCI-over-UARTs.patch @@ -0,0 +1,33 @@ +From ab8cd307467682fa1df2bfadeb3ac88c086f861c Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 15 Jan 2016 16:48:27 +0000 +Subject: [PATCH 152/232] config: Enable HCI over UARTs + +--- + arch/arm/configs/bcm2709_defconfig | 3 +++ + arch/arm/configs/bcmrpi_defconfig | 2 ++ + 2 files changed, 5 insertions(+) + +--- a/arch/arm/configs/bcm2709_defconfig ++++ b/arch/arm/configs/bcm2709_defconfig +@@ -376,6 +376,9 @@ CONFIG_BT_BNEP_PROTO_FILTER=y + CONFIG_BT_HIDP=m + CONFIG_BT_6LOWPAN=m + CONFIG_BT_HCIBTUSB=m ++CONFIG_BT_HCIUART=m ++CONFIG_BT_HCIUART_3WIRE=y ++CONFIG_BT_HCIUART_BCM=y + CONFIG_BT_HCIBCM203X=m + CONFIG_BT_HCIBPA10X=m + CONFIG_BT_HCIBFUSB=m +--- a/arch/arm/configs/bcmrpi_defconfig ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -370,6 +370,8 @@ CONFIG_BT_HIDP=m + CONFIG_BT_6LOWPAN=m + CONFIG_BT_HCIBTUSB=m + CONFIG_BT_HCIUART=m ++CONFIG_BT_HCIUART_3WIRE=y ++CONFIG_BT_HCIUART_BCM=y + CONFIG_BT_HCIBCM203X=m + CONFIG_BT_HCIBPA10X=m + CONFIG_BT_HCIBFUSB=m diff --git a/target/linux/brcm2708/patches-4.4/0153-Revert-Add-blk_pos-parameter-to-mmc-multi_io_quirk-c.patch b/target/linux/brcm2708/patches-4.4/0153-Revert-Add-blk_pos-parameter-to-mmc-multi_io_quirk-c.patch deleted file mode 100644 index 1554f8a9b5..0000000000 --- a/target/linux/brcm2708/patches-4.4/0153-Revert-Add-blk_pos-parameter-to-mmc-multi_io_quirk-c.patch +++ /dev/null @@ -1,79 +0,0 @@ -From d2943c4ee1c52cb2c9802be194ca90442e4fe34f Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Tue, 16 Feb 2016 08:47:56 +0000 -Subject: [PATCH 153/170] Revert "Add blk_pos parameter to mmc multi_io_quirk - callback" - -This reverts commit aab95f9b10e8c3d32de2bf163b86f220c88214fe. - -The bcm2835-sdhost driver no longer needs this patch. ---- - drivers/mmc/card/block.c | 1 - - drivers/mmc/host/omap_hsmmc.c | 4 +--- - drivers/mmc/host/sh_mobile_sdhi.c | 4 +--- - drivers/mmc/host/tmio_mmc_pio.c | 4 +--- - include/linux/mmc/host.h | 4 +--- - 5 files changed, 4 insertions(+), 13 deletions(-) - ---- a/drivers/mmc/card/block.c -+++ b/drivers/mmc/card/block.c -@@ -1518,7 +1518,6 @@ static void mmc_blk_rw_rq_prep(struct mm - brq->data.blocks = card->host->ops->multi_io_quirk(card, - (rq_data_dir(req) == READ) ? - MMC_DATA_READ : MMC_DATA_WRITE, -- blk_rq_pos(req), - brq->data.blocks); - } - ---- a/drivers/mmc/host/omap_hsmmc.c -+++ b/drivers/mmc/host/omap_hsmmc.c -@@ -1832,9 +1832,7 @@ static void omap_hsmmc_conf_bus_power(st - } - - static int omap_hsmmc_multi_io_quirk(struct mmc_card *card, -- unsigned int direction, -- u32 blk_pos, -- int blk_size) -+ unsigned int direction, int blk_size) - { - /* This controller can't do multiblock reads due to hw bugs */ - if (direction == MMC_DATA_READ) ---- a/drivers/mmc/host/sh_mobile_sdhi.c -+++ b/drivers/mmc/host/sh_mobile_sdhi.c -@@ -170,9 +170,7 @@ static int sh_mobile_sdhi_write16_hook(s - } - - static int sh_mobile_sdhi_multi_io_quirk(struct mmc_card *card, -- unsigned int direction, -- u32 blk_pos, -- int blk_size) -+ unsigned int direction, int blk_size) - { - /* - * In Renesas controllers, when performing a ---- a/drivers/mmc/host/tmio_mmc_pio.c -+++ b/drivers/mmc/host/tmio_mmc_pio.c -@@ -1003,9 +1003,7 @@ static int tmio_mmc_get_ro(struct mmc_ho - } - - static int tmio_multi_io_quirk(struct mmc_card *card, -- unsigned int direction, -- u32 blk_pos, -- int blk_size) -+ unsigned int direction, int blk_size) - { - struct tmio_mmc_host *host = mmc_priv(card->host); - ---- a/include/linux/mmc/host.h -+++ b/include/linux/mmc/host.h -@@ -143,9 +143,7 @@ struct mmc_host_ops { - * I/O. Returns the number of supported blocks for the request. - */ - int (*multi_io_quirk)(struct mmc_card *card, -- unsigned int direction, -- u32 blk_pos, -- int blk_size); -+ unsigned int direction, int blk_size); - }; - - struct mmc_card; diff --git a/target/linux/brcm2708/patches-4.4/0153-hci_h5-Don-t-send-conf_req-when-ACTIVE.patch b/target/linux/brcm2708/patches-4.4/0153-hci_h5-Don-t-send-conf_req-when-ACTIVE.patch new file mode 100644 index 0000000000..8de4bec792 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0153-hci_h5-Don-t-send-conf_req-when-ACTIVE.patch @@ -0,0 +1,23 @@ +From 85dc4378167d99218df01bb0512a4def9710a0a5 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 17 Dec 2015 13:37:07 +0000 +Subject: [PATCH 153/232] hci_h5: Don't send conf_req when ACTIVE + +Without this patch, a modem and kernel can continuously bombard each +other with conf_req and conf_rsp messages, in a demented game of tag. +--- + drivers/bluetooth/hci_h5.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/bluetooth/hci_h5.c ++++ b/drivers/bluetooth/hci_h5.c +@@ -314,7 +314,8 @@ static void h5_handle_internal_rx(struct + h5_link_control(hu, conf_req, 3); + } else if (memcmp(data, conf_req, 2) == 0) { + h5_link_control(hu, conf_rsp, 2); +- h5_link_control(hu, conf_req, 3); ++ if (h5->state != H5_ACTIVE) ++ h5_link_control(hu, conf_req, 3); + } else if (memcmp(data, conf_rsp, 2) == 0) { + if (H5_HDR_LEN(hdr) > 2) + h5->tx_win = (data[2] & 7); diff --git a/target/linux/brcm2708/patches-4.4/0154-Updated-smsc95xx-driver-to-check-for-a-valid-MAC-add.patch b/target/linux/brcm2708/patches-4.4/0154-Updated-smsc95xx-driver-to-check-for-a-valid-MAC-add.patch deleted file mode 100644 index 058725d10b..0000000000 --- a/target/linux/brcm2708/patches-4.4/0154-Updated-smsc95xx-driver-to-check-for-a-valid-MAC-add.patch +++ /dev/null @@ -1,40 +0,0 @@ -From ee83592659ae5dc5ceee63790a8cba57ebfa57b3 Mon Sep 17 00:00:00 2001 -From: Craig Roberts -Date: Tue, 16 Feb 2016 10:03:42 +0000 -Subject: [PATCH 154/170] Updated smsc95xx driver to check for a valid MAC - address in eeprom before using smsc95xx.macaddr parameter passed on command - line. - -The built-in RPi adaptor will still get a MAC address based on the parameter passed on the command line as the RPi hardware does not have an eeprom, -however usb->ethernet adaptors using the same driver should have an eeprom with MAC address as part of their hardware and therefore will use this -meaning they don't end up with the same MAC address as the built-in RPi adaptor. ---- - drivers/net/usb/smsc95xx.c | 10 +++++----- - 1 file changed, 5 insertions(+), 5 deletions(-) - ---- a/drivers/net/usb/smsc95xx.c -+++ b/drivers/net/usb/smsc95xx.c -@@ -817,10 +817,6 @@ static int smsc95xx_is_macaddr_param(str - - 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) { -@@ -831,7 +827,11 @@ static void smsc95xx_init_mac_address(st - } - } - -- /* no eeprom, or eeprom values are invalid. generate random MAC */ -+ /* Check module parameters */ -+ if (smsc95xx_is_macaddr_param(dev, dev->net->dev_addr)) -+ return; -+ -+ /* no eeprom, or eeprom values are invalid, and no module parameter specified to set MAC. Generate random MAC */ - eth_hw_addr_random(dev->net); - netif_dbg(dev, ifup, dev->net, "MAC address set to eth_random_addr\n"); - } diff --git a/target/linux/brcm2708/patches-4.4/0154-amba_pl011-Don-t-use-DT-aliases-for-numbering.patch b/target/linux/brcm2708/patches-4.4/0154-amba_pl011-Don-t-use-DT-aliases-for-numbering.patch new file mode 100644 index 0000000000..3b2b1e2c45 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0154-amba_pl011-Don-t-use-DT-aliases-for-numbering.patch @@ -0,0 +1,29 @@ +From e9950fa4e93333baad36537cc46121cc7cecf455 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 23 Feb 2016 17:26:48 +0000 +Subject: [PATCH 154/232] amba_pl011: Don't use DT aliases for numbering + +The pl011 driver looks for DT aliases of the form "serial", +and if found uses as the device ID. This can cause +/dev/ttyAMA0 to become /dev/ttyAMA1, which is confusing if the +other serial port is provided by the 8250 driver which doesn't +use the same logic. +--- + drivers/tty/serial/amba-pl011.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/tty/serial/amba-pl011.c ++++ b/drivers/tty/serial/amba-pl011.c +@@ -2313,7 +2313,12 @@ static int pl011_setup_port(struct devic + if (IS_ERR(base)) + return PTR_ERR(base); + ++ /* Don't use DT serial aliases - it causes the device to ++ be renumbered to ttyAMA1 if it is the second serial port in the ++ system, even though the other one is ttyS0. The 8250 driver ++ doesn't use this logic, so always remains ttyS0. + index = pl011_probe_dt_alias(index, dev); ++ */ + + uap->old_cr = 0; + uap->port.dev = dev; diff --git a/target/linux/brcm2708/patches-4.4/0155-clk-bcm2835-Add-bindings-for-the-auxiliary-periphera.patch b/target/linux/brcm2708/patches-4.4/0155-clk-bcm2835-Add-bindings-for-the-auxiliary-periphera.patch new file mode 100644 index 0000000000..24c0ccabe6 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0155-clk-bcm2835-Add-bindings-for-the-auxiliary-periphera.patch @@ -0,0 +1,72 @@ +From 0316ebbf545738333e188197d196b618c17b171e Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Tue, 15 Dec 2015 15:35:57 -0800 +Subject: [PATCH 155/232] clk: bcm2835: Add bindings for the auxiliary + peripheral clock gates. + +These will be used for enabling UART1, SPI1, and SPI2. + +Signed-off-by: Eric Anholt +Acked-by: Rob Herring +Signed-off-by: Michael Turquette +--- + .../bindings/clock/brcm,bcm2835-aux-clock.txt | 31 ++++++++++++++++++++++ + include/dt-bindings/clock/bcm2835-aux.h | 17 ++++++++++++ + 2 files changed, 48 insertions(+) + create mode 100644 Documentation/devicetree/bindings/clock/brcm,bcm2835-aux-clock.txt + create mode 100644 include/dt-bindings/clock/bcm2835-aux.h + +--- /dev/null ++++ b/Documentation/devicetree/bindings/clock/brcm,bcm2835-aux-clock.txt +@@ -0,0 +1,31 @@ ++Broadcom BCM2835 auxiliary peripheral support ++ ++This binding uses the common clock binding: ++ Documentation/devicetree/bindings/clock/clock-bindings.txt ++ ++The auxiliary peripherals (UART, SPI1, and SPI2) have a small register ++area controlling clock gating to the peripherals, and providing an IRQ ++status register. ++ ++Required properties: ++- compatible: Should be "brcm,bcm2835-aux" ++- #clock-cells: Should be <1>. The permitted clock-specifier values can be ++ found in include/dt-bindings/clock/bcm2835-aux.h ++- reg: Specifies base physical address and size of the registers ++- clocks: The parent clock phandle ++ ++Example: ++ ++ clocks: cprman@7e101000 { ++ compatible = "brcm,bcm2835-cprman"; ++ #clock-cells = <1>; ++ reg = <0x7e101000 0x2000>; ++ clocks = <&clk_osc>; ++ }; ++ ++ aux: aux@0x7e215004 { ++ compatible = "brcm,bcm2835-aux"; ++ #clock-cells = <1>; ++ reg = <0x7e215000 0x8>; ++ clocks = <&clocks BCM2835_CLOCK_VPU>; ++ }; +--- /dev/null ++++ b/include/dt-bindings/clock/bcm2835-aux.h +@@ -0,0 +1,17 @@ ++/* ++ * Copyright (C) 2015 Broadcom 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 version 2. ++ * ++ * This program is distributed "as is" WITHOUT ANY WARRANTY of any ++ * kind, whether express or implied; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#define BCM2835_AUX_CLOCK_UART 0 ++#define BCM2835_AUX_CLOCK_SPI1 1 ++#define BCM2835_AUX_CLOCK_SPI2 2 ++#define BCM2835_AUX_CLOCK_COUNT 3 diff --git a/target/linux/brcm2708/patches-4.4/0155-dcw_otg-Make-trimming-messages-less-noisy.patch b/target/linux/brcm2708/patches-4.4/0155-dcw_otg-Make-trimming-messages-less-noisy.patch deleted file mode 100644 index ef188f1c66..0000000000 --- a/target/linux/brcm2708/patches-4.4/0155-dcw_otg-Make-trimming-messages-less-noisy.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 1c43ff69763f4537b76b3248850256da2bda211b Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 17 Feb 2016 19:02:31 +0000 -Subject: [PATCH 155/170] dcw_otg: Make trimming messages less noisy - ---- - drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 6 ++++-- - 1 file changed, 4 insertions(+), 2 deletions(-) - ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -@@ -738,7 +738,8 @@ static int update_urb_state_xfer_comp(dw - &short_read); - - if (urb->actual_length + xfer_length > urb->length) { -- DWC_WARN("%s(): trimming xfer length\n", __func__); -+ printk_once(KERN_DEBUG "dwc_otg: DEVICE:%03d : %s:%d:trimming xfer length\n", -+ hc->dev_addr, __func__, __LINE__); - xfer_length = urb->length - urb->actual_length; - } - -@@ -1430,7 +1431,8 @@ static void update_urb_state_xfer_intr(d - halt_status, NULL); - - if (urb->actual_length + bytes_transferred > urb->length) { -- DWC_WARN("%s(): trimming xfer length\n", __func__); -+ printk_once(KERN_DEBUG "dwc_otg: DEVICE:%03d : %s:%d:trimming xfer length\n", -+ hc->dev_addr, __func__, __LINE__); - bytes_transferred = urb->length - urb->actual_length; - } - diff --git a/target/linux/brcm2708/patches-4.4/0156-BCM270X_DT-at86rf233-overlay-drop-to-3MHz.patch b/target/linux/brcm2708/patches-4.4/0156-BCM270X_DT-at86rf233-overlay-drop-to-3MHz.patch deleted file mode 100644 index 9338280595..0000000000 --- a/target/linux/brcm2708/patches-4.4/0156-BCM270X_DT-at86rf233-overlay-drop-to-3MHz.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 7f9781f9875f2aa638d5afdaa9709fa1ef9bda8d Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Thu, 18 Feb 2016 15:28:14 +0000 -Subject: [PATCH 156/170] BCM270X_DT: at86rf233 overlay - drop to 3MHz - -The consensus is that 6MHz is too fast, but that 3MHz is OK. - -See: https://github.com/raspberrypi/linux/issues/1294 - https://github.com/raspberrypi/linux/issues/1151 ---- - arch/arm/boot/dts/overlays/README | 2 +- - arch/arm/boot/dts/overlays/at86rf233-overlay.dts | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -188,7 +188,7 @@ Load: dtoverlay=at86rf233,=; /* active high */ - reset-gpio = <&gpio 24 1>; - sleep-gpio = <&gpio 25 1>; -- spi-max-frequency = <6000000>; -+ spi-max-frequency = <3000000>; - xtal-trim = /bits/ 8 <0xf>; - }; - }; diff --git a/target/linux/brcm2708/patches-4.4/0156-clk-bcm2835-Add-a-driver-for-the-auxiliary-periphera.patch b/target/linux/brcm2708/patches-4.4/0156-clk-bcm2835-Add-a-driver-for-the-auxiliary-periphera.patch new file mode 100644 index 0000000000..4518af1a28 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0156-clk-bcm2835-Add-a-driver-for-the-auxiliary-periphera.patch @@ -0,0 +1,118 @@ +From cfe1737d9aab03ecd152291df704606a7724eb21 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Tue, 15 Dec 2015 15:35:58 -0800 +Subject: [PATCH 156/232] clk: bcm2835: Add a driver for the auxiliary + peripheral clock gates. + +There are a pair of SPI masters and a mini UART that were last minute +additions. As a result, they didn't get integrated in the same way as +the other gates off of the VPU clock in CPRMAN. + +Signed-off-by: Eric Anholt +Signed-off-by: Michael Turquette + +updated Makefile to preserve the rasoberry pi architectures +--- + drivers/clk/bcm/Makefile | 1 + + drivers/clk/bcm/clk-bcm2835-aux.c | 85 +++++++++++++++++++++++++++++++++++++++ + 2 files changed, 86 insertions(+) + create mode 100644 drivers/clk/bcm/clk-bcm2835-aux.c + +--- a/drivers/clk/bcm/Makefile ++++ b/drivers/clk/bcm/Makefile +@@ -4,6 +4,7 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281 + obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o + obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o + obj-$(CONFIG_ARCH_BCM2835)$(CONFIG_ARCH_BCM2708)$(CONFIG_ARCH_BCM2709) += clk-bcm2835.o ++obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835-aux.o + obj-$(CONFIG_COMMON_CLK_IPROC) += clk-ns2.o + obj-$(CONFIG_ARCH_BCM_CYGNUS) += clk-cygnus.o + obj-$(CONFIG_ARCH_BCM_NSP) += clk-nsp.o +--- /dev/null ++++ b/drivers/clk/bcm/clk-bcm2835-aux.c +@@ -0,0 +1,85 @@ ++/* ++ * Copyright (C) 2015 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define BCM2835_AUXIRQ 0x00 ++#define BCM2835_AUXENB 0x04 ++ ++static int bcm2835_aux_clk_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct clk_onecell_data *onecell; ++ const char *parent; ++ struct clk *parent_clk; ++ struct resource *res; ++ void __iomem *reg, *gate; ++ ++ parent_clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(parent_clk)) ++ return PTR_ERR(parent_clk); ++ parent = __clk_get_name(parent_clk); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ reg = devm_ioremap_resource(dev, res); ++ if (!reg) ++ return -ENODEV; ++ ++ onecell = devm_kmalloc(dev, sizeof(*onecell), GFP_KERNEL); ++ if (!onecell) ++ return -ENOMEM; ++ onecell->clk_num = BCM2835_AUX_CLOCK_COUNT; ++ onecell->clks = devm_kcalloc(dev, BCM2835_AUX_CLOCK_COUNT, ++ sizeof(*onecell->clks), GFP_KERNEL); ++ if (!onecell->clks) ++ return -ENOMEM; ++ ++ gate = reg + BCM2835_AUXENB; ++ onecell->clks[BCM2835_AUX_CLOCK_UART] = ++ clk_register_gate(dev, "aux_uart", parent, 0, gate, 0, 0, NULL); ++ ++ onecell->clks[BCM2835_AUX_CLOCK_SPI1] = ++ clk_register_gate(dev, "aux_spi1", parent, 0, gate, 1, 0, NULL); ++ ++ onecell->clks[BCM2835_AUX_CLOCK_SPI2] = ++ clk_register_gate(dev, "aux_spi2", parent, 0, gate, 2, 0, NULL); ++ ++ of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get, onecell); ++ ++ return 0; ++} ++ ++static const struct of_device_id bcm2835_aux_clk_of_match[] = { ++ { .compatible = "brcm,bcm2835-aux", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, bcm2835_aux_clk_of_match); ++ ++static struct platform_driver bcm2835_aux_clk_driver = { ++ .driver = { ++ .name = "bcm2835-aux-clk", ++ .of_match_table = bcm2835_aux_clk_of_match, ++ }, ++ .probe = bcm2835_aux_clk_probe, ++}; ++builtin_platform_driver(bcm2835_aux_clk_driver); ++ ++MODULE_AUTHOR("Eric Anholt "); ++MODULE_DESCRIPTION("BCM2835 auxiliary peripheral clock driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/brcm2708/patches-4.4/0157-Aux-SPI-1-2-implementation.patch b/target/linux/brcm2708/patches-4.4/0157-Aux-SPI-1-2-implementation.patch new file mode 100644 index 0000000000..54a9fefb69 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0157-Aux-SPI-1-2-implementation.patch @@ -0,0 +1,700 @@ +From 2cf8e02af4645b90aa63f8cdbd0cb4403e2bee8f Mon Sep 17 00:00:00 2001 +From: Fraser +Date: Tue, 23 Feb 2016 10:04:37 +1100 +Subject: [PATCH 157/232] Aux SPI 1&2 implementation + +Adds aux spi 1 & 2 devices to compatible raspberry PIs. +* Minor config of the driver build environment to ensure they get built +for CONFIG_ARCH_BCM2708 & CONFIG_ARCH_BCM2709 devices. +* Adds the aux spi driver into the defconfigs as a module. +* Adds the auxiliary and spi1/2 devices into the device tree in a +disabled state +* Provides decide tree overlays which enables the devices and gives +users a degree of control over how they are setup. +--- + arch/arm/boot/dts/bcm2708_common.dtsi | 34 ++++++++- + arch/arm/boot/dts/overlays/Makefile | 6 ++ + arch/arm/boot/dts/overlays/README | 99 +++++++++++++++++++++++++ + arch/arm/boot/dts/overlays/spi1-1cs-overlay.dts | 57 ++++++++++++++ + arch/arm/boot/dts/overlays/spi1-2cs-overlay.dts | 69 +++++++++++++++++ + arch/arm/boot/dts/overlays/spi1-3cs-overlay.dts | 81 ++++++++++++++++++++ + arch/arm/boot/dts/overlays/spi2-1cs-overlay.dts | 57 ++++++++++++++ + arch/arm/boot/dts/overlays/spi2-2cs-overlay.dts | 69 +++++++++++++++++ + arch/arm/boot/dts/overlays/spi2-3cs-overlay.dts | 81 ++++++++++++++++++++ + arch/arm/configs/bcm2709_defconfig | 1 + + arch/arm/configs/bcmrpi_defconfig | 1 + + drivers/clk/bcm/Makefile | 2 +- + drivers/spi/Kconfig | 2 +- + 13 files changed, 556 insertions(+), 3 deletions(-) + create mode 100644 arch/arm/boot/dts/overlays/spi1-1cs-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/spi1-2cs-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/spi1-3cs-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/spi2-1cs-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/spi2-2cs-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/spi2-3cs-overlay.dts + +--- a/arch/arm/boot/dts/bcm2708_common.dtsi ++++ b/arch/arm/boot/dts/bcm2708_common.dtsi +@@ -1,3 +1,4 @@ ++#include + #include "skeleton.dtsi" + + / { +@@ -5,6 +6,7 @@ + + aliases { + audio = &audio; ++ aux = &aux; + sound = &sound; + soc = &soc; + dma = &dma; +@@ -19,6 +21,8 @@ + spi0 = &spi0; + i2c0 = &i2c0; + uart1 = &uart1; ++ spi1 = &spi1; ++ spi2 = &spi2; + mmc = &mmc; + i2c1 = &i2c1; + i2c2 = &i2c2; +@@ -186,6 +190,14 @@ + status = "disabled"; + }; + ++ aux: aux@0x7e215004 { ++ compatible = "brcm,bcm2835-aux"; ++ #clock-cells = <1>; ++ reg = <0x7e215000 0x8>; ++ clocks = <&clk_core>; ++ status = "disabled"; ++ }; ++ + uart1: uart@7e215040 { + compatible = "brcm,bcm2835-aux-uart", "ns16550"; + reg = <0x7e215040 0x40>; +@@ -194,7 +206,27 @@ + reg-shift = <2>; + no-loopback-test; + status = "disabled"; +- }; ++ }; ++ ++ spi1: spi@7e215080 { ++ compatible = "brcm,bcm2835-aux-spi"; ++ reg = <0x7e215080 0x40>, <0x7e215000 0x8>; ++ interrupts = <1 29>; ++ clocks = <&aux BCM2835_AUX_CLOCK_SPI1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ spi2: spi@7e2150C0 { ++ compatible = "brcm,bcm2835-aux-spi"; ++ reg = <0x7e2150C0 0x40>, <0x7e215000 0x8>; ++ interrupts = <1 29>; ++ clocks = <&aux BCM2835_AUX_CLOCK_SPI2>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; + + mmc: mmc@7e300000 { + compatible = "brcm,bcm2835-mmc"; +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -57,6 +57,12 @@ dtb-$(RPI_DT_OVERLAYS) += sdtweak-overla + dtb-$(RPI_DT_OVERLAYS) += smi-dev-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += smi-nand-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += smi-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += spi1-1cs-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += spi1-2cs-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += spi1-3cs-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += spi2-1cs-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += spi2-2cs-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += spi2-3cs-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += spi-gpio35-39-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += tinylcd35-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += uart1-overlay.dtb +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -713,6 +713,105 @@ Load: dtoverlay=spi-gpio35-39 + Params: + + ++Name: spi1-1cs ++Info: Enables spi1 with a single chip select (CS) line and associated spidev ++ dev node. The gpio pin number for the CS line and spidev device node ++ creation are configurable. ++ N.B.: spi1 is only accessible on devices with a 40pin header, eg: ++ A+, B+, Zero and PI2 B; as well as the Compute Module. ++Load: dtoverlay=spi1-1cs,= ++Params: cs0_pin GPIO pin for CS0 (default 18 - BCM SPI1_CE0). ++ cs0_spidev Set to 'disabled' to stop the creation of a ++ userspace device node /dev/spidev1.0 (default ++ is 'okay' or enabled). ++ ++ ++Name: spi1-2cs ++Info: Enables spi1 with two chip select (CS) lines and associated spidev ++ dev nodes. The gpio pin numbers for the CS lines and spidev device node ++ creation are configurable. ++ N.B.: spi1 is only accessible on devices with a 40pin header, eg: ++ A+, B+, Zero and PI2 B; as well as the Compute Module. ++Load: dtoverlay=spi1-2cs,= ++Params: cs0_pin GPIO pin for CS0 (default 18 - BCM SPI1_CE0). ++ cs1_pin GPIO pin for CS1 (default 17 - BCM SPI1_CE1). ++ cs0_spidev Set to 'disabled' to stop the creation of a ++ userspace device node /dev/spidev1.0 (default ++ is 'okay' or enabled). ++ cs1_spidev Set to 'disabled' to stop the creation of a ++ userspace device node /dev/spidev1.1 (default ++ is 'okay' or enabled). ++ ++ ++Name: spi1-3cs ++Info: Enables spi1 with three chip select (CS) lines and associated spidev ++ dev nodes. The gpio pin numbers for the CS lines and spidev device node ++ creation are configurable. ++ N.B.: spi1 is only accessible on devices with a 40pin header, eg: ++ A+, B+, Zero and PI2 B; as well as the Compute Module. ++Load: dtoverlay=spi1-3cs,= ++Params: cs0_pin GPIO pin for CS0 (default 18 - BCM SPI1_CE0). ++ cs1_pin GPIO pin for CS1 (default 17 - BCM SPI1_CE1). ++ cs2_pin GPIO pin for CS2 (default 16 - BCM SPI1_CE2). ++ cs0_spidev Set to 'disabled' to stop the creation of a ++ userspace device node /dev/spidev1.0 (default ++ is 'okay' or enabled). ++ cs1_spidev Set to 'disabled' to stop the creation of a ++ userspace device node /dev/spidev1.1 (default ++ is 'okay' or enabled). ++ cs2_spidev Set to 'disabled' to stop the creation of a ++ userspace device node /dev/spidev1.2 (default ++ is 'okay' or enabled). ++ ++ ++Name: spi2-1cs ++Info: Enables spi2 with a single chip select (CS) line and associated spidev ++ dev node. The gpio pin number for the CS line and spidev device node ++ creation are configurable. ++ N.B.: spi2 is only accessible with the Compute Module. ++Load: dtoverlay=spi2-1cs,= ++Params: cs0_pin GPIO pin for CS0 (default 43 - BCM SPI2_CE0). ++ cs0_spidev Set to 'disabled' to stop the creation of a ++ userspace device node /dev/spidev2.0 (default ++ is 'okay' or enabled). ++ ++ ++Name: spi2-2cs ++Info: Enables spi2 with two chip select (CS) lines and associated spidev ++ dev nodes. The gpio pin numbers for the CS lines and spidev device node ++ creation are configurable. ++ N.B.: spi2 is only accessible with the Compute Module. ++Load: dtoverlay=spi2-2cs,= ++Params: cs0_pin GPIO pin for CS0 (default 43 - BCM SPI2_CE0). ++ cs1_pin GPIO pin for CS1 (default 44 - BCM SPI2_CE1). ++ cs0_spidev Set to 'disabled' to stop the creation of a ++ userspace device node /dev/spidev2.0 (default ++ is 'okay' or enabled). ++ cs1_spidev Set to 'disabled' to stop the creation of a ++ userspace device node /dev/spidev2.1 (default ++ is 'okay' or enabled). ++ ++ ++Name: spi2-3cs ++Info: Enables spi2 with three chip select (CS) lines and associated spidev ++ dev nodes. The gpio pin numbers for the CS lines and spidev device node ++ creation are configurable. ++ N.B.: spi2 is only accessible with the Compute Module. ++Load: dtoverlay=spi2-3cs,= ++Params: cs0_pin GPIO pin for CS0 (default 43 - BCM SPI2_CE0). ++ cs1_pin GPIO pin for CS1 (default 44 - BCM SPI2_CE1). ++ cs2_pin GPIO pin for CS2 (default 45 - BCM SPI2_CE2). ++ cs0_spidev Set to 'disabled' to stop the creation of a ++ userspace device node /dev/spidev2.0 (default ++ is 'okay' or enabled). ++ cs1_spidev Set to 'disabled' to stop the creation of a ++ userspace device node /dev/spidev2.1 (default ++ is 'okay' or enabled). ++ cs2_spidev Set to 'disabled' to stop the creation of a ++ userspace device node /dev/spidev2.2 (default ++ is 'okay' or enabled). ++ ++ + Name: tinylcd35 + Info: 3.5" Color TFT Display by www.tinylcd.com + Options: Touch, RTC, keypad +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/spi1-1cs-overlay.dts +@@ -0,0 +1,57 @@ ++/dts-v1/; ++/plugin/; ++ ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ spi1_pins: spi1_pins { ++ brcm,pins = <19 20 21>; ++ brcm,function = <3>; /* alt4 */ ++ }; ++ ++ spi1_cs_pins: spi1_cs_pins { ++ brcm,pins = <18>; ++ brcm,function = <1>; /* output */ ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&spi1>; ++ frag1: __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi1_pins &spi1_cs_pins>; ++ cs-gpios = <&gpio 18 1>; ++ status = "okay"; ++ ++ spidev1_0: spidev@0 { ++ compatible = "spidev"; ++ reg = <0>; /* CE0 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ status = "okay"; ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&aux>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ __overrides__ { ++ cs0_pin = <&spi1_cs_pins>,"brcm,pins:0", ++ <&frag1>,"cs-gpios:4"; ++ cs0_spidev = <&spidev1_0>,"status"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/spi1-2cs-overlay.dts +@@ -0,0 +1,69 @@ ++/dts-v1/; ++/plugin/; ++ ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ spi1_pins: spi1_pins { ++ brcm,pins = <19 20 21>; ++ brcm,function = <3>; /* alt4 */ ++ }; ++ ++ spi1_cs_pins: spi1_cs_pins { ++ brcm,pins = <18 17>; ++ brcm,function = <1>; /* output */ ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&spi1>; ++ frag1: __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi1_pins &spi1_cs_pins>; ++ cs-gpios = <&gpio 18 1>, <&gpio 17 1>; ++ status = "okay"; ++ ++ spidev1_0: spidev@0 { ++ compatible = "spidev"; ++ reg = <0>; /* CE0 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ status = "okay"; ++ }; ++ ++ spidev1_1: spidev@1 { ++ compatible = "spidev"; ++ reg = <1>; /* CE1 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ status = "okay"; ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&aux>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ __overrides__ { ++ cs0_pin = <&spi1_cs_pins>,"brcm,pins:0", ++ <&frag1>,"cs-gpios:4"; ++ cs1_pin = <&spi1_cs_pins>,"brcm,pins:4", ++ <&frag1>,"cs-gpios:16"; ++ cs0_spidev = <&spidev1_0>,"status"; ++ cs1_spidev = <&spidev1_1>,"status"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/spi1-3cs-overlay.dts +@@ -0,0 +1,81 @@ ++/dts-v1/; ++/plugin/; ++ ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ spi1_pins: spi1_pins { ++ brcm,pins = <19 20 21>; ++ brcm,function = <3>; /* alt4 */ ++ }; ++ ++ spi1_cs_pins: spi1_cs_pins { ++ brcm,pins = <18 17 16>; ++ brcm,function = <1>; /* output */ ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&spi1>; ++ frag1: __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi1_pins &spi1_cs_pins>; ++ cs-gpios = <&gpio 18 1>, <&gpio 17 1>, <&gpio 16 1>; ++ status = "okay"; ++ ++ spidev1_0: spidev@0 { ++ compatible = "spidev"; ++ reg = <0>; /* CE0 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ status = "okay"; ++ }; ++ ++ spidev1_1: spidev@1 { ++ compatible = "spidev"; ++ reg = <1>; /* CE1 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ status = "okay"; ++ }; ++ ++ spidev1_2: spidev@2 { ++ compatible = "spidev"; ++ reg = <2>; /* CE2 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ status = "okay"; ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&aux>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ __overrides__ { ++ cs0_pin = <&spi1_cs_pins>,"brcm,pins:0", ++ <&frag1>,"cs-gpios:4"; ++ cs1_pin = <&spi1_cs_pins>,"brcm,pins:4", ++ <&frag1>,"cs-gpios:16"; ++ cs2_pin = <&spi1_cs_pins>,"brcm,pins:8", ++ <&frag1>,"cs-gpios:28"; ++ cs0_spidev = <&spidev1_0>,"status"; ++ cs1_spidev = <&spidev1_1>,"status"; ++ cs2_spidev = <&spidev1_2>,"status"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/spi2-1cs-overlay.dts +@@ -0,0 +1,57 @@ ++/dts-v1/; ++/plugin/; ++ ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ spi2_pins: spi2_pins { ++ brcm,pins = <40 41 42>; ++ brcm,function = <3>; /* alt4 */ ++ }; ++ ++ spi2_cs_pins: spi2_cs_pins { ++ brcm,pins = <43>; ++ brcm,function = <1>; /* output */ ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&spi2>; ++ frag1: __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi2_pins &spi2_cs_pins>; ++ cs-gpios = <&gpio 43 1>; ++ status = "okay"; ++ ++ spidev2_0: spidev@0 { ++ compatible = "spidev"; ++ reg = <0>; /* CE0 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ status = "okay"; ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&aux>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ __overrides__ { ++ cs0_pin = <&spi2_cs_pins>,"brcm,pins:0", ++ <&frag1>,"cs-gpios:4"; ++ cs0_spidev = <&spidev2_0>,"status"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/spi2-2cs-overlay.dts +@@ -0,0 +1,69 @@ ++/dts-v1/; ++/plugin/; ++ ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ spi2_pins: spi2_pins { ++ brcm,pins = <40 41 42>; ++ brcm,function = <3>; /* alt4 */ ++ }; ++ ++ spi2_cs_pins: spi2_cs_pins { ++ brcm,pins = <43 44>; ++ brcm,function = <1>; /* output */ ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&spi2>; ++ frag1: __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi2_pins &spi2_cs_pins>; ++ cs-gpios = <&gpio 43 1>, <&gpio 44 1>; ++ status = "okay"; ++ ++ spidev2_0: spidev@0 { ++ compatible = "spidev"; ++ reg = <0>; /* CE0 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ status = "okay"; ++ }; ++ ++ spidev2_1: spidev@1 { ++ compatible = "spidev"; ++ reg = <1>; /* CE1 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ status = "okay"; ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&aux>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ __overrides__ { ++ cs0_pin = <&spi2_cs_pins>,"brcm,pins:0", ++ <&frag1>,"cs-gpios:4"; ++ cs1_pin = <&spi2_cs_pins>,"brcm,pins:4", ++ <&frag1>,"cs-gpios:16"; ++ cs0_spidev = <&spidev2_0>,"status"; ++ cs1_spidev = <&spidev2_1>,"status"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/spi2-3cs-overlay.dts +@@ -0,0 +1,81 @@ ++/dts-v1/; ++/plugin/; ++ ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ spi2_pins: spi2_pins { ++ brcm,pins = <40 41 42>; ++ brcm,function = <3>; /* alt4 */ ++ }; ++ ++ spi2_cs_pins: spi2_cs_pins { ++ brcm,pins = <43 44 45>; ++ brcm,function = <1>; /* output */ ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&spi2>; ++ frag1: __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi2_pins &spi2_cs_pins>; ++ cs-gpios = <&gpio 43 1>, <&gpio 44 1>, <&gpio 45 1>; ++ status = "okay"; ++ ++ spidev2_0: spidev@0 { ++ compatible = "spidev"; ++ reg = <0>; /* CE0 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ status = "okay"; ++ }; ++ ++ spidev2_1: spidev@1 { ++ compatible = "spidev"; ++ reg = <1>; /* CE1 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ status = "okay"; ++ }; ++ ++ spidev2_2: spidev@2 { ++ compatible = "spidev"; ++ reg = <2>; /* CE2 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ status = "okay"; ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&aux>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ __overrides__ { ++ cs0_pin = <&spi2_cs_pins>,"brcm,pins:0", ++ <&frag1>,"cs-gpios:4"; ++ cs1_pin = <&spi2_cs_pins>,"brcm,pins:4", ++ <&frag1>,"cs-gpios:16"; ++ cs2_pin = <&spi2_cs_pins>,"brcm,pins:8", ++ <&frag1>,"cs-gpios:28"; ++ cs0_spidev = <&spidev2_0>,"status"; ++ cs1_spidev = <&spidev2_1>,"status"; ++ cs2_spidev = <&spidev2_2>,"status"; ++ }; ++}; +--- a/arch/arm/configs/bcm2709_defconfig ++++ b/arch/arm/configs/bcm2709_defconfig +@@ -601,6 +601,7 @@ CONFIG_I2C_BCM2708=m + CONFIG_I2C_GPIO=m + CONFIG_SPI=y + CONFIG_SPI_BCM2835=m ++CONFIG_SPI_BCM2835AUX=m + CONFIG_SPI_SPIDEV=y + CONFIG_PPS=m + CONFIG_PPS_CLIENT_LDISC=m +--- a/arch/arm/configs/bcmrpi_defconfig ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -593,6 +593,7 @@ CONFIG_I2C_BCM2708=m + CONFIG_I2C_GPIO=m + CONFIG_SPI=y + CONFIG_SPI_BCM2835=m ++CONFIG_SPI_BCM2835AUX=m + CONFIG_SPI_SPIDEV=y + CONFIG_PPS=m + CONFIG_PPS_CLIENT_LDISC=m +--- a/drivers/clk/bcm/Makefile ++++ b/drivers/clk/bcm/Makefile +@@ -4,7 +4,7 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281 + obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o + obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o + obj-$(CONFIG_ARCH_BCM2835)$(CONFIG_ARCH_BCM2708)$(CONFIG_ARCH_BCM2709) += clk-bcm2835.o +-obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835-aux.o ++obj-$(CONFIG_ARCH_BCM2835)$(CONFIG_ARCH_BCM2708)$(CONFIG_ARCH_BCM2709) += clk-bcm2835-aux.o + obj-$(CONFIG_COMMON_CLK_IPROC) += clk-ns2.o + obj-$(CONFIG_ARCH_BCM_CYGNUS) += clk-cygnus.o + obj-$(CONFIG_ARCH_BCM_NSP) += clk-nsp.o +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -90,7 +90,7 @@ config SPI_BCM2835 + + config SPI_BCM2835AUX + tristate "BCM2835 SPI auxiliary controller" +- depends on ARCH_BCM2835 || COMPILE_TEST ++ depends on ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709 || COMPILE_TEST + depends on GPIOLIB + help + This selects a driver for the Broadcom BCM2835 SPI aux master. diff --git a/target/linux/brcm2708/patches-4.4/0157-bcm2835-sdhost-Downgrade-log-message-status.patch b/target/linux/brcm2708/patches-4.4/0157-bcm2835-sdhost-Downgrade-log-message-status.patch deleted file mode 100644 index 6654a0c0d0..0000000000 --- a/target/linux/brcm2708/patches-4.4/0157-bcm2835-sdhost-Downgrade-log-message-status.patch +++ /dev/null @@ -1,22 +0,0 @@ -From 5af831a3f879c89c8c0b39c8f88e7600bdc3a765 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Fri, 19 Feb 2016 12:04:48 +0000 -Subject: [PATCH 157/170] bcm2835-sdhost: Downgrade log message status - ---- - drivers/mmc/host/bcm2835-sdhost.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - ---- a/drivers/mmc/host/bcm2835-sdhost.c -+++ b/drivers/mmc/host/bcm2835-sdhost.c -@@ -232,8 +232,8 @@ static void log_init(u32 bus_to_phys) - sdhost_log_buf = dma_zalloc_coherent(NULL, LOG_SIZE, &sdhost_log_addr, - GFP_KERNEL); - if (sdhost_log_buf) { -- pr_err("sdhost: log_buf @ %p (%x)\n", -- sdhost_log_buf, sdhost_log_addr); -+ pr_info("sdhost: log_buf @ %p (%x)\n", -+ sdhost_log_buf, sdhost_log_addr); - timer_base = ioremap_nocache(bus_to_phys + 0x7e003000, SZ_4K); - if (!timer_base) - pr_err("sdhost: failed to remap timer\n"); diff --git a/target/linux/brcm2708/patches-4.4/0158-ASoC-bcm-add-missing-.owner-fields-in-sound-card-dri.patch b/target/linux/brcm2708/patches-4.4/0158-ASoC-bcm-add-missing-.owner-fields-in-sound-card-dri.patch new file mode 100644 index 0000000000..d43c8ef31a --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0158-ASoC-bcm-add-missing-.owner-fields-in-sound-card-dri.patch @@ -0,0 +1,108 @@ +From 29c79ce79c70db703a4621ecc7d24208ca991981 Mon Sep 17 00:00:00 2001 +From: Matthias Reichl +Date: Tue, 23 Feb 2016 17:28:23 +0100 +Subject: [PATCH 158/232] ASoC: bcm: add missing .owner fields in sound card + drivers + +If snd_soc_card.owner is not set the kernel won't do usage refcounting +and one can remove the card driver module while it's in use (eg playback +active) - which leads to a kernel crash. + +The missing owner field also prevents ALSA slot ordering +(options snd slots=module-name1,module-name-2,...) from working with +the I2S cards as it has no module name to match against. + +Fix these issues by setting the .owner field in the snd_soc_card structs. + +Signed-off-by: Matthias Reichl +--- + sound/soc/bcm/hifiberry_amp.c | 1 + + sound/soc/bcm/hifiberry_dac.c | 1 + + sound/soc/bcm/hifiberry_dacplus.c | 1 + + sound/soc/bcm/hifiberry_digi.c | 1 + + sound/soc/bcm/iqaudio-dac.c | 1 + + sound/soc/bcm/raspidac3.c | 1 + + sound/soc/bcm/rpi-dac.c | 1 + + sound/soc/bcm/rpi-proto.c | 1 + + 8 files changed, 8 insertions(+) + +--- a/sound/soc/bcm/hifiberry_amp.c ++++ b/sound/soc/bcm/hifiberry_amp.c +@@ -61,6 +61,7 @@ static struct snd_soc_dai_link snd_rpi_h + + static struct snd_soc_card snd_rpi_hifiberry_amp = { + .name = "snd_rpi_hifiberry_amp", ++ .owner = THIS_MODULE, + .dai_link = snd_rpi_hifiberry_amp_dai, + .num_links = ARRAY_SIZE(snd_rpi_hifiberry_amp_dai), + }; +--- a/sound/soc/bcm/hifiberry_dac.c ++++ b/sound/soc/bcm/hifiberry_dac.c +@@ -63,6 +63,7 @@ static struct snd_soc_dai_link snd_rpi_h + /* audio machine driver */ + static struct snd_soc_card snd_rpi_hifiberry_dac = { + .name = "snd_rpi_hifiberry_dac", ++ .owner = THIS_MODULE, + .dai_link = snd_rpi_hifiberry_dac_dai, + .num_links = ARRAY_SIZE(snd_rpi_hifiberry_dac_dai), + }; +--- a/sound/soc/bcm/hifiberry_dacplus.c ++++ b/sound/soc/bcm/hifiberry_dacplus.c +@@ -287,6 +287,7 @@ static struct snd_soc_dai_link snd_rpi_h + /* audio machine driver */ + static struct snd_soc_card snd_rpi_hifiberry_dacplus = { + .name = "snd_rpi_hifiberry_dacplus", ++ .owner = THIS_MODULE, + .dai_link = snd_rpi_hifiberry_dacplus_dai, + .num_links = ARRAY_SIZE(snd_rpi_hifiberry_dacplus_dai), + }; +--- a/sound/soc/bcm/hifiberry_digi.c ++++ b/sound/soc/bcm/hifiberry_digi.c +@@ -164,6 +164,7 @@ static struct snd_soc_dai_link snd_rpi_h + /* audio machine driver */ + static struct snd_soc_card snd_rpi_hifiberry_digi = { + .name = "snd_rpi_hifiberry_digi", ++ .owner = THIS_MODULE, + .dai_link = snd_rpi_hifiberry_digi_dai, + .num_links = ARRAY_SIZE(snd_rpi_hifiberry_digi_dai), + }; +--- a/sound/soc/bcm/iqaudio-dac.c ++++ b/sound/soc/bcm/iqaudio-dac.c +@@ -77,6 +77,7 @@ static struct snd_soc_dai_link snd_rpi_i + /* audio machine driver */ + static struct snd_soc_card snd_rpi_iqaudio_dac = { + .name = "IQaudIODAC", ++ .owner = THIS_MODULE, + .dai_link = snd_rpi_iqaudio_dac_dai, + .num_links = ARRAY_SIZE(snd_rpi_iqaudio_dac_dai), + }; +--- a/sound/soc/bcm/raspidac3.c ++++ b/sound/soc/bcm/raspidac3.c +@@ -128,6 +128,7 @@ static struct snd_soc_dai_link snd_rpi_r + /* audio machine driver */ + static struct snd_soc_card snd_rpi_raspidac3 = { + .name = "RaspiDAC Rev.3x HiFi Audio Card", ++ .owner = THIS_MODULE, + .dai_link = snd_rpi_raspidac3_dai, + .num_links = ARRAY_SIZE(snd_rpi_raspidac3_dai), + }; +--- a/sound/soc/bcm/rpi-dac.c ++++ b/sound/soc/bcm/rpi-dac.c +@@ -60,6 +60,7 @@ static struct snd_soc_dai_link snd_rpi_r + /* audio machine driver */ + static struct snd_soc_card snd_rpi_rpi_dac = { + .name = "snd_rpi_rpi_dac", ++ .owner = THIS_MODULE, + .dai_link = snd_rpi_rpi_dac_dai, + .num_links = ARRAY_SIZE(snd_rpi_rpi_dac_dai), + }; +--- a/sound/soc/bcm/rpi-proto.c ++++ b/sound/soc/bcm/rpi-proto.c +@@ -91,6 +91,7 @@ static struct snd_soc_dai_link snd_rpi_p + /* audio machine driver */ + static struct snd_soc_card snd_rpi_proto = { + .name = "snd_rpi_proto", ++ .owner = THIS_MODULE, + .dai_link = snd_rpi_proto_dai, + .num_links = ARRAY_SIZE(snd_rpi_proto_dai), + }; diff --git a/target/linux/brcm2708/patches-4.4/0158-config-Enable-HCI-over-UARTs.patch b/target/linux/brcm2708/patches-4.4/0158-config-Enable-HCI-over-UARTs.patch deleted file mode 100644 index c55ff17b7d..0000000000 --- a/target/linux/brcm2708/patches-4.4/0158-config-Enable-HCI-over-UARTs.patch +++ /dev/null @@ -1,33 +0,0 @@ -From e08f6b3aad073d8130d2ec0f8398f55983c0eac8 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Fri, 15 Jan 2016 16:48:27 +0000 -Subject: [PATCH 158/170] config: Enable HCI over UARTs - ---- - arch/arm/configs/bcm2709_defconfig | 3 +++ - arch/arm/configs/bcmrpi_defconfig | 2 ++ - 2 files changed, 5 insertions(+) - ---- a/arch/arm/configs/bcm2709_defconfig -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -376,6 +376,9 @@ CONFIG_BT_BNEP_PROTO_FILTER=y - CONFIG_BT_HIDP=m - CONFIG_BT_6LOWPAN=m - CONFIG_BT_HCIBTUSB=m -+CONFIG_BT_HCIUART=m -+CONFIG_BT_HCIUART_3WIRE=y -+CONFIG_BT_HCIUART_BCM=y - CONFIG_BT_HCIBCM203X=m - CONFIG_BT_HCIBPA10X=m - CONFIG_BT_HCIBFUSB=m ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -370,6 +370,8 @@ CONFIG_BT_HIDP=m - CONFIG_BT_6LOWPAN=m - CONFIG_BT_HCIBTUSB=m - CONFIG_BT_HCIUART=m -+CONFIG_BT_HCIUART_3WIRE=y -+CONFIG_BT_HCIUART_BCM=y - CONFIG_BT_HCIBCM203X=m - CONFIG_BT_HCIBPA10X=m - CONFIG_BT_HCIBFUSB=m diff --git a/target/linux/brcm2708/patches-4.4/0159-hci_h5-Don-t-send-conf_req-when-ACTIVE.patch b/target/linux/brcm2708/patches-4.4/0159-hci_h5-Don-t-send-conf_req-when-ACTIVE.patch deleted file mode 100644 index 70e424b21a..0000000000 --- a/target/linux/brcm2708/patches-4.4/0159-hci_h5-Don-t-send-conf_req-when-ACTIVE.patch +++ /dev/null @@ -1,23 +0,0 @@ -From 20c4c47bca3674d746518b1cca1e066e46b52900 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Thu, 17 Dec 2015 13:37:07 +0000 -Subject: [PATCH 159/170] hci_h5: Don't send conf_req when ACTIVE - -Without this patch, a modem and kernel can continuously bombard each -other with conf_req and conf_rsp messages, in a demented game of tag. ---- - drivers/bluetooth/hci_h5.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - ---- a/drivers/bluetooth/hci_h5.c -+++ b/drivers/bluetooth/hci_h5.c -@@ -314,7 +314,8 @@ static void h5_handle_internal_rx(struct - h5_link_control(hu, conf_req, 3); - } else if (memcmp(data, conf_req, 2) == 0) { - h5_link_control(hu, conf_rsp, 2); -- h5_link_control(hu, conf_req, 3); -+ if (h5->state != H5_ACTIVE) -+ h5_link_control(hu, conf_req, 3); - } else if (memcmp(data, conf_rsp, 2) == 0) { - if (H5_HDR_LEN(hdr) > 2) - h5->tx_win = (data[2] & 7); diff --git a/target/linux/brcm2708/patches-4.4/0159-smsx95xx-Add-option-to-disable-the-crimes-against-tr.patch b/target/linux/brcm2708/patches-4.4/0159-smsx95xx-Add-option-to-disable-the-crimes-against-tr.patch new file mode 100644 index 0000000000..7518c65bfc --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0159-smsx95xx-Add-option-to-disable-the-crimes-against-tr.patch @@ -0,0 +1,44 @@ +From edd0a6e9b22c8edad94b9e8a6ca370912fbeb642 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 20 Jan 2016 17:50:09 +0000 +Subject: [PATCH 159/232] smsx95xx: Add option to disable the crimes against + truesize fix + +It may improve iperf numbers on Pi 1, but may generate dmesg warnings and possibly cause network issues +See issue 1248. +--- + drivers/net/usb/smsc95xx.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + mode change 100755 => 100644 drivers/net/usb/smsc95xx.c + +--- a/drivers/net/usb/smsc95xx.c ++++ b/drivers/net/usb/smsc95xx.c +@@ -75,6 +75,10 @@ static bool turbo_mode = false; + module_param(turbo_mode, bool, 0644); + MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); + ++static bool truesize_mode = false; ++module_param(truesize_mode, bool, 0644); ++MODULE_PARM_DESC(truesize_mode, "Report larger truesize value"); ++ + static char *macaddr = ":"; + module_param(macaddr, charp, 0); + MODULE_PARM_DESC(macaddr, "MAC address"); +@@ -1841,6 +1845,8 @@ static int smsc95xx_rx_fixup(struct usbn + if (dev->net->features & NETIF_F_RXCSUM) + smsc95xx_rx_csum_offload(skb); + skb_trim(skb, skb->len - 4); /* remove fcs */ ++ if (truesize_mode) ++ skb->truesize = size + sizeof(struct sk_buff); + + return 1; + } +@@ -1858,6 +1864,8 @@ static int smsc95xx_rx_fixup(struct usbn + if (dev->net->features & NETIF_F_RXCSUM) + smsc95xx_rx_csum_offload(ax_skb); + skb_trim(ax_skb, ax_skb->len - 4); /* remove fcs */ ++ if (truesize_mode) ++ ax_skb->truesize = size + sizeof(struct sk_buff); + + usbnet_skb_return(dev, ax_skb); + } diff --git a/target/linux/brcm2708/patches-4.4/0160-amba_pl011-Don-t-use-DT-aliases-for-numbering.patch b/target/linux/brcm2708/patches-4.4/0160-amba_pl011-Don-t-use-DT-aliases-for-numbering.patch deleted file mode 100644 index 344fbd9103..0000000000 --- a/target/linux/brcm2708/patches-4.4/0160-amba_pl011-Don-t-use-DT-aliases-for-numbering.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 00dba6ec2ac004fc17075febd4504646eb3dc543 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Tue, 23 Feb 2016 17:26:48 +0000 -Subject: [PATCH 160/170] amba_pl011: Don't use DT aliases for numbering - -The pl011 driver looks for DT aliases of the form "serial", -and if found uses as the device ID. This can cause -/dev/ttyAMA0 to become /dev/ttyAMA1, which is confusing if the -other serial port is provided by the 8250 driver which doesn't -use the same logic. ---- - drivers/tty/serial/amba-pl011.c | 5 +++++ - 1 file changed, 5 insertions(+) - ---- a/drivers/tty/serial/amba-pl011.c -+++ b/drivers/tty/serial/amba-pl011.c -@@ -2313,7 +2313,12 @@ static int pl011_setup_port(struct devic - if (IS_ERR(base)) - return PTR_ERR(base); - -+ /* Don't use DT serial aliases - it causes the device to -+ be renumbered to ttyAMA1 if it is the second serial port in the -+ system, even though the other one is ttyS0. The 8250 driver -+ doesn't use this logic, so always remains ttyS0. - index = pl011_probe_dt_alias(index, dev); -+ */ - - uap->old_cr = 0; - uap->port.dev = dev; diff --git a/target/linux/brcm2708/patches-4.4/0160-bcm2835-virtgpio-Virtual-GPIO-driver.patch b/target/linux/brcm2708/patches-4.4/0160-bcm2835-virtgpio-Virtual-GPIO-driver.patch new file mode 100644 index 0000000000..19f73972a3 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0160-bcm2835-virtgpio-Virtual-GPIO-driver.patch @@ -0,0 +1,244 @@ +From e1024a4269e314d45522f24b0ac86c77675e7909 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Tue, 23 Feb 2016 19:56:04 +0000 +Subject: [PATCH 160/232] bcm2835-virtgpio: Virtual GPIO driver + +Add a virtual GPIO driver that uses the firmware mailbox interface to +request that the VPU toggles LEDs. +--- + arch/arm/configs/bcm2709_defconfig | 1 + + drivers/gpio/Kconfig | 6 + + drivers/gpio/Makefile | 1 + + drivers/gpio/gpio-bcm-virt.c | 180 +++++++++++++++++++++++++++++ + include/soc/bcm2835/raspberrypi-firmware.h | 1 + + 5 files changed, 189 insertions(+) + create mode 100644 drivers/gpio/gpio-bcm-virt.c + +--- a/arch/arm/configs/bcm2709_defconfig ++++ b/arch/arm/configs/bcm2709_defconfig +@@ -607,6 +607,7 @@ CONFIG_PPS=m + CONFIG_PPS_CLIENT_LDISC=m + CONFIG_PPS_CLIENT_GPIO=m + CONFIG_GPIO_SYSFS=y ++CONFIG_GPIO_BCM_VIRT=y + CONFIG_GPIO_ARIZONA=m + CONFIG_GPIO_STMPE=y + CONFIG_W1=m +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -132,6 +132,12 @@ config GPIO_BCM_KONA + help + Turn on GPIO support for Broadcom "Kona" chips. + ++config GPIO_BCM_VIRT ++ bool "Broadcom Virt GPIO" ++ depends on OF_GPIO && RASPBERRYPI_FIRMWARE && (ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709 || COMPILE_TEST) ++ help ++ Turn on virtual GPIO support for Broadcom BCM283X chips. ++ + config GPIO_BRCMSTB + tristate "BRCMSTB GPIO support" + default y if ARCH_BRCMSTB +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -24,6 +24,7 @@ obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o + obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o + obj-$(CONFIG_ATH79) += gpio-ath79.o + obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o ++obj-$(CONFIG_GPIO_BCM_VIRT) += gpio-bcm-virt.o + obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o + obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o + obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o +--- /dev/null ++++ b/drivers/gpio/gpio-bcm-virt.c +@@ -0,0 +1,180 @@ ++/* ++ * brcmvirt GPIO driver ++ * ++ * Copyright (C) 2012,2013 Dom Cobley ++ * Based on gpio-clps711x.c by Alexander Shiyan ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MODULE_NAME "brcmvirt-gpio" ++#define NUM_GPIO 2 ++ ++struct brcmvirt_gpio { ++ struct gpio_chip gc; ++ u32 __iomem *ts_base; ++ /* two packed 16-bit counts of enabled and disables ++ Allows host to detect a brief enable that was missed */ ++ u32 enables_disables[NUM_GPIO]; ++}; ++ ++static int brcmvirt_gpio_dir_in(struct gpio_chip *gc, unsigned off) ++{ ++ struct brcmvirt_gpio *gpio; ++ gpio = container_of(gc, struct brcmvirt_gpio, gc); ++ return -EINVAL; ++} ++ ++static int brcmvirt_gpio_dir_out(struct gpio_chip *gc, unsigned off, int val) ++{ ++ struct brcmvirt_gpio *gpio; ++ gpio = container_of(gc, struct brcmvirt_gpio, gc); ++ return 0; ++} ++ ++static int brcmvirt_gpio_get(struct gpio_chip *gc, unsigned off) ++{ ++ struct brcmvirt_gpio *gpio; ++ unsigned v; ++ gpio = container_of(gc, struct brcmvirt_gpio, gc); ++ v = readl(gpio->ts_base + off); ++ return (v >> off) & 1; ++} ++ ++static void brcmvirt_gpio_set(struct gpio_chip *gc, unsigned off, int val) ++{ ++ struct brcmvirt_gpio *gpio; ++ u16 enables, disables; ++ s16 diff; ++ bool lit; ++ gpio = container_of(gc, struct brcmvirt_gpio, gc); ++ enables = gpio->enables_disables[off] >> 16; ++ disables = gpio->enables_disables[off] >> 0; ++ diff = (s16)(enables - disables); ++ lit = diff > 0; ++ if ((val && lit) || (!val && !lit)) ++ return; ++ if (val) ++ enables++; ++ else ++ disables++; ++ diff = (s16)(enables - disables); ++ BUG_ON(diff != 0 && diff != 1); ++ gpio->enables_disables[off] = (enables << 16) | (disables << 0); ++ writel(gpio->enables_disables[off], gpio->ts_base + off); ++} ++ ++static int brcmvirt_gpio_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct device_node *fw_node; ++ struct rpi_firmware *fw; ++ struct brcmvirt_gpio *ucb; ++ u32 gpiovirtbuf; ++ int err = 0; ++ ++ fw_node = of_parse_phandle(np, "firmware", 0); ++ if (!fw_node) { ++ dev_err(dev, "Missing firmware node\n"); ++ return -ENOENT; ++ } ++ ++ fw = rpi_firmware_get(fw_node); ++ if (!fw) ++ return -EPROBE_DEFER; ++ ++ err = rpi_firmware_property(fw, RPI_FIRMWARE_FRAMEBUFFER_GET_GPIOVIRTBUF, ++ &gpiovirtbuf, sizeof(gpiovirtbuf)); ++ ++ if (err) { ++ dev_err(dev, "Failed to get gpiovirtbuf\n"); ++ goto err; ++ } ++ ++ if (!gpiovirtbuf) { ++ dev_err(dev, "No virtgpio buffer\n"); ++ err = -ENOENT; ++ goto err; ++ } ++ ++ ucb = devm_kzalloc(dev, sizeof *ucb, GFP_KERNEL); ++ if (!ucb) { ++ err = -EINVAL; ++ goto err; ++ } ++ ++ // mmap the physical memory ++ gpiovirtbuf &= ~0xc0000000; ++ ucb->ts_base = ioremap(gpiovirtbuf, 4096); ++ if (ucb->ts_base == NULL) { ++ dev_err(dev, "Failed to map physical address\n"); ++ err = -ENOENT; ++ goto err; ++ } ++ ++ ucb->gc.label = MODULE_NAME; ++ ucb->gc.owner = THIS_MODULE; ++ ucb->gc.dev = dev; ++ ucb->gc.of_node = np; ++ ucb->gc.base = 100; ++ ucb->gc.ngpio = NUM_GPIO; ++ ++ ucb->gc.direction_input = brcmvirt_gpio_dir_in; ++ ucb->gc.direction_output = brcmvirt_gpio_dir_out; ++ ucb->gc.get = brcmvirt_gpio_get; ++ ucb->gc.set = brcmvirt_gpio_set; ++ ucb->gc.can_sleep = true; ++ ++ err = gpiochip_add(&ucb->gc); ++ if (err) ++ goto err; ++ ++ platform_set_drvdata(pdev, ucb); ++ ++err: ++ return err; ++ ++} ++ ++static int brcmvirt_gpio_remove(struct platform_device *pdev) ++{ ++ int err = 0; ++ struct brcmvirt_gpio *ucb = platform_get_drvdata(pdev); ++ ++ gpiochip_remove(&ucb->gc); ++ iounmap(ucb->ts_base); ++ return err; ++} ++ ++static const struct of_device_id __maybe_unused brcmvirt_gpio_ids[] = { ++ { .compatible = "brcm,bcm2835-virtgpio" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, brcmvirt_gpio_ids); ++ ++static struct platform_driver brcmvirt_gpio_driver = { ++ .driver = { ++ .name = MODULE_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(brcmvirt_gpio_ids), ++ }, ++ .probe = brcmvirt_gpio_probe, ++ .remove = brcmvirt_gpio_remove, ++}; ++module_platform_driver(brcmvirt_gpio_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Dom Cobley "); ++MODULE_DESCRIPTION("brcmvirt GPIO driver"); ++MODULE_ALIAS("platform:brcmvirt-gpio"); +--- a/include/soc/bcm2835/raspberrypi-firmware.h ++++ b/include/soc/bcm2835/raspberrypi-firmware.h +@@ -93,6 +93,7 @@ enum rpi_firmware_property_tag { + RPI_FIRMWARE_FRAMEBUFFER_GET_OVERSCAN = 0x0004000a, + RPI_FIRMWARE_FRAMEBUFFER_GET_PALETTE = 0x0004000b, + RPI_FIRMWARE_FRAMEBUFFER_GET_TOUCHBUF = 0x0004000f, ++ RPI_FIRMWARE_FRAMEBUFFER_GET_GPIOVIRTBUF = 0x00040010, + RPI_FIRMWARE_FRAMEBUFFER_RELEASE = 0x00048001, + RPI_FIRMWARE_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT = 0x00044003, + RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT = 0x00044004, diff --git a/target/linux/brcm2708/patches-4.4/0161-BCM270X_DT-Add-Pi3-support.patch b/target/linux/brcm2708/patches-4.4/0161-BCM270X_DT-Add-Pi3-support.patch new file mode 100644 index 0000000000..6f38349396 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0161-BCM270X_DT-Add-Pi3-support.patch @@ -0,0 +1,323 @@ +From 4aa902b37606ca425a2d1dedd400347b11a0e61d Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 21 Jan 2016 17:57:49 +0000 +Subject: [PATCH 161/232] BCM270X_DT: Add Pi3 support + +--- + arch/arm/boot/dts/Makefile | 1 + + arch/arm/boot/dts/bcm2710-rpi-3-b.dts | 192 ++++++++++++++++++++++++++++++++++ + arch/arm/boot/dts/bcm2710.dtsi | 102 ++++++++++++++++++ + 3 files changed, 295 insertions(+) + create mode 100644 arch/arm/boot/dts/bcm2710-rpi-3-b.dts + create mode 100644 arch/arm/boot/dts/bcm2710.dtsi + +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -5,6 +5,7 @@ dtb-$(CONFIG_ARCH_BCM2708) += bcm2708-rp + dtb-$(CONFIG_ARCH_BCM2708) += bcm2708-rpi-cm.dtb + dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-cm.dtb + dtb-$(CONFIG_ARCH_BCM2709) += bcm2709-rpi-2-b.dtb ++dtb-$(CONFIG_ARCH_BCM2709) += bcm2710-rpi-3-b.dtb + + # Raspberry Pi + ifeq ($(CONFIG_ARCH_BCM2708),y) +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts +@@ -0,0 +1,192 @@ ++/dts-v1/; ++ ++#include "bcm2710.dtsi" ++ ++/ { ++ compatible = "brcm,bcm2710","brcm,bcm2709"; ++ model = "Raspberry Pi 3 Model B"; ++}; ++ ++&gpio { ++ sdhost_pins: sdhost_pins { ++ brcm,pins = <48 49 50 51 52 53>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++ ++ spi0_pins: spi0_pins { ++ brcm,pins = <9 10 11>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++ ++ spi0_cs_pins: spi0_cs_pins { ++ brcm,pins = <8 7>; ++ brcm,function = <1>; /* output */ ++ }; ++ ++ 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 */ ++ }; ++ ++ sdio_pins: sdio_pins { ++ brcm,pins = <34 35 36 37 38 39>; ++ brcm,function = <7>; // alt3 = SD1 ++ brcm,pull = <0 2 2 2 2 2>; ++ }; ++ ++ bt_pins: bt_pins { ++ brcm,pins = <28 29 30 31 43>; ++ brcm,function = <6 6 6 6 4>; /* alt2:PCM alt0:GPCLK2 */ ++ brcm,pull = <0 0 0 0 0>; ++ }; ++ ++ uart0_pins: uart0_pins { ++ brcm,pins = <32 33>; ++ brcm,function = <7>; /* alt3=UART0 */ ++ brcm,pull = <0 0>; ++ }; ++ ++ uart1_pins: uart1_pins { ++ brcm,pins = <14 15>; ++ brcm,function = <2>; /* alt5=UART1 */ ++ brcm,pull = <0 0>; ++ }; ++}; ++ ++&sdhost { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdhost_pins>; ++ bus-width = <4>; ++ status = "okay"; ++}; ++ ++&mmc { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdio_pins>; ++ non-removable; ++ bus-width = <4>; ++ status = "okay"; ++ brcm,overclock-50 = <0>; ++}; ++ ++&soc { ++ virtgpio: virtgpio { ++ compatible = "brcm,bcm2835-virtgpio"; ++ gpio-controller; ++ #gpio-cells = <2>; ++ firmware = <&firmware>; ++ status = "okay"; ++ }; ++}; ++ ++&fb { ++ status = "okay"; ++}; ++ ++&uart0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart0_pins &bt_pins>; ++ status = "okay"; ++}; ++ ++&uart1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart1_pins>; ++ status = "okay"; ++}; ++ ++&spi0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi0_pins &spi0_cs_pins>; ++ cs-gpios = <&gpio 8 1>, <&gpio 7 1>; ++ ++ 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>; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++}; ++ ++&i2s { ++ #sound-dai-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s_pins>; ++}; ++ ++&random { ++ status = "okay"; ++}; ++ ++&leds { ++ act_led: act { ++ label = "led0"; ++ linux,default-trigger = "mmc0"; ++ gpios = <&virtgpio 0 0>; ++ }; ++}; ++ ++/ { ++ chosen { ++ bootargs = "8250.nr_uarts=1"; ++ }; ++}; ++ ++/ { ++ __overrides__ { ++ uart0 = <&uart0>,"status"; ++ uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; ++ i2s = <&i2s>,"status"; ++ spi = <&spi0>,"status"; ++ i2c0 = <&i2c0>,"status"; ++ i2c1 = <&i2c1>,"status"; ++ i2c2_iknowwhatimdoing = <&i2c2>,"status"; ++ i2c0_baudrate = <&i2c0>,"clock-frequency:0"; ++ i2c1_baudrate = <&i2c1>,"clock-frequency:0"; ++ i2c2_baudrate = <&i2c2>,"clock-frequency:0"; ++ core_freq = <&clk_core>,"clock-frequency:0"; ++ ++ act_led_gpio = <&act_led>,"gpios:4"; ++ act_led_activelow = <&act_led>,"gpios:8"; ++ act_led_trigger = <&act_led>,"linux,default-trigger"; ++ ++ audio = <&audio>,"status"; ++ watchdog = <&watchdog>,"status"; ++ random = <&random>,"status"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2710.dtsi +@@ -0,0 +1,102 @@ ++#include "bcm2708_common.dtsi" ++ ++/ { ++ compatible = "brcm,bcm2710","brcm,bcm2709"; ++ model = "BCM2710"; ++ ++ chosen { ++ /* No padding required - the boot loader can do that. */ ++ bootargs = ""; ++ }; ++ ++ soc { ++ ranges = <0x7e000000 0x3f000000 0x01000000>, ++ <0x40000000 0x40000000 0x00040000>; ++ ++ local_intc: local_intc { ++ compatible = "brcm,bcm2836-l1-intc"; ++ reg = <0x40000000 0x100>; ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ interrupt-parent = <&local_intc>; ++ }; ++ ++ arm-pmu { ++ compatible = "arm,cortex-a7-pmu"; ++ interrupt-parent = <&local_intc>; ++ interrupts = <9>; ++ }; ++ ++ gpiomem { ++ compatible = "brcm,bcm2835-gpiomem"; ++ reg = <0x7e200000 0x1000>; ++ status = "okay"; ++ }; ++ ++ timer { ++ compatible = "arm,armv7-timer"; ++ clock-frequency = <19200000>; ++ interrupt-parent = <&local_intc>; ++ interrupts = <0>, // PHYS_SECURE_PPI ++ <1>, // PHYS_NONSECURE_PPI ++ <3>, // VIRT_PPI ++ <2>; // HYP_PPI ++ always-on; ++ }; ++ ++ syscon@40000000 { ++ compatible = "brcm,bcm2836-arm-local", "syscon"; ++ reg = <0x40000000 0x100>; ++ }; ++ }; ++ ++ cpus: cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ v7_cpu0: cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0x000>; ++ clock-frequency = <800000000>; ++ }; ++ ++ v7_cpu1: cpu@1 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0x001>; ++ clock-frequency = <800000000>; ++ }; ++ ++ v7_cpu2: cpu@2 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0x002>; ++ clock-frequency = <800000000>; ++ }; ++ ++ v7_cpu3: cpu@3 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0x003>; ++ 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"; ++ }; ++}; ++ ++&watchdog { ++ status = "okay"; ++}; ++ ++&intc { ++ compatible = "brcm,bcm2836-armctrl-ic"; ++ interrupt-parent = <&local_intc>; ++ interrupts = <8>; ++}; diff --git a/target/linux/brcm2708/patches-4.4/0161-clk-bcm2835-Add-bindings-for-the-auxiliary-periphera.patch b/target/linux/brcm2708/patches-4.4/0161-clk-bcm2835-Add-bindings-for-the-auxiliary-periphera.patch deleted file mode 100644 index 1bee99e79b..0000000000 --- a/target/linux/brcm2708/patches-4.4/0161-clk-bcm2835-Add-bindings-for-the-auxiliary-periphera.patch +++ /dev/null @@ -1,72 +0,0 @@ -From 95136c932ac4433a6a50d394817812f8eb2cc914 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Tue, 15 Dec 2015 15:35:57 -0800 -Subject: [PATCH 161/170] clk: bcm2835: Add bindings for the auxiliary - peripheral clock gates. - -These will be used for enabling UART1, SPI1, and SPI2. - -Signed-off-by: Eric Anholt -Acked-by: Rob Herring -Signed-off-by: Michael Turquette ---- - .../bindings/clock/brcm,bcm2835-aux-clock.txt | 31 ++++++++++++++++++++++ - include/dt-bindings/clock/bcm2835-aux.h | 17 ++++++++++++ - 2 files changed, 48 insertions(+) - create mode 100644 Documentation/devicetree/bindings/clock/brcm,bcm2835-aux-clock.txt - create mode 100644 include/dt-bindings/clock/bcm2835-aux.h - ---- /dev/null -+++ b/Documentation/devicetree/bindings/clock/brcm,bcm2835-aux-clock.txt -@@ -0,0 +1,31 @@ -+Broadcom BCM2835 auxiliary peripheral support -+ -+This binding uses the common clock binding: -+ Documentation/devicetree/bindings/clock/clock-bindings.txt -+ -+The auxiliary peripherals (UART, SPI1, and SPI2) have a small register -+area controlling clock gating to the peripherals, and providing an IRQ -+status register. -+ -+Required properties: -+- compatible: Should be "brcm,bcm2835-aux" -+- #clock-cells: Should be <1>. The permitted clock-specifier values can be -+ found in include/dt-bindings/clock/bcm2835-aux.h -+- reg: Specifies base physical address and size of the registers -+- clocks: The parent clock phandle -+ -+Example: -+ -+ clocks: cprman@7e101000 { -+ compatible = "brcm,bcm2835-cprman"; -+ #clock-cells = <1>; -+ reg = <0x7e101000 0x2000>; -+ clocks = <&clk_osc>; -+ }; -+ -+ aux: aux@0x7e215004 { -+ compatible = "brcm,bcm2835-aux"; -+ #clock-cells = <1>; -+ reg = <0x7e215000 0x8>; -+ clocks = <&clocks BCM2835_CLOCK_VPU>; -+ }; ---- /dev/null -+++ b/include/dt-bindings/clock/bcm2835-aux.h -@@ -0,0 +1,17 @@ -+/* -+ * Copyright (C) 2015 Broadcom 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 version 2. -+ * -+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any -+ * kind, whether express or implied; without even the implied warranty -+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#define BCM2835_AUX_CLOCK_UART 0 -+#define BCM2835_AUX_CLOCK_SPI1 1 -+#define BCM2835_AUX_CLOCK_SPI2 2 -+#define BCM2835_AUX_CLOCK_COUNT 3 diff --git a/target/linux/brcm2708/patches-4.4/0162-DT-Add-overlays-to-configure-I2C-pins.patch b/target/linux/brcm2708/patches-4.4/0162-DT-Add-overlays-to-configure-I2C-pins.patch new file mode 100644 index 0000000000..fdd38383f2 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0162-DT-Add-overlays-to-configure-I2C-pins.patch @@ -0,0 +1,134 @@ +From 6014b99c7f39813c76b51e940677219c7dd41041 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <6by9@users.noreply.github.com> +Date: Mon, 8 Feb 2016 23:49:41 +0000 +Subject: [PATCH 162/232] DT: Add overlays to configure I2C pins + +Lifted from +https://www.raspberrypi.org/forums/viewtopic.php?f=107&t=120938&p=825883 +so not claiming this to be my own work. +Adds overlays i2c0-bcm2708 and i2c1-bcm2708 that allow the pin +allocations for i2c-0 and i2c-1 to be changed. +--- + arch/arm/boot/dts/overlays/Makefile | 2 ++ + arch/arm/boot/dts/overlays/README | 16 ++++++++++ + .../arm/boot/dts/overlays/i2c0-bcm2708-overlay.dts | 36 +++++++++++++++++++++ + .../arm/boot/dts/overlays/i2c1-bcm2708-overlay.dts | 37 ++++++++++++++++++++++ + 4 files changed, 91 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/i2c0-bcm2708-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/i2c1-bcm2708-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -29,6 +29,8 @@ dtb-$(RPI_DT_OVERLAYS) += hy28a-overlay. + dtb-$(RPI_DT_OVERLAYS) += hy28b-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += i2c-rtc-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += i2c-gpio-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += i2c0-bcm2708-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += i2c1-bcm2708-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += i2s-mmap-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -371,6 +371,22 @@ Params: ds1307 Select t + pcf8563 Select the PCF8563 device + + ++Name: i2c0-bcm2708 ++Info: Enable the i2c_bcm2708 driver for the i2c0 bus ++Load: dtoverlay=i2c0-bcm2708,= ++Params: sda0_pin GPIO pin for SDA0 (0, 28 [or 44] - default 0) ++ scl0_pin GPIO pin for SCL0 (1, 29 [or 45] - default 1) ++ ++ ++Name: i2c1-bcm2708 ++Info: Enable the i2c_bcm2708 driver for the i2c1 bus ++Load: dtoverlay=i2c1-bcm2708,= ++Params: sda1_pin GPIO pin for SDA1 (2 or 44 - default 2) ++ scl1_pin GPIO pin for SCL1 (3 or 45 - default 3) ++ pin_func Alternative pin function (4 (alt0), 6 (alt2) - ++ default 4) ++ ++ + Name: i2s-mmap + Info: Enables mmap support in the bcm2708-i2s driver + Load: dtoverlay=i2s-mmap +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/i2c0-bcm2708-overlay.dts +@@ -0,0 +1,36 @@ ++/* ++ * Device tree overlay for i2c_bcm2708, i2c0 bus ++ * ++ * Compile: ++ * dtc -@ -I dts -O dtb -o i2c0-bcm2708-overlay.dtb i2c0-bcm2708-overlay.dts ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/{ ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&i2c0>; ++ __overlay__ { ++ pinctrl-0 = <&i2c0_pins>; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ i2c0_pins: i2c0 { ++ brcm,pins = <0 1>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ sda0_pin = <&i2c0_pins>,"brcm,pins:0"; ++ scl0_pin = <&i2c0_pins>,"brcm,pins:4"; ++ }; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/i2c1-bcm2708-overlay.dts +@@ -0,0 +1,37 @@ ++/* ++ * Device tree overlay for i2c_bcm2708, i2c1 bus ++ * ++ * Compile: ++ * dtc -@ -I dts -O dtb -o i2c1-bcm2708-overlay.dtb i2c1-bcm2708-overlay.dts ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/{ ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&i2c1>; ++ __overlay__ { ++ pinctrl-0 = <&i2c1_pins>; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ i2c1_pins: i2c1 { ++ brcm,pins = <2 3>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ sda1_pin = <&i2c1_pins>,"brcm,pins:0"; ++ scl1_pin = <&i2c1_pins>,"brcm,pins:4"; ++ pin_func = <&i2c1_pins>,"brcm,function:0"; ++ }; ++}; diff --git a/target/linux/brcm2708/patches-4.4/0162-clk-bcm2835-Add-a-driver-for-the-auxiliary-periphera.patch b/target/linux/brcm2708/patches-4.4/0162-clk-bcm2835-Add-a-driver-for-the-auxiliary-periphera.patch deleted file mode 100644 index 3e9667a900..0000000000 --- a/target/linux/brcm2708/patches-4.4/0162-clk-bcm2835-Add-a-driver-for-the-auxiliary-periphera.patch +++ /dev/null @@ -1,118 +0,0 @@ -From 924276b92ff47f0e778a9405d00637be4ca88736 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Tue, 15 Dec 2015 15:35:58 -0800 -Subject: [PATCH 162/170] clk: bcm2835: Add a driver for the auxiliary - peripheral clock gates. - -There are a pair of SPI masters and a mini UART that were last minute -additions. As a result, they didn't get integrated in the same way as -the other gates off of the VPU clock in CPRMAN. - -Signed-off-by: Eric Anholt -Signed-off-by: Michael Turquette - -updated Makefile to preserve the rasoberry pi architectures ---- - drivers/clk/bcm/Makefile | 1 + - drivers/clk/bcm/clk-bcm2835-aux.c | 85 +++++++++++++++++++++++++++++++++++++++ - 2 files changed, 86 insertions(+) - create mode 100644 drivers/clk/bcm/clk-bcm2835-aux.c - ---- a/drivers/clk/bcm/Makefile -+++ b/drivers/clk/bcm/Makefile -@@ -4,6 +4,7 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281 - obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o - obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o - obj-$(CONFIG_ARCH_BCM2835)$(CONFIG_ARCH_BCM2708)$(CONFIG_ARCH_BCM2709) += clk-bcm2835.o -+obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835-aux.o - obj-$(CONFIG_COMMON_CLK_IPROC) += clk-ns2.o - obj-$(CONFIG_ARCH_BCM_CYGNUS) += clk-cygnus.o - obj-$(CONFIG_ARCH_BCM_NSP) += clk-nsp.o ---- /dev/null -+++ b/drivers/clk/bcm/clk-bcm2835-aux.c -@@ -0,0 +1,85 @@ -+/* -+ * Copyright (C) 2015 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. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define BCM2835_AUXIRQ 0x00 -+#define BCM2835_AUXENB 0x04 -+ -+static int bcm2835_aux_clk_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct clk_onecell_data *onecell; -+ const char *parent; -+ struct clk *parent_clk; -+ struct resource *res; -+ void __iomem *reg, *gate; -+ -+ parent_clk = devm_clk_get(dev, NULL); -+ if (IS_ERR(parent_clk)) -+ return PTR_ERR(parent_clk); -+ parent = __clk_get_name(parent_clk); -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ reg = devm_ioremap_resource(dev, res); -+ if (!reg) -+ return -ENODEV; -+ -+ onecell = devm_kmalloc(dev, sizeof(*onecell), GFP_KERNEL); -+ if (!onecell) -+ return -ENOMEM; -+ onecell->clk_num = BCM2835_AUX_CLOCK_COUNT; -+ onecell->clks = devm_kcalloc(dev, BCM2835_AUX_CLOCK_COUNT, -+ sizeof(*onecell->clks), GFP_KERNEL); -+ if (!onecell->clks) -+ return -ENOMEM; -+ -+ gate = reg + BCM2835_AUXENB; -+ onecell->clks[BCM2835_AUX_CLOCK_UART] = -+ clk_register_gate(dev, "aux_uart", parent, 0, gate, 0, 0, NULL); -+ -+ onecell->clks[BCM2835_AUX_CLOCK_SPI1] = -+ clk_register_gate(dev, "aux_spi1", parent, 0, gate, 1, 0, NULL); -+ -+ onecell->clks[BCM2835_AUX_CLOCK_SPI2] = -+ clk_register_gate(dev, "aux_spi2", parent, 0, gate, 2, 0, NULL); -+ -+ of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get, onecell); -+ -+ return 0; -+} -+ -+static const struct of_device_id bcm2835_aux_clk_of_match[] = { -+ { .compatible = "brcm,bcm2835-aux", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, bcm2835_aux_clk_of_match); -+ -+static struct platform_driver bcm2835_aux_clk_driver = { -+ .driver = { -+ .name = "bcm2835-aux-clk", -+ .of_match_table = bcm2835_aux_clk_of_match, -+ }, -+ .probe = bcm2835_aux_clk_probe, -+}; -+builtin_platform_driver(bcm2835_aux_clk_driver); -+ -+MODULE_AUTHOR("Eric Anholt "); -+MODULE_DESCRIPTION("BCM2835 auxiliary peripheral clock driver"); -+MODULE_LICENSE("GPL v2"); diff --git a/target/linux/brcm2708/patches-4.4/0163-Aux-SPI-1-2-implementation.patch b/target/linux/brcm2708/patches-4.4/0163-Aux-SPI-1-2-implementation.patch deleted file mode 100644 index c47f3bd5b1..0000000000 --- a/target/linux/brcm2708/patches-4.4/0163-Aux-SPI-1-2-implementation.patch +++ /dev/null @@ -1,700 +0,0 @@ -From 43477e669f736f6da34afbd8a96683b239b1fd1f Mon Sep 17 00:00:00 2001 -From: Fraser -Date: Tue, 23 Feb 2016 10:04:37 +1100 -Subject: [PATCH 163/170] Aux SPI 1&2 implementation - -Adds aux spi 1 & 2 devices to compatible raspberry PIs. -* Minor config of the driver build environment to ensure they get built -for CONFIG_ARCH_BCM2708 & CONFIG_ARCH_BCM2709 devices. -* Adds the aux spi driver into the defconfigs as a module. -* Adds the auxiliary and spi1/2 devices into the device tree in a -disabled state -* Provides decide tree overlays which enables the devices and gives -users a degree of control over how they are setup. ---- - arch/arm/boot/dts/bcm2708_common.dtsi | 34 ++++++++- - arch/arm/boot/dts/overlays/Makefile | 6 ++ - arch/arm/boot/dts/overlays/README | 99 +++++++++++++++++++++++++ - arch/arm/boot/dts/overlays/spi1-1cs-overlay.dts | 57 ++++++++++++++ - arch/arm/boot/dts/overlays/spi1-2cs-overlay.dts | 69 +++++++++++++++++ - arch/arm/boot/dts/overlays/spi1-3cs-overlay.dts | 81 ++++++++++++++++++++ - arch/arm/boot/dts/overlays/spi2-1cs-overlay.dts | 57 ++++++++++++++ - arch/arm/boot/dts/overlays/spi2-2cs-overlay.dts | 69 +++++++++++++++++ - arch/arm/boot/dts/overlays/spi2-3cs-overlay.dts | 81 ++++++++++++++++++++ - arch/arm/configs/bcm2709_defconfig | 1 + - arch/arm/configs/bcmrpi_defconfig | 1 + - drivers/clk/bcm/Makefile | 2 +- - drivers/spi/Kconfig | 2 +- - 13 files changed, 556 insertions(+), 3 deletions(-) - create mode 100644 arch/arm/boot/dts/overlays/spi1-1cs-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/spi1-2cs-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/spi1-3cs-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/spi2-1cs-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/spi2-2cs-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/spi2-3cs-overlay.dts - ---- a/arch/arm/boot/dts/bcm2708_common.dtsi -+++ b/arch/arm/boot/dts/bcm2708_common.dtsi -@@ -1,3 +1,4 @@ -+#include - #include "skeleton.dtsi" - - / { -@@ -5,6 +6,7 @@ - - aliases { - audio = &audio; -+ aux = &aux; - sound = &sound; - soc = &soc; - dma = &dma; -@@ -19,6 +21,8 @@ - spi0 = &spi0; - i2c0 = &i2c0; - uart1 = &uart1; -+ spi1 = &spi1; -+ spi2 = &spi2; - mmc = &mmc; - i2c1 = &i2c1; - i2c2 = &i2c2; -@@ -186,6 +190,14 @@ - status = "disabled"; - }; - -+ aux: aux@0x7e215004 { -+ compatible = "brcm,bcm2835-aux"; -+ #clock-cells = <1>; -+ reg = <0x7e215000 0x8>; -+ clocks = <&clk_core>; -+ status = "disabled"; -+ }; -+ - uart1: uart@7e215040 { - compatible = "brcm,bcm2835-aux-uart", "ns16550"; - reg = <0x7e215040 0x40>; -@@ -194,7 +206,27 @@ - reg-shift = <2>; - no-loopback-test; - status = "disabled"; -- }; -+ }; -+ -+ spi1: spi@7e215080 { -+ compatible = "brcm,bcm2835-aux-spi"; -+ reg = <0x7e215080 0x40>, <0x7e215000 0x8>; -+ interrupts = <1 29>; -+ clocks = <&aux BCM2835_AUX_CLOCK_SPI1>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; -+ -+ spi2: spi@7e2150C0 { -+ compatible = "brcm,bcm2835-aux-spi"; -+ reg = <0x7e2150C0 0x40>, <0x7e215000 0x8>; -+ interrupts = <1 29>; -+ clocks = <&aux BCM2835_AUX_CLOCK_SPI2>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; - - mmc: mmc@7e300000 { - compatible = "brcm,bcm2835-mmc"; ---- a/arch/arm/boot/dts/overlays/Makefile -+++ b/arch/arm/boot/dts/overlays/Makefile -@@ -57,6 +57,12 @@ dtb-$(RPI_DT_OVERLAYS) += sdtweak-overla - dtb-$(RPI_DT_OVERLAYS) += smi-dev-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += smi-nand-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += smi-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += spi1-1cs-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += spi1-2cs-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += spi1-3cs-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += spi2-1cs-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += spi2-2cs-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += spi2-3cs-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += spi-gpio35-39-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += tinylcd35-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += uart1-overlay.dtb ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -713,6 +713,105 @@ Load: dtoverlay=spi-gpio35-39 - Params: - - -+Name: spi1-1cs -+Info: Enables spi1 with a single chip select (CS) line and associated spidev -+ dev node. The gpio pin number for the CS line and spidev device node -+ creation are configurable. -+ N.B.: spi1 is only accessible on devices with a 40pin header, eg: -+ A+, B+, Zero and PI2 B; as well as the Compute Module. -+Load: dtoverlay=spi1-1cs,= -+Params: cs0_pin GPIO pin for CS0 (default 18 - BCM SPI1_CE0). -+ cs0_spidev Set to 'disabled' to stop the creation of a -+ userspace device node /dev/spidev1.0 (default -+ is 'okay' or enabled). -+ -+ -+Name: spi1-2cs -+Info: Enables spi1 with two chip select (CS) lines and associated spidev -+ dev nodes. The gpio pin numbers for the CS lines and spidev device node -+ creation are configurable. -+ N.B.: spi1 is only accessible on devices with a 40pin header, eg: -+ A+, B+, Zero and PI2 B; as well as the Compute Module. -+Load: dtoverlay=spi1-2cs,= -+Params: cs0_pin GPIO pin for CS0 (default 18 - BCM SPI1_CE0). -+ cs1_pin GPIO pin for CS1 (default 17 - BCM SPI1_CE1). -+ cs0_spidev Set to 'disabled' to stop the creation of a -+ userspace device node /dev/spidev1.0 (default -+ is 'okay' or enabled). -+ cs1_spidev Set to 'disabled' to stop the creation of a -+ userspace device node /dev/spidev1.1 (default -+ is 'okay' or enabled). -+ -+ -+Name: spi1-3cs -+Info: Enables spi1 with three chip select (CS) lines and associated spidev -+ dev nodes. The gpio pin numbers for the CS lines and spidev device node -+ creation are configurable. -+ N.B.: spi1 is only accessible on devices with a 40pin header, eg: -+ A+, B+, Zero and PI2 B; as well as the Compute Module. -+Load: dtoverlay=spi1-3cs,= -+Params: cs0_pin GPIO pin for CS0 (default 18 - BCM SPI1_CE0). -+ cs1_pin GPIO pin for CS1 (default 17 - BCM SPI1_CE1). -+ cs2_pin GPIO pin for CS2 (default 16 - BCM SPI1_CE2). -+ cs0_spidev Set to 'disabled' to stop the creation of a -+ userspace device node /dev/spidev1.0 (default -+ is 'okay' or enabled). -+ cs1_spidev Set to 'disabled' to stop the creation of a -+ userspace device node /dev/spidev1.1 (default -+ is 'okay' or enabled). -+ cs2_spidev Set to 'disabled' to stop the creation of a -+ userspace device node /dev/spidev1.2 (default -+ is 'okay' or enabled). -+ -+ -+Name: spi2-1cs -+Info: Enables spi2 with a single chip select (CS) line and associated spidev -+ dev node. The gpio pin number for the CS line and spidev device node -+ creation are configurable. -+ N.B.: spi2 is only accessible with the Compute Module. -+Load: dtoverlay=spi2-1cs,= -+Params: cs0_pin GPIO pin for CS0 (default 43 - BCM SPI2_CE0). -+ cs0_spidev Set to 'disabled' to stop the creation of a -+ userspace device node /dev/spidev2.0 (default -+ is 'okay' or enabled). -+ -+ -+Name: spi2-2cs -+Info: Enables spi2 with two chip select (CS) lines and associated spidev -+ dev nodes. The gpio pin numbers for the CS lines and spidev device node -+ creation are configurable. -+ N.B.: spi2 is only accessible with the Compute Module. -+Load: dtoverlay=spi2-2cs,= -+Params: cs0_pin GPIO pin for CS0 (default 43 - BCM SPI2_CE0). -+ cs1_pin GPIO pin for CS1 (default 44 - BCM SPI2_CE1). -+ cs0_spidev Set to 'disabled' to stop the creation of a -+ userspace device node /dev/spidev2.0 (default -+ is 'okay' or enabled). -+ cs1_spidev Set to 'disabled' to stop the creation of a -+ userspace device node /dev/spidev2.1 (default -+ is 'okay' or enabled). -+ -+ -+Name: spi2-3cs -+Info: Enables spi2 with three chip select (CS) lines and associated spidev -+ dev nodes. The gpio pin numbers for the CS lines and spidev device node -+ creation are configurable. -+ N.B.: spi2 is only accessible with the Compute Module. -+Load: dtoverlay=spi2-3cs,= -+Params: cs0_pin GPIO pin for CS0 (default 43 - BCM SPI2_CE0). -+ cs1_pin GPIO pin for CS1 (default 44 - BCM SPI2_CE1). -+ cs2_pin GPIO pin for CS2 (default 45 - BCM SPI2_CE2). -+ cs0_spidev Set to 'disabled' to stop the creation of a -+ userspace device node /dev/spidev2.0 (default -+ is 'okay' or enabled). -+ cs1_spidev Set to 'disabled' to stop the creation of a -+ userspace device node /dev/spidev2.1 (default -+ is 'okay' or enabled). -+ cs2_spidev Set to 'disabled' to stop the creation of a -+ userspace device node /dev/spidev2.2 (default -+ is 'okay' or enabled). -+ -+ - Name: tinylcd35 - Info: 3.5" Color TFT Display by www.tinylcd.com - Options: Touch, RTC, keypad ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/spi1-1cs-overlay.dts -@@ -0,0 +1,57 @@ -+/dts-v1/; -+/plugin/; -+ -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&gpio>; -+ __overlay__ { -+ spi1_pins: spi1_pins { -+ brcm,pins = <19 20 21>; -+ brcm,function = <3>; /* alt4 */ -+ }; -+ -+ spi1_cs_pins: spi1_cs_pins { -+ brcm,pins = <18>; -+ brcm,function = <1>; /* output */ -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&spi1>; -+ frag1: __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&spi1_pins &spi1_cs_pins>; -+ cs-gpios = <&gpio 18 1>; -+ status = "okay"; -+ -+ spidev1_0: spidev@0 { -+ compatible = "spidev"; -+ reg = <0>; /* CE0 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ status = "okay"; -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&aux>; -+ __overlay__ { -+ status = "okay"; -+ }; -+ }; -+ -+ __overrides__ { -+ cs0_pin = <&spi1_cs_pins>,"brcm,pins:0", -+ <&frag1>,"cs-gpios:4"; -+ cs0_spidev = <&spidev1_0>,"status"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/spi1-2cs-overlay.dts -@@ -0,0 +1,69 @@ -+/dts-v1/; -+/plugin/; -+ -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&gpio>; -+ __overlay__ { -+ spi1_pins: spi1_pins { -+ brcm,pins = <19 20 21>; -+ brcm,function = <3>; /* alt4 */ -+ }; -+ -+ spi1_cs_pins: spi1_cs_pins { -+ brcm,pins = <18 17>; -+ brcm,function = <1>; /* output */ -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&spi1>; -+ frag1: __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&spi1_pins &spi1_cs_pins>; -+ cs-gpios = <&gpio 18 1>, <&gpio 17 1>; -+ status = "okay"; -+ -+ spidev1_0: spidev@0 { -+ compatible = "spidev"; -+ reg = <0>; /* CE0 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ status = "okay"; -+ }; -+ -+ spidev1_1: spidev@1 { -+ compatible = "spidev"; -+ reg = <1>; /* CE1 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ status = "okay"; -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&aux>; -+ __overlay__ { -+ status = "okay"; -+ }; -+ }; -+ -+ __overrides__ { -+ cs0_pin = <&spi1_cs_pins>,"brcm,pins:0", -+ <&frag1>,"cs-gpios:4"; -+ cs1_pin = <&spi1_cs_pins>,"brcm,pins:4", -+ <&frag1>,"cs-gpios:16"; -+ cs0_spidev = <&spidev1_0>,"status"; -+ cs1_spidev = <&spidev1_1>,"status"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/spi1-3cs-overlay.dts -@@ -0,0 +1,81 @@ -+/dts-v1/; -+/plugin/; -+ -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&gpio>; -+ __overlay__ { -+ spi1_pins: spi1_pins { -+ brcm,pins = <19 20 21>; -+ brcm,function = <3>; /* alt4 */ -+ }; -+ -+ spi1_cs_pins: spi1_cs_pins { -+ brcm,pins = <18 17 16>; -+ brcm,function = <1>; /* output */ -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&spi1>; -+ frag1: __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&spi1_pins &spi1_cs_pins>; -+ cs-gpios = <&gpio 18 1>, <&gpio 17 1>, <&gpio 16 1>; -+ status = "okay"; -+ -+ spidev1_0: spidev@0 { -+ compatible = "spidev"; -+ reg = <0>; /* CE0 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ status = "okay"; -+ }; -+ -+ spidev1_1: spidev@1 { -+ compatible = "spidev"; -+ reg = <1>; /* CE1 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ status = "okay"; -+ }; -+ -+ spidev1_2: spidev@2 { -+ compatible = "spidev"; -+ reg = <2>; /* CE2 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ status = "okay"; -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&aux>; -+ __overlay__ { -+ status = "okay"; -+ }; -+ }; -+ -+ __overrides__ { -+ cs0_pin = <&spi1_cs_pins>,"brcm,pins:0", -+ <&frag1>,"cs-gpios:4"; -+ cs1_pin = <&spi1_cs_pins>,"brcm,pins:4", -+ <&frag1>,"cs-gpios:16"; -+ cs2_pin = <&spi1_cs_pins>,"brcm,pins:8", -+ <&frag1>,"cs-gpios:28"; -+ cs0_spidev = <&spidev1_0>,"status"; -+ cs1_spidev = <&spidev1_1>,"status"; -+ cs2_spidev = <&spidev1_2>,"status"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/spi2-1cs-overlay.dts -@@ -0,0 +1,57 @@ -+/dts-v1/; -+/plugin/; -+ -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&gpio>; -+ __overlay__ { -+ spi2_pins: spi2_pins { -+ brcm,pins = <40 41 42>; -+ brcm,function = <3>; /* alt4 */ -+ }; -+ -+ spi2_cs_pins: spi2_cs_pins { -+ brcm,pins = <43>; -+ brcm,function = <1>; /* output */ -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&spi2>; -+ frag1: __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&spi2_pins &spi2_cs_pins>; -+ cs-gpios = <&gpio 43 1>; -+ status = "okay"; -+ -+ spidev2_0: spidev@0 { -+ compatible = "spidev"; -+ reg = <0>; /* CE0 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ status = "okay"; -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&aux>; -+ __overlay__ { -+ status = "okay"; -+ }; -+ }; -+ -+ __overrides__ { -+ cs0_pin = <&spi2_cs_pins>,"brcm,pins:0", -+ <&frag1>,"cs-gpios:4"; -+ cs0_spidev = <&spidev2_0>,"status"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/spi2-2cs-overlay.dts -@@ -0,0 +1,69 @@ -+/dts-v1/; -+/plugin/; -+ -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&gpio>; -+ __overlay__ { -+ spi2_pins: spi2_pins { -+ brcm,pins = <40 41 42>; -+ brcm,function = <3>; /* alt4 */ -+ }; -+ -+ spi2_cs_pins: spi2_cs_pins { -+ brcm,pins = <43 44>; -+ brcm,function = <1>; /* output */ -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&spi2>; -+ frag1: __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&spi2_pins &spi2_cs_pins>; -+ cs-gpios = <&gpio 43 1>, <&gpio 44 1>; -+ status = "okay"; -+ -+ spidev2_0: spidev@0 { -+ compatible = "spidev"; -+ reg = <0>; /* CE0 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ status = "okay"; -+ }; -+ -+ spidev2_1: spidev@1 { -+ compatible = "spidev"; -+ reg = <1>; /* CE1 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ status = "okay"; -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&aux>; -+ __overlay__ { -+ status = "okay"; -+ }; -+ }; -+ -+ __overrides__ { -+ cs0_pin = <&spi2_cs_pins>,"brcm,pins:0", -+ <&frag1>,"cs-gpios:4"; -+ cs1_pin = <&spi2_cs_pins>,"brcm,pins:4", -+ <&frag1>,"cs-gpios:16"; -+ cs0_spidev = <&spidev2_0>,"status"; -+ cs1_spidev = <&spidev2_1>,"status"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/spi2-3cs-overlay.dts -@@ -0,0 +1,81 @@ -+/dts-v1/; -+/plugin/; -+ -+ -+/ { -+ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; -+ -+ fragment@0 { -+ target = <&gpio>; -+ __overlay__ { -+ spi2_pins: spi2_pins { -+ brcm,pins = <40 41 42>; -+ brcm,function = <3>; /* alt4 */ -+ }; -+ -+ spi2_cs_pins: spi2_cs_pins { -+ brcm,pins = <43 44 45>; -+ brcm,function = <1>; /* output */ -+ }; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&spi2>; -+ frag1: __overlay__ { -+ /* needed to avoid dtc warning */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&spi2_pins &spi2_cs_pins>; -+ cs-gpios = <&gpio 43 1>, <&gpio 44 1>, <&gpio 45 1>; -+ status = "okay"; -+ -+ spidev2_0: spidev@0 { -+ compatible = "spidev"; -+ reg = <0>; /* CE0 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ status = "okay"; -+ }; -+ -+ spidev2_1: spidev@1 { -+ compatible = "spidev"; -+ reg = <1>; /* CE1 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ status = "okay"; -+ }; -+ -+ spidev2_2: spidev@2 { -+ compatible = "spidev"; -+ reg = <2>; /* CE2 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ status = "okay"; -+ }; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&aux>; -+ __overlay__ { -+ status = "okay"; -+ }; -+ }; -+ -+ __overrides__ { -+ cs0_pin = <&spi2_cs_pins>,"brcm,pins:0", -+ <&frag1>,"cs-gpios:4"; -+ cs1_pin = <&spi2_cs_pins>,"brcm,pins:4", -+ <&frag1>,"cs-gpios:16"; -+ cs2_pin = <&spi2_cs_pins>,"brcm,pins:8", -+ <&frag1>,"cs-gpios:28"; -+ cs0_spidev = <&spidev2_0>,"status"; -+ cs1_spidev = <&spidev2_1>,"status"; -+ cs2_spidev = <&spidev2_2>,"status"; -+ }; -+}; ---- a/arch/arm/configs/bcm2709_defconfig -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -601,6 +601,7 @@ CONFIG_I2C_BCM2708=m - CONFIG_I2C_GPIO=m - CONFIG_SPI=y - CONFIG_SPI_BCM2835=m -+CONFIG_SPI_BCM2835AUX=m - CONFIG_SPI_SPIDEV=y - CONFIG_PPS=m - CONFIG_PPS_CLIENT_LDISC=m ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -593,6 +593,7 @@ CONFIG_I2C_BCM2708=m - CONFIG_I2C_GPIO=m - CONFIG_SPI=y - CONFIG_SPI_BCM2835=m -+CONFIG_SPI_BCM2835AUX=m - CONFIG_SPI_SPIDEV=y - CONFIG_PPS=m - CONFIG_PPS_CLIENT_LDISC=m ---- a/drivers/clk/bcm/Makefile -+++ b/drivers/clk/bcm/Makefile -@@ -4,7 +4,7 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281 - obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o - obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o - obj-$(CONFIG_ARCH_BCM2835)$(CONFIG_ARCH_BCM2708)$(CONFIG_ARCH_BCM2709) += clk-bcm2835.o --obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835-aux.o -+obj-$(CONFIG_ARCH_BCM2835)$(CONFIG_ARCH_BCM2708)$(CONFIG_ARCH_BCM2709) += clk-bcm2835-aux.o - obj-$(CONFIG_COMMON_CLK_IPROC) += clk-ns2.o - obj-$(CONFIG_ARCH_BCM_CYGNUS) += clk-cygnus.o - obj-$(CONFIG_ARCH_BCM_NSP) += clk-nsp.o ---- a/drivers/spi/Kconfig -+++ b/drivers/spi/Kconfig -@@ -90,7 +90,7 @@ config SPI_BCM2835 - - config SPI_BCM2835AUX - tristate "BCM2835 SPI auxiliary controller" -- depends on ARCH_BCM2835 || COMPILE_TEST -+ depends on ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709 || COMPILE_TEST - depends on GPIOLIB - help - This selects a driver for the Broadcom BCM2835 SPI aux master. diff --git a/target/linux/brcm2708/patches-4.4/0163-bcm2835-camera-fix-a-bug-in-computation-of-frame-tim.patch b/target/linux/brcm2708/patches-4.4/0163-bcm2835-camera-fix-a-bug-in-computation-of-frame-tim.patch new file mode 100644 index 0000000000..ecc52298f5 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0163-bcm2835-camera-fix-a-bug-in-computation-of-frame-tim.patch @@ -0,0 +1,23 @@ +From f22f155684481bb96748677bc58705d27bf4f06b Mon Sep 17 00:00:00 2001 +From: Dhiraj Goel +Date: Thu, 3 Mar 2016 21:10:50 -0800 +Subject: [PATCH 163/232] bcm2835-camera: fix a bug in computation of frame + timestamp + +Fixes #1318 +--- + drivers/media/platform/bcm2835/bcm2835-camera.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/media/platform/bcm2835/bcm2835-camera.c ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.c +@@ -360,8 +360,7 @@ static void buffer_cb(struct vchiq_mmal_ + div = + div_u64_rem(runtime_us, USEC_PER_SEC, &rem); + buf->vb.timestamp.tv_sec = +- dev->capture.kernel_start_ts.tv_sec - 1 + +- div; ++ dev->capture.kernel_start_ts.tv_sec + div; + buf->vb.timestamp.tv_usec = + dev->capture.kernel_start_ts.tv_usec + rem; + diff --git a/target/linux/brcm2708/patches-4.4/0164-ASoC-bcm-add-missing-.owner-fields-in-sound-card-dri.patch b/target/linux/brcm2708/patches-4.4/0164-ASoC-bcm-add-missing-.owner-fields-in-sound-card-dri.patch deleted file mode 100644 index 0a68d3c881..0000000000 --- a/target/linux/brcm2708/patches-4.4/0164-ASoC-bcm-add-missing-.owner-fields-in-sound-card-dri.patch +++ /dev/null @@ -1,108 +0,0 @@ -From 84c2e063885bb0ae3d5d2ad2e24e7a2bdb5729ae Mon Sep 17 00:00:00 2001 -From: Matthias Reichl -Date: Tue, 23 Feb 2016 17:28:23 +0100 -Subject: [PATCH 164/170] ASoC: bcm: add missing .owner fields in sound card - drivers - -If snd_soc_card.owner is not set the kernel won't do usage refcounting -and one can remove the card driver module while it's in use (eg playback -active) - which leads to a kernel crash. - -The missing owner field also prevents ALSA slot ordering -(options snd slots=module-name1,module-name-2,...) from working with -the I2S cards as it has no module name to match against. - -Fix these issues by setting the .owner field in the snd_soc_card structs. - -Signed-off-by: Matthias Reichl ---- - sound/soc/bcm/hifiberry_amp.c | 1 + - sound/soc/bcm/hifiberry_dac.c | 1 + - sound/soc/bcm/hifiberry_dacplus.c | 1 + - sound/soc/bcm/hifiberry_digi.c | 1 + - sound/soc/bcm/iqaudio-dac.c | 1 + - sound/soc/bcm/raspidac3.c | 1 + - sound/soc/bcm/rpi-dac.c | 1 + - sound/soc/bcm/rpi-proto.c | 1 + - 8 files changed, 8 insertions(+) - ---- a/sound/soc/bcm/hifiberry_amp.c -+++ b/sound/soc/bcm/hifiberry_amp.c -@@ -61,6 +61,7 @@ static struct snd_soc_dai_link snd_rpi_h - - static struct snd_soc_card snd_rpi_hifiberry_amp = { - .name = "snd_rpi_hifiberry_amp", -+ .owner = THIS_MODULE, - .dai_link = snd_rpi_hifiberry_amp_dai, - .num_links = ARRAY_SIZE(snd_rpi_hifiberry_amp_dai), - }; ---- a/sound/soc/bcm/hifiberry_dac.c -+++ b/sound/soc/bcm/hifiberry_dac.c -@@ -63,6 +63,7 @@ static struct snd_soc_dai_link snd_rpi_h - /* audio machine driver */ - static struct snd_soc_card snd_rpi_hifiberry_dac = { - .name = "snd_rpi_hifiberry_dac", -+ .owner = THIS_MODULE, - .dai_link = snd_rpi_hifiberry_dac_dai, - .num_links = ARRAY_SIZE(snd_rpi_hifiberry_dac_dai), - }; ---- a/sound/soc/bcm/hifiberry_dacplus.c -+++ b/sound/soc/bcm/hifiberry_dacplus.c -@@ -287,6 +287,7 @@ static struct snd_soc_dai_link snd_rpi_h - /* audio machine driver */ - static struct snd_soc_card snd_rpi_hifiberry_dacplus = { - .name = "snd_rpi_hifiberry_dacplus", -+ .owner = THIS_MODULE, - .dai_link = snd_rpi_hifiberry_dacplus_dai, - .num_links = ARRAY_SIZE(snd_rpi_hifiberry_dacplus_dai), - }; ---- a/sound/soc/bcm/hifiberry_digi.c -+++ b/sound/soc/bcm/hifiberry_digi.c -@@ -164,6 +164,7 @@ static struct snd_soc_dai_link snd_rpi_h - /* audio machine driver */ - static struct snd_soc_card snd_rpi_hifiberry_digi = { - .name = "snd_rpi_hifiberry_digi", -+ .owner = THIS_MODULE, - .dai_link = snd_rpi_hifiberry_digi_dai, - .num_links = ARRAY_SIZE(snd_rpi_hifiberry_digi_dai), - }; ---- a/sound/soc/bcm/iqaudio-dac.c -+++ b/sound/soc/bcm/iqaudio-dac.c -@@ -77,6 +77,7 @@ static struct snd_soc_dai_link snd_rpi_i - /* audio machine driver */ - static struct snd_soc_card snd_rpi_iqaudio_dac = { - .name = "IQaudIODAC", -+ .owner = THIS_MODULE, - .dai_link = snd_rpi_iqaudio_dac_dai, - .num_links = ARRAY_SIZE(snd_rpi_iqaudio_dac_dai), - }; ---- a/sound/soc/bcm/raspidac3.c -+++ b/sound/soc/bcm/raspidac3.c -@@ -128,6 +128,7 @@ static struct snd_soc_dai_link snd_rpi_r - /* audio machine driver */ - static struct snd_soc_card snd_rpi_raspidac3 = { - .name = "RaspiDAC Rev.3x HiFi Audio Card", -+ .owner = THIS_MODULE, - .dai_link = snd_rpi_raspidac3_dai, - .num_links = ARRAY_SIZE(snd_rpi_raspidac3_dai), - }; ---- a/sound/soc/bcm/rpi-dac.c -+++ b/sound/soc/bcm/rpi-dac.c -@@ -60,6 +60,7 @@ static struct snd_soc_dai_link snd_rpi_r - /* audio machine driver */ - static struct snd_soc_card snd_rpi_rpi_dac = { - .name = "snd_rpi_rpi_dac", -+ .owner = THIS_MODULE, - .dai_link = snd_rpi_rpi_dac_dai, - .num_links = ARRAY_SIZE(snd_rpi_rpi_dac_dai), - }; ---- a/sound/soc/bcm/rpi-proto.c -+++ b/sound/soc/bcm/rpi-proto.c -@@ -91,6 +91,7 @@ static struct snd_soc_dai_link snd_rpi_p - /* audio machine driver */ - static struct snd_soc_card snd_rpi_proto = { - .name = "snd_rpi_proto", -+ .owner = THIS_MODULE, - .dai_link = snd_rpi_proto_dai, - .num_links = ARRAY_SIZE(snd_rpi_proto_dai), - }; diff --git a/target/linux/brcm2708/patches-4.4/0164-BCM270X_DT-Add-pi3-disable-bt-overlay.patch b/target/linux/brcm2708/patches-4.4/0164-BCM270X_DT-Add-pi3-disable-bt-overlay.patch new file mode 100644 index 0000000000..00ed1dad23 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0164-BCM270X_DT-Add-pi3-disable-bt-overlay.patch @@ -0,0 +1,96 @@ +From 1807dd78b1af8eff8976f3431e4bf98dccd2113d Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 2 Mar 2016 10:59:05 +0000 +Subject: [PATCH 164/232] BCM270X_DT: Add pi3-disable-bt overlay + +Disable Bluetooth and restore UART0/ttyAMA0 over GPIOs 14 & 15. To disable +the systemd service that initialises the modem so it doesn't use the UART: + + sudo systemctl disable hciuart + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 8 ++++ + .../boot/dts/overlays/pi3-disable-bt-overlay.dts | 48 ++++++++++++++++++++++ + 3 files changed, 57 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/pi3-disable-bt-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -39,6 +39,7 @@ dtb-$(RPI_DT_OVERLAYS) += mcp2515-can0-o + dtb-$(RPI_DT_OVERLAYS) += mcp2515-can1-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += mmc-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += mz61581-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += pi3-disable-bt-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += piscreen-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += piscreen2r-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += pitft28-capacitive-overlay.dtb +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -496,6 +496,14 @@ Params: speed Display + [ The pcf8563-rtc overlay has been deleted. See i2c-rtc. ] + + ++Name: pi3-disable-bt ++Info: Disable Pi3 Bluetooth and restore UART0/ttyAMA0 over GPIOs 14 & 15 ++ N.B. To disable the systemd service that initialises the modem so it ++ doesn't use the UART, use 'sudo systemctl disable hciuart'. ++Load: dtoverlay=pi3-disable-bt ++Params: ++ ++ + Name: piscreen + Info: PiScreen display by OzzMaker.com + Load: dtoverlay=piscreen,= +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/pi3-disable-bt-overlay.dts +@@ -0,0 +1,48 @@ ++/dts-v1/; ++/plugin/; ++ ++/* Disable Bluetooth and restore UART0/ttyAMA0 over GPIOs 14 & 15. ++ To disable the systemd service that initialises the modem so it doesn't use ++ the UART: ++ ++ sudo systemctl disable hciuart ++*/ ++ ++/{ ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&uart1>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&uart0>; ++ __overlay__ { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart0_pins>; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&gpio>; ++ __overlay__ { ++ uart0_pins: uart0_pins { ++ brcm,pins = <14 15>; ++ brcm,function = <4>; /* alt0 */ ++ brcm,pull = <0 2>; ++ }; ++ }; ++ }; ++ ++ fragment@3 { ++ target-path = "/aliases"; ++ __overlay__ { ++ serial0 = "/soc/uart@7e201000"; ++ serial1 = "/soc/uart@7e215040"; ++ }; ++ }; ++}; diff --git a/target/linux/brcm2708/patches-4.4/0165-BCM270X_DT-Add-pi3-miniuart-bt-DT-overlay.patch b/target/linux/brcm2708/patches-4.4/0165-BCM270X_DT-Add-pi3-miniuart-bt-DT-overlay.patch new file mode 100644 index 0000000000..69d2532f02 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0165-BCM270X_DT-Add-pi3-miniuart-bt-DT-overlay.patch @@ -0,0 +1,117 @@ +From 4adf0d94785a304b51f8205ecade7bc97b26a00b Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 7 Mar 2016 09:53:03 +0000 +Subject: [PATCH 165/232] BCM270X_DT: Add pi3-miniuart-bt DT overlay + +Switch Pi3 Bluetooth function to use the mini-UART (ttyS0) and restore +UART0/ttyAMA0 over GPIOs 14 & 15. Note that this may reduce the maximum +usable baudrate. + +It is also necessary to edit /lib/systemd/system/hciuart.server and +replace ttyAMA0 with ttyS0. + +If cmdline.txt uses the alias serial0 to refer to the user-accessable port +then the firmware will replace with the appropriate port whether or not +this overlay is used. + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 10 ++++ + .../boot/dts/overlays/pi3-miniuart-bt-overlay.dts | 61 ++++++++++++++++++++++ + 3 files changed, 72 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -40,6 +40,7 @@ dtb-$(RPI_DT_OVERLAYS) += mcp2515-can1-o + dtb-$(RPI_DT_OVERLAYS) += mmc-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += mz61581-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += pi3-disable-bt-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += pi3-miniuart-bt-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += piscreen-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += piscreen2r-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += pitft28-capacitive-overlay.dtb +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -504,6 +504,16 @@ Load: dtoverlay=pi3-disable-bt + Params: + + ++Name: pi3-miniuart-bt ++Info: Switch Pi3 Bluetooth function to use the mini-UART (ttyS0) and restore ++ UART0/ttyAMA0 over GPIOs 14 & 15. Note that this may reduce the maximum ++ usable baudrate. ++ N.B. It is also necessary to edit /lib/systemd/system/hciuart.server ++ and replace ttyAMA0 with ttyS0. ++Load: dtoverlay=pi3-miniuart-bt ++Params: ++ ++ + Name: piscreen + Info: PiScreen display by OzzMaker.com + Load: dtoverlay=piscreen,= +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts +@@ -0,0 +1,61 @@ ++/dts-v1/; ++/plugin/; ++ ++/* Switch Pi3 Bluetooth function to use the mini-UART (ttyS0) and restore ++ UART0/ttyAMA0 over GPIOs 14 & 15. Note that this may reduce the maximum ++ usable baudrate. ++ ++ It is also necessary to edit /lib/systemd/system/hciuart.server and ++ replace ttyAMA0 with ttyS0. ++ ++ If cmdline.txt uses the alias serial0 to refer to the user-accessable port ++ then the firmware will replace with the appropriate port whether or not ++ this overlay is used. ++*/ ++ ++/{ ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&uart0>; ++ __overlay__ { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart0_pins>; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&uart1>; ++ __overlay__ { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart1_pins>; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&gpio>; ++ __overlay__ { ++ uart0_pins: uart0_pins { ++ brcm,pins = <14 15>; ++ brcm,function = <4>; /* alt0 */ ++ brcm,pull = <0 2>; ++ }; ++ ++ uart1_pins: uart1_pins { ++ brcm,pins = <32 33>; ++ brcm,function = <2>; /* alt5=UART1 */ ++ brcm,pull = <0 0>; ++ }; ++ }; ++ }; ++ ++ fragment@3 { ++ target-path = "/aliases"; ++ __overlay__ { ++ serial0 = "/soc/uart@7e201000"; ++ serial1 = "/soc/uart@7e215040"; ++ }; ++ }; ++}; diff --git a/target/linux/brcm2708/patches-4.4/0165-smsx95xx-Add-option-to-disable-the-crimes-against-tr.patch b/target/linux/brcm2708/patches-4.4/0165-smsx95xx-Add-option-to-disable-the-crimes-against-tr.patch deleted file mode 100644 index 81ac7cb9db..0000000000 --- a/target/linux/brcm2708/patches-4.4/0165-smsx95xx-Add-option-to-disable-the-crimes-against-tr.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 18674a7da1c3d50d9c957a8f88aaea2aa653d223 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 20 Jan 2016 17:50:09 +0000 -Subject: [PATCH 165/170] smsx95xx: Add option to disable the crimes against - truesize fix - -It may improve iperf numbers on Pi 1, but may generate dmesg warnings and possibly cause network issues -See issue 1248. ---- - drivers/net/usb/smsc95xx.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - mode change 100755 => 100644 drivers/net/usb/smsc95xx.c - ---- a/drivers/net/usb/smsc95xx.c -+++ b/drivers/net/usb/smsc95xx.c -@@ -75,6 +75,10 @@ static bool turbo_mode = false; - module_param(turbo_mode, bool, 0644); - MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); - -+static bool truesize_mode = false; -+module_param(truesize_mode, bool, 0644); -+MODULE_PARM_DESC(truesize_mode, "Report larger truesize value"); -+ - static char *macaddr = ":"; - module_param(macaddr, charp, 0); - MODULE_PARM_DESC(macaddr, "MAC address"); -@@ -1841,6 +1845,8 @@ static int smsc95xx_rx_fixup(struct usbn - if (dev->net->features & NETIF_F_RXCSUM) - smsc95xx_rx_csum_offload(skb); - skb_trim(skb, skb->len - 4); /* remove fcs */ -+ if (truesize_mode) -+ skb->truesize = size + sizeof(struct sk_buff); - - return 1; - } -@@ -1858,6 +1864,8 @@ static int smsc95xx_rx_fixup(struct usbn - if (dev->net->features & NETIF_F_RXCSUM) - smsc95xx_rx_csum_offload(ax_skb); - skb_trim(ax_skb, ax_skb->len - 4); /* remove fcs */ -+ if (truesize_mode) -+ ax_skb->truesize = size + sizeof(struct sk_buff); - - usbnet_skb_return(dev, ax_skb); - } diff --git a/target/linux/brcm2708/patches-4.4/0166-Pi3-DT-Add-dtparams-for-the-SD-interface.patch b/target/linux/brcm2708/patches-4.4/0166-Pi3-DT-Add-dtparams-for-the-SD-interface.patch new file mode 100644 index 0000000000..0388794aeb --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0166-Pi3-DT-Add-dtparams-for-the-SD-interface.patch @@ -0,0 +1,25 @@ +From 2efc547c5e535a2061312e63a459cdee9635cdf9 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 7 Mar 2016 13:38:39 +0000 +Subject: [PATCH 166/232] Pi3 DT: Add dtparams for the SD interface + +Add new base dtparams sd_overclock, sd_force_pio, sd_pio_limit +and sd_debug. These were missed out of the initial Pi3 DTB. + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/bcm2710-rpi-3-b.dts | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/arch/arm/boot/dts/bcm2710-rpi-3-b.dts ++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts +@@ -188,5 +188,9 @@ + audio = <&audio>,"status"; + watchdog = <&watchdog>,"status"; + random = <&random>,"status"; ++ sd_overclock = <&sdhost>,"brcm,overclock-50:0"; ++ sd_force_pio = <&sdhost>,"brcm,force-pio?"; ++ sd_pio_limit = <&sdhost>,"brcm,pio-limit:0"; ++ sd_debug = <&sdhost>,"brcm,debug"; + }; + }; diff --git a/target/linux/brcm2708/patches-4.4/0166-bcm2835-virtgpio-Virtual-GPIO-driver.patch b/target/linux/brcm2708/patches-4.4/0166-bcm2835-virtgpio-Virtual-GPIO-driver.patch deleted file mode 100644 index 428c253c8d..0000000000 --- a/target/linux/brcm2708/patches-4.4/0166-bcm2835-virtgpio-Virtual-GPIO-driver.patch +++ /dev/null @@ -1,244 +0,0 @@ -From 5e3c3e845f998f86c2f22017576cb19e5d7fe9bb Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 23 Feb 2016 19:56:04 +0000 -Subject: [PATCH 166/170] bcm2835-virtgpio: Virtual GPIO driver - -Add a virtual GPIO driver that uses the firmware mailbox interface to -request that the VPU toggles LEDs. ---- - arch/arm/configs/bcm2709_defconfig | 1 + - drivers/gpio/Kconfig | 6 + - drivers/gpio/Makefile | 1 + - drivers/gpio/gpio-bcm-virt.c | 180 +++++++++++++++++++++++++++++ - include/soc/bcm2835/raspberrypi-firmware.h | 1 + - 5 files changed, 189 insertions(+) - create mode 100644 drivers/gpio/gpio-bcm-virt.c - ---- a/arch/arm/configs/bcm2709_defconfig -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -607,6 +607,7 @@ CONFIG_PPS=m - CONFIG_PPS_CLIENT_LDISC=m - CONFIG_PPS_CLIENT_GPIO=m - CONFIG_GPIO_SYSFS=y -+CONFIG_GPIO_BCM_VIRT=y - CONFIG_GPIO_ARIZONA=m - CONFIG_GPIO_STMPE=y - CONFIG_W1=m ---- a/drivers/gpio/Kconfig -+++ b/drivers/gpio/Kconfig -@@ -132,6 +132,12 @@ config GPIO_BCM_KONA - help - Turn on GPIO support for Broadcom "Kona" chips. - -+config GPIO_BCM_VIRT -+ bool "Broadcom Virt GPIO" -+ depends on OF_GPIO && RASPBERRYPI_FIRMWARE && (ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709 || COMPILE_TEST) -+ help -+ Turn on virtual GPIO support for Broadcom BCM283X chips. -+ - config GPIO_BRCMSTB - tristate "BRCMSTB GPIO support" - default y if ARCH_BRCMSTB ---- a/drivers/gpio/Makefile -+++ b/drivers/gpio/Makefile -@@ -24,6 +24,7 @@ obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o - obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o - obj-$(CONFIG_ATH79) += gpio-ath79.o - obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o -+obj-$(CONFIG_GPIO_BCM_VIRT) += gpio-bcm-virt.o - obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o - obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o - obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o ---- /dev/null -+++ b/drivers/gpio/gpio-bcm-virt.c -@@ -0,0 +1,180 @@ -+/* -+ * brcmvirt GPIO driver -+ * -+ * Copyright (C) 2012,2013 Dom Cobley -+ * Based on gpio-clps711x.c by Alexander Shiyan -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define MODULE_NAME "brcmvirt-gpio" -+#define NUM_GPIO 2 -+ -+struct brcmvirt_gpio { -+ struct gpio_chip gc; -+ u32 __iomem *ts_base; -+ /* two packed 16-bit counts of enabled and disables -+ Allows host to detect a brief enable that was missed */ -+ u32 enables_disables[NUM_GPIO]; -+}; -+ -+static int brcmvirt_gpio_dir_in(struct gpio_chip *gc, unsigned off) -+{ -+ struct brcmvirt_gpio *gpio; -+ gpio = container_of(gc, struct brcmvirt_gpio, gc); -+ return -EINVAL; -+} -+ -+static int brcmvirt_gpio_dir_out(struct gpio_chip *gc, unsigned off, int val) -+{ -+ struct brcmvirt_gpio *gpio; -+ gpio = container_of(gc, struct brcmvirt_gpio, gc); -+ return 0; -+} -+ -+static int brcmvirt_gpio_get(struct gpio_chip *gc, unsigned off) -+{ -+ struct brcmvirt_gpio *gpio; -+ unsigned v; -+ gpio = container_of(gc, struct brcmvirt_gpio, gc); -+ v = readl(gpio->ts_base + off); -+ return (v >> off) & 1; -+} -+ -+static void brcmvirt_gpio_set(struct gpio_chip *gc, unsigned off, int val) -+{ -+ struct brcmvirt_gpio *gpio; -+ u16 enables, disables; -+ s16 diff; -+ bool lit; -+ gpio = container_of(gc, struct brcmvirt_gpio, gc); -+ enables = gpio->enables_disables[off] >> 16; -+ disables = gpio->enables_disables[off] >> 0; -+ diff = (s16)(enables - disables); -+ lit = diff > 0; -+ if ((val && lit) || (!val && !lit)) -+ return; -+ if (val) -+ enables++; -+ else -+ disables++; -+ diff = (s16)(enables - disables); -+ BUG_ON(diff != 0 && diff != 1); -+ gpio->enables_disables[off] = (enables << 16) | (disables << 0); -+ writel(gpio->enables_disables[off], gpio->ts_base + off); -+} -+ -+static int brcmvirt_gpio_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct device_node *np = dev->of_node; -+ struct device_node *fw_node; -+ struct rpi_firmware *fw; -+ struct brcmvirt_gpio *ucb; -+ u32 gpiovirtbuf; -+ int err = 0; -+ -+ fw_node = of_parse_phandle(np, "firmware", 0); -+ if (!fw_node) { -+ dev_err(dev, "Missing firmware node\n"); -+ return -ENOENT; -+ } -+ -+ fw = rpi_firmware_get(fw_node); -+ if (!fw) -+ return -EPROBE_DEFER; -+ -+ err = rpi_firmware_property(fw, RPI_FIRMWARE_FRAMEBUFFER_GET_GPIOVIRTBUF, -+ &gpiovirtbuf, sizeof(gpiovirtbuf)); -+ -+ if (err) { -+ dev_err(dev, "Failed to get gpiovirtbuf\n"); -+ goto err; -+ } -+ -+ if (!gpiovirtbuf) { -+ dev_err(dev, "No virtgpio buffer\n"); -+ err = -ENOENT; -+ goto err; -+ } -+ -+ ucb = devm_kzalloc(dev, sizeof *ucb, GFP_KERNEL); -+ if (!ucb) { -+ err = -EINVAL; -+ goto err; -+ } -+ -+ // mmap the physical memory -+ gpiovirtbuf &= ~0xc0000000; -+ ucb->ts_base = ioremap(gpiovirtbuf, 4096); -+ if (ucb->ts_base == NULL) { -+ dev_err(dev, "Failed to map physical address\n"); -+ err = -ENOENT; -+ goto err; -+ } -+ -+ ucb->gc.label = MODULE_NAME; -+ ucb->gc.owner = THIS_MODULE; -+ ucb->gc.dev = dev; -+ ucb->gc.of_node = np; -+ ucb->gc.base = 100; -+ ucb->gc.ngpio = NUM_GPIO; -+ -+ ucb->gc.direction_input = brcmvirt_gpio_dir_in; -+ ucb->gc.direction_output = brcmvirt_gpio_dir_out; -+ ucb->gc.get = brcmvirt_gpio_get; -+ ucb->gc.set = brcmvirt_gpio_set; -+ ucb->gc.can_sleep = true; -+ -+ err = gpiochip_add(&ucb->gc); -+ if (err) -+ goto err; -+ -+ platform_set_drvdata(pdev, ucb); -+ -+err: -+ return err; -+ -+} -+ -+static int brcmvirt_gpio_remove(struct platform_device *pdev) -+{ -+ int err = 0; -+ struct brcmvirt_gpio *ucb = platform_get_drvdata(pdev); -+ -+ gpiochip_remove(&ucb->gc); -+ iounmap(ucb->ts_base); -+ return err; -+} -+ -+static const struct of_device_id __maybe_unused brcmvirt_gpio_ids[] = { -+ { .compatible = "brcm,bcm2835-virtgpio" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, brcmvirt_gpio_ids); -+ -+static struct platform_driver brcmvirt_gpio_driver = { -+ .driver = { -+ .name = MODULE_NAME, -+ .owner = THIS_MODULE, -+ .of_match_table = of_match_ptr(brcmvirt_gpio_ids), -+ }, -+ .probe = brcmvirt_gpio_probe, -+ .remove = brcmvirt_gpio_remove, -+}; -+module_platform_driver(brcmvirt_gpio_driver); -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Dom Cobley "); -+MODULE_DESCRIPTION("brcmvirt GPIO driver"); -+MODULE_ALIAS("platform:brcmvirt-gpio"); ---- a/include/soc/bcm2835/raspberrypi-firmware.h -+++ b/include/soc/bcm2835/raspberrypi-firmware.h -@@ -93,6 +93,7 @@ enum rpi_firmware_property_tag { - RPI_FIRMWARE_FRAMEBUFFER_GET_OVERSCAN = 0x0004000a, - RPI_FIRMWARE_FRAMEBUFFER_GET_PALETTE = 0x0004000b, - RPI_FIRMWARE_FRAMEBUFFER_GET_TOUCHBUF = 0x0004000f, -+ RPI_FIRMWARE_FRAMEBUFFER_GET_GPIOVIRTBUF = 0x00040010, - RPI_FIRMWARE_FRAMEBUFFER_RELEASE = 0x00048001, - RPI_FIRMWARE_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT = 0x00044003, - RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT = 0x00044004, diff --git a/target/linux/brcm2708/patches-4.4/0167-BCM270X_DT-Add-Pi3-support.patch b/target/linux/brcm2708/patches-4.4/0167-BCM270X_DT-Add-Pi3-support.patch deleted file mode 100644 index dc11b76825..0000000000 --- a/target/linux/brcm2708/patches-4.4/0167-BCM270X_DT-Add-Pi3-support.patch +++ /dev/null @@ -1,305 +0,0 @@ -From 6aa2c847f76f21c830544e8c79f9030a170ef475 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Thu, 21 Jan 2016 17:57:49 +0000 -Subject: [PATCH 167/170] BCM270X_DT: Add Pi3 support - ---- - arch/arm/boot/dts/Makefile | 1 + - arch/arm/boot/dts/bcm2710-rpi-3-b.dts | 174 ++++++++++++++++++++++++++++++++++ - arch/arm/boot/dts/bcm2710.dtsi | 102 ++++++++++++++++++++ - 3 files changed, 277 insertions(+) - create mode 100644 arch/arm/boot/dts/bcm2710-rpi-3-b.dts - create mode 100644 arch/arm/boot/dts/bcm2710.dtsi - ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -5,6 +5,7 @@ dtb-$(CONFIG_ARCH_BCM2708) += bcm2708-rp - dtb-$(CONFIG_ARCH_BCM2708) += bcm2708-rpi-cm.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-cm.dtb - dtb-$(CONFIG_ARCH_BCM2709) += bcm2709-rpi-2-b.dtb -+dtb-$(CONFIG_ARCH_BCM2709) += bcm2710-rpi-3-b.dtb - - # Raspberry Pi - ifeq ($(CONFIG_ARCH_BCM2708),y) ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts -@@ -0,0 +1,174 @@ -+/dts-v1/; -+ -+#include "bcm2710.dtsi" -+ -+/ { -+ compatible = "brcm,bcm2710","brcm,bcm2709"; -+ model = "Raspberry Pi 3 Model B"; -+}; -+ -+&gpio { -+ sdhost_pins: sdhost_pins { -+ brcm,pins = <48 49 50 51 52 53>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+ -+ spi0_pins: spi0_pins { -+ brcm,pins = <9 10 11>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+ -+ spi0_cs_pins: spi0_cs_pins { -+ brcm,pins = <8 7>; -+ brcm,function = <1>; /* output */ -+ }; -+ -+ 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 */ -+ }; -+ -+ sdio_pins: sdio_pins { -+ brcm,pins = <34 35 36 37 38 39>; -+ brcm,function = <7>; // alt3 = SD1 -+ brcm,pull = <0 2 2 2 2 2>; -+ }; -+ -+ bt_pins: bt_pins { -+ brcm,pins = <28 29 30 31 14 15 43>; -+ brcm,function = <6 6 6 6 2 2 4>; -+ // alt2:PCM alt5:UART1 alt0:GPCLK2 -+ brcm,pull = <0 0 0 0 0 2 0>; -+ }; -+ -+ uart1_pins: uart1_pins { -+ brcm,pins = <32 33>; -+ brcm,function = <7>; /* alt3=UART0 */ -+ brcm,pull = <0>; -+ }; -+}; -+ -+&sdhost { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&sdhost_pins>; -+ bus-width = <4>; -+ status = "okay"; -+}; -+ -+&mmc { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&sdio_pins>; -+ non-removable; -+ bus-width = <4>; -+ status = "okay"; -+}; -+ -+&fb { -+ status = "okay"; -+}; -+ -+&uart0 { -+ status = "okay"; -+}; -+ -+&uart1 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&uart1_pins &bt_pins>; -+ status = "okay"; -+}; -+ -+&spi0 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&spi0_pins &spi0_cs_pins>; -+ cs-gpios = <&gpio 8 1>, <&gpio 7 1>; -+ -+ 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>; -+}; -+ -+&i2c2 { -+ clock-frequency = <100000>; -+}; -+ -+&i2s { -+ #sound-dai-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2s_pins>; -+}; -+ -+&random { -+ status = "okay"; -+}; -+ -+&leds { -+ act_led: act { -+ label = "led0"; -+ linux,default-trigger = "mmc0"; -+ gpios = <&gpio 47 0>; -+ }; -+}; -+ -+/ { -+ chosen { -+ bootargs = "8250.nr_uarts=1"; -+ }; -+}; -+ -+/ { -+ __overrides__ { -+ uart0 = <&uart0>,"status"; -+ uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; -+ i2s = <&i2s>,"status"; -+ spi = <&spi0>,"status"; -+ i2c0 = <&i2c0>,"status"; -+ i2c1 = <&i2c1>,"status"; -+ i2c2_iknowwhatimdoing = <&i2c2>,"status"; -+ i2c0_baudrate = <&i2c0>,"clock-frequency:0"; -+ i2c1_baudrate = <&i2c1>,"clock-frequency:0"; -+ i2c2_baudrate = <&i2c2>,"clock-frequency:0"; -+ core_freq = <&clk_core>,"clock-frequency:0"; -+ -+ act_led_gpio = <&act_led>,"gpios:4"; -+ act_led_activelow = <&act_led>,"gpios:8"; -+ act_led_trigger = <&act_led>,"linux,default-trigger"; -+ -+ audio = <&audio>,"status"; -+ watchdog = <&watchdog>,"status"; -+ random = <&random>,"status"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2710.dtsi -@@ -0,0 +1,102 @@ -+#include "bcm2708_common.dtsi" -+ -+/ { -+ compatible = "brcm,bcm2710","brcm,bcm2709"; -+ model = "BCM2710"; -+ -+ chosen { -+ /* No padding required - the boot loader can do that. */ -+ bootargs = ""; -+ }; -+ -+ soc { -+ ranges = <0x7e000000 0x3f000000 0x01000000>, -+ <0x40000000 0x40000000 0x00040000>; -+ -+ local_intc: local_intc { -+ compatible = "brcm,bcm2836-l1-intc"; -+ reg = <0x40000000 0x100>; -+ interrupt-controller; -+ #interrupt-cells = <1>; -+ interrupt-parent = <&local_intc>; -+ }; -+ -+ arm-pmu { -+ compatible = "arm,cortex-a7-pmu"; -+ interrupt-parent = <&local_intc>; -+ interrupts = <9>; -+ }; -+ -+ gpiomem { -+ compatible = "brcm,bcm2835-gpiomem"; -+ reg = <0x7e200000 0x1000>; -+ status = "okay"; -+ }; -+ -+ timer { -+ compatible = "arm,armv7-timer"; -+ clock-frequency = <19200000>; -+ interrupt-parent = <&local_intc>; -+ interrupts = <0>, // PHYS_SECURE_PPI -+ <1>, // PHYS_NONSECURE_PPI -+ <3>, // VIRT_PPI -+ <2>; // HYP_PPI -+ always-on; -+ }; -+ -+ syscon@40000000 { -+ compatible = "brcm,bcm2836-arm-local", "syscon"; -+ reg = <0x40000000 0x100>; -+ }; -+ }; -+ -+ cpus: cpus { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ v7_cpu0: cpu@0 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a7"; -+ reg = <0x000>; -+ clock-frequency = <800000000>; -+ }; -+ -+ v7_cpu1: cpu@1 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a7"; -+ reg = <0x001>; -+ clock-frequency = <800000000>; -+ }; -+ -+ v7_cpu2: cpu@2 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a7"; -+ reg = <0x002>; -+ clock-frequency = <800000000>; -+ }; -+ -+ v7_cpu3: cpu@3 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a7"; -+ reg = <0x003>; -+ 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"; -+ }; -+}; -+ -+&watchdog { -+ status = "okay"; -+}; -+ -+&intc { -+ compatible = "brcm,bcm2836-armctrl-ic"; -+ interrupt-parent = <&local_intc>; -+ interrupts = <8>; -+}; diff --git a/target/linux/brcm2708/patches-4.4/0167-vchiq_arm-Tweak-the-logging-output.patch b/target/linux/brcm2708/patches-4.4/0167-vchiq_arm-Tweak-the-logging-output.patch new file mode 100644 index 0000000000..ef77a82212 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0167-vchiq_arm-Tweak-the-logging-output.patch @@ -0,0 +1,75 @@ +From 68c89607e3451adc83a158e99a582be85f047465 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 7 Mar 2016 15:05:11 +0000 +Subject: [PATCH 167/232] vchiq_arm: Tweak the logging output + +Signed-off-by: Phil Elwell +--- + .../vc04_services/interface/vchiq_arm/vchiq_core.c | 31 +++++++++------------- + 1 file changed, 13 insertions(+), 18 deletions(-) + +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c +@@ -891,16 +891,14 @@ queue_message(VCHIQ_STATE_T *state, VCHI + 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)); +- } + } + ++ if (SRVTRACE_ENABLED(service, ++ VCHIQ_LOG_INFO)) ++ vchiq_log_dump_mem("Sent", 0, ++ header->data, ++ min(16, pos)); ++ + spin_lock("a_spinlock); + service_quota->message_use_count++; + +@@ -1039,16 +1037,13 @@ queue_message_sync(VCHIQ_STATE_T *state, + 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)); +- } + } + ++ if (vchiq_sync_log_level >= VCHIQ_LOG_TRACE) ++ vchiq_log_dump_mem("Sent Sync", ++ 0, header->data, ++ min(16, pos)); ++ + VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count); + VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size); + } else { +@@ -1720,7 +1715,7 @@ parse_rx_slots(VCHIQ_STATE_T *state) + remoteport, localport, size); + if (size > 0) + vchiq_log_dump_mem("Rcvd", 0, header->data, +- min(64, size)); ++ min(16, size)); + } + + if (((unsigned int)header & VCHIQ_SLOT_MASK) + calc_stride(size) +@@ -2187,7 +2182,7 @@ sync_func(void *v) + remoteport, localport, size); + if (size > 0) + vchiq_log_dump_mem("Rcvd", 0, header->data, +- min(64, size)); ++ min(16, size)); + } + + switch (type) { diff --git a/target/linux/brcm2708/patches-4.4/0168-FIXUP-BCM270X_DT-Update-to-latest-Pi3-DTS.patch b/target/linux/brcm2708/patches-4.4/0168-FIXUP-BCM270X_DT-Update-to-latest-Pi3-DTS.patch deleted file mode 100644 index 686f5c6ac9..0000000000 --- a/target/linux/brcm2708/patches-4.4/0168-FIXUP-BCM270X_DT-Update-to-latest-Pi3-DTS.patch +++ /dev/null @@ -1,82 +0,0 @@ -From 52015bd5f0bb4d64ca51c5f8539cf2552dfb8a42 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Thu, 21 Jan 2016 17:57:49 +0000 -Subject: [PATCH 168/170] FIXUP: BCM270X_DT: Update to latest Pi3 DTS - ---- - arch/arm/boot/dts/bcm2710-rpi-3-b.dts | 34 ++++++++++++++++++++++++++-------- - 1 file changed, 26 insertions(+), 8 deletions(-) - ---- a/arch/arm/boot/dts/bcm2710-rpi-3-b.dts -+++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts -@@ -45,16 +45,21 @@ - }; - - bt_pins: bt_pins { -- brcm,pins = <28 29 30 31 14 15 43>; -- brcm,function = <6 6 6 6 2 2 4>; -- // alt2:PCM alt5:UART1 alt0:GPCLK2 -- brcm,pull = <0 0 0 0 0 2 0>; -+ brcm,pins = <28 29 30 31 43>; -+ brcm,function = <6 6 6 6 4>; /* alt2:PCM alt0:GPCLK2 */ -+ brcm,pull = <0 0 0 0 0>; - }; - -- uart1_pins: uart1_pins { -+ uart0_pins: uart0_pins { - brcm,pins = <32 33>; - brcm,function = <7>; /* alt3=UART0 */ -- brcm,pull = <0>; -+ brcm,pull = <0 0>; -+ }; -+ -+ uart1_pins: uart1_pins { -+ brcm,pins = <14 15>; -+ brcm,function = <2>; /* alt5=UART1 */ -+ brcm,pull = <0 0>; - }; - }; - -@@ -71,6 +76,17 @@ - non-removable; - bus-width = <4>; - status = "okay"; -+ brcm,overclock-50 = <0>; -+}; -+ -+&soc { -+ virtgpio: virtgpio { -+ compatible = "brcm,bcm2835-virtgpio"; -+ gpio-controller; -+ #gpio-cells = <2>; -+ firmware = <&firmware>; -+ status = "okay"; -+ }; - }; - - &fb { -@@ -78,12 +94,14 @@ - }; - - &uart0 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&uart0_pins &bt_pins>; - status = "okay"; - }; - - &uart1 { - pinctrl-names = "default"; -- pinctrl-0 = <&uart1_pins &bt_pins>; -+ pinctrl-0 = <&uart1_pins>; - status = "okay"; - }; - -@@ -139,7 +157,7 @@ - act_led: act { - label = "led0"; - linux,default-trigger = "mmc0"; -- gpios = <&gpio 47 0>; -+ gpios = <&virtgpio 0 0>; - }; - }; - diff --git a/target/linux/brcm2708/patches-4.4/0168-bcm2835-sdhost-Only-claim-one-DMA-channel.patch b/target/linux/brcm2708/patches-4.4/0168-bcm2835-sdhost-Only-claim-one-DMA-channel.patch new file mode 100644 index 0000000000..3432ccfcc0 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0168-bcm2835-sdhost-Only-claim-one-DMA-channel.patch @@ -0,0 +1,160 @@ +From 1131510a589ae7262b8861af8d2f4a000710d59c Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 7 Mar 2016 16:46:39 +0000 +Subject: [PATCH 168/232] bcm2835-sdhost: Only claim one DMA channel + +With both MMC controllers enabled there are few DMA channels left. The +bcm2835-sdhost driver only uses DMA in one direction at a time, so it +doesn't need to claim two channels. + +See: https://github.com/raspberrypi/linux/issues/1327 + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/bcm2708_common.dtsi | 5 +-- + drivers/mmc/host/bcm2835-sdhost.c | 70 ++++++++++++++++++++++++----------- + 2 files changed, 50 insertions(+), 25 deletions(-) + +--- a/arch/arm/boot/dts/bcm2708_common.dtsi ++++ b/arch/arm/boot/dts/bcm2708_common.dtsi +@@ -136,9 +136,8 @@ + reg = <0x7e202000 0x100>; + interrupts = <2 24>; + clocks = <&clk_core>; +- dmas = <&dma 13>, +- <&dma 13>; +- dma-names = "tx", "rx"; ++ dmas = <&dma 13>; ++ dma-names = "rx-tx"; + brcm,overclock-50 = <0>; + brcm,pio-limit = <1>; + status = "disabled"; +--- a/drivers/mmc/host/bcm2835-sdhost.c ++++ b/drivers/mmc/host/bcm2835-sdhost.c +@@ -185,9 +185,10 @@ struct bcm2835_host { + unsigned int debug:1; /* Enable debug output */ + + /*DMA part*/ +- struct dma_chan *dma_chan_rx; /* DMA channel for reads */ +- struct dma_chan *dma_chan_tx; /* DMA channel for writes */ +- struct dma_chan *dma_chan; /* Channel in used */ ++ struct dma_chan *dma_chan_rxtx; /* DMA channel for reads and writes */ ++ struct dma_chan *dma_chan; /* Channel in use */ ++ struct dma_slave_config dma_cfg_rx; ++ struct dma_slave_config dma_cfg_tx; + struct dma_async_tx_descriptor *dma_desc; + u32 dma_dir; + u32 drain_words; +@@ -771,12 +772,11 @@ static void bcm2835_sdhost_prepare_dma(s + log_event("PRD<", (u32)data, 0); + pr_debug("bcm2835_sdhost_prepare_dma()\n"); + ++ dma_chan = host->dma_chan_rxtx; + if (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; + } +@@ -813,6 +813,12 @@ static void bcm2835_sdhost_prepare_dma(s + host->drain_words = len/4; + } + ++ /* The parameters have already been validated, so this will not fail */ ++ (void)dmaengine_slave_config(dma_chan, ++ (dir_data == DMA_FROM_DEVICE) ? ++ &host->dma_cfg_rx : ++ &host->dma_cfg_tx); ++ + len = dma_map_sg(dma_chan->device->dev, data->sg, data->sg_len, + dir_data); + +@@ -1805,28 +1811,46 @@ int bcm2835_sdhost_add_host(struct bcm28 + spin_lock_init(&host->lock); + + if (host->allow_dma) { +- if (IS_ERR_OR_NULL(host->dma_chan_tx) || +- IS_ERR_OR_NULL(host->dma_chan_rx)) { +- pr_err("%s: unable to initialise DMA channels. " ++ if (IS_ERR_OR_NULL(host->dma_chan_rxtx)) { ++ pr_err("%s: unable to initialise DMA channel. " + "Falling back to PIO\n", + mmc_hostname(mmc)); + host->use_dma = false; + } else { +- host->use_dma = true; +- + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.slave_id = 13; /* DREQ channel */ + ++ /* Validate the slave configurations */ ++ + cfg.direction = DMA_MEM_TO_DEV; + cfg.src_addr = 0; + cfg.dst_addr = host->bus_addr + SDDATA; +- ret = dmaengine_slave_config(host->dma_chan_tx, &cfg); + +- cfg.direction = DMA_DEV_TO_MEM; +- cfg.src_addr = host->bus_addr + SDDATA; +- cfg.dst_addr = 0; +- ret = dmaengine_slave_config(host->dma_chan_rx, &cfg); ++ ret = dmaengine_slave_config(host->dma_chan_rxtx, &cfg); ++ ++ if (ret == 0) { ++ host->dma_cfg_tx = cfg; ++ ++ cfg.direction = DMA_DEV_TO_MEM; ++ cfg.src_addr = host->bus_addr + SDDATA; ++ cfg.dst_addr = 0; ++ ++ ret = dmaengine_slave_config(host->dma_chan_rxtx, &cfg); ++ } ++ ++ if (ret == 0) { ++ host->dma_cfg_rx = cfg; ++ ++ host->use_dma = true; ++ } else { ++ pr_err("%s: unable to configure DMA channel. " ++ "Falling back to PIO\n", ++ mmc_hostname(mmc)); ++ dma_release_channel(host->dma_chan_rxtx); ++ host->dma_chan_rxtx = NULL; ++ host->use_dma = false; ++ } + } + } else { + host->use_dma = false; +@@ -1948,19 +1972,21 @@ static int bcm2835_sdhost_probe(struct p + + if (host->allow_dma) { + if (node) { +- host->dma_chan_tx = +- dma_request_slave_channel(dev, "tx"); +- host->dma_chan_rx = +- dma_request_slave_channel(dev, "rx"); ++ host->dma_chan_rxtx = ++ dma_request_slave_channel(dev, "rx-tx"); ++ if (!host->dma_chan_rxtx) ++ host->dma_chan_rxtx = ++ dma_request_slave_channel(dev, "tx"); ++ if (!host->dma_chan_rxtx) ++ host->dma_chan_rxtx = ++ dma_request_slave_channel(dev, "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 = ++ host->dma_chan_rxtx = + dma_request_channel(mask, NULL, NULL); + } + } diff --git a/target/linux/brcm2708/patches-4.4/0169-DT-Add-overlays-to-configure-I2C-pins.patch b/target/linux/brcm2708/patches-4.4/0169-DT-Add-overlays-to-configure-I2C-pins.patch deleted file mode 100644 index a010fca7b1..0000000000 --- a/target/linux/brcm2708/patches-4.4/0169-DT-Add-overlays-to-configure-I2C-pins.patch +++ /dev/null @@ -1,134 +0,0 @@ -From 310f5af54ca26b68795f0376c67b41e2bb18a0e0 Mon Sep 17 00:00:00 2001 -From: Dave Stevenson <6by9@users.noreply.github.com> -Date: Mon, 8 Feb 2016 23:49:41 +0000 -Subject: [PATCH 169/170] DT: Add overlays to configure I2C pins - -Lifted from -https://www.raspberrypi.org/forums/viewtopic.php?f=107&t=120938&p=825883 -so not claiming this to be my own work. -Adds overlays i2c0-bcm2708 and i2c1-bcm2708 that allow the pin -allocations for i2c-0 and i2c-1 to be changed. ---- - arch/arm/boot/dts/overlays/Makefile | 2 ++ - arch/arm/boot/dts/overlays/README | 16 ++++++++++ - .../arm/boot/dts/overlays/i2c0-bcm2708-overlay.dts | 36 +++++++++++++++++++++ - .../arm/boot/dts/overlays/i2c1-bcm2708-overlay.dts | 37 ++++++++++++++++++++++ - 4 files changed, 91 insertions(+) - create mode 100644 arch/arm/boot/dts/overlays/i2c0-bcm2708-overlay.dts - create mode 100644 arch/arm/boot/dts/overlays/i2c1-bcm2708-overlay.dts - ---- a/arch/arm/boot/dts/overlays/Makefile -+++ b/arch/arm/boot/dts/overlays/Makefile -@@ -29,6 +29,8 @@ dtb-$(RPI_DT_OVERLAYS) += hy28a-overlay. - dtb-$(RPI_DT_OVERLAYS) += hy28b-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += i2c-rtc-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += i2c-gpio-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += i2c0-bcm2708-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += i2c1-bcm2708-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += i2s-mmap-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -371,6 +371,22 @@ Params: ds1307 Select t - pcf8563 Select the PCF8563 device - - -+Name: i2c0-bcm2708 -+Info: Enable the i2c_bcm2708 driver for the i2c0 bus -+Load: dtoverlay=i2c0-bcm2708,= -+Params: sda0_pin GPIO pin for SDA0 (0, 28 [or 44] - default 0) -+ scl0_pin GPIO pin for SCL0 (1, 29 [or 45] - default 1) -+ -+ -+Name: i2c1-bcm2708 -+Info: Enable the i2c_bcm2708 driver for the i2c1 bus -+Load: dtoverlay=i2c1-bcm2708,= -+Params: sda1_pin GPIO pin for SDA1 (2 or 44 - default 2) -+ scl1_pin GPIO pin for SCL1 (3 or 45 - default 3) -+ pin_func Alternative pin function (4 (alt0), 6 (alt2) - -+ default 4) -+ -+ - Name: i2s-mmap - Info: Enables mmap support in the bcm2708-i2s driver - Load: dtoverlay=i2s-mmap ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/i2c0-bcm2708-overlay.dts -@@ -0,0 +1,36 @@ -+/* -+ * Device tree overlay for i2c_bcm2708, i2c0 bus -+ * -+ * Compile: -+ * dtc -@ -I dts -O dtb -o i2c0-bcm2708-overlay.dtb i2c0-bcm2708-overlay.dts -+ */ -+ -+/dts-v1/; -+/plugin/; -+ -+/{ -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&i2c0>; -+ __overlay__ { -+ pinctrl-0 = <&i2c0_pins>; -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ i2c0_pins: i2c0 { -+ brcm,pins = <0 1>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+ }; -+ }; -+ -+ __overrides__ { -+ sda0_pin = <&i2c0_pins>,"brcm,pins:0"; -+ scl0_pin = <&i2c0_pins>,"brcm,pins:4"; -+ }; -+}; ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/i2c1-bcm2708-overlay.dts -@@ -0,0 +1,37 @@ -+/* -+ * Device tree overlay for i2c_bcm2708, i2c1 bus -+ * -+ * Compile: -+ * dtc -@ -I dts -O dtb -o i2c1-bcm2708-overlay.dtb i2c1-bcm2708-overlay.dts -+ */ -+ -+/dts-v1/; -+/plugin/; -+ -+/{ -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&i2c1>; -+ __overlay__ { -+ pinctrl-0 = <&i2c1_pins>; -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&gpio>; -+ __overlay__ { -+ i2c1_pins: i2c1 { -+ brcm,pins = <2 3>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+ }; -+ }; -+ -+ __overrides__ { -+ sda1_pin = <&i2c1_pins>,"brcm,pins:0"; -+ scl1_pin = <&i2c1_pins>,"brcm,pins:4"; -+ pin_func = <&i2c1_pins>,"brcm,function:0"; -+ }; -+}; diff --git a/target/linux/brcm2708/patches-4.4/0169-bcm2835-mmc-Only-claim-one-DMA-channel.patch b/target/linux/brcm2708/patches-4.4/0169-bcm2835-mmc-Only-claim-one-DMA-channel.patch new file mode 100644 index 0000000000..3ff9b3e977 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0169-bcm2835-mmc-Only-claim-one-DMA-channel.patch @@ -0,0 +1,170 @@ +From ea4b1c5c2ddbb6caba43ab9b0103542a4ca7e1f0 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 8 Mar 2016 09:49:16 +0000 +Subject: [PATCH 169/232] bcm2835-mmc: Only claim one DMA channel + +With both MMC controllers enabled there are few DMA channels left. The +bcm2835-mmc driver only uses DMA in one direction at a time, so it +doesn't need to claim two channels. + +See: https://github.com/raspberrypi/linux/issues/1327 + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/bcm2708_common.dtsi | 5 +-- + drivers/mmc/host/bcm2835-mmc.c | 69 +++++++++++++++++++++++++---------- + 2 files changed, 51 insertions(+), 23 deletions(-) + +--- a/arch/arm/boot/dts/bcm2708_common.dtsi ++++ b/arch/arm/boot/dts/bcm2708_common.dtsi +@@ -232,9 +232,8 @@ + reg = <0x7e300000 0x100>; + interrupts = <2 30>; + clocks = <&clk_mmc>; +- dmas = <&dma 11>, +- <&dma 11>; +- dma-names = "tx", "rx"; ++ dmas = <&dma 11>; ++ dma-names = "rx-tx"; + brcm,overclock-50 = <0>; + status = "disabled"; + }; +--- a/drivers/mmc/host/bcm2835-mmc.c ++++ b/drivers/mmc/host/bcm2835-mmc.c +@@ -108,8 +108,9 @@ struct bcm2835_host { + 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_chan *dma_chan_rxtx; /* DMA channel for reads and writes */ ++ struct dma_slave_config dma_cfg_rx; ++ struct dma_slave_config dma_cfg_tx; + struct dma_async_tx_descriptor *tx_desc; /* descriptor */ + + bool have_dma; +@@ -342,7 +343,7 @@ static void bcm2835_mmc_dma_complete(voi + + if (host->data && !(host->data->flags & MMC_DATA_WRITE)) { + /* otherwise handled in SDHCI IRQ */ +- dma_chan = host->dma_chan_rx; ++ dma_chan = host->dma_chan_rxtx; + dir_data = DMA_FROM_DEVICE; + + dma_unmap_sg(dma_chan->device->dev, +@@ -493,16 +494,21 @@ static void bcm2835_mmc_transfer_dma(str + if (host->blocks == 0) + return; + ++ dma_chan = host->dma_chan_rxtx; + 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; + } + ++ /* The parameters have already been validated, so this will not fail */ ++ (void)dmaengine_slave_config(dma_chan, ++ (dir_data == DMA_FROM_DEVICE) ? ++ &host->dma_cfg_rx : ++ &host->dma_cfg_tx); ++ + BUG_ON(!dma_chan->device); + BUG_ON(!dma_chan->device->dev); + BUG_ON(!host->data->sg); +@@ -936,7 +942,7 @@ static void bcm2835_mmc_data_irq(struct + if (host->data->flags & MMC_DATA_WRITE) { + /* IRQ handled here */ + +- dma_chan = host->dma_chan_tx; ++ dma_chan = host->dma_chan_rxtx; + dir_data = DMA_TO_DEVICE; + dma_unmap_sg(dma_chan->device->dev, + host->data->sg, host->data->sg_len, +@@ -1316,28 +1322,47 @@ static int bcm2835_mmc_add_host(struct b + 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", ++ if (IS_ERR_OR_NULL(host->dma_chan_rxtx)) { ++ dev_err(dev, "%s: Unable to initialise DMA channel. Falling back to PIO\n", + DRIVER_NAME); + host->have_dma = false; + } else { +- dev_info(dev, "DMA channels allocated"); +- host->have_dma = true; ++ dev_info(dev, "DMA channel allocated"); + + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.slave_id = 11; /* DREQ channel */ + ++ /* Validate the slave configurations */ ++ + cfg.direction = DMA_MEM_TO_DEV; + cfg.src_addr = 0; + cfg.dst_addr = host->bus_addr + SDHCI_BUFFER; +- ret = dmaengine_slave_config(host->dma_chan_tx, &cfg); + +- cfg.direction = DMA_DEV_TO_MEM; +- cfg.src_addr = host->bus_addr + SDHCI_BUFFER; +- cfg.dst_addr = 0; +- ret = dmaengine_slave_config(host->dma_chan_rx, &cfg); ++ ret = dmaengine_slave_config(host->dma_chan_rxtx, &cfg); ++ ++ if (ret == 0) { ++ host->dma_cfg_tx = cfg; ++ ++ cfg.direction = DMA_DEV_TO_MEM; ++ cfg.src_addr = host->bus_addr + SDHCI_BUFFER; ++ cfg.dst_addr = 0; ++ ++ ret = dmaengine_slave_config(host->dma_chan_rxtx, &cfg); ++ } ++ ++ if (ret == 0) { ++ host->dma_cfg_rx = cfg; ++ ++ host->use_dma = true; ++ } else { ++ pr_err("%s: unable to configure DMA channel. " ++ "Faling back to PIO\n", ++ mmc_hostname(mmc)); ++ dma_release_channel(host->dma_chan_rxtx); ++ host->dma_chan_rxtx = NULL; ++ host->use_dma = false; ++ } + } + #endif + mmc->max_segs = 128; +@@ -1416,16 +1441,20 @@ static int bcm2835_mmc_probe(struct plat + + #ifndef FORCE_PIO + if (node) { +- host->dma_chan_tx = dma_request_slave_channel(dev, "tx"); +- host->dma_chan_rx = dma_request_slave_channel(dev, "rx"); ++ host->dma_chan_rxtx = dma_request_slave_channel(dev, "rx-tx"); ++ if (!host->dma_chan_rxtx) ++ host->dma_chan_rxtx = ++ dma_request_slave_channel(dev, "tx"); ++ if (!host->dma_chan_rxtx) ++ host->dma_chan_rxtx = ++ dma_request_slave_channel(dev, "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); ++ host->dma_chan_rxtx = dma_request_channel(mask, NULL, NULL); + } + #endif + clk = devm_clk_get(dev, NULL); diff --git a/target/linux/brcm2708/patches-4.4/0170-bcm2835-camera-fix-a-bug-in-computation-of-frame-tim.patch b/target/linux/brcm2708/patches-4.4/0170-bcm2835-camera-fix-a-bug-in-computation-of-frame-tim.patch deleted file mode 100644 index 95a193a881..0000000000 --- a/target/linux/brcm2708/patches-4.4/0170-bcm2835-camera-fix-a-bug-in-computation-of-frame-tim.patch +++ /dev/null @@ -1,23 +0,0 @@ -From c5cbb66686e7e289e8a7aff49a954f86893e628d Mon Sep 17 00:00:00 2001 -From: Dhiraj Goel -Date: Thu, 3 Mar 2016 21:10:50 -0800 -Subject: [PATCH 170/170] bcm2835-camera: fix a bug in computation of frame - timestamp - -Fixes #1318 ---- - drivers/media/platform/bcm2835/bcm2835-camera.c | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - ---- a/drivers/media/platform/bcm2835/bcm2835-camera.c -+++ b/drivers/media/platform/bcm2835/bcm2835-camera.c -@@ -360,8 +360,7 @@ static void buffer_cb(struct vchiq_mmal_ - div = - div_u64_rem(runtime_us, USEC_PER_SEC, &rem); - buf->vb.timestamp.tv_sec = -- dev->capture.kernel_start_ts.tv_sec - 1 + -- div; -+ dev->capture.kernel_start_ts.tv_sec + div; - buf->vb.timestamp.tv_usec = - dev->capture.kernel_start_ts.tv_usec + rem; - diff --git a/target/linux/brcm2708/patches-4.4/0170-config-rebuild-with-savedefconfig.patch b/target/linux/brcm2708/patches-4.4/0170-config-rebuild-with-savedefconfig.patch new file mode 100644 index 0000000000..f7dcd936a5 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0170-config-rebuild-with-savedefconfig.patch @@ -0,0 +1,28 @@ +From 83428789f8b11c2caf2b85998db0f0874cd3e896 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Tue, 8 Mar 2016 17:08:39 +0000 +Subject: [PATCH 170/232] config: rebuild with savedefconfig + +--- + arch/arm/configs/bcm2709_defconfig | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/arch/arm/configs/bcm2709_defconfig ++++ b/arch/arm/configs/bcm2709_defconfig +@@ -593,7 +593,6 @@ CONFIG_SERIAL_AMBA_PL011_CONSOLE=y + CONFIG_SERIAL_OF_PLATFORM=y + CONFIG_TTY_PRINTK=y + CONFIG_HW_RANDOM=y +-CONFIG_HW_RANDOM_BCM2835=y + CONFIG_RAW_DRIVER=y + CONFIG_I2C=y + CONFIG_I2C_CHARDEV=m +@@ -1112,7 +1111,7 @@ CONFIG_EXTCON=m + CONFIG_EXTCON_ARIZONA=m + CONFIG_IIO=m + CONFIG_IIO_BUFFER=y +-CONFIG_IIO_BUFFER_CB=y ++CONFIG_IIO_BUFFER_CB=m + CONFIG_IIO_KFIFO_BUF=m + CONFIG_MCP320X=m + CONFIG_DHT11=m diff --git a/target/linux/brcm2708/patches-4.4/0171-BCM270X_DT-Add-pi3-disable-bt-overlay.patch b/target/linux/brcm2708/patches-4.4/0171-BCM270X_DT-Add-pi3-disable-bt-overlay.patch deleted file mode 100644 index d00439eaa5..0000000000 --- a/target/linux/brcm2708/patches-4.4/0171-BCM270X_DT-Add-pi3-disable-bt-overlay.patch +++ /dev/null @@ -1,96 +0,0 @@ -From 42a9bb566fe376a1add4b3780c1b830f64500faa Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Wed, 2 Mar 2016 10:59:05 +0000 -Subject: [PATCH 171/180] BCM270X_DT: Add pi3-disable-bt overlay - -Disable Bluetooth and restore UART0/ttyAMA0 over GPIOs 14 & 15. To disable -the systemd service that initialises the modem so it doesn't use the UART: - - sudo systemctl disable hciuart - -Signed-off-by: Phil Elwell ---- - arch/arm/boot/dts/overlays/Makefile | 1 + - arch/arm/boot/dts/overlays/README | 8 ++++ - .../boot/dts/overlays/pi3-disable-bt-overlay.dts | 48 ++++++++++++++++++++++ - 3 files changed, 57 insertions(+) - create mode 100644 arch/arm/boot/dts/overlays/pi3-disable-bt-overlay.dts - ---- a/arch/arm/boot/dts/overlays/Makefile -+++ b/arch/arm/boot/dts/overlays/Makefile -@@ -39,6 +39,7 @@ dtb-$(RPI_DT_OVERLAYS) += mcp2515-can0-o - dtb-$(RPI_DT_OVERLAYS) += mcp2515-can1-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += mmc-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += mz61581-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += pi3-disable-bt-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += piscreen-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += piscreen2r-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += pitft28-capacitive-overlay.dtb ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -496,6 +496,14 @@ Params: speed Display - [ The pcf8563-rtc overlay has been deleted. See i2c-rtc. ] - - -+Name: pi3-disable-bt -+Info: Disable Pi3 Bluetooth and restore UART0/ttyAMA0 over GPIOs 14 & 15 -+ N.B. To disable the systemd service that initialises the modem so it -+ doesn't use the UART, use 'sudo systemctl disable hciuart'. -+Load: dtoverlay=pi3-disable-bt -+Params: -+ -+ - Name: piscreen - Info: PiScreen display by OzzMaker.com - Load: dtoverlay=piscreen,= ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/pi3-disable-bt-overlay.dts -@@ -0,0 +1,48 @@ -+/dts-v1/; -+/plugin/; -+ -+/* Disable Bluetooth and restore UART0/ttyAMA0 over GPIOs 14 & 15. -+ To disable the systemd service that initialises the modem so it doesn't use -+ the UART: -+ -+ sudo systemctl disable hciuart -+*/ -+ -+/{ -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&uart1>; -+ __overlay__ { -+ status = "disabled"; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&uart0>; -+ __overlay__ { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&uart0_pins>; -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&gpio>; -+ __overlay__ { -+ uart0_pins: uart0_pins { -+ brcm,pins = <14 15>; -+ brcm,function = <4>; /* alt0 */ -+ brcm,pull = <0 2>; -+ }; -+ }; -+ }; -+ -+ fragment@3 { -+ target-path = "/aliases"; -+ __overlay__ { -+ serial0 = "/soc/uart@7e201000"; -+ serial1 = "/soc/uart@7e215040"; -+ }; -+ }; -+}; diff --git a/target/linux/brcm2708/patches-4.4/0171-config-Add-module-for-mcp3422-ADC.patch b/target/linux/brcm2708/patches-4.4/0171-config-Add-module-for-mcp3422-ADC.patch new file mode 100644 index 0000000000..3843ec474d --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0171-config-Add-module-for-mcp3422-ADC.patch @@ -0,0 +1,30 @@ +From b704288cc9104b018bebf77528f9ff74b6c355a4 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Tue, 8 Mar 2016 17:06:33 +0000 +Subject: [PATCH 171/232] config: Add module for mcp3422 ADC + +--- + arch/arm/configs/bcm2709_defconfig | 1 + + arch/arm/configs/bcmrpi_defconfig | 1 + + 2 files changed, 2 insertions(+) + +--- a/arch/arm/configs/bcm2709_defconfig ++++ b/arch/arm/configs/bcm2709_defconfig +@@ -1114,6 +1114,7 @@ CONFIG_IIO_BUFFER=y + CONFIG_IIO_BUFFER_CB=m + CONFIG_IIO_KFIFO_BUF=m + CONFIG_MCP320X=m ++CONFIG_MCP3422=m + CONFIG_DHT11=m + CONFIG_PWM_BCM2835=m + CONFIG_RASPBERRYPI_FIRMWARE=y +--- a/arch/arm/configs/bcmrpi_defconfig ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -1121,6 +1121,7 @@ CONFIG_IIO_BUFFER=y + CONFIG_IIO_BUFFER_CB=m + CONFIG_IIO_KFIFO_BUF=m + CONFIG_MCP320X=m ++CONFIG_MCP3422=m + CONFIG_DHT11=m + CONFIG_PWM_BCM2835=m + CONFIG_RASPBERRYPI_FIRMWARE=y diff --git a/target/linux/brcm2708/patches-4.4/0172-BCM270X_DT-Add-pi3-miniuart-bt-DT-overlay.patch b/target/linux/brcm2708/patches-4.4/0172-BCM270X_DT-Add-pi3-miniuart-bt-DT-overlay.patch deleted file mode 100644 index 3e1031c109..0000000000 --- a/target/linux/brcm2708/patches-4.4/0172-BCM270X_DT-Add-pi3-miniuart-bt-DT-overlay.patch +++ /dev/null @@ -1,117 +0,0 @@ -From 3b93a5e60a2272bea6b50561c6e871dde5295dfb Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 7 Mar 2016 09:53:03 +0000 -Subject: [PATCH 172/180] BCM270X_DT: Add pi3-miniuart-bt DT overlay - -Switch Pi3 Bluetooth function to use the mini-UART (ttyS0) and restore -UART0/ttyAMA0 over GPIOs 14 & 15. Note that this may reduce the maximum -usable baudrate. - -It is also necessary to edit /lib/systemd/system/hciuart.server and -replace ttyAMA0 with ttyS0. - -If cmdline.txt uses the alias serial0 to refer to the user-accessable port -then the firmware will replace with the appropriate port whether or not -this overlay is used. - -Signed-off-by: Phil Elwell ---- - arch/arm/boot/dts/overlays/Makefile | 1 + - arch/arm/boot/dts/overlays/README | 10 ++++ - .../boot/dts/overlays/pi3-miniuart-bt-overlay.dts | 61 ++++++++++++++++++++++ - 3 files changed, 72 insertions(+) - create mode 100644 arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts - ---- a/arch/arm/boot/dts/overlays/Makefile -+++ b/arch/arm/boot/dts/overlays/Makefile -@@ -40,6 +40,7 @@ dtb-$(RPI_DT_OVERLAYS) += mcp2515-can1-o - dtb-$(RPI_DT_OVERLAYS) += mmc-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += mz61581-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += pi3-disable-bt-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += pi3-miniuart-bt-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += piscreen-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += piscreen2r-overlay.dtb - dtb-$(RPI_DT_OVERLAYS) += pitft28-capacitive-overlay.dtb ---- a/arch/arm/boot/dts/overlays/README -+++ b/arch/arm/boot/dts/overlays/README -@@ -504,6 +504,16 @@ Load: dtoverlay=pi3-disable-bt - Params: - - -+Name: pi3-miniuart-bt -+Info: Switch Pi3 Bluetooth function to use the mini-UART (ttyS0) and restore -+ UART0/ttyAMA0 over GPIOs 14 & 15. Note that this may reduce the maximum -+ usable baudrate. -+ N.B. It is also necessary to edit /lib/systemd/system/hciuart.server -+ and replace ttyAMA0 with ttyS0. -+Load: dtoverlay=pi3-miniuart-bt -+Params: -+ -+ - Name: piscreen - Info: PiScreen display by OzzMaker.com - Load: dtoverlay=piscreen,= ---- /dev/null -+++ b/arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts -@@ -0,0 +1,61 @@ -+/dts-v1/; -+/plugin/; -+ -+/* Switch Pi3 Bluetooth function to use the mini-UART (ttyS0) and restore -+ UART0/ttyAMA0 over GPIOs 14 & 15. Note that this may reduce the maximum -+ usable baudrate. -+ -+ It is also necessary to edit /lib/systemd/system/hciuart.server and -+ replace ttyAMA0 with ttyS0. -+ -+ If cmdline.txt uses the alias serial0 to refer to the user-accessable port -+ then the firmware will replace with the appropriate port whether or not -+ this overlay is used. -+*/ -+ -+/{ -+ compatible = "brcm,bcm2708"; -+ -+ fragment@0 { -+ target = <&uart0>; -+ __overlay__ { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&uart0_pins>; -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@1 { -+ target = <&uart1>; -+ __overlay__ { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&uart1_pins>; -+ status = "okay"; -+ }; -+ }; -+ -+ fragment@2 { -+ target = <&gpio>; -+ __overlay__ { -+ uart0_pins: uart0_pins { -+ brcm,pins = <14 15>; -+ brcm,function = <4>; /* alt0 */ -+ brcm,pull = <0 2>; -+ }; -+ -+ uart1_pins: uart1_pins { -+ brcm,pins = <32 33>; -+ brcm,function = <2>; /* alt5=UART1 */ -+ brcm,pull = <0 0>; -+ }; -+ }; -+ }; -+ -+ fragment@3 { -+ target-path = "/aliases"; -+ __overlay__ { -+ serial0 = "/soc/uart@7e201000"; -+ serial1 = "/soc/uart@7e215040"; -+ }; -+ }; -+}; diff --git a/target/linux/brcm2708/patches-4.4/0172-Pi3-DT-Add-pull-ups-on-the-UART-RX-lines.patch b/target/linux/brcm2708/patches-4.4/0172-Pi3-DT-Add-pull-ups-on-the-UART-RX-lines.patch new file mode 100644 index 0000000000..cd8569675a --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0172-Pi3-DT-Add-pull-ups-on-the-UART-RX-lines.patch @@ -0,0 +1,40 @@ +From a25355e1fe4284b87170bec5d2ee77f90a62602c Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 8 Mar 2016 16:18:57 +0000 +Subject: [PATCH 172/232] Pi3 DT: Add pull-ups on the UART RX lines + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/bcm2710-rpi-3-b.dts | 4 ++-- + arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts | 2 +- + 2 files changed, 3 insertions(+), 3 deletions(-) + +--- a/arch/arm/boot/dts/bcm2710-rpi-3-b.dts ++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts +@@ -53,13 +53,13 @@ + uart0_pins: uart0_pins { + brcm,pins = <32 33>; + brcm,function = <7>; /* alt3=UART0 */ +- brcm,pull = <0 0>; ++ brcm,pull = <0 2>; + }; + + uart1_pins: uart1_pins { + brcm,pins = <14 15>; + brcm,function = <2>; /* alt5=UART1 */ +- brcm,pull = <0 0>; ++ brcm,pull = <0 2>; + }; + }; + +--- a/arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts +@@ -46,7 +46,7 @@ + uart1_pins: uart1_pins { + brcm,pins = <32 33>; + brcm,function = <2>; /* alt5=UART1 */ +- brcm,pull = <0 0>; ++ brcm,pull = <0 2>; + }; + }; + }; diff --git a/target/linux/brcm2708/patches-4.4/0173-Pi3-DT-Add-dtparams-for-the-SD-interface.patch b/target/linux/brcm2708/patches-4.4/0173-Pi3-DT-Add-dtparams-for-the-SD-interface.patch deleted file mode 100644 index f9e4767f8a..0000000000 --- a/target/linux/brcm2708/patches-4.4/0173-Pi3-DT-Add-dtparams-for-the-SD-interface.patch +++ /dev/null @@ -1,25 +0,0 @@ -From c8b56d0359160d2aec2ab3168ef9d7c080ae9841 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 7 Mar 2016 13:38:39 +0000 -Subject: [PATCH 173/180] Pi3 DT: Add dtparams for the SD interface - -Add new base dtparams sd_overclock, sd_force_pio, sd_pio_limit -and sd_debug. These were missed out of the initial Pi3 DTB. - -Signed-off-by: Phil Elwell ---- - arch/arm/boot/dts/bcm2710-rpi-3-b.dts | 4 ++++ - 1 file changed, 4 insertions(+) - ---- a/arch/arm/boot/dts/bcm2710-rpi-3-b.dts -+++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts -@@ -188,5 +188,9 @@ - audio = <&audio>,"status"; - watchdog = <&watchdog>,"status"; - random = <&random>,"status"; -+ sd_overclock = <&sdhost>,"brcm,overclock-50:0"; -+ sd_force_pio = <&sdhost>,"brcm,force-pio?"; -+ sd_pio_limit = <&sdhost>,"brcm,pio-limit:0"; -+ sd_debug = <&sdhost>,"brcm,debug"; - }; - }; diff --git a/target/linux/brcm2708/patches-4.4/0174-BCM270X_DT-rpi-display-overlay-add-swapxy-param.patch b/target/linux/brcm2708/patches-4.4/0174-BCM270X_DT-rpi-display-overlay-add-swapxy-param.patch new file mode 100644 index 0000000000..3be457bf9c --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0174-BCM270X_DT-rpi-display-overlay-add-swapxy-param.patch @@ -0,0 +1,38 @@ +From 209d0307ba01e0f15f5e75bc77d8edaa51aa91ab Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 9 Mar 2016 21:28:52 +0000 +Subject: [PATCH 174/232] BCM270X_DT: rpi-display overlay - add swapxy param + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/overlays/README | 5 +---- + arch/arm/boot/dts/overlays/rpi-display-overlay.dts | 1 + + 2 files changed, 2 insertions(+), 4 deletions(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -644,14 +644,11 @@ Name: rpi-display + Info: RPi-Display - 2.8" Touch Display by Watterott + Load: dtoverlay=rpi-display,= + Params: speed Display SPI bus speed +- + rotate Display rotation {0,90,180,270} +- + fps Delay between frame updates +- + debug Debug output level {0-7} +- + xohms Touchpanel sensitivity (X-plate resistance) ++ swapxy Swap x and y axis + + + Name: rpi-ft5406 +--- a/arch/arm/boot/dts/overlays/rpi-display-overlay.dts ++++ b/arch/arm/boot/dts/overlays/rpi-display-overlay.dts +@@ -78,5 +78,6 @@ + fps = <&rpidisplay>,"fps:0"; + debug = <&rpidisplay>,"debug:0"; + xohms = <&rpidisplay_ts>,"ti,x-plate-ohms;0"; ++ swapxy = <&rpidisplay_ts>,"ti,swap-xy?"; + }; + }; diff --git a/target/linux/brcm2708/patches-4.4/0174-vchiq_arm-Tweak-the-logging-output.patch b/target/linux/brcm2708/patches-4.4/0174-vchiq_arm-Tweak-the-logging-output.patch deleted file mode 100644 index 536c6ce2bc..0000000000 --- a/target/linux/brcm2708/patches-4.4/0174-vchiq_arm-Tweak-the-logging-output.patch +++ /dev/null @@ -1,75 +0,0 @@ -From 48f3f7707a43b019e14216cec7376eba21972f9b Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 7 Mar 2016 15:05:11 +0000 -Subject: [PATCH 174/180] vchiq_arm: Tweak the logging output - -Signed-off-by: Phil Elwell ---- - .../vc04_services/interface/vchiq_arm/vchiq_core.c | 31 +++++++++------------- - 1 file changed, 13 insertions(+), 18 deletions(-) - ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c -@@ -891,16 +891,14 @@ queue_message(VCHIQ_STATE_T *state, VCHI - 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)); -- } - } - -+ if (SRVTRACE_ENABLED(service, -+ VCHIQ_LOG_INFO)) -+ vchiq_log_dump_mem("Sent", 0, -+ header->data, -+ min(16, pos)); -+ - spin_lock("a_spinlock); - service_quota->message_use_count++; - -@@ -1039,16 +1037,13 @@ queue_message_sync(VCHIQ_STATE_T *state, - 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)); -- } - } - -+ if (vchiq_sync_log_level >= VCHIQ_LOG_TRACE) -+ vchiq_log_dump_mem("Sent Sync", -+ 0, header->data, -+ min(16, pos)); -+ - VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count); - VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size); - } else { -@@ -1720,7 +1715,7 @@ parse_rx_slots(VCHIQ_STATE_T *state) - remoteport, localport, size); - if (size > 0) - vchiq_log_dump_mem("Rcvd", 0, header->data, -- min(64, size)); -+ min(16, size)); - } - - if (((unsigned int)header & VCHIQ_SLOT_MASK) + calc_stride(size) -@@ -2187,7 +2182,7 @@ sync_func(void *v) - remoteport, localport, size); - if (size > 0) - vchiq_log_dump_mem("Rcvd", 0, header->data, -- min(64, size)); -+ min(16, size)); - } - - switch (type) { diff --git a/target/linux/brcm2708/patches-4.4/0175-Remove-I2S-config-from-bt_pins.patch b/target/linux/brcm2708/patches-4.4/0175-Remove-I2S-config-from-bt_pins.patch new file mode 100644 index 0000000000..7c1d01d604 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0175-Remove-I2S-config-from-bt_pins.patch @@ -0,0 +1,38 @@ +From eb4d55c34018bbb92808264217a0b2857d2b2ae6 Mon Sep 17 00:00:00 2001 +From: DigitalDreamtime +Date: Fri, 11 Mar 2016 11:44:35 +0000 +Subject: [PATCH 175/232] Remove I2S config from bt_pins. + +Remove I2S config from bt_pins. Causes issues with clock alignment when I2S is +used by an external DAC via GPIO header. +--- + arch/arm/boot/dts/bcm2710-rpi-3-b.dts | 6 +++--- + arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts | 2 +- + 2 files changed, 4 insertions(+), 4 deletions(-) + +--- a/arch/arm/boot/dts/bcm2710-rpi-3-b.dts ++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts +@@ -45,9 +45,9 @@ + }; + + bt_pins: bt_pins { +- brcm,pins = <28 29 30 31 43>; +- brcm,function = <6 6 6 6 4>; /* alt2:PCM alt0:GPCLK2 */ +- brcm,pull = <0 0 0 0 0>; ++ brcm,pins = <43>; ++ brcm,function = <4>; /* alt0:GPCLK2 */ ++ brcm,pull = <0>; + }; + + uart0_pins: uart0_pins { +--- a/arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts +@@ -29,7 +29,7 @@ + target = <&uart1>; + __overlay__ { + pinctrl-names = "default"; +- pinctrl-0 = <&uart1_pins>; ++ pinctrl-0 = <&uart1_pins &bt_pins>; + status = "okay"; + }; + }; diff --git a/target/linux/brcm2708/patches-4.4/0175-bcm2835-sdhost-Only-claim-one-DMA-channel.patch b/target/linux/brcm2708/patches-4.4/0175-bcm2835-sdhost-Only-claim-one-DMA-channel.patch deleted file mode 100644 index fb68bcb5f8..0000000000 --- a/target/linux/brcm2708/patches-4.4/0175-bcm2835-sdhost-Only-claim-one-DMA-channel.patch +++ /dev/null @@ -1,160 +0,0 @@ -From 06c169985c0884ce67377c79d27383e23f41e2cd Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 7 Mar 2016 16:46:39 +0000 -Subject: [PATCH 175/180] bcm2835-sdhost: Only claim one DMA channel - -With both MMC controllers enabled there are few DMA channels left. The -bcm2835-sdhost driver only uses DMA in one direction at a time, so it -doesn't need to claim two channels. - -See: https://github.com/raspberrypi/linux/issues/1327 - -Signed-off-by: Phil Elwell ---- - arch/arm/boot/dts/bcm2708_common.dtsi | 5 +-- - drivers/mmc/host/bcm2835-sdhost.c | 70 ++++++++++++++++++++++++----------- - 2 files changed, 50 insertions(+), 25 deletions(-) - ---- a/arch/arm/boot/dts/bcm2708_common.dtsi -+++ b/arch/arm/boot/dts/bcm2708_common.dtsi -@@ -136,9 +136,8 @@ - reg = <0x7e202000 0x100>; - interrupts = <2 24>; - clocks = <&clk_core>; -- dmas = <&dma 13>, -- <&dma 13>; -- dma-names = "tx", "rx"; -+ dmas = <&dma 13>; -+ dma-names = "rx-tx"; - brcm,overclock-50 = <0>; - brcm,pio-limit = <1>; - status = "disabled"; ---- a/drivers/mmc/host/bcm2835-sdhost.c -+++ b/drivers/mmc/host/bcm2835-sdhost.c -@@ -185,9 +185,10 @@ struct bcm2835_host { - unsigned int debug:1; /* Enable debug output */ - - /*DMA part*/ -- struct dma_chan *dma_chan_rx; /* DMA channel for reads */ -- struct dma_chan *dma_chan_tx; /* DMA channel for writes */ -- struct dma_chan *dma_chan; /* Channel in used */ -+ struct dma_chan *dma_chan_rxtx; /* DMA channel for reads and writes */ -+ struct dma_chan *dma_chan; /* Channel in use */ -+ struct dma_slave_config dma_cfg_rx; -+ struct dma_slave_config dma_cfg_tx; - struct dma_async_tx_descriptor *dma_desc; - u32 dma_dir; - u32 drain_words; -@@ -771,12 +772,11 @@ static void bcm2835_sdhost_prepare_dma(s - log_event("PRD<", (u32)data, 0); - pr_debug("bcm2835_sdhost_prepare_dma()\n"); - -+ dma_chan = host->dma_chan_rxtx; - if (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; - } -@@ -813,6 +813,12 @@ static void bcm2835_sdhost_prepare_dma(s - host->drain_words = len/4; - } - -+ /* The parameters have already been validated, so this will not fail */ -+ (void)dmaengine_slave_config(dma_chan, -+ (dir_data == DMA_FROM_DEVICE) ? -+ &host->dma_cfg_rx : -+ &host->dma_cfg_tx); -+ - len = dma_map_sg(dma_chan->device->dev, data->sg, data->sg_len, - dir_data); - -@@ -1805,28 +1811,46 @@ int bcm2835_sdhost_add_host(struct bcm28 - spin_lock_init(&host->lock); - - if (host->allow_dma) { -- if (IS_ERR_OR_NULL(host->dma_chan_tx) || -- IS_ERR_OR_NULL(host->dma_chan_rx)) { -- pr_err("%s: unable to initialise DMA channels. " -+ if (IS_ERR_OR_NULL(host->dma_chan_rxtx)) { -+ pr_err("%s: unable to initialise DMA channel. " - "Falling back to PIO\n", - mmc_hostname(mmc)); - host->use_dma = false; - } else { -- host->use_dma = true; -- - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - cfg.slave_id = 13; /* DREQ channel */ - -+ /* Validate the slave configurations */ -+ - cfg.direction = DMA_MEM_TO_DEV; - cfg.src_addr = 0; - cfg.dst_addr = host->bus_addr + SDDATA; -- ret = dmaengine_slave_config(host->dma_chan_tx, &cfg); - -- cfg.direction = DMA_DEV_TO_MEM; -- cfg.src_addr = host->bus_addr + SDDATA; -- cfg.dst_addr = 0; -- ret = dmaengine_slave_config(host->dma_chan_rx, &cfg); -+ ret = dmaengine_slave_config(host->dma_chan_rxtx, &cfg); -+ -+ if (ret == 0) { -+ host->dma_cfg_tx = cfg; -+ -+ cfg.direction = DMA_DEV_TO_MEM; -+ cfg.src_addr = host->bus_addr + SDDATA; -+ cfg.dst_addr = 0; -+ -+ ret = dmaengine_slave_config(host->dma_chan_rxtx, &cfg); -+ } -+ -+ if (ret == 0) { -+ host->dma_cfg_rx = cfg; -+ -+ host->use_dma = true; -+ } else { -+ pr_err("%s: unable to configure DMA channel. " -+ "Falling back to PIO\n", -+ mmc_hostname(mmc)); -+ dma_release_channel(host->dma_chan_rxtx); -+ host->dma_chan_rxtx = NULL; -+ host->use_dma = false; -+ } - } - } else { - host->use_dma = false; -@@ -1948,19 +1972,21 @@ static int bcm2835_sdhost_probe(struct p - - if (host->allow_dma) { - if (node) { -- host->dma_chan_tx = -- dma_request_slave_channel(dev, "tx"); -- host->dma_chan_rx = -- dma_request_slave_channel(dev, "rx"); -+ host->dma_chan_rxtx = -+ dma_request_slave_channel(dev, "rx-tx"); -+ if (!host->dma_chan_rxtx) -+ host->dma_chan_rxtx = -+ dma_request_slave_channel(dev, "tx"); -+ if (!host->dma_chan_rxtx) -+ host->dma_chan_rxtx = -+ dma_request_slave_channel(dev, "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 = -+ host->dma_chan_rxtx = - dma_request_channel(mask, NULL, NULL); - } - } diff --git a/target/linux/brcm2708/patches-4.4/0176-Revert-scripts-dtc-Add-overlay-support.patch b/target/linux/brcm2708/patches-4.4/0176-Revert-scripts-dtc-Add-overlay-support.patch new file mode 100644 index 0000000000..d2001e1d80 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0176-Revert-scripts-dtc-Add-overlay-support.patch @@ -0,0 +1,4390 @@ +From fa95cd11c54c77d6151b7588b1f41457d8775c60 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 10 Aug 2015 09:44:59 +0100 +Subject: [PATCH 176/232] Revert "scripts/dtc: Add overlay support" + +This reverts commit fa6d1755c2fdd9451077d8248e3804f0619f19b9. +--- + scripts/dtc/checks.c | 119 +-- + scripts/dtc/dtc-lexer.l | 5 - + scripts/dtc/dtc-lexer.lex.c_shipped | 490 +++++---- + scripts/dtc/dtc-parser.tab.c_shipped | 1896 +++++++++++++++------------------- + scripts/dtc/dtc-parser.tab.h_shipped | 107 +- + scripts/dtc/dtc-parser.y | 23 +- + scripts/dtc/dtc.c | 9 +- + scripts/dtc/dtc.h | 38 - + scripts/dtc/flattree.c | 141 +-- + scripts/dtc/version_gen.h | 2 +- + 10 files changed, 1145 insertions(+), 1685 deletions(-) + +--- a/scripts/dtc/checks.c ++++ b/scripts/dtc/checks.c +@@ -458,91 +458,21 @@ static void fixup_phandle_references(str + struct node *node, struct property *prop) + { + struct marker *m = prop->val.markers; +- struct fixup *f, **fp; +- struct fixup_entry *fe, **fep; + struct node *refnode; + cell_t phandle; +- int has_phandle_refs; +- +- has_phandle_refs = 0; +- for_each_marker_of_type(m, REF_PHANDLE) { +- has_phandle_refs = 1; +- break; +- } +- +- if (!has_phandle_refs) +- return; + + for_each_marker_of_type(m, REF_PHANDLE) { + assert(m->offset + sizeof(cell_t) <= prop->val.len); + + refnode = get_node_by_ref(dt, m->ref); +- if (!refnode && !symbol_fixup_support) { ++ if (! refnode) { + FAIL(c, "Reference to non-existent node or label \"%s\"\n", +- m->ref); ++ m->ref); + continue; + } + +- if (!refnode) { +- /* allocate fixup entry */ +- fe = xmalloc(sizeof(*fe)); +- +- fe->node = node; +- fe->prop = prop; +- fe->offset = m->offset; +- fe->next = NULL; +- +- /* search for an already existing fixup */ +- for_each_fixup(dt, f) +- if (strcmp(f->ref, m->ref) == 0) +- break; +- +- /* no fixup found, add new */ +- if (f == NULL) { +- f = xmalloc(sizeof(*f)); +- f->ref = m->ref; +- f->entries = NULL; +- f->next = NULL; +- +- /* add it to the tree */ +- fp = &dt->fixups; +- while (*fp) +- fp = &(*fp)->next; +- *fp = f; +- } +- +- /* and now append fixup entry */ +- fep = &f->entries; +- while (*fep) +- fep = &(*fep)->next; +- *fep = fe; +- +- /* mark the entry as unresolved */ +- phandle = 0xdeadbeef; +- } else { +- phandle = get_node_phandle(dt, refnode); +- +- /* if it's a plugin, we need to record it */ +- if (symbol_fixup_support && dt->is_plugin) { +- +- /* allocate a new local fixup entry */ +- fe = xmalloc(sizeof(*fe)); +- +- fe->node = node; +- fe->prop = prop; +- fe->offset = m->offset; +- fe->next = NULL; +- +- /* append it to the local fixups */ +- fep = &dt->local_fixups; +- while (*fep) +- fep = &(*fep)->next; +- *fep = fe; +- } +- } +- +- *((cell_t *)(prop->val.val + m->offset)) = +- cpu_to_fdt32(phandle); ++ phandle = get_node_phandle(dt, refnode); ++ *((cell_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle); + } + } + ERROR(phandle_references, NULL, NULL, fixup_phandle_references, NULL, +@@ -722,45 +652,6 @@ static void check_obsolete_chosen_interr + } + TREE_WARNING(obsolete_chosen_interrupt_controller, NULL); + +-static void check_auto_label_phandles(struct check *c, struct node *dt, +- struct node *node) +-{ +- struct label *l; +- struct symbol *s, **sp; +- int has_label; +- +- if (!symbol_fixup_support) +- return; +- +- has_label = 0; +- for_each_label(node->labels, l) { +- has_label = 1; +- break; +- } +- +- if (!has_label) +- return; +- +- /* force allocation of a phandle for this node */ +- (void)get_node_phandle(dt, node); +- +- /* add the symbol */ +- for_each_label(node->labels, l) { +- +- s = xmalloc(sizeof(*s)); +- s->label = l; +- s->node = node; +- s->next = NULL; +- +- /* add it to the symbols list */ +- sp = &dt->symbols; +- while (*sp) +- sp = &((*sp)->next); +- *sp = s; +- } +-} +-NODE_WARNING(auto_label_phandles, NULL); +- + static struct check *check_table[] = { + &duplicate_node_names, &duplicate_property_names, + &node_name_chars, &node_name_format, &property_name_chars, +@@ -779,8 +670,6 @@ static struct check *check_table[] = { + &avoid_default_addr_size, + &obsolete_chosen_interrupt_controller, + +- &auto_label_phandles, +- + &always_fail, + }; + +--- a/scripts/dtc/dtc-lexer.l ++++ b/scripts/dtc/dtc-lexer.l +@@ -113,11 +113,6 @@ static void lexical_error(const char *fm + return DT_V1; + } + +-<*>"/plugin/" { +- DPRINT("Keyword: /plugin/\n"); +- return DT_PLUGIN; +- } +- + <*>"/memreserve/" { + DPRINT("Keyword: /memreserve/\n"); + BEGIN_DEFAULT(); +--- a/scripts/dtc/dtc-lexer.lex.c_shipped ++++ b/scripts/dtc/dtc-lexer.lex.c_shipped +@@ -9,7 +9,7 @@ + #define FLEX_SCANNER + #define YY_FLEX_MAJOR_VERSION 2 + #define YY_FLEX_MINOR_VERSION 5 +-#define YY_FLEX_SUBMINOR_VERSION 35 ++#define YY_FLEX_SUBMINOR_VERSION 39 + #if YY_FLEX_SUBMINOR_VERSION > 0 + #define FLEX_BETA + #endif +@@ -162,7 +162,12 @@ typedef unsigned int flex_uint32_t; + typedef struct yy_buffer_state *YY_BUFFER_STATE; + #endif + +-extern int yyleng; ++#ifndef YY_TYPEDEF_YY_SIZE_T ++#define YY_TYPEDEF_YY_SIZE_T ++typedef size_t yy_size_t; ++#endif ++ ++extern yy_size_t yyleng; + + extern FILE *yyin, *yyout; + +@@ -171,6 +176,7 @@ extern FILE *yyin, *yyout; + #define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) ++ #define YY_LINENO_REWIND_TO(ptr) + + /* Return all but the first "n" matched characters back to the input stream. */ + #define yyless(n) \ +@@ -188,11 +194,6 @@ extern FILE *yyin, *yyout; + + #define unput(c) yyunput( c, (yytext_ptr) ) + +-#ifndef YY_TYPEDEF_YY_SIZE_T +-#define YY_TYPEDEF_YY_SIZE_T +-typedef size_t yy_size_t; +-#endif +- + #ifndef YY_STRUCT_YY_BUFFER_STATE + #define YY_STRUCT_YY_BUFFER_STATE + struct yy_buffer_state +@@ -210,7 +211,7 @@ struct yy_buffer_state + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ +- int yy_n_chars; ++ yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to +@@ -280,8 +281,8 @@ static YY_BUFFER_STATE * yy_buffer_stack + + /* yy_hold_char holds the character lost when yytext is formed. */ + static char yy_hold_char; +-static int yy_n_chars; /* number of characters read into yy_ch_buf */ +-int yyleng; ++static yy_size_t yy_n_chars; /* number of characters read into yy_ch_buf */ ++yy_size_t yyleng; + + /* Points to current character in buffer. */ + static char *yy_c_buf_p = (char *) 0; +@@ -309,7 +310,7 @@ static void yy_init_buffer (YY_BUFFER_ST + + YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); + YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); +-YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); ++YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ); + + void *yyalloc (yy_size_t ); + void *yyrealloc (void *,yy_size_t ); +@@ -341,7 +342,7 @@ void yyfree (void * ); + + /* Begin user sect3 */ + +-#define yywrap(n) 1 ++#define yywrap() 1 + #define YY_SKIP_YYWRAP + + typedef unsigned char YY_CHAR; +@@ -372,8 +373,8 @@ static void yy_fatal_error (yyconst char + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +-#define YY_NUM_RULES 31 +-#define YY_END_OF_BUFFER 32 ++#define YY_NUM_RULES 30 ++#define YY_END_OF_BUFFER 31 + /* This struct is not used in this scanner, + but its presence is necessary. */ + struct yy_trans_info +@@ -381,26 +382,25 @@ struct yy_trans_info + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +-static yyconst flex_int16_t yy_accept[166] = ++static yyconst flex_int16_t yy_accept[159] = + { 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 32, 30, +- 19, 19, 30, 30, 30, 30, 30, 30, 30, 30, +- 30, 30, 30, 30, 30, 30, 16, 17, 17, 30, +- 17, 11, 11, 19, 27, 0, 3, 0, 28, 13, +- 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, +- 0, 22, 24, 26, 25, 23, 0, 10, 29, 0, +- 0, 0, 15, 15, 17, 17, 17, 11, 11, 11, +- 0, 13, 0, 12, 0, 0, 0, 21, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 17, 11, 11, +- 11, 0, 14, 20, 0, 0, 0, 0, 0, 0, +- +- 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 17, 7, 0, 0, 0, +- 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 4, 18, 0, 0, 5, 2, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 1, 0, 0, 0, 0, 6, 9, 0, +- 0, 0, 0, 8, 0 ++ 0, 0, 0, 0, 0, 0, 0, 0, 31, 29, ++ 18, 18, 29, 29, 29, 29, 29, 29, 29, 29, ++ 29, 29, 29, 29, 29, 29, 15, 16, 16, 29, ++ 16, 10, 10, 18, 26, 0, 3, 0, 27, 12, ++ 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, ++ 21, 23, 25, 24, 22, 0, 9, 28, 0, 0, ++ 0, 14, 14, 16, 16, 16, 10, 10, 10, 0, ++ 12, 0, 11, 0, 0, 0, 20, 0, 0, 0, ++ 0, 0, 0, 0, 0, 16, 10, 10, 10, 0, ++ 13, 19, 0, 0, 0, 0, 0, 0, 0, 0, ++ ++ 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 16, 6, 0, 0, 0, 0, 0, 0, 2, ++ 0, 0, 0, 0, 0, 0, 0, 0, 4, 17, ++ 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, ++ 5, 8, 0, 0, 0, 0, 7, 0 + } ; + + static yyconst flex_int32_t yy_ec[256] = +@@ -416,9 +416,9 @@ static yyconst flex_int32_t yy_ec[256] = + 22, 22, 22, 22, 24, 22, 22, 25, 22, 22, + 1, 26, 27, 1, 22, 1, 21, 28, 29, 30, + +- 31, 21, 32, 22, 33, 22, 22, 34, 35, 36, +- 37, 38, 22, 39, 40, 41, 42, 43, 22, 25, +- 44, 22, 45, 46, 47, 1, 1, 1, 1, 1, ++ 31, 21, 22, 22, 32, 22, 22, 33, 34, 35, ++ 36, 37, 22, 38, 39, 40, 41, 42, 22, 25, ++ 43, 22, 44, 45, 46, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +@@ -435,165 +435,163 @@ static yyconst flex_int32_t yy_ec[256] = + 1, 1, 1, 1, 1 + } ; + +-static yyconst flex_int32_t yy_meta[48] = ++static yyconst flex_int32_t yy_meta[47] = + { 0, + 1, 1, 1, 1, 1, 1, 2, 3, 1, 2, + 2, 2, 4, 5, 5, 5, 6, 1, 1, 1, + 7, 8, 8, 8, 8, 1, 1, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, +- 8, 8, 8, 8, 3, 1, 4 ++ 8, 8, 8, 3, 1, 4 + } ; + +-static yyconst flex_int16_t yy_base[180] = ++static yyconst flex_int16_t yy_base[173] = + { 0, +- 0, 393, 35, 392, 66, 391, 38, 107, 397, 401, +- 55, 113, 377, 112, 111, 111, 114, 42, 376, 106, +- 377, 347, 126, 120, 0, 147, 401, 0, 124, 0, +- 137, 158, 170, 163, 401, 153, 401, 389, 401, 0, +- 378, 120, 401, 131, 380, 386, 355, 139, 351, 355, +- 351, 401, 401, 401, 401, 401, 367, 401, 401, 185, +- 350, 346, 401, 364, 0, 185, 347, 189, 356, 355, +- 0, 0, 330, 180, 366, 141, 372, 361, 332, 338, +- 331, 341, 334, 326, 205, 331, 337, 329, 401, 341, +- 167, 316, 401, 349, 348, 320, 328, 346, 180, 318, +- +- 324, 209, 324, 320, 322, 342, 338, 309, 306, 315, +- 305, 315, 312, 192, 342, 341, 401, 293, 306, 282, +- 268, 252, 255, 203, 285, 282, 272, 268, 252, 233, +- 232, 239, 208, 107, 401, 401, 238, 211, 401, 211, +- 212, 208, 228, 203, 215, 207, 233, 222, 212, 211, +- 203, 227, 401, 237, 225, 204, 185, 401, 401, 149, +- 128, 88, 42, 401, 401, 253, 259, 267, 271, 275, +- 281, 288, 292, 300, 308, 312, 318, 326, 334 ++ 0, 383, 34, 382, 65, 381, 37, 105, 387, 391, ++ 54, 111, 367, 110, 109, 109, 112, 41, 366, 104, ++ 367, 338, 124, 117, 0, 144, 391, 0, 121, 0, ++ 135, 155, 140, 179, 391, 160, 391, 379, 391, 0, ++ 368, 141, 391, 167, 370, 376, 346, 103, 342, 345, ++ 391, 391, 391, 391, 391, 358, 391, 391, 175, 342, ++ 338, 391, 355, 0, 185, 339, 184, 347, 346, 0, ++ 0, 322, 175, 357, 175, 363, 352, 324, 330, 323, ++ 332, 326, 201, 324, 329, 322, 391, 333, 181, 309, ++ 391, 341, 340, 313, 320, 338, 178, 311, 146, 317, ++ ++ 314, 315, 335, 331, 303, 300, 309, 299, 308, 188, ++ 336, 335, 391, 305, 320, 281, 283, 271, 203, 288, ++ 281, 271, 266, 264, 245, 242, 208, 104, 391, 391, ++ 244, 218, 204, 219, 206, 224, 201, 212, 204, 229, ++ 215, 208, 207, 200, 219, 391, 233, 221, 200, 181, ++ 391, 391, 149, 122, 86, 41, 391, 391, 245, 251, ++ 259, 263, 267, 273, 280, 284, 292, 300, 304, 310, ++ 318, 326 + } ; + +-static yyconst flex_int16_t yy_def[180] = ++static yyconst flex_int16_t yy_def[173] = + { 0, +- 165, 1, 1, 3, 165, 5, 1, 1, 165, 165, +- 165, 165, 165, 166, 167, 168, 165, 165, 165, 165, +- 169, 165, 165, 165, 170, 169, 165, 171, 172, 171, +- 171, 165, 165, 165, 165, 166, 165, 166, 165, 173, +- 165, 168, 165, 168, 174, 175, 165, 165, 165, 165, +- 165, 165, 165, 165, 165, 165, 169, 165, 165, 165, +- 165, 165, 165, 169, 171, 172, 171, 165, 165, 165, +- 176, 173, 177, 168, 174, 174, 175, 165, 165, 165, +- 165, 165, 165, 165, 165, 165, 165, 171, 165, 165, +- 176, 177, 165, 165, 165, 165, 165, 165, 165, 165, +- +- 165, 165, 165, 165, 171, 165, 165, 165, 165, 165, +- 165, 165, 165, 178, 165, 171, 165, 165, 165, 165, +- 165, 165, 165, 178, 165, 178, 165, 165, 165, 165, +- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, +- 165, 165, 165, 165, 165, 165, 165, 179, 165, 165, +- 165, 179, 165, 179, 165, 165, 165, 165, 165, 165, +- 165, 165, 165, 165, 0, 165, 165, 165, 165, 165, +- 165, 165, 165, 165, 165, 165, 165, 165, 165 ++ 158, 1, 1, 3, 158, 5, 1, 1, 158, 158, ++ 158, 158, 158, 159, 160, 161, 158, 158, 158, 158, ++ 162, 158, 158, 158, 163, 162, 158, 164, 165, 164, ++ 164, 158, 158, 158, 158, 159, 158, 159, 158, 166, ++ 158, 161, 158, 161, 167, 168, 158, 158, 158, 158, ++ 158, 158, 158, 158, 158, 162, 158, 158, 158, 158, ++ 158, 158, 162, 164, 165, 164, 158, 158, 158, 169, ++ 166, 170, 161, 167, 167, 168, 158, 158, 158, 158, ++ 158, 158, 158, 158, 158, 164, 158, 158, 169, 170, ++ 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, ++ ++ 158, 164, 158, 158, 158, 158, 158, 158, 158, 171, ++ 158, 164, 158, 158, 158, 158, 158, 158, 171, 158, ++ 171, 158, 158, 158, 158, 158, 158, 158, 158, 158, ++ 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, ++ 172, 158, 158, 158, 172, 158, 172, 158, 158, 158, ++ 158, 158, 158, 158, 158, 158, 158, 0, 158, 158, ++ 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, ++ 158, 158 + } ; + +-static yyconst flex_int16_t yy_nxt[449] = ++static yyconst flex_int16_t yy_nxt[438] = + { 0, + 10, 11, 12, 11, 13, 14, 10, 15, 16, 10, + 10, 10, 17, 10, 10, 10, 10, 18, 19, 20, + 21, 21, 21, 21, 21, 10, 10, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, +- 21, 21, 21, 21, 10, 22, 10, 24, 25, 25, +- 25, 32, 33, 33, 164, 26, 34, 34, 34, 52, +- 53, 27, 26, 26, 26, 26, 10, 11, 12, 11, +- 13, 14, 28, 15, 16, 28, 28, 28, 24, 28, +- 28, 28, 10, 18, 19, 20, 29, 29, 29, 29, +- 29, 30, 10, 29, 29, 29, 29, 29, 29, 29, +- +- 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +- 10, 22, 10, 23, 34, 34, 34, 37, 39, 43, +- 32, 33, 33, 45, 55, 56, 46, 60, 43, 45, +- 65, 163, 46, 65, 65, 65, 44, 38, 60, 74, +- 58, 47, 141, 48, 142, 44, 49, 47, 50, 48, +- 76, 51, 62, 94, 50, 41, 44, 51, 37, 61, +- 64, 64, 64, 58, 34, 34, 34, 64, 162, 80, +- 67, 68, 68, 68, 64, 64, 64, 64, 38, 81, +- 69, 70, 71, 68, 68, 68, 60, 161, 43, 69, +- 70, 65, 69, 70, 65, 65, 65, 125, 85, 85, +- +- 85, 58, 68, 68, 68, 44, 102, 110, 125, 133, +- 102, 69, 70, 111, 114, 160, 159, 126, 85, 85, +- 85, 140, 140, 140, 140, 140, 140, 153, 126, 147, +- 147, 147, 153, 148, 147, 147, 147, 158, 148, 165, +- 157, 156, 155, 151, 150, 149, 146, 154, 145, 144, +- 143, 139, 154, 36, 36, 36, 36, 36, 36, 36, +- 36, 40, 138, 137, 136, 40, 40, 42, 42, 42, +- 42, 42, 42, 42, 42, 57, 57, 57, 57, 63, +- 135, 63, 65, 134, 165, 65, 133, 65, 65, 66, +- 132, 131, 66, 66, 66, 66, 72, 130, 72, 72, +- +- 75, 75, 75, 75, 75, 75, 75, 75, 77, 77, +- 77, 77, 77, 77, 77, 77, 91, 129, 91, 92, +- 128, 92, 92, 127, 92, 92, 124, 124, 124, 124, +- 124, 124, 124, 124, 152, 152, 152, 152, 152, 152, +- 152, 152, 60, 60, 123, 122, 121, 120, 119, 118, +- 117, 45, 116, 111, 115, 113, 112, 109, 108, 107, +- 46, 106, 93, 89, 105, 104, 103, 101, 100, 99, +- 98, 97, 96, 95, 78, 76, 93, 90, 89, 88, +- 58, 87, 86, 58, 84, 83, 82, 79, 78, 76, +- 73, 165, 59, 58, 54, 35, 165, 31, 23, 23, +- +- 9, 165, 165, 165, 165, 165, 165, 165, 165, 165, +- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, +- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, +- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, +- 165, 165, 165, 165, 165, 165, 165, 165 ++ 21, 21, 21, 10, 22, 10, 24, 25, 25, 25, ++ 32, 33, 33, 157, 26, 34, 34, 34, 51, 52, ++ 27, 26, 26, 26, 26, 10, 11, 12, 11, 13, ++ 14, 28, 15, 16, 28, 28, 28, 24, 28, 28, ++ 28, 10, 18, 19, 20, 29, 29, 29, 29, 29, ++ 30, 10, 29, 29, 29, 29, 29, 29, 29, 29, ++ ++ 29, 29, 29, 29, 29, 29, 29, 29, 10, 22, ++ 10, 23, 34, 34, 34, 37, 39, 43, 32, 33, ++ 33, 45, 54, 55, 46, 59, 45, 64, 156, 46, ++ 64, 64, 64, 79, 44, 38, 59, 57, 134, 47, ++ 135, 48, 80, 49, 47, 50, 48, 99, 61, 43, ++ 50, 110, 41, 67, 67, 67, 60, 63, 63, 63, ++ 57, 155, 68, 69, 63, 37, 44, 66, 67, 67, ++ 67, 63, 63, 63, 63, 73, 59, 68, 69, 70, ++ 34, 34, 34, 43, 75, 38, 154, 92, 83, 83, ++ 83, 64, 44, 120, 64, 64, 64, 67, 67, 67, ++ ++ 44, 57, 99, 68, 69, 107, 68, 69, 120, 127, ++ 108, 153, 152, 121, 83, 83, 83, 133, 133, 133, ++ 146, 133, 133, 133, 146, 140, 140, 140, 121, 141, ++ 140, 140, 140, 151, 141, 158, 150, 149, 148, 144, ++ 147, 143, 142, 139, 147, 36, 36, 36, 36, 36, ++ 36, 36, 36, 40, 138, 137, 136, 40, 40, 42, ++ 42, 42, 42, 42, 42, 42, 42, 56, 56, 56, ++ 56, 62, 132, 62, 64, 131, 130, 64, 129, 64, ++ 64, 65, 128, 158, 65, 65, 65, 65, 71, 127, ++ 71, 71, 74, 74, 74, 74, 74, 74, 74, 74, ++ ++ 76, 76, 76, 76, 76, 76, 76, 76, 89, 126, ++ 89, 90, 125, 90, 90, 124, 90, 90, 119, 119, ++ 119, 119, 119, 119, 119, 119, 145, 145, 145, 145, ++ 145, 145, 145, 145, 123, 122, 59, 59, 118, 117, ++ 116, 115, 114, 113, 45, 112, 108, 111, 109, 106, ++ 105, 104, 46, 103, 91, 87, 102, 101, 100, 98, ++ 97, 96, 95, 94, 93, 77, 75, 91, 88, 87, ++ 86, 57, 85, 84, 57, 82, 81, 78, 77, 75, ++ 72, 158, 58, 57, 53, 35, 158, 31, 23, 23, ++ 9, 158, 158, 158, 158, 158, 158, 158, 158, 158, ++ ++ 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, ++ 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, ++ 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, ++ 158, 158, 158, 158, 158, 158, 158 + } ; + +-static yyconst flex_int16_t yy_chk[449] = ++static yyconst flex_int16_t yy_chk[438] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +- 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, +- 3, 7, 7, 7, 163, 3, 11, 11, 11, 18, +- 18, 3, 3, 3, 3, 3, 5, 5, 5, 5, ++ 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, ++ 7, 7, 7, 156, 3, 11, 11, 11, 18, 18, ++ 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, +- 5, 5, 5, 8, 12, 12, 12, 14, 15, 16, +- 8, 8, 8, 17, 20, 20, 17, 23, 42, 24, +- 29, 162, 24, 29, 29, 29, 16, 14, 31, 44, +- 29, 17, 134, 17, 134, 42, 17, 24, 17, 24, +- 76, 17, 24, 76, 24, 15, 44, 24, 36, 23, +- 26, 26, 26, 26, 34, 34, 34, 26, 161, 48, +- 31, 32, 32, 32, 26, 26, 26, 26, 36, 48, +- 32, 32, 32, 33, 33, 33, 60, 160, 74, 91, +- 91, 66, 33, 33, 66, 66, 66, 114, 60, 60, +- +- 60, 66, 68, 68, 68, 74, 85, 99, 124, 133, +- 102, 68, 68, 99, 102, 157, 156, 114, 85, 85, +- 85, 133, 133, 133, 140, 140, 140, 148, 124, 143, +- 143, 143, 152, 143, 147, 147, 147, 155, 147, 154, +- 151, 150, 149, 146, 145, 144, 142, 148, 141, 138, +- 137, 132, 152, 166, 166, 166, 166, 166, 166, 166, +- 166, 167, 131, 130, 129, 167, 167, 168, 168, 168, +- 168, 168, 168, 168, 168, 169, 169, 169, 169, 170, +- 128, 170, 171, 127, 126, 171, 125, 171, 171, 172, +- 123, 122, 172, 172, 172, 172, 173, 121, 173, 173, +- +- 174, 174, 174, 174, 174, 174, 174, 174, 175, 175, +- 175, 175, 175, 175, 175, 175, 176, 120, 176, 177, +- 119, 177, 177, 118, 177, 177, 178, 178, 178, 178, +- 178, 178, 178, 178, 179, 179, 179, 179, 179, 179, +- 179, 179, 116, 115, 113, 112, 111, 110, 109, 108, +- 107, 106, 105, 104, 103, 101, 100, 98, 97, 96, +- 95, 94, 92, 90, 88, 87, 86, 84, 83, 82, +- 81, 80, 79, 78, 77, 75, 73, 70, 69, 67, +- 64, 62, 61, 57, 51, 50, 49, 47, 46, 45, ++ 5, 8, 12, 12, 12, 14, 15, 16, 8, 8, ++ 8, 17, 20, 20, 17, 23, 24, 29, 155, 24, ++ 29, 29, 29, 48, 16, 14, 31, 29, 128, 17, ++ 128, 17, 48, 17, 24, 17, 24, 99, 24, 42, ++ 24, 99, 15, 33, 33, 33, 23, 26, 26, 26, ++ 26, 154, 33, 33, 26, 36, 42, 31, 32, 32, ++ 32, 26, 26, 26, 26, 44, 59, 32, 32, 32, ++ 34, 34, 34, 73, 75, 36, 153, 75, 59, 59, ++ 59, 65, 44, 110, 65, 65, 65, 67, 67, 67, ++ ++ 73, 65, 83, 89, 89, 97, 67, 67, 119, 127, ++ 97, 150, 149, 110, 83, 83, 83, 133, 133, 133, ++ 141, 127, 127, 127, 145, 136, 136, 136, 119, 136, ++ 140, 140, 140, 148, 140, 147, 144, 143, 142, 139, ++ 141, 138, 137, 135, 145, 159, 159, 159, 159, 159, ++ 159, 159, 159, 160, 134, 132, 131, 160, 160, 161, ++ 161, 161, 161, 161, 161, 161, 161, 162, 162, 162, ++ 162, 163, 126, 163, 164, 125, 124, 164, 123, 164, ++ 164, 165, 122, 121, 165, 165, 165, 165, 166, 120, ++ 166, 166, 167, 167, 167, 167, 167, 167, 167, 167, ++ ++ 168, 168, 168, 168, 168, 168, 168, 168, 169, 118, ++ 169, 170, 117, 170, 170, 116, 170, 170, 171, 171, ++ 171, 171, 171, 171, 171, 171, 172, 172, 172, 172, ++ 172, 172, 172, 172, 115, 114, 112, 111, 109, 108, ++ 107, 106, 105, 104, 103, 102, 101, 100, 98, 96, ++ 95, 94, 93, 92, 90, 88, 86, 85, 84, 82, ++ 81, 80, 79, 78, 77, 76, 74, 72, 69, 68, ++ 66, 63, 61, 60, 56, 50, 49, 47, 46, 45, + 41, 38, 22, 21, 19, 13, 9, 6, 4, 2, ++ 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, + +- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, +- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, +- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, +- 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, +- 165, 165, 165, 165, 165, 165, 165, 165 ++ 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, ++ 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, ++ 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, ++ 158, 158, 158, 158, 158, 158, 158 + } ; + + static yy_state_type yy_last_accepting_state; +@@ -664,7 +662,7 @@ static int dts_version = 1; + static void push_input_file(const char *filename); + static bool pop_input_file(void); + static void lexical_error(const char *fmt, ...); +-#line 668 "dtc-lexer.lex.c" ++#line 666 "dtc-lexer.lex.c" + + #define INITIAL 0 + #define BYTESTRING 1 +@@ -706,7 +704,7 @@ FILE *yyget_out (void ); + + void yyset_out (FILE * out_str ); + +-int yyget_leng (void ); ++yy_size_t yyget_leng (void ); + + char *yyget_text (void ); + +@@ -855,10 +853,6 @@ YY_DECL + register char *yy_cp, *yy_bp; + register int yy_act; + +-#line 68 "dtc-lexer.l" +- +-#line 861 "dtc-lexer.lex.c" +- + if ( !(yy_init) ) + { + (yy_init) = 1; +@@ -885,6 +879,11 @@ YY_DECL + yy_load_buffer_state( ); + } + ++ { ++#line 68 "dtc-lexer.l" ++ ++#line 886 "dtc-lexer.lex.c" ++ + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); +@@ -902,7 +901,7 @@ YY_DECL + yy_match: + do + { +- register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; ++ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; +@@ -911,13 +910,13 @@ yy_match: + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; +- if ( yy_current_state >= 166 ) ++ if ( yy_current_state >= 159 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } +- while ( yy_current_state != 165 ); ++ while ( yy_current_state != 158 ); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + +@@ -1008,31 +1007,23 @@ case 5: + YY_RULE_SETUP + #line 116 "dtc-lexer.l" + { +- DPRINT("Keyword: /plugin/\n"); +- return DT_PLUGIN; +- } +- YY_BREAK +-case 6: +-YY_RULE_SETUP +-#line 121 "dtc-lexer.l" +-{ + DPRINT("Keyword: /memreserve/\n"); + BEGIN_DEFAULT(); + return DT_MEMRESERVE; + } + YY_BREAK +-case 7: ++case 6: + YY_RULE_SETUP +-#line 127 "dtc-lexer.l" ++#line 122 "dtc-lexer.l" + { + DPRINT("Keyword: /bits/\n"); + BEGIN_DEFAULT(); + return DT_BITS; + } + YY_BREAK +-case 8: ++case 7: + YY_RULE_SETUP +-#line 133 "dtc-lexer.l" ++#line 128 "dtc-lexer.l" + { + DPRINT("Keyword: /delete-property/\n"); + DPRINT("\n"); +@@ -1040,9 +1031,9 @@ YY_RULE_SETUP + return DT_DEL_PROP; + } + YY_BREAK +-case 9: ++case 8: + YY_RULE_SETUP +-#line 140 "dtc-lexer.l" ++#line 135 "dtc-lexer.l" + { + DPRINT("Keyword: /delete-node/\n"); + DPRINT("\n"); +@@ -1050,9 +1041,9 @@ YY_RULE_SETUP + return DT_DEL_NODE; + } + YY_BREAK +-case 10: ++case 9: + YY_RULE_SETUP +-#line 147 "dtc-lexer.l" ++#line 142 "dtc-lexer.l" + { + DPRINT("Label: %s\n", yytext); + yylval.labelref = xstrdup(yytext); +@@ -1060,9 +1051,9 @@ YY_RULE_SETUP + return DT_LABEL; + } + YY_BREAK +-case 11: ++case 10: + YY_RULE_SETUP +-#line 154 "dtc-lexer.l" ++#line 149 "dtc-lexer.l" + { + char *e; + DPRINT("Integer Literal: '%s'\n", yytext); +@@ -1082,10 +1073,10 @@ YY_RULE_SETUP + return DT_LITERAL; + } + YY_BREAK +-case 12: +-/* rule 12 can match eol */ ++case 11: ++/* rule 11 can match eol */ + YY_RULE_SETUP +-#line 173 "dtc-lexer.l" ++#line 168 "dtc-lexer.l" + { + struct data d; + DPRINT("Character literal: %s\n", yytext); +@@ -1107,18 +1098,18 @@ YY_RULE_SETUP + return DT_CHAR_LITERAL; + } + YY_BREAK +-case 13: ++case 12: + YY_RULE_SETUP +-#line 194 "dtc-lexer.l" ++#line 189 "dtc-lexer.l" + { /* label reference */ + DPRINT("Ref: %s\n", yytext+1); + yylval.labelref = xstrdup(yytext+1); + return DT_REF; + } + YY_BREAK +-case 14: ++case 13: + YY_RULE_SETUP +-#line 200 "dtc-lexer.l" ++#line 195 "dtc-lexer.l" + { /* new-style path reference */ + yytext[yyleng-1] = '\0'; + DPRINT("Ref: %s\n", yytext+2); +@@ -1126,27 +1117,27 @@ YY_RULE_SETUP + return DT_REF; + } + YY_BREAK +-case 15: ++case 14: + YY_RULE_SETUP +-#line 207 "dtc-lexer.l" ++#line 202 "dtc-lexer.l" + { + yylval.byte = strtol(yytext, NULL, 16); + DPRINT("Byte: %02x\n", (int)yylval.byte); + return DT_BYTE; + } + YY_BREAK +-case 16: ++case 15: + YY_RULE_SETUP +-#line 213 "dtc-lexer.l" ++#line 208 "dtc-lexer.l" + { + DPRINT("/BYTESTRING\n"); + BEGIN_DEFAULT(); + return ']'; + } + YY_BREAK +-case 17: ++case 16: + YY_RULE_SETUP +-#line 219 "dtc-lexer.l" ++#line 214 "dtc-lexer.l" + { + DPRINT("PropNodeName: %s\n", yytext); + yylval.propnodename = xstrdup((yytext[0] == '\\') ? +@@ -1155,75 +1146,75 @@ YY_RULE_SETUP + return DT_PROPNODENAME; + } + YY_BREAK +-case 18: ++case 17: + YY_RULE_SETUP +-#line 227 "dtc-lexer.l" ++#line 222 "dtc-lexer.l" + { + DPRINT("Binary Include\n"); + return DT_INCBIN; + } + YY_BREAK +-case 19: +-/* rule 19 can match eol */ ++case 18: ++/* rule 18 can match eol */ + YY_RULE_SETUP +-#line 232 "dtc-lexer.l" ++#line 227 "dtc-lexer.l" + /* eat whitespace */ + YY_BREAK +-case 20: +-/* rule 20 can match eol */ ++case 19: ++/* rule 19 can match eol */ + YY_RULE_SETUP +-#line 233 "dtc-lexer.l" ++#line 228 "dtc-lexer.l" + /* eat C-style comments */ + YY_BREAK +-case 21: +-/* rule 21 can match eol */ ++case 20: ++/* rule 20 can match eol */ + YY_RULE_SETUP +-#line 234 "dtc-lexer.l" ++#line 229 "dtc-lexer.l" + /* eat C++-style comments */ + YY_BREAK +-case 22: ++case 21: + YY_RULE_SETUP +-#line 236 "dtc-lexer.l" ++#line 231 "dtc-lexer.l" + { return DT_LSHIFT; }; + YY_BREAK +-case 23: ++case 22: + YY_RULE_SETUP +-#line 237 "dtc-lexer.l" ++#line 232 "dtc-lexer.l" + { return DT_RSHIFT; }; + YY_BREAK +-case 24: ++case 23: + YY_RULE_SETUP +-#line 238 "dtc-lexer.l" ++#line 233 "dtc-lexer.l" + { return DT_LE; }; + YY_BREAK +-case 25: ++case 24: + YY_RULE_SETUP +-#line 239 "dtc-lexer.l" ++#line 234 "dtc-lexer.l" + { return DT_GE; }; + YY_BREAK +-case 26: ++case 25: + YY_RULE_SETUP +-#line 240 "dtc-lexer.l" ++#line 235 "dtc-lexer.l" + { return DT_EQ; }; + YY_BREAK +-case 27: ++case 26: + YY_RULE_SETUP +-#line 241 "dtc-lexer.l" ++#line 236 "dtc-lexer.l" + { return DT_NE; }; + YY_BREAK +-case 28: ++case 27: + YY_RULE_SETUP +-#line 242 "dtc-lexer.l" ++#line 237 "dtc-lexer.l" + { return DT_AND; }; + YY_BREAK +-case 29: ++case 28: + YY_RULE_SETUP +-#line 243 "dtc-lexer.l" ++#line 238 "dtc-lexer.l" + { return DT_OR; }; + YY_BREAK +-case 30: ++case 29: + YY_RULE_SETUP +-#line 245 "dtc-lexer.l" ++#line 240 "dtc-lexer.l" + { + DPRINT("Char: %c (\\x%02x)\n", yytext[0], + (unsigned)yytext[0]); +@@ -1239,12 +1230,12 @@ YY_RULE_SETUP + return yytext[0]; + } + YY_BREAK +-case 31: ++case 30: + YY_RULE_SETUP +-#line 260 "dtc-lexer.l" ++#line 255 "dtc-lexer.l" + ECHO; + YY_BREAK +-#line 1248 "dtc-lexer.lex.c" ++#line 1239 "dtc-lexer.lex.c" + + case YY_END_OF_BUFFER: + { +@@ -1374,6 +1365,7 @@ ECHO; + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ ++ } /* end of user's declarations */ + } /* end of yylex */ + + /* yy_get_next_buffer - try to read in a new buffer +@@ -1429,21 +1421,21 @@ static int yy_get_next_buffer (void) + + else + { +- int num_to_read = ++ yy_size_t num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ +- YY_BUFFER_STATE b = YY_CURRENT_BUFFER; ++ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { +- int new_size = b->yy_buf_size * 2; ++ yy_size_t new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; +@@ -1474,7 +1466,7 @@ static int yy_get_next_buffer (void) + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), +- (yy_n_chars), (size_t) num_to_read ); ++ (yy_n_chars), num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } +@@ -1536,7 +1528,7 @@ static int yy_get_next_buffer (void) + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; +- if ( yy_current_state >= 166 ) ++ if ( yy_current_state >= 159 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; +@@ -1564,13 +1556,13 @@ static int yy_get_next_buffer (void) + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; +- if ( yy_current_state >= 166 ) ++ if ( yy_current_state >= 159 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; +- yy_is_jam = (yy_current_state == 165); ++ yy_is_jam = (yy_current_state == 158); + +- return yy_is_jam ? 0 : yy_current_state; ++ return yy_is_jam ? 0 : yy_current_state; + } + + #ifndef YY_NO_INPUT +@@ -1597,7 +1589,7 @@ static int yy_get_next_buffer (void) + + else + { /* need more input */ +- int offset = (yy_c_buf_p) - (yytext_ptr); ++ yy_size_t offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) +@@ -1871,7 +1863,7 @@ void yypop_buffer_state (void) + */ + static void yyensure_buffer_stack (void) + { +- int num_to_alloc; ++ yy_size_t num_to_alloc; + + if (!(yy_buffer_stack)) { + +@@ -1968,12 +1960,12 @@ YY_BUFFER_STATE yy_scan_string (yyconst + * + * @return the newly allocated buffer state object. + */ +-YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) ++YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len ) + { + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; +- int i; ++ yy_size_t i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; +@@ -2055,7 +2047,7 @@ FILE *yyget_out (void) + /** Get the length of the current token. + * + */ +-int yyget_leng (void) ++yy_size_t yyget_leng (void) + { + return yyleng; + } +@@ -2203,7 +2195,7 @@ void yyfree (void * ptr ) + + #define YYTABLES_NAME "yytables" + +-#line 260 "dtc-lexer.l" ++#line 254 "dtc-lexer.l" + + + +--- a/scripts/dtc/dtc-parser.tab.c_shipped ++++ b/scripts/dtc/dtc-parser.tab.c_shipped +@@ -1,19 +1,19 @@ +-/* A Bison parser, made by GNU Bison 2.5. */ ++/* A Bison parser, made by GNU Bison 3.0.2. */ + + /* Bison implementation for Yacc-like parsers in C +- +- Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. +- ++ ++ Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. ++ + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +- ++ + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU 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 . */ + +@@ -26,7 +26,7 @@ + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. +- ++ + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +@@ -44,7 +44,7 @@ + #define YYBISON 1 + + /* Bison version. */ +-#define YYBISON_VERSION "2.5" ++#define YYBISON_VERSION "3.0.2" + + /* Skeleton name. */ + #define YYSKELETON_NAME "yacc.c" +@@ -58,18 +58,13 @@ + /* Pull parsers. */ + #define YYPULL 1 + +-/* Using locations. */ +-#define YYLSP_NEEDED 1 + + + + /* Copy the first part of user declarations. */ +- +-/* Line 268 of yacc.c */ +-#line 20 "dtc-parser.y" ++#line 20 "dtc-parser.y" /* yacc.c:339 */ + + #include +-#include + + #include "dtc.h" + #include "srcpos.h" +@@ -85,14 +80,15 @@ extern void yyerror(char const *s); + extern struct boot_info *the_boot_info; + extern bool treesource_error; + ++#line 84 "dtc-parser.tab.c" /* yacc.c:339 */ + +-/* Line 268 of yacc.c */ +-#line 91 "dtc-parser.tab.c" +- +-/* Enabling traces. */ +-#ifndef YYDEBUG +-# define YYDEBUG 0 +-#endif ++# ifndef YY_NULLPTR ++# if defined __cplusplus && 201103L <= __cplusplus ++# define YY_NULLPTR nullptr ++# else ++# define YY_NULLPTR 0 ++# endif ++# endif + + /* Enabling verbose error messages. */ + #ifdef YYERROR_VERBOSE +@@ -102,51 +98,53 @@ extern bool treesource_error; + # define YYERROR_VERBOSE 0 + #endif + +-/* Enabling the token table. */ +-#ifndef YYTOKEN_TABLE +-# define YYTOKEN_TABLE 0 ++/* In a future release of Bison, this section will be replaced ++ by #include "dtc-parser.tab.h". */ ++#ifndef YY_YY_DTC_PARSER_TAB_H_INCLUDED ++# define YY_YY_DTC_PARSER_TAB_H_INCLUDED ++/* Debug traces. */ ++#ifndef YYDEBUG ++# define YYDEBUG 0 ++#endif ++#if YYDEBUG ++extern int yydebug; + #endif + +- +-/* Tokens. */ ++/* Token type. */ + #ifndef YYTOKENTYPE + # define YYTOKENTYPE +- /* Put the tokens into the symbol table, so that GDB and other debuggers +- know about them. */ +- enum yytokentype { +- DT_V1 = 258, +- DT_PLUGIN = 259, +- DT_MEMRESERVE = 260, +- DT_LSHIFT = 261, +- DT_RSHIFT = 262, +- DT_LE = 263, +- DT_GE = 264, +- DT_EQ = 265, +- DT_NE = 266, +- DT_AND = 267, +- DT_OR = 268, +- DT_BITS = 269, +- DT_DEL_PROP = 270, +- DT_DEL_NODE = 271, +- DT_PROPNODENAME = 272, +- DT_LITERAL = 273, +- DT_CHAR_LITERAL = 274, +- DT_BYTE = 275, +- DT_STRING = 276, +- DT_LABEL = 277, +- DT_REF = 278, +- DT_INCBIN = 279 +- }; ++ enum yytokentype ++ { ++ DT_V1 = 258, ++ DT_MEMRESERVE = 259, ++ DT_LSHIFT = 260, ++ DT_RSHIFT = 261, ++ DT_LE = 262, ++ DT_GE = 263, ++ DT_EQ = 264, ++ DT_NE = 265, ++ DT_AND = 266, ++ DT_OR = 267, ++ DT_BITS = 268, ++ DT_DEL_PROP = 269, ++ DT_DEL_NODE = 270, ++ DT_PROPNODENAME = 271, ++ DT_LITERAL = 272, ++ DT_CHAR_LITERAL = 273, ++ DT_BYTE = 274, ++ DT_STRING = 275, ++ DT_LABEL = 276, ++ DT_REF = 277, ++ DT_INCBIN = 278 ++ }; + #endif + +- +- ++/* Value type. */ + #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +-typedef union YYSTYPE ++typedef union YYSTYPE YYSTYPE; ++union YYSTYPE + { +- +-/* Line 293 of yacc.c */ +-#line 39 "dtc-parser.y" ++#line 38 "dtc-parser.y" /* yacc.c:355 */ + + char *propnodename; + char *labelref; +@@ -164,37 +162,37 @@ typedef union YYSTYPE + struct node *nodelist; + struct reserve_info *re; + uint64_t integer; +- int is_plugin; +- +- + +-/* Line 293 of yacc.c */ +-#line 173 "dtc-parser.tab.c" +-} YYSTYPE; ++#line 167 "dtc-parser.tab.c" /* yacc.c:355 */ ++}; + # define YYSTYPE_IS_TRIVIAL 1 +-# define yystype YYSTYPE /* obsolescent; will be withdrawn */ + # define YYSTYPE_IS_DECLARED 1 + #endif + ++/* Location type. */ + #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +-typedef struct YYLTYPE ++typedef struct YYLTYPE YYLTYPE; ++struct YYLTYPE + { + int first_line; + int first_column; + int last_line; + int last_column; +-} YYLTYPE; +-# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ ++}; + # define YYLTYPE_IS_DECLARED 1 + # define YYLTYPE_IS_TRIVIAL 1 + #endif + + +-/* Copy the second part of user declarations. */ ++extern YYSTYPE yylval; ++extern YYLTYPE yylloc; ++int yyparse (void); ++ ++#endif /* !YY_YY_DTC_PARSER_TAB_H_INCLUDED */ + ++/* Copy the second part of user declarations. */ + +-/* Line 343 of yacc.c */ +-#line 198 "dtc-parser.tab.c" ++#line 196 "dtc-parser.tab.c" /* yacc.c:358 */ + + #ifdef short + # undef short +@@ -208,11 +206,8 @@ typedef unsigned char yytype_uint8; + + #ifdef YYTYPE_INT8 + typedef YYTYPE_INT8 yytype_int8; +-#elif (defined __STDC__ || defined __C99__FUNC__ \ +- || defined __cplusplus || defined _MSC_VER) +-typedef signed char yytype_int8; + #else +-typedef short int yytype_int8; ++typedef signed char yytype_int8; + #endif + + #ifdef YYTYPE_UINT16 +@@ -232,8 +227,7 @@ typedef short int yytype_int16; + # define YYSIZE_T __SIZE_TYPE__ + # elif defined size_t + # define YYSIZE_T size_t +-# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ +- || defined __cplusplus || defined _MSC_VER) ++# elif ! defined YYSIZE_T + # include /* INFRINGES ON USER NAME SPACE */ + # define YYSIZE_T size_t + # else +@@ -247,39 +241,68 @@ typedef short int yytype_int16; + # if defined YYENABLE_NLS && YYENABLE_NLS + # if ENABLE_NLS + # include /* INFRINGES ON USER NAME SPACE */ +-# define YY_(msgid) dgettext ("bison-runtime", msgid) ++# define YY_(Msgid) dgettext ("bison-runtime", Msgid) + # endif + # endif + # ifndef YY_ +-# define YY_(msgid) msgid ++# define YY_(Msgid) Msgid ++# endif ++#endif ++ ++#ifndef YY_ATTRIBUTE ++# if (defined __GNUC__ \ ++ && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ ++ || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C ++# define YY_ATTRIBUTE(Spec) __attribute__(Spec) ++# else ++# define YY_ATTRIBUTE(Spec) /* empty */ ++# endif ++#endif ++ ++#ifndef YY_ATTRIBUTE_PURE ++# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) ++#endif ++ ++#ifndef YY_ATTRIBUTE_UNUSED ++# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) ++#endif ++ ++#if !defined _Noreturn \ ++ && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112) ++# if defined _MSC_VER && 1200 <= _MSC_VER ++# define _Noreturn __declspec (noreturn) ++# else ++# define _Noreturn YY_ATTRIBUTE ((__noreturn__)) + # endif + #endif + + /* Suppress unused-variable warnings by "using" E. */ + #if ! defined lint || defined __GNUC__ +-# define YYUSE(e) ((void) (e)) ++# define YYUSE(E) ((void) (E)) + #else +-# define YYUSE(e) /* empty */ ++# define YYUSE(E) /* empty */ + #endif + +-/* Identity function, used to suppress warnings about constant conditions. */ +-#ifndef lint +-# define YYID(n) (n) +-#else +-#if (defined __STDC__ || defined __C99__FUNC__ \ +- || defined __cplusplus || defined _MSC_VER) +-static int +-YYID (int yyi) ++#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ ++/* Suppress an incorrect diagnostic about yylval being uninitialized. */ ++# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ ++ _Pragma ("GCC diagnostic push") \ ++ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ ++ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") ++# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ ++ _Pragma ("GCC diagnostic pop") + #else +-static int +-YYID (yyi) +- int yyi; ++# define YY_INITIAL_VALUE(Value) Value + #endif +-{ +- return yyi; +-} ++#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN ++# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN ++# define YY_IGNORE_MAYBE_UNINITIALIZED_END ++#endif ++#ifndef YY_INITIAL_VALUE ++# define YY_INITIAL_VALUE(Value) /* Nothing. */ + #endif + ++ + #if ! defined yyoverflow || YYERROR_VERBOSE + + /* The parser invokes alloca or malloc; define the necessary symbols. */ +@@ -297,9 +320,9 @@ YYID (yyi) + # define alloca _alloca + # else + # define YYSTACK_ALLOC alloca +-# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ +- || defined __cplusplus || defined _MSC_VER) ++# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS + # include /* INFRINGES ON USER NAME SPACE */ ++ /* Use EXIT_SUCCESS as a witness for stdlib.h. */ + # ifndef EXIT_SUCCESS + # define EXIT_SUCCESS 0 + # endif +@@ -309,8 +332,8 @@ YYID (yyi) + # endif + + # ifdef YYSTACK_ALLOC +- /* Pacify GCC's `empty if-body' warning. */ +-# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) ++ /* Pacify GCC's 'empty if-body' warning. */ ++# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) + # ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely +@@ -326,7 +349,7 @@ YYID (yyi) + # endif + # if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ +- && (defined YYFREE || defined free))) ++ && (defined YYFREE || defined free))) + # include /* INFRINGES ON USER NAME SPACE */ + # ifndef EXIT_SUCCESS + # define EXIT_SUCCESS 0 +@@ -334,15 +357,13 @@ YYID (yyi) + # endif + # ifndef YYMALLOC + # define YYMALLOC malloc +-# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ +- || defined __cplusplus || defined _MSC_VER) ++# if ! defined malloc && ! defined EXIT_SUCCESS + void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ + # endif + # endif + # ifndef YYFREE + # define YYFREE free +-# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ +- || defined __cplusplus || defined _MSC_VER) ++# if ! defined free && ! defined EXIT_SUCCESS + void free (void *); /* INFRINGES ON USER NAME SPACE */ + # endif + # endif +@@ -352,8 +373,8 @@ void free (void *); /* INFRINGES ON USER + + #if (! defined yyoverflow \ + && (! defined __cplusplus \ +- || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ +- && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) ++ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ ++ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + + /* A type that is properly aligned for any stack member. */ + union yyalloc +@@ -379,35 +400,35 @@ union yyalloc + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +-# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ +- do \ +- { \ +- YYSIZE_T yynewbytes; \ +- YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ +- Stack = &yyptr->Stack_alloc; \ +- yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ +- yyptr += yynewbytes / sizeof (*yyptr); \ +- } \ +- while (YYID (0)) ++# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ ++ do \ ++ { \ ++ YYSIZE_T yynewbytes; \ ++ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ ++ Stack = &yyptr->Stack_alloc; \ ++ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ ++ yyptr += yynewbytes / sizeof (*yyptr); \ ++ } \ ++ while (0) + + #endif + + #if defined YYCOPY_NEEDED && YYCOPY_NEEDED +-/* Copy COUNT objects from FROM to TO. The source and destination do ++/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ + # ifndef YYCOPY + # if defined __GNUC__ && 1 < __GNUC__ +-# define YYCOPY(To, From, Count) \ +- __builtin_memcpy (To, From, (Count) * sizeof (*(From))) ++# define YYCOPY(Dst, Src, Count) \ ++ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) + # else +-# define YYCOPY(To, From, Count) \ +- do \ +- { \ +- YYSIZE_T yyi; \ +- for (yyi = 0; yyi < (Count); yyi++) \ +- (To)[yyi] = (From)[yyi]; \ +- } \ +- while (YYID (0)) ++# define YYCOPY(Dst, Src, Count) \ ++ do \ ++ { \ ++ YYSIZE_T yyi; \ ++ for (yyi = 0; yyi < (Count); yyi++) \ ++ (Dst)[yyi] = (Src)[yyi]; \ ++ } \ ++ while (0) + # endif + # endif + #endif /* !YYCOPY_NEEDED */ +@@ -418,37 +439,39 @@ union yyalloc + #define YYLAST 136 + + /* YYNTOKENS -- Number of terminals. */ +-#define YYNTOKENS 48 ++#define YYNTOKENS 47 + /* YYNNTS -- Number of nonterminals. */ +-#define YYNNTS 29 ++#define YYNNTS 28 + /* YYNRULES -- Number of rules. */ +-#define YYNRULES 82 +-/* YYNRULES -- Number of states. */ +-#define YYNSTATES 147 ++#define YYNRULES 80 ++/* YYNSTATES -- Number of states. */ ++#define YYNSTATES 144 + +-/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ ++/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned ++ by yylex, with out-of-bounds checking. */ + #define YYUNDEFTOK 2 +-#define YYMAXUTOK 279 ++#define YYMAXUTOK 278 + +-#define YYTRANSLATE(YYX) \ ++#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +-/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ ++/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM ++ as returned by yylex, without out-of-bounds checking. */ + static const yytype_uint8 yytranslate[] = + { + 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, 47, 2, 2, 2, 45, 41, 2, +- 33, 35, 44, 42, 34, 43, 2, 26, 2, 2, +- 2, 2, 2, 2, 2, 2, 2, 2, 38, 25, +- 36, 29, 30, 37, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 46, 2, 2, 2, 44, 40, 2, ++ 32, 34, 43, 41, 33, 42, 2, 25, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 37, 24, ++ 35, 28, 29, 36, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +- 2, 31, 2, 32, 40, 2, 2, 2, 2, 2, ++ 2, 30, 2, 31, 39, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +- 2, 2, 2, 27, 39, 28, 46, 2, 2, 2, ++ 2, 2, 2, 26, 38, 27, 45, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +@@ -463,335 +486,292 @@ static const yytype_uint8 yytranslate[] + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, +- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 ++ 15, 16, 17, 18, 19, 20, 21, 22, 23 + }; + + #if YYDEBUG +-/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in +- YYRHS. */ +-static const yytype_uint16 yyprhs[] = +-{ +- 0, 0, 3, 9, 10, 13, 14, 17, 22, 25, +- 28, 32, 37, 41, 46, 52, 53, 56, 61, 64, +- 68, 71, 74, 78, 83, 86, 96, 102, 105, 106, +- 109, 112, 116, 118, 121, 124, 127, 129, 131, 135, +- 137, 139, 145, 147, 151, 153, 157, 159, 163, 165, +- 169, 171, 175, 177, 181, 185, 187, 191, 195, 199, +- 203, 207, 211, 213, 217, 221, 223, 227, 231, 235, +- 237, 239, 242, 245, 248, 249, 252, 255, 256, 259, +- 262, 265, 269 +-}; +- +-/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +-static const yytype_int8 yyrhs[] = +-{ +- 49, 0, -1, 3, 25, 50, 51, 53, -1, -1, +- 4, 25, -1, -1, 52, 51, -1, 5, 60, 60, +- 25, -1, 22, 52, -1, 26, 54, -1, 53, 26, +- 54, -1, 53, 22, 23, 54, -1, 53, 23, 54, +- -1, 53, 16, 23, 25, -1, 27, 55, 75, 28, +- 25, -1, -1, 55, 56, -1, 17, 29, 57, 25, +- -1, 17, 25, -1, 15, 17, 25, -1, 22, 56, +- -1, 58, 21, -1, 58, 59, 30, -1, 58, 31, +- 74, 32, -1, 58, 23, -1, 58, 24, 33, 21, +- 34, 60, 34, 60, 35, -1, 58, 24, 33, 21, +- 35, -1, 57, 22, -1, -1, 57, 34, -1, 58, +- 22, -1, 14, 18, 36, -1, 36, -1, 59, 60, +- -1, 59, 23, -1, 59, 22, -1, 18, -1, 19, +- -1, 33, 61, 35, -1, 62, -1, 63, -1, 63, +- 37, 61, 38, 62, -1, 64, -1, 63, 13, 64, +- -1, 65, -1, 64, 12, 65, -1, 66, -1, 65, +- 39, 66, -1, 67, -1, 66, 40, 67, -1, 68, +- -1, 67, 41, 68, -1, 69, -1, 68, 10, 69, +- -1, 68, 11, 69, -1, 70, -1, 69, 36, 70, +- -1, 69, 30, 70, -1, 69, 8, 70, -1, 69, +- 9, 70, -1, 70, 6, 71, -1, 70, 7, 71, +- -1, 71, -1, 71, 42, 72, -1, 71, 43, 72, +- -1, 72, -1, 72, 44, 73, -1, 72, 26, 73, +- -1, 72, 45, 73, -1, 73, -1, 60, -1, 43, +- 73, -1, 46, 73, -1, 47, 73, -1, -1, 74, +- 20, -1, 74, 22, -1, -1, 76, 75, -1, 76, +- 56, -1, 17, 54, -1, 16, 17, 25, -1, 22, +- 76, -1 +-}; +- +-/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ ++ /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ + static const yytype_uint16 yyrline[] = + { +- 0, 108, 108, 119, 122, 130, 133, 140, 144, 152, +- 156, 161, 172, 182, 197, 205, 208, 215, 219, 223, +- 227, 235, 239, 243, 247, 251, 267, 277, 285, 288, +- 292, 299, 315, 320, 339, 353, 360, 361, 362, 369, +- 373, 374, 378, 379, 383, 384, 388, 389, 393, 394, +- 398, 399, 403, 404, 405, 409, 410, 411, 412, 413, +- 417, 418, 419, 423, 424, 425, 429, 430, 431, 432, +- 436, 437, 438, 439, 444, 447, 451, 459, 462, 466, +- 474, 478, 482 ++ 0, 104, 104, 113, 116, 123, 127, 135, 139, 144, ++ 155, 165, 180, 188, 191, 198, 202, 206, 210, 218, ++ 222, 226, 230, 234, 250, 260, 268, 271, 275, 282, ++ 298, 303, 322, 336, 343, 344, 345, 352, 356, 357, ++ 361, 362, 366, 367, 371, 372, 376, 377, 381, 382, ++ 386, 387, 388, 392, 393, 394, 395, 396, 400, 401, ++ 402, 406, 407, 408, 412, 413, 414, 415, 419, 420, ++ 421, 422, 427, 430, 434, 442, 445, 449, 457, 461, ++ 465 + }; + #endif + +-#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE ++#if YYDEBUG || YYERROR_VERBOSE || 0 + /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ + static const char *const yytname[] = + { +- "$end", "error", "$undefined", "DT_V1", "DT_PLUGIN", "DT_MEMRESERVE", +- "DT_LSHIFT", "DT_RSHIFT", "DT_LE", "DT_GE", "DT_EQ", "DT_NE", "DT_AND", +- "DT_OR", "DT_BITS", "DT_DEL_PROP", "DT_DEL_NODE", "DT_PROPNODENAME", +- "DT_LITERAL", "DT_CHAR_LITERAL", "DT_BYTE", "DT_STRING", "DT_LABEL", +- "DT_REF", "DT_INCBIN", "';'", "'/'", "'{'", "'}'", "'='", "'>'", "'['", +- "']'", "'('", "','", "')'", "'<'", "'?'", "':'", "'|'", "'^'", "'&'", +- "'+'", "'-'", "'*'", "'%'", "'~'", "'!'", "$accept", "sourcefile", +- "plugindecl", "memreserves", "memreserve", "devicetree", "nodedef", +- "proplist", "propdef", "propdata", "propdataprefix", "arrayprefix", +- "integer_prim", "integer_expr", "integer_trinary", "integer_or", +- "integer_and", "integer_bitor", "integer_bitxor", "integer_bitand", +- "integer_eq", "integer_rela", "integer_shift", "integer_add", +- "integer_mul", "integer_unary", "bytestring", "subnodes", "subnode", 0 ++ "$end", "error", "$undefined", "DT_V1", "DT_MEMRESERVE", "DT_LSHIFT", ++ "DT_RSHIFT", "DT_LE", "DT_GE", "DT_EQ", "DT_NE", "DT_AND", "DT_OR", ++ "DT_BITS", "DT_DEL_PROP", "DT_DEL_NODE", "DT_PROPNODENAME", "DT_LITERAL", ++ "DT_CHAR_LITERAL", "DT_BYTE", "DT_STRING", "DT_LABEL", "DT_REF", ++ "DT_INCBIN", "';'", "'/'", "'{'", "'}'", "'='", "'>'", "'['", "']'", ++ "'('", "','", "')'", "'<'", "'?'", "':'", "'|'", "'^'", "'&'", "'+'", ++ "'-'", "'*'", "'%'", "'~'", "'!'", "$accept", "sourcefile", ++ "memreserves", "memreserve", "devicetree", "nodedef", "proplist", ++ "propdef", "propdata", "propdataprefix", "arrayprefix", "integer_prim", ++ "integer_expr", "integer_trinary", "integer_or", "integer_and", ++ "integer_bitor", "integer_bitxor", "integer_bitand", "integer_eq", ++ "integer_rela", "integer_shift", "integer_add", "integer_mul", ++ "integer_unary", "bytestring", "subnodes", "subnode", YY_NULLPTR + }; + #endif + + # ifdef YYPRINT +-/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to +- token YYLEX-NUM. */ ++/* YYTOKNUM[NUM] -- (External) token number corresponding to the ++ (internal) symbol number NUM (which must be that of a token). */ + static const yytype_uint16 yytoknum[] = + { + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, +- 275, 276, 277, 278, 279, 59, 47, 123, 125, 61, +- 62, 91, 93, 40, 44, 41, 60, 63, 58, 124, +- 94, 38, 43, 45, 42, 37, 126, 33 ++ 275, 276, 277, 278, 59, 47, 123, 125, 61, 62, ++ 91, 93, 40, 44, 41, 60, 63, 58, 124, 94, ++ 38, 43, 45, 42, 37, 126, 33 + }; + # endif + +-/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +-static const yytype_uint8 yyr1[] = +-{ +- 0, 48, 49, 50, 50, 51, 51, 52, 52, 53, +- 53, 53, 53, 53, 54, 55, 55, 56, 56, 56, +- 56, 57, 57, 57, 57, 57, 57, 57, 58, 58, +- 58, 59, 59, 59, 59, 59, 60, 60, 60, 61, +- 62, 62, 63, 63, 64, 64, 65, 65, 66, 66, +- 67, 67, 68, 68, 68, 69, 69, 69, 69, 69, +- 70, 70, 70, 71, 71, 71, 72, 72, 72, 72, +- 73, 73, 73, 73, 74, 74, 74, 75, 75, 75, +- 76, 76, 76 +-}; ++#define YYPACT_NINF -81 + +-/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +-static const yytype_uint8 yyr2[] = ++#define yypact_value_is_default(Yystate) \ ++ (!!((Yystate) == (-81))) ++ ++#define YYTABLE_NINF -1 ++ ++#define yytable_value_is_error(Yytable_value) \ ++ 0 ++ ++ /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing ++ STATE-NUM. */ ++static const yytype_int8 yypact[] = + { +- 0, 2, 5, 0, 2, 0, 2, 4, 2, 2, +- 3, 4, 3, 4, 5, 0, 2, 4, 2, 3, +- 2, 2, 3, 4, 2, 9, 5, 2, 0, 2, +- 2, 3, 1, 2, 2, 2, 1, 1, 3, 1, +- 1, 5, 1, 3, 1, 3, 1, 3, 1, 3, +- 1, 3, 1, 3, 3, 1, 3, 3, 3, 3, +- 3, 3, 1, 3, 3, 1, 3, 3, 3, 1, +- 1, 2, 2, 2, 0, 2, 2, 0, 2, 2, +- 2, 3, 2 ++ 16, -11, 21, 10, -81, 25, 10, 19, 10, -81, ++ -81, -9, 25, -81, 2, 51, -81, -9, -9, -9, ++ -81, 1, -81, -6, 50, 14, 28, 29, 36, 3, ++ 58, 44, -3, -81, 47, -81, -81, 65, 68, 2, ++ 2, -81, -81, -81, -81, -9, -9, -9, -9, -9, ++ -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, ++ -9, -9, -9, -9, -81, 63, 69, 2, -81, -81, ++ 50, 57, 14, 28, 29, 36, 3, 3, 58, 58, ++ 58, 58, 44, 44, -3, -3, -81, -81, -81, 79, ++ 80, -8, 63, -81, 72, 63, -81, -81, -9, 76, ++ 77, -81, -81, -81, -81, -81, 78, -81, -81, -81, ++ -81, -81, 35, 4, -81, -81, -81, -81, 86, -81, ++ -81, -81, 73, -81, -81, 33, 71, 84, 39, -81, ++ -81, -81, -81, -81, 41, -81, -81, -81, 25, -81, ++ 74, 25, 75, -81 + }; + +-/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. +- Performed when YYTABLE doesn't specify something else to do. Zero +- means the default is an error. */ ++ /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. ++ Performed when YYTABLE does not specify something else to do. Zero ++ means the default is an error. */ + static const yytype_uint8 yydefact[] = + { +- 0, 0, 0, 3, 1, 0, 5, 4, 0, 0, +- 0, 5, 36, 37, 0, 0, 8, 0, 2, 6, +- 0, 0, 0, 70, 0, 39, 40, 42, 44, 46, +- 48, 50, 52, 55, 62, 65, 69, 0, 15, 9, +- 0, 0, 0, 0, 71, 72, 73, 38, 0, 0, ++ 0, 0, 0, 3, 1, 0, 0, 0, 3, 34, ++ 35, 0, 0, 6, 0, 2, 4, 0, 0, 0, ++ 68, 0, 37, 38, 40, 42, 44, 46, 48, 50, ++ 53, 60, 63, 67, 0, 13, 7, 0, 0, 0, ++ 0, 69, 70, 71, 36, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 7, 77, 0, +- 0, 12, 10, 43, 0, 45, 47, 49, 51, 53, +- 54, 58, 59, 57, 56, 60, 61, 63, 64, 67, +- 66, 68, 0, 0, 0, 0, 16, 0, 77, 13, +- 11, 0, 0, 0, 18, 28, 80, 20, 82, 0, +- 79, 78, 41, 19, 81, 0, 0, 14, 27, 17, +- 29, 0, 21, 30, 24, 0, 74, 32, 0, 0, +- 0, 0, 35, 34, 22, 33, 31, 0, 75, 76, +- 23, 0, 26, 0, 0, 0, 25 ++ 0, 0, 0, 0, 5, 75, 0, 0, 10, 8, ++ 41, 0, 43, 45, 47, 49, 51, 52, 56, 57, ++ 55, 54, 58, 59, 61, 62, 65, 64, 66, 0, ++ 0, 0, 0, 14, 0, 75, 11, 9, 0, 0, ++ 0, 16, 26, 78, 18, 80, 0, 77, 76, 39, ++ 17, 79, 0, 0, 12, 25, 15, 27, 0, 19, ++ 28, 22, 0, 72, 30, 0, 0, 0, 0, 33, ++ 32, 20, 31, 29, 0, 73, 74, 21, 0, 24, ++ 0, 0, 0, 23 + }; + +-/* YYDEFGOTO[NTERM-NUM]. */ +-static const yytype_int16 yydefgoto[] = ++ /* YYPGOTO[NTERM-NUM]. */ ++static const yytype_int8 yypgoto[] = + { +- -1, 2, 6, 10, 11, 18, 39, 68, 96, 115, +- 116, 128, 23, 24, 25, 26, 27, 28, 29, 30, +- 31, 32, 33, 34, 35, 36, 131, 97, 98 ++ -81, -81, 100, 104, -81, -38, -81, -80, -81, -81, ++ -81, -5, 66, 13, -81, 70, 67, 81, 64, 82, ++ 37, 27, 34, 38, -14, -81, 22, 24 + }; + +-/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing +- STATE-NUM. */ +-#define YYPACT_NINF -84 +-static const yytype_int8 yypact[] = ++ /* YYDEFGOTO[NTERM-NUM]. */ ++static const yytype_int16 yydefgoto[] = + { +- 15, -12, 35, 42, -84, 27, 9, -84, 24, 9, +- 43, 9, -84, -84, -10, 24, -84, 60, 44, -84, +- -10, -10, -10, -84, 55, -84, -7, 52, 53, 51, +- 54, 10, 2, 38, 37, -4, -84, 68, -84, -84, +- 71, 73, 60, 60, -84, -84, -84, -84, -10, -10, +- -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, +- -10, -10, -10, -10, -10, -10, -10, -84, 56, 72, +- 60, -84, -84, 52, 61, 53, 51, 54, 10, 2, +- 2, 38, 38, 38, 38, 37, 37, -4, -4, -84, +- -84, -84, 81, 83, 34, 56, -84, 74, 56, -84, +- -84, -10, 76, 78, -84, -84, -84, -84, -84, 79, +- -84, -84, -84, -84, -84, -6, 3, -84, -84, -84, +- -84, 87, -84, -84, -84, 75, -84, -84, 32, 70, +- 86, 36, -84, -84, -84, -84, -84, 47, -84, -84, +- -84, 24, -84, 77, 24, 80, -84 ++ -1, 2, 7, 8, 15, 36, 65, 93, 112, 113, ++ 125, 20, 21, 22, 23, 24, 25, 26, 27, 28, ++ 29, 30, 31, 32, 33, 128, 94, 95 + }; + +-/* YYPGOTO[NTERM-NUM]. */ +-static const yytype_int8 yypgoto[] = ++ /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If ++ positive, shift that token. If negative, reduce the rule whose ++ number is the opposite. If YYTABLE_NINF, syntax error. */ ++static const yytype_uint8 yytable[] = + { +- -84, -84, -84, 98, 101, -84, -41, -84, -83, -84, +- -84, -84, -8, 63, 12, -84, 66, 67, 65, 69, +- 82, 29, 18, 25, 26, -17, -84, 20, 28 ++ 12, 68, 69, 41, 42, 43, 45, 34, 9, 10, ++ 53, 54, 104, 3, 5, 107, 101, 118, 35, 1, ++ 102, 4, 61, 11, 119, 120, 121, 122, 35, 97, ++ 46, 6, 55, 17, 123, 44, 18, 19, 56, 124, ++ 62, 63, 9, 10, 14, 51, 52, 86, 87, 88, ++ 9, 10, 48, 103, 129, 130, 115, 11, 135, 116, ++ 136, 47, 131, 57, 58, 11, 37, 49, 117, 50, ++ 137, 64, 38, 39, 138, 139, 40, 89, 90, 91, ++ 78, 79, 80, 81, 92, 59, 60, 66, 76, 77, ++ 67, 82, 83, 96, 98, 99, 100, 84, 85, 106, ++ 110, 111, 114, 126, 134, 127, 133, 141, 16, 143, ++ 13, 109, 71, 74, 72, 70, 105, 108, 0, 0, ++ 132, 0, 0, 0, 0, 0, 0, 0, 0, 73, ++ 0, 0, 75, 140, 0, 0, 142 + }; + +-/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If +- positive, shift that token. If negative, reduce the rule which +- number is the opposite. If YYTABLE_NINF, syntax error. */ +-#define YYTABLE_NINF -1 +-static const yytype_uint8 yytable[] = ++static const yytype_int16 yycheck[] = + { +- 15, 71, 72, 44, 45, 46, 48, 37, 12, 13, +- 56, 57, 107, 3, 8, 110, 118, 121, 1, 119, +- 54, 55, 64, 14, 122, 123, 124, 125, 120, 100, +- 49, 9, 58, 20, 126, 4, 21, 22, 59, 127, +- 65, 66, 12, 13, 60, 61, 5, 89, 90, 91, +- 12, 13, 7, 106, 132, 133, 138, 14, 139, 104, +- 40, 38, 134, 105, 50, 14, 41, 42, 140, 17, +- 43, 92, 93, 94, 81, 82, 83, 84, 95, 62, +- 63, 141, 142, 79, 80, 85, 86, 38, 87, 88, +- 47, 52, 51, 67, 69, 53, 70, 99, 102, 101, +- 103, 113, 109, 114, 117, 129, 136, 137, 130, 19, +- 16, 144, 74, 112, 73, 146, 76, 75, 111, 0, +- 135, 77, 0, 108, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 143, 0, 78, 145 ++ 5, 39, 40, 17, 18, 19, 12, 12, 17, 18, ++ 7, 8, 92, 24, 4, 95, 24, 13, 26, 3, ++ 28, 0, 25, 32, 20, 21, 22, 23, 26, 67, ++ 36, 21, 29, 42, 30, 34, 45, 46, 35, 35, ++ 43, 44, 17, 18, 25, 9, 10, 61, 62, 63, ++ 17, 18, 38, 91, 21, 22, 21, 32, 19, 24, ++ 21, 11, 29, 5, 6, 32, 15, 39, 33, 40, ++ 31, 24, 21, 22, 33, 34, 25, 14, 15, 16, ++ 53, 54, 55, 56, 21, 41, 42, 22, 51, 52, ++ 22, 57, 58, 24, 37, 16, 16, 59, 60, 27, ++ 24, 24, 24, 17, 20, 32, 35, 33, 8, 34, ++ 6, 98, 46, 49, 47, 45, 92, 95, -1, -1, ++ 125, -1, -1, -1, -1, -1, -1, -1, -1, 48, ++ -1, -1, 50, 138, -1, -1, 141 + }; + +-#define yypact_value_is_default(yystate) \ +- ((yystate) == (-84)) +- +-#define yytable_value_is_error(yytable_value) \ +- YYID (0) ++ /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing ++ symbol of state STATE-NUM. */ ++static const yytype_uint8 yystos[] = ++{ ++ 0, 3, 48, 24, 0, 4, 21, 49, 50, 17, ++ 18, 32, 58, 50, 25, 51, 49, 42, 45, 46, ++ 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, ++ 68, 69, 70, 71, 58, 26, 52, 15, 21, 22, ++ 25, 71, 71, 71, 34, 12, 36, 11, 38, 39, ++ 40, 9, 10, 7, 8, 29, 35, 5, 6, 41, ++ 42, 25, 43, 44, 24, 53, 22, 22, 52, 52, ++ 62, 59, 63, 64, 65, 66, 67, 67, 68, 68, ++ 68, 68, 69, 69, 70, 70, 71, 71, 71, 14, ++ 15, 16, 21, 54, 73, 74, 24, 52, 37, 16, ++ 16, 24, 28, 52, 54, 74, 27, 54, 73, 60, ++ 24, 24, 55, 56, 24, 21, 24, 33, 13, 20, ++ 21, 22, 23, 30, 35, 57, 17, 32, 72, 21, ++ 22, 29, 58, 35, 20, 19, 21, 31, 33, 34, ++ 58, 33, 58, 34 ++}; + +-static const yytype_int16 yycheck[] = ++ /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ ++static const yytype_uint8 yyr1[] = + { +- 8, 42, 43, 20, 21, 22, 13, 15, 18, 19, +- 8, 9, 95, 25, 5, 98, 22, 14, 3, 25, +- 10, 11, 26, 33, 21, 22, 23, 24, 34, 70, +- 37, 22, 30, 43, 31, 0, 46, 47, 36, 36, +- 44, 45, 18, 19, 6, 7, 4, 64, 65, 66, +- 18, 19, 25, 94, 22, 23, 20, 33, 22, 25, +- 16, 27, 30, 29, 12, 33, 22, 23, 32, 26, +- 26, 15, 16, 17, 56, 57, 58, 59, 22, 42, +- 43, 34, 35, 54, 55, 60, 61, 27, 62, 63, +- 35, 40, 39, 25, 23, 41, 23, 25, 17, 38, +- 17, 25, 28, 25, 25, 18, 36, 21, 33, 11, +- 9, 34, 49, 101, 48, 35, 51, 50, 98, -1, +- 128, 52, -1, 95, -1, -1, -1, -1, -1, -1, +- -1, -1, -1, 141, -1, 53, 144 ++ 0, 47, 48, 49, 49, 50, 50, 51, 51, 51, ++ 51, 51, 52, 53, 53, 54, 54, 54, 54, 55, ++ 55, 55, 55, 55, 55, 55, 56, 56, 56, 57, ++ 57, 57, 57, 57, 58, 58, 58, 59, 60, 60, ++ 61, 61, 62, 62, 63, 63, 64, 64, 65, 65, ++ 66, 66, 66, 67, 67, 67, 67, 67, 68, 68, ++ 68, 69, 69, 69, 70, 70, 70, 70, 71, 71, ++ 71, 71, 72, 72, 72, 73, 73, 73, 74, 74, ++ 74 + }; + +-/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing +- symbol of state STATE-NUM. */ +-static const yytype_uint8 yystos[] = ++ /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ ++static const yytype_uint8 yyr2[] = + { +- 0, 3, 49, 25, 0, 4, 50, 25, 5, 22, +- 51, 52, 18, 19, 33, 60, 52, 26, 53, 51, +- 43, 46, 47, 60, 61, 62, 63, 64, 65, 66, +- 67, 68, 69, 70, 71, 72, 73, 60, 27, 54, +- 16, 22, 23, 26, 73, 73, 73, 35, 13, 37, +- 12, 39, 40, 41, 10, 11, 8, 9, 30, 36, +- 6, 7, 42, 43, 26, 44, 45, 25, 55, 23, +- 23, 54, 54, 64, 61, 65, 66, 67, 68, 69, +- 69, 70, 70, 70, 70, 71, 71, 72, 72, 73, +- 73, 73, 15, 16, 17, 22, 56, 75, 76, 25, +- 54, 38, 17, 17, 25, 29, 54, 56, 76, 28, +- 56, 75, 62, 25, 25, 57, 58, 25, 22, 25, +- 34, 14, 21, 22, 23, 24, 31, 36, 59, 18, +- 33, 74, 22, 23, 30, 60, 36, 21, 20, 22, +- 32, 34, 35, 60, 34, 60, 35 ++ 0, 2, 4, 0, 2, 4, 2, 2, 3, 4, ++ 3, 4, 5, 0, 2, 4, 2, 3, 2, 2, ++ 3, 4, 2, 9, 5, 2, 0, 2, 2, 3, ++ 1, 2, 2, 2, 1, 1, 3, 1, 1, 5, ++ 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, ++ 1, 3, 3, 1, 3, 3, 3, 3, 3, 3, ++ 1, 3, 3, 1, 3, 3, 3, 1, 1, 2, ++ 2, 2, 0, 2, 2, 0, 2, 2, 2, 3, ++ 2 + }; + +-#define yyerrok (yyerrstatus = 0) +-#define yyclearin (yychar = YYEMPTY) +-#define YYEMPTY (-2) +-#define YYEOF 0 +- +-#define YYACCEPT goto yyacceptlab +-#define YYABORT goto yyabortlab +-#define YYERROR goto yyerrorlab +- +- +-/* Like YYERROR except do call yyerror. This remains here temporarily +- to ease the transition to the new meaning of YYERROR, for GCC. +- Once GCC version 2 has supplanted version 1, this can go. However, +- YYFAIL appears to be in use. Nevertheless, it is formally deprecated +- in Bison 2.4.2's NEWS entry, where a plan to phase it out is +- discussed. */ +- +-#define YYFAIL goto yyerrlab +-#if defined YYFAIL +- /* This is here to suppress warnings from the GCC cpp's +- -Wunused-macros. Normally we don't worry about that warning, but +- some users do, and we want to make it easy for users to remove +- YYFAIL uses, which will produce warnings from Bison 2.5. */ +-#endif + +-#define YYRECOVERING() (!!yyerrstatus) ++#define yyerrok (yyerrstatus = 0) ++#define yyclearin (yychar = YYEMPTY) ++#define YYEMPTY (-2) ++#define YYEOF 0 ++ ++#define YYACCEPT goto yyacceptlab ++#define YYABORT goto yyabortlab ++#define YYERROR goto yyerrorlab + +-#define YYBACKUP(Token, Value) \ +-do \ +- if (yychar == YYEMPTY && yylen == 1) \ +- { \ +- yychar = (Token); \ +- yylval = (Value); \ +- YYPOPSTACK (1); \ +- goto yybackup; \ +- } \ +- else \ +- { \ +- yyerror (YY_("syntax error: cannot back up")); \ +- YYERROR; \ +- } \ +-while (YYID (0)) + ++#define YYRECOVERING() (!!yyerrstatus) + +-#define YYTERROR 1 +-#define YYERRCODE 256 ++#define YYBACKUP(Token, Value) \ ++do \ ++ if (yychar == YYEMPTY) \ ++ { \ ++ yychar = (Token); \ ++ yylval = (Value); \ ++ YYPOPSTACK (yylen); \ ++ yystate = *yyssp; \ ++ goto yybackup; \ ++ } \ ++ else \ ++ { \ ++ yyerror (YY_("syntax error: cannot back up")); \ ++ YYERROR; \ ++ } \ ++while (0) ++ ++/* Error token number */ ++#define YYTERROR 1 ++#define YYERRCODE 256 + + + /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +-#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + #ifndef YYLLOC_DEFAULT +-# define YYLLOC_DEFAULT(Current, Rhs, N) \ +- do \ +- if (YYID (N)) \ +- { \ +- (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ +- (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ +- (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ +- (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ +- } \ +- else \ +- { \ +- (Current).first_line = (Current).last_line = \ +- YYRHSLOC (Rhs, 0).last_line; \ +- (Current).first_column = (Current).last_column = \ +- YYRHSLOC (Rhs, 0).last_column; \ +- } \ +- while (YYID (0)) ++# define YYLLOC_DEFAULT(Current, Rhs, N) \ ++ do \ ++ if (N) \ ++ { \ ++ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ ++ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ ++ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ ++ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ ++ } \ ++ else \ ++ { \ ++ (Current).first_line = (Current).last_line = \ ++ YYRHSLOC (Rhs, 0).last_line; \ ++ (Current).first_column = (Current).last_column = \ ++ YYRHSLOC (Rhs, 0).last_column; \ ++ } \ ++ while (0) + #endif + ++#define YYRHSLOC(Rhs, K) ((Rhs)[K]) ++ ++ ++/* Enable debugging if requested. */ ++#if YYDEBUG ++ ++# ifndef YYFPRINTF ++# include /* INFRINGES ON USER NAME SPACE */ ++# define YYFPRINTF fprintf ++# endif ++ ++# define YYDPRINTF(Args) \ ++do { \ ++ if (yydebug) \ ++ YYFPRINTF Args; \ ++} while (0) ++ + + /* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know +@@ -799,82 +779,73 @@ while (YYID (0)) + + #ifndef YY_LOCATION_PRINT + # if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +-# define YY_LOCATION_PRINT(File, Loc) \ +- fprintf (File, "%d.%d-%d.%d", \ +- (Loc).first_line, (Loc).first_column, \ +- (Loc).last_line, (Loc).last_column) +-# else +-# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +-# endif +-#endif +- + +-/* YYLEX -- calling `yylex' with the right arguments. */ ++/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +-#ifdef YYLEX_PARAM +-# define YYLEX yylex (YYLEX_PARAM) +-#else +-# define YYLEX yylex () +-#endif ++YY_ATTRIBUTE_UNUSED ++static unsigned ++yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) ++{ ++ unsigned res = 0; ++ int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; ++ if (0 <= yylocp->first_line) ++ { ++ res += YYFPRINTF (yyo, "%d", yylocp->first_line); ++ if (0 <= yylocp->first_column) ++ res += YYFPRINTF (yyo, ".%d", yylocp->first_column); ++ } ++ if (0 <= yylocp->last_line) ++ { ++ if (yylocp->first_line < yylocp->last_line) ++ { ++ res += YYFPRINTF (yyo, "-%d", yylocp->last_line); ++ if (0 <= end_col) ++ res += YYFPRINTF (yyo, ".%d", end_col); ++ } ++ else if (0 <= end_col && yylocp->first_column < end_col) ++ res += YYFPRINTF (yyo, "-%d", end_col); ++ } ++ return res; ++ } + +-/* Enable debugging if requested. */ +-#if YYDEBUG ++# define YY_LOCATION_PRINT(File, Loc) \ ++ yy_location_print_ (File, &(Loc)) + +-# ifndef YYFPRINTF +-# include /* INFRINGES ON USER NAME SPACE */ +-# define YYFPRINTF fprintf ++# else ++# define YY_LOCATION_PRINT(File, Loc) ((void) 0) + # endif ++#endif + +-# define YYDPRINTF(Args) \ +-do { \ +- if (yydebug) \ +- YYFPRINTF Args; \ +-} while (YYID (0)) +- +-# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +-do { \ +- if (yydebug) \ +- { \ +- YYFPRINTF (stderr, "%s ", Title); \ +- yy_symbol_print (stderr, \ +- Type, Value, Location); \ +- YYFPRINTF (stderr, "\n"); \ +- } \ +-} while (YYID (0)) + ++# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ ++do { \ ++ if (yydebug) \ ++ { \ ++ YYFPRINTF (stderr, "%s ", Title); \ ++ yy_symbol_print (stderr, \ ++ Type, Value, Location); \ ++ YYFPRINTF (stderr, "\n"); \ ++ } \ ++} while (0) + +-/*--------------------------------. +-| Print this symbol on YYOUTPUT. | +-`--------------------------------*/ + +-/*ARGSUSED*/ +-#if (defined __STDC__ || defined __C99__FUNC__ \ +- || defined __cplusplus || defined _MSC_VER) ++/*----------------------------------------. ++| Print this symbol's value on YYOUTPUT. | ++`----------------------------------------*/ ++ + static void + yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp) +-#else +-static void +-yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp) +- FILE *yyoutput; +- int yytype; +- YYSTYPE const * const yyvaluep; +- YYLTYPE const * const yylocationp; +-#endif + { ++ FILE *yyo = yyoutput; ++ YYUSE (yyo); ++ YYUSE (yylocationp); + if (!yyvaluep) + return; +- YYUSE (yylocationp); + # ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +-# else +- YYUSE (yyoutput); + # endif +- switch (yytype) +- { +- default: +- break; +- } ++ YYUSE (yytype); + } + + +@@ -882,23 +853,11 @@ yy_symbol_value_print (yyoutput, yytype, + | Print this symbol on YYOUTPUT. | + `--------------------------------*/ + +-#if (defined __STDC__ || defined __C99__FUNC__ \ +- || defined __cplusplus || defined _MSC_VER) + static void + yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp) +-#else +-static void +-yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp) +- FILE *yyoutput; +- int yytype; +- YYSTYPE const * const yyvaluep; +- YYLTYPE const * const yylocationp; +-#endif + { +- if (yytype < YYNTOKENS) +- YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); +- else +- YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); ++ YYFPRINTF (yyoutput, "%s %s (", ++ yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + YY_LOCATION_PRINT (yyoutput, *yylocationp); + YYFPRINTF (yyoutput, ": "); +@@ -911,16 +870,8 @@ yy_symbol_print (yyoutput, yytype, yyval + | TOP (included). | + `------------------------------------------------------------------*/ + +-#if (defined __STDC__ || defined __C99__FUNC__ \ +- || defined __cplusplus || defined _MSC_VER) + static void + yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +-#else +-static void +-yy_stack_print (yybottom, yytop) +- yytype_int16 *yybottom; +- yytype_int16 *yytop; +-#endif + { + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) +@@ -931,50 +882,42 @@ yy_stack_print (yybottom, yytop) + YYFPRINTF (stderr, "\n"); + } + +-# define YY_STACK_PRINT(Bottom, Top) \ +-do { \ +- if (yydebug) \ +- yy_stack_print ((Bottom), (Top)); \ +-} while (YYID (0)) ++# define YY_STACK_PRINT(Bottom, Top) \ ++do { \ ++ if (yydebug) \ ++ yy_stack_print ((Bottom), (Top)); \ ++} while (0) + + + /*------------------------------------------------. + | Report that the YYRULE is going to be reduced. | + `------------------------------------------------*/ + +-#if (defined __STDC__ || defined __C99__FUNC__ \ +- || defined __cplusplus || defined _MSC_VER) +-static void +-yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule) +-#else + static void +-yy_reduce_print (yyvsp, yylsp, yyrule) +- YYSTYPE *yyvsp; +- YYLTYPE *yylsp; +- int yyrule; +-#endif ++yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule) + { ++ unsigned long int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; +- unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", +- yyrule - 1, yylno); ++ yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); +- yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], +- &(yyvsp[(yyi + 1) - (yynrhs)]) +- , &(yylsp[(yyi + 1) - (yynrhs)]) ); ++ yy_symbol_print (stderr, ++ yystos[yyssp[yyi + 1 - yynrhs]], ++ &(yyvsp[(yyi + 1) - (yynrhs)]) ++ , &(yylsp[(yyi + 1) - (yynrhs)]) ); + YYFPRINTF (stderr, "\n"); + } + } + +-# define YY_REDUCE_PRINT(Rule) \ +-do { \ +- if (yydebug) \ +- yy_reduce_print (yyvsp, yylsp, Rule); \ +-} while (YYID (0)) ++# define YY_REDUCE_PRINT(Rule) \ ++do { \ ++ if (yydebug) \ ++ yy_reduce_print (yyssp, yyvsp, yylsp, Rule); \ ++} while (0) + + /* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +@@ -988,7 +931,7 @@ int yydebug; + + + /* YYINITDEPTH -- initial size of the parser's stacks. */ +-#ifndef YYINITDEPTH ++#ifndef YYINITDEPTH + # define YYINITDEPTH 200 + #endif + +@@ -1011,15 +954,8 @@ int yydebug; + # define yystrlen strlen + # else + /* Return the length of YYSTR. */ +-#if (defined __STDC__ || defined __C99__FUNC__ \ +- || defined __cplusplus || defined _MSC_VER) + static YYSIZE_T + yystrlen (const char *yystr) +-#else +-static YYSIZE_T +-yystrlen (yystr) +- const char *yystr; +-#endif + { + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) +@@ -1035,16 +971,8 @@ yystrlen (yystr) + # else + /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +-#if (defined __STDC__ || defined __C99__FUNC__ \ +- || defined __cplusplus || defined _MSC_VER) + static char * + yystpcpy (char *yydest, const char *yysrc) +-#else +-static char * +-yystpcpy (yydest, yysrc) +- char *yydest; +- const char *yysrc; +-#endif + { + char *yyd = yydest; + const char *yys = yysrc; +@@ -1074,27 +1002,27 @@ yytnamerr (char *yyres, const char *yyst + char const *yyp = yystr; + + for (;;) +- switch (*++yyp) +- { +- case '\'': +- case ',': +- goto do_not_strip_quotes; +- +- case '\\': +- if (*++yyp != '\\') +- goto do_not_strip_quotes; +- /* Fall through. */ +- default: +- if (yyres) +- yyres[yyn] = *yyp; +- yyn++; +- break; +- +- case '"': +- if (yyres) +- yyres[yyn] = '\0'; +- return yyn; +- } ++ switch (*++yyp) ++ { ++ case '\'': ++ case ',': ++ goto do_not_strip_quotes; ++ ++ case '\\': ++ if (*++yyp != '\\') ++ goto do_not_strip_quotes; ++ /* Fall through. */ ++ default: ++ if (yyres) ++ yyres[yyn] = *yyp; ++ yyn++; ++ break; ++ ++ case '"': ++ if (yyres) ++ yyres[yyn] = '\0'; ++ return yyn; ++ } + do_not_strip_quotes: ; + } + +@@ -1117,12 +1045,11 @@ static int + yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) + { +- YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]); ++ YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + YYSIZE_T yysize = yysize0; +- YYSIZE_T yysize1; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ +- const char *yyformat = 0; ++ const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per +@@ -1130,10 +1057,6 @@ yysyntax_error (YYSIZE_T *yymsg_alloc, c + int yycount = 0; + + /* There are many possibilities here to consider: +- - Assume YYFAIL is not used. It's too flawed to consider. See +- +- for details. YYERROR is fine as it does not invoke this +- function. + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected +@@ -1182,11 +1105,13 @@ yysyntax_error (YYSIZE_T *yymsg_alloc, c + break; + } + yyarg[yycount++] = yytname[yyx]; +- yysize1 = yysize + yytnamerr (0, yytname[yyx]); +- if (! (yysize <= yysize1 +- && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) +- return 2; +- yysize = yysize1; ++ { ++ YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); ++ if (! (yysize <= yysize1 ++ && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) ++ return 2; ++ yysize = yysize1; ++ } + } + } + } +@@ -1206,10 +1131,12 @@ yysyntax_error (YYSIZE_T *yymsg_alloc, c + # undef YYCASE_ + } + +- yysize1 = yysize + yystrlen (yyformat); +- if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) +- return 2; +- yysize = yysize1; ++ { ++ YYSIZE_T yysize1 = yysize + yystrlen (yyformat); ++ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) ++ return 2; ++ yysize = yysize1; ++ } + + if (*yymsg_alloc < yysize) + { +@@ -1246,50 +1173,21 @@ yysyntax_error (YYSIZE_T *yymsg_alloc, c + | Release the memory associated to this symbol. | + `-----------------------------------------------*/ + +-/*ARGSUSED*/ +-#if (defined __STDC__ || defined __C99__FUNC__ \ +- || defined __cplusplus || defined _MSC_VER) + static void + yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp) +-#else +-static void +-yydestruct (yymsg, yytype, yyvaluep, yylocationp) +- const char *yymsg; +- int yytype; +- YYSTYPE *yyvaluep; +- YYLTYPE *yylocationp; +-#endif + { + YYUSE (yyvaluep); + YYUSE (yylocationp); +- + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + +- switch (yytype) +- { +- +- default: +- break; +- } ++ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN ++ YYUSE (yytype); ++ YY_IGNORE_MAYBE_UNINITIALIZED_END + } + + +-/* Prevent warnings from -Wmissing-prototypes. */ +-#ifdef YYPARSE_PARAM +-#if defined __STDC__ || defined __cplusplus +-int yyparse (void *YYPARSE_PARAM); +-#else +-int yyparse (); +-#endif +-#else /* ! YYPARSE_PARAM */ +-#if defined __STDC__ || defined __cplusplus +-int yyparse (void); +-#else +-int yyparse (); +-#endif +-#endif /* ! YYPARSE_PARAM */ + + + /* The lookahead symbol. */ +@@ -1297,10 +1195,12 @@ int yychar; + + /* The semantic value of the lookahead symbol. */ + YYSTYPE yylval; +- + /* Location data for the lookahead symbol. */ +-YYLTYPE yylloc; +- ++YYLTYPE yylloc ++# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL ++ = { 1, 1, 1, 1 } ++# endif ++; + /* Number of syntax errors so far. */ + int yynerrs; + +@@ -1309,38 +1209,19 @@ int yynerrs; + | yyparse. | + `----------*/ + +-#ifdef YYPARSE_PARAM +-#if (defined __STDC__ || defined __C99__FUNC__ \ +- || defined __cplusplus || defined _MSC_VER) +-int +-yyparse (void *YYPARSE_PARAM) +-#else +-int +-yyparse (YYPARSE_PARAM) +- void *YYPARSE_PARAM; +-#endif +-#else /* ! YYPARSE_PARAM */ +-#if (defined __STDC__ || defined __C99__FUNC__ \ +- || defined __cplusplus || defined _MSC_VER) + int + yyparse (void) +-#else +-int +-yyparse () +- +-#endif +-#endif + { + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: +- `yyss': related to states. +- `yyvs': related to semantic values. +- `yyls': related to locations. ++ 'yyss': related to states. ++ 'yyvs': related to semantic values. ++ 'yyls': related to locations. + +- Refer to the stacks thru separate pointers, to allow yyoverflow ++ Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ +@@ -1366,7 +1247,7 @@ yyparse () + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ +- int yytoken; ++ int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; +@@ -1385,10 +1266,9 @@ yyparse () + Keep to zero when no symbol should be popped. */ + int yylen = 0; + +- yytoken = 0; +- yyss = yyssa; +- yyvs = yyvsa; +- yyls = yylsa; ++ yyssp = yyss = yyssa; ++ yyvsp = yyvs = yyvsa; ++ yylsp = yyls = yylsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); +@@ -1397,21 +1277,7 @@ yyparse () + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ +- +- /* Initialize stack pointers. +- Waste one element of value and location stack +- so that they stay on the same level as the state stack. +- The wasted elements are never initialized. */ +- yyssp = yyss; +- yyvsp = yyvs; +- yylsp = yyls; +- +-#if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +- /* Initialize the default location before parsing starts. */ +- yylloc.first_line = yylloc.last_line = 1; +- yylloc.first_column = yylloc.last_column = 1; +-#endif +- ++ yylsp[0] = yylloc; + goto yysetstate; + + /*------------------------------------------------------------. +@@ -1432,26 +1298,26 @@ yyparse () + + #ifdef yyoverflow + { +- /* Give user a chance to reallocate the stack. Use copies of +- these so that the &'s don't force the real ones into +- memory. */ +- YYSTYPE *yyvs1 = yyvs; +- yytype_int16 *yyss1 = yyss; +- YYLTYPE *yyls1 = yyls; +- +- /* Each stack pointer address is followed by the size of the +- data in use in that stack, in bytes. This used to be a +- conditional around just the two extra args, but that might +- be undefined if yyoverflow is a macro. */ +- yyoverflow (YY_("memory exhausted"), +- &yyss1, yysize * sizeof (*yyssp), +- &yyvs1, yysize * sizeof (*yyvsp), +- &yyls1, yysize * sizeof (*yylsp), +- &yystacksize); +- +- yyls = yyls1; +- yyss = yyss1; +- yyvs = yyvs1; ++ /* Give user a chance to reallocate the stack. Use copies of ++ these so that the &'s don't force the real ones into ++ memory. */ ++ YYSTYPE *yyvs1 = yyvs; ++ yytype_int16 *yyss1 = yyss; ++ YYLTYPE *yyls1 = yyls; ++ ++ /* Each stack pointer address is followed by the size of the ++ data in use in that stack, in bytes. This used to be a ++ conditional around just the two extra args, but that might ++ be undefined if yyoverflow is a macro. */ ++ yyoverflow (YY_("memory exhausted"), ++ &yyss1, yysize * sizeof (*yyssp), ++ &yyvs1, yysize * sizeof (*yyvsp), ++ &yyls1, yysize * sizeof (*yylsp), ++ &yystacksize); ++ ++ yyls = yyls1; ++ yyss = yyss1; ++ yyvs = yyvs1; + } + #else /* no yyoverflow */ + # ifndef YYSTACK_RELOCATE +@@ -1459,23 +1325,23 @@ yyparse () + # else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) +- goto yyexhaustedlab; ++ goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) +- yystacksize = YYMAXDEPTH; ++ yystacksize = YYMAXDEPTH; + + { +- yytype_int16 *yyss1 = yyss; +- union yyalloc *yyptr = +- (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); +- if (! yyptr) +- goto yyexhaustedlab; +- YYSTACK_RELOCATE (yyss_alloc, yyss); +- YYSTACK_RELOCATE (yyvs_alloc, yyvs); +- YYSTACK_RELOCATE (yyls_alloc, yyls); ++ yytype_int16 *yyss1 = yyss; ++ union yyalloc *yyptr = ++ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); ++ if (! yyptr) ++ goto yyexhaustedlab; ++ YYSTACK_RELOCATE (yyss_alloc, yyss); ++ YYSTACK_RELOCATE (yyvs_alloc, yyvs); ++ YYSTACK_RELOCATE (yyls_alloc, yyls); + # undef YYSTACK_RELOCATE +- if (yyss1 != yyssa) +- YYSTACK_FREE (yyss1); ++ if (yyss1 != yyssa) ++ YYSTACK_FREE (yyss1); + } + # endif + #endif /* no yyoverflow */ +@@ -1485,10 +1351,10 @@ yyparse () + yylsp = yyls + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", +- (unsigned long int) yystacksize)); ++ (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) +- YYABORT; ++ YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); +@@ -1517,7 +1383,7 @@ yybackup: + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); +- yychar = YYLEX; ++ yychar = yylex (); + } + + if (yychar <= YYEOF) +@@ -1557,7 +1423,9 @@ yybackup: + yychar = YYEMPTY; + + yystate = yyn; ++ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; ++ YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + goto yynewstate; + +@@ -1580,7 +1448,7 @@ yyreduce: + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: +- `$$ = $1'. ++ '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison +@@ -1595,322 +1463,273 @@ yyreduce: + switch (yyn) + { + case 2: +- +-/* Line 1806 of yacc.c */ +-#line 109 "dtc-parser.y" ++#line 105 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyvsp[(5) - (5)].node)->is_plugin = (yyvsp[(3) - (5)].is_plugin); +- (yyvsp[(5) - (5)].node)->is_root = 1; +- the_boot_info = build_boot_info((yyvsp[(4) - (5)].re), (yyvsp[(5) - (5)].node), +- guess_boot_cpuid((yyvsp[(5) - (5)].node))); ++ the_boot_info = build_boot_info((yyvsp[-1].re), (yyvsp[0].node), ++ guess_boot_cpuid((yyvsp[0].node))); + } ++#line 1472 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 3: +- +-/* Line 1806 of yacc.c */ +-#line 119 "dtc-parser.y" ++#line 113 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.is_plugin) = 0; ++ (yyval.re) = NULL; + } ++#line 1480 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 4: +- +-/* Line 1806 of yacc.c */ +-#line 123 "dtc-parser.y" ++#line 117 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.is_plugin) = 1; ++ (yyval.re) = chain_reserve_entry((yyvsp[-1].re), (yyvsp[0].re)); + } ++#line 1488 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 5: +- +-/* Line 1806 of yacc.c */ +-#line 130 "dtc-parser.y" ++#line 124 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.re) = NULL; ++ (yyval.re) = build_reserve_entry((yyvsp[-2].integer), (yyvsp[-1].integer)); + } ++#line 1496 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 6: +- +-/* Line 1806 of yacc.c */ +-#line 134 "dtc-parser.y" ++#line 128 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.re) = chain_reserve_entry((yyvsp[(1) - (2)].re), (yyvsp[(2) - (2)].re)); ++ add_label(&(yyvsp[0].re)->labels, (yyvsp[-1].labelref)); ++ (yyval.re) = (yyvsp[0].re); + } ++#line 1505 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 7: +- +-/* Line 1806 of yacc.c */ +-#line 141 "dtc-parser.y" ++#line 136 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.re) = build_reserve_entry((yyvsp[(2) - (4)].integer), (yyvsp[(3) - (4)].integer)); ++ (yyval.node) = name_node((yyvsp[0].node), ""); + } ++#line 1513 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 8: +- +-/* Line 1806 of yacc.c */ +-#line 145 "dtc-parser.y" ++#line 140 "dtc-parser.y" /* yacc.c:1646 */ + { +- add_label(&(yyvsp[(2) - (2)].re)->labels, (yyvsp[(1) - (2)].labelref)); +- (yyval.re) = (yyvsp[(2) - (2)].re); ++ (yyval.node) = merge_nodes((yyvsp[-2].node), (yyvsp[0].node)); + } ++#line 1521 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 9: +- +-/* Line 1806 of yacc.c */ +-#line 153 "dtc-parser.y" ++#line 145 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.node) = name_node((yyvsp[(2) - (2)].node), ""); ++ struct node *target = get_node_by_ref((yyvsp[-3].node), (yyvsp[-1].labelref)); ++ ++ add_label(&target->labels, (yyvsp[-2].labelref)); ++ if (target) ++ merge_nodes(target, (yyvsp[0].node)); ++ else ++ ERROR(&(yylsp[-1]), "Label or path %s not found", (yyvsp[-1].labelref)); ++ (yyval.node) = (yyvsp[-3].node); + } ++#line 1536 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 10: +- +-/* Line 1806 of yacc.c */ +-#line 157 "dtc-parser.y" ++#line 156 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.node) = merge_nodes((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); ++ struct node *target = get_node_by_ref((yyvsp[-2].node), (yyvsp[-1].labelref)); ++ ++ if (target) ++ merge_nodes(target, (yyvsp[0].node)); ++ else ++ ERROR(&(yylsp[-1]), "Label or path %s not found", (yyvsp[-1].labelref)); ++ (yyval.node) = (yyvsp[-2].node); + } ++#line 1550 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 11: +- +-/* Line 1806 of yacc.c */ +-#line 162 "dtc-parser.y" ++#line 166 "dtc-parser.y" /* yacc.c:1646 */ + { +- struct node *target = get_node_by_ref((yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].labelref)); ++ struct node *target = get_node_by_ref((yyvsp[-3].node), (yyvsp[-1].labelref)); + +- add_label(&target->labels, (yyvsp[(2) - (4)].labelref)); + if (target) +- merge_nodes(target, (yyvsp[(4) - (4)].node)); ++ delete_node(target); + else +- ERROR(&(yylsp[(3) - (4)]), "Label or path %s not found", (yyvsp[(3) - (4)].labelref)); +- (yyval.node) = (yyvsp[(1) - (4)].node); ++ ERROR(&(yylsp[-1]), "Label or path %s not found", (yyvsp[-1].labelref)); ++ ++ ++ (yyval.node) = (yyvsp[-3].node); + } ++#line 1566 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 12: +- +-/* Line 1806 of yacc.c */ +-#line 173 "dtc-parser.y" ++#line 181 "dtc-parser.y" /* yacc.c:1646 */ + { +- struct node *target = get_node_by_ref((yyvsp[(1) - (3)].node), (yyvsp[(2) - (3)].labelref)); +- +- if (target) +- merge_nodes(target, (yyvsp[(3) - (3)].node)); +- else +- ERROR(&(yylsp[(2) - (3)]), "Label or path %s not found", (yyvsp[(2) - (3)].labelref)); +- (yyval.node) = (yyvsp[(1) - (3)].node); ++ (yyval.node) = build_node((yyvsp[-3].proplist), (yyvsp[-2].nodelist)); + } ++#line 1574 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 13: +- +-/* Line 1806 of yacc.c */ +-#line 183 "dtc-parser.y" ++#line 188 "dtc-parser.y" /* yacc.c:1646 */ + { +- struct node *target = get_node_by_ref((yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].labelref)); +- +- if (target) +- delete_node(target); +- else +- ERROR(&(yylsp[(3) - (4)]), "Label or path %s not found", (yyvsp[(3) - (4)].labelref)); +- +- +- (yyval.node) = (yyvsp[(1) - (4)].node); ++ (yyval.proplist) = NULL; + } ++#line 1582 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 14: +- +-/* Line 1806 of yacc.c */ +-#line 198 "dtc-parser.y" ++#line 192 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.node) = build_node((yyvsp[(2) - (5)].proplist), (yyvsp[(3) - (5)].nodelist)); ++ (yyval.proplist) = chain_property((yyvsp[0].prop), (yyvsp[-1].proplist)); + } ++#line 1590 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 15: +- +-/* Line 1806 of yacc.c */ +-#line 205 "dtc-parser.y" ++#line 199 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.proplist) = NULL; ++ (yyval.prop) = build_property((yyvsp[-3].propnodename), (yyvsp[-1].data)); + } ++#line 1598 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 16: +- +-/* Line 1806 of yacc.c */ +-#line 209 "dtc-parser.y" ++#line 203 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.proplist) = chain_property((yyvsp[(2) - (2)].prop), (yyvsp[(1) - (2)].proplist)); ++ (yyval.prop) = build_property((yyvsp[-1].propnodename), empty_data); + } ++#line 1606 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 17: +- +-/* Line 1806 of yacc.c */ +-#line 216 "dtc-parser.y" ++#line 207 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.prop) = build_property((yyvsp[(1) - (4)].propnodename), (yyvsp[(3) - (4)].data)); ++ (yyval.prop) = build_property_delete((yyvsp[-1].propnodename)); + } ++#line 1614 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 18: +- +-/* Line 1806 of yacc.c */ +-#line 220 "dtc-parser.y" ++#line 211 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.prop) = build_property((yyvsp[(1) - (2)].propnodename), empty_data); ++ add_label(&(yyvsp[0].prop)->labels, (yyvsp[-1].labelref)); ++ (yyval.prop) = (yyvsp[0].prop); + } ++#line 1623 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 19: +- +-/* Line 1806 of yacc.c */ +-#line 224 "dtc-parser.y" ++#line 219 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.prop) = build_property_delete((yyvsp[(2) - (3)].propnodename)); ++ (yyval.data) = data_merge((yyvsp[-1].data), (yyvsp[0].data)); + } ++#line 1631 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 20: +- +-/* Line 1806 of yacc.c */ +-#line 228 "dtc-parser.y" ++#line 223 "dtc-parser.y" /* yacc.c:1646 */ + { +- add_label(&(yyvsp[(2) - (2)].prop)->labels, (yyvsp[(1) - (2)].labelref)); +- (yyval.prop) = (yyvsp[(2) - (2)].prop); ++ (yyval.data) = data_merge((yyvsp[-2].data), (yyvsp[-1].array).data); + } ++#line 1639 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 21: +- +-/* Line 1806 of yacc.c */ +-#line 236 "dtc-parser.y" ++#line 227 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.data) = data_merge((yyvsp[(1) - (2)].data), (yyvsp[(2) - (2)].data)); ++ (yyval.data) = data_merge((yyvsp[-3].data), (yyvsp[-1].data)); + } ++#line 1647 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 22: +- +-/* Line 1806 of yacc.c */ +-#line 240 "dtc-parser.y" ++#line 231 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.data) = data_merge((yyvsp[(1) - (3)].data), (yyvsp[(2) - (3)].array).data); ++ (yyval.data) = data_add_marker((yyvsp[-1].data), REF_PATH, (yyvsp[0].labelref)); + } ++#line 1655 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 23: +- +-/* Line 1806 of yacc.c */ +-#line 244 "dtc-parser.y" +- { +- (yyval.data) = data_merge((yyvsp[(1) - (4)].data), (yyvsp[(3) - (4)].data)); +- } +- break; +- +- case 24: +- +-/* Line 1806 of yacc.c */ +-#line 248 "dtc-parser.y" +- { +- (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), REF_PATH, (yyvsp[(2) - (2)].labelref)); +- } +- break; +- +- case 25: +- +-/* Line 1806 of yacc.c */ +-#line 252 "dtc-parser.y" ++#line 235 "dtc-parser.y" /* yacc.c:1646 */ + { +- FILE *f = srcfile_relative_open((yyvsp[(4) - (9)].data).val, NULL); ++ FILE *f = srcfile_relative_open((yyvsp[-5].data).val, NULL); + struct data d; + +- if ((yyvsp[(6) - (9)].integer) != 0) +- if (fseek(f, (yyvsp[(6) - (9)].integer), SEEK_SET) != 0) ++ if ((yyvsp[-3].integer) != 0) ++ if (fseek(f, (yyvsp[-3].integer), SEEK_SET) != 0) + die("Couldn't seek to offset %llu in \"%s\": %s", +- (unsigned long long)(yyvsp[(6) - (9)].integer), (yyvsp[(4) - (9)].data).val, ++ (unsigned long long)(yyvsp[-3].integer), (yyvsp[-5].data).val, + strerror(errno)); + +- d = data_copy_file(f, (yyvsp[(8) - (9)].integer)); ++ d = data_copy_file(f, (yyvsp[-1].integer)); + +- (yyval.data) = data_merge((yyvsp[(1) - (9)].data), d); ++ (yyval.data) = data_merge((yyvsp[-8].data), d); + fclose(f); + } ++#line 1675 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 26: +- +-/* Line 1806 of yacc.c */ +-#line 268 "dtc-parser.y" ++ case 24: ++#line 251 "dtc-parser.y" /* yacc.c:1646 */ + { +- FILE *f = srcfile_relative_open((yyvsp[(4) - (5)].data).val, NULL); ++ FILE *f = srcfile_relative_open((yyvsp[-1].data).val, NULL); + struct data d = empty_data; + + d = data_copy_file(f, -1); + +- (yyval.data) = data_merge((yyvsp[(1) - (5)].data), d); ++ (yyval.data) = data_merge((yyvsp[-4].data), d); + fclose(f); + } ++#line 1689 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 27: +- +-/* Line 1806 of yacc.c */ +-#line 278 "dtc-parser.y" ++ case 25: ++#line 261 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref)); ++ (yyval.data) = data_add_marker((yyvsp[-1].data), LABEL, (yyvsp[0].labelref)); + } ++#line 1697 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 28: +- +-/* Line 1806 of yacc.c */ +-#line 285 "dtc-parser.y" ++ case 26: ++#line 268 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.data) = empty_data; + } ++#line 1705 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 29: +- +-/* Line 1806 of yacc.c */ +-#line 289 "dtc-parser.y" ++ case 27: ++#line 272 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.data) = (yyvsp[(1) - (2)].data); ++ (yyval.data) = (yyvsp[-1].data); + } ++#line 1713 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 30: +- +-/* Line 1806 of yacc.c */ +-#line 293 "dtc-parser.y" ++ case 28: ++#line 276 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref)); ++ (yyval.data) = data_add_marker((yyvsp[-1].data), LABEL, (yyvsp[0].labelref)); + } ++#line 1721 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 31: +- +-/* Line 1806 of yacc.c */ +-#line 300 "dtc-parser.y" ++ case 29: ++#line 283 "dtc-parser.y" /* yacc.c:1646 */ + { + unsigned long long bits; + +- bits = (yyvsp[(2) - (3)].integer); ++ bits = (yyvsp[-1].integer); + + if ((bits != 8) && (bits != 16) && + (bits != 32) && (bits != 64)) { +- ERROR(&(yylsp[(2) - (3)]), "Array elements must be" ++ ERROR(&(yylsp[-1]), "Array elements must be" + " 8, 16, 32 or 64-bits"); + bits = 32; + } +@@ -1918,25 +1737,23 @@ yyreduce: + (yyval.array).data = empty_data; + (yyval.array).bits = bits; + } ++#line 1741 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 32: +- +-/* Line 1806 of yacc.c */ +-#line 316 "dtc-parser.y" ++ case 30: ++#line 299 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.array).data = empty_data; + (yyval.array).bits = 32; + } ++#line 1750 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 33: +- +-/* Line 1806 of yacc.c */ +-#line 321 "dtc-parser.y" ++ case 31: ++#line 304 "dtc-parser.y" /* yacc.c:1646 */ + { +- if ((yyvsp[(1) - (2)].array).bits < 64) { +- uint64_t mask = (1ULL << (yyvsp[(1) - (2)].array).bits) - 1; ++ if ((yyvsp[-1].array).bits < 64) { ++ uint64_t mask = (1ULL << (yyvsp[-1].array).bits) - 1; + /* + * Bits above mask must either be all zero + * (positive within range of mask) or all one +@@ -1945,293 +1762,258 @@ yyreduce: + * within the mask to one (i.e. | in the + * mask), all bits are one. + */ +- if (((yyvsp[(2) - (2)].integer) > mask) && (((yyvsp[(2) - (2)].integer) | mask) != -1ULL)) +- ERROR(&(yylsp[(2) - (2)]), "Value out of range for" +- " %d-bit array element", (yyvsp[(1) - (2)].array).bits); ++ if (((yyvsp[0].integer) > mask) && (((yyvsp[0].integer) | mask) != -1ULL)) ++ ERROR(&(yylsp[0]), "Value out of range for" ++ " %d-bit array element", (yyvsp[-1].array).bits); + } + +- (yyval.array).data = data_append_integer((yyvsp[(1) - (2)].array).data, (yyvsp[(2) - (2)].integer), (yyvsp[(1) - (2)].array).bits); ++ (yyval.array).data = data_append_integer((yyvsp[-1].array).data, (yyvsp[0].integer), (yyvsp[-1].array).bits); + } ++#line 1773 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 34: +- +-/* Line 1806 of yacc.c */ +-#line 340 "dtc-parser.y" ++ case 32: ++#line 323 "dtc-parser.y" /* yacc.c:1646 */ + { +- uint64_t val = ~0ULL >> (64 - (yyvsp[(1) - (2)].array).bits); ++ uint64_t val = ~0ULL >> (64 - (yyvsp[-1].array).bits); + +- if ((yyvsp[(1) - (2)].array).bits == 32) +- (yyvsp[(1) - (2)].array).data = data_add_marker((yyvsp[(1) - (2)].array).data, ++ if ((yyvsp[-1].array).bits == 32) ++ (yyvsp[-1].array).data = data_add_marker((yyvsp[-1].array).data, + REF_PHANDLE, +- (yyvsp[(2) - (2)].labelref)); ++ (yyvsp[0].labelref)); + else +- ERROR(&(yylsp[(2) - (2)]), "References are only allowed in " ++ ERROR(&(yylsp[0]), "References are only allowed in " + "arrays with 32-bit elements."); + +- (yyval.array).data = data_append_integer((yyvsp[(1) - (2)].array).data, val, (yyvsp[(1) - (2)].array).bits); ++ (yyval.array).data = data_append_integer((yyvsp[-1].array).data, val, (yyvsp[-1].array).bits); + } ++#line 1791 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 35: +- +-/* Line 1806 of yacc.c */ +-#line 354 "dtc-parser.y" ++ case 33: ++#line 337 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.array).data = data_add_marker((yyvsp[(1) - (2)].array).data, LABEL, (yyvsp[(2) - (2)].labelref)); ++ (yyval.array).data = data_add_marker((yyvsp[-1].array).data, LABEL, (yyvsp[0].labelref)); + } ++#line 1799 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 38: +- +-/* Line 1806 of yacc.c */ +-#line 363 "dtc-parser.y" ++ case 36: ++#line 346 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.integer) = (yyvsp[(2) - (3)].integer); ++ (yyval.integer) = (yyvsp[-1].integer); + } ++#line 1807 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 41: ++ case 39: ++#line 357 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-4].integer) ? (yyvsp[-2].integer) : (yyvsp[0].integer); } ++#line 1813 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ break; + +-/* Line 1806 of yacc.c */ +-#line 374 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (5)].integer) ? (yyvsp[(3) - (5)].integer) : (yyvsp[(5) - (5)].integer); } ++ case 41: ++#line 362 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-2].integer) || (yyvsp[0].integer); } ++#line 1819 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 43: +- +-/* Line 1806 of yacc.c */ +-#line 379 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (3)].integer) || (yyvsp[(3) - (3)].integer); } ++#line 367 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-2].integer) && (yyvsp[0].integer); } ++#line 1825 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 45: +- +-/* Line 1806 of yacc.c */ +-#line 384 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (3)].integer) && (yyvsp[(3) - (3)].integer); } ++#line 372 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-2].integer) | (yyvsp[0].integer); } ++#line 1831 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 47: +- +-/* Line 1806 of yacc.c */ +-#line 389 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (3)].integer) | (yyvsp[(3) - (3)].integer); } ++#line 377 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-2].integer) ^ (yyvsp[0].integer); } ++#line 1837 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 49: +- +-/* Line 1806 of yacc.c */ +-#line 394 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (3)].integer) ^ (yyvsp[(3) - (3)].integer); } ++#line 382 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-2].integer) & (yyvsp[0].integer); } ++#line 1843 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 51: +- +-/* Line 1806 of yacc.c */ +-#line 399 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (3)].integer) & (yyvsp[(3) - (3)].integer); } ++#line 387 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-2].integer) == (yyvsp[0].integer); } ++#line 1849 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 53: +- +-/* Line 1806 of yacc.c */ +-#line 404 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (3)].integer) == (yyvsp[(3) - (3)].integer); } ++ case 52: ++#line 388 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-2].integer) != (yyvsp[0].integer); } ++#line 1855 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 54: ++#line 393 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-2].integer) < (yyvsp[0].integer); } ++#line 1861 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ break; + +-/* Line 1806 of yacc.c */ +-#line 405 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (3)].integer) != (yyvsp[(3) - (3)].integer); } ++ case 55: ++#line 394 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-2].integer) > (yyvsp[0].integer); } ++#line 1867 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 56: +- +-/* Line 1806 of yacc.c */ +-#line 410 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (3)].integer) < (yyvsp[(3) - (3)].integer); } ++#line 395 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-2].integer) <= (yyvsp[0].integer); } ++#line 1873 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 57: +- +-/* Line 1806 of yacc.c */ +-#line 411 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (3)].integer) > (yyvsp[(3) - (3)].integer); } ++#line 396 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-2].integer) >= (yyvsp[0].integer); } ++#line 1879 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 58: +- +-/* Line 1806 of yacc.c */ +-#line 412 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (3)].integer) <= (yyvsp[(3) - (3)].integer); } ++#line 400 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-2].integer) << (yyvsp[0].integer); } ++#line 1885 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 59: +- +-/* Line 1806 of yacc.c */ +-#line 413 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (3)].integer) >= (yyvsp[(3) - (3)].integer); } +- break; +- +- case 60: +- +-/* Line 1806 of yacc.c */ +-#line 417 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (3)].integer) << (yyvsp[(3) - (3)].integer); } ++#line 401 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-2].integer) >> (yyvsp[0].integer); } ++#line 1891 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 61: +- +-/* Line 1806 of yacc.c */ +-#line 418 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (3)].integer) >> (yyvsp[(3) - (3)].integer); } ++#line 406 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-2].integer) + (yyvsp[0].integer); } ++#line 1897 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 63: +- +-/* Line 1806 of yacc.c */ +-#line 423 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (3)].integer) + (yyvsp[(3) - (3)].integer); } ++ case 62: ++#line 407 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-2].integer) - (yyvsp[0].integer); } ++#line 1903 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 64: ++#line 412 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-2].integer) * (yyvsp[0].integer); } ++#line 1909 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ break; + +-/* Line 1806 of yacc.c */ +-#line 424 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (3)].integer) - (yyvsp[(3) - (3)].integer); } ++ case 65: ++#line 413 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-2].integer) / (yyvsp[0].integer); } ++#line 1915 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 66: +- +-/* Line 1806 of yacc.c */ +-#line 429 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (3)].integer) * (yyvsp[(3) - (3)].integer); } ++#line 414 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = (yyvsp[-2].integer) % (yyvsp[0].integer); } ++#line 1921 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 67: +- +-/* Line 1806 of yacc.c */ +-#line 430 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (3)].integer) / (yyvsp[(3) - (3)].integer); } ++ case 69: ++#line 420 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = -(yyvsp[0].integer); } ++#line 1927 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 68: +- +-/* Line 1806 of yacc.c */ +-#line 431 "dtc-parser.y" +- { (yyval.integer) = (yyvsp[(1) - (3)].integer) % (yyvsp[(3) - (3)].integer); } ++ case 70: ++#line 421 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = ~(yyvsp[0].integer); } ++#line 1933 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 71: +- +-/* Line 1806 of yacc.c */ +-#line 437 "dtc-parser.y" +- { (yyval.integer) = -(yyvsp[(2) - (2)].integer); } ++#line 422 "dtc-parser.y" /* yacc.c:1646 */ ++ { (yyval.integer) = !(yyvsp[0].integer); } ++#line 1939 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 72: +- +-/* Line 1806 of yacc.c */ +-#line 438 "dtc-parser.y" +- { (yyval.integer) = ~(yyvsp[(2) - (2)].integer); } ++#line 427 "dtc-parser.y" /* yacc.c:1646 */ ++ { ++ (yyval.data) = empty_data; ++ } ++#line 1947 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 73: +- +-/* Line 1806 of yacc.c */ +-#line 439 "dtc-parser.y" +- { (yyval.integer) = !(yyvsp[(2) - (2)].integer); } ++#line 431 "dtc-parser.y" /* yacc.c:1646 */ ++ { ++ (yyval.data) = data_append_byte((yyvsp[-1].data), (yyvsp[0].byte)); ++ } ++#line 1955 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 74: +- +-/* Line 1806 of yacc.c */ +-#line 444 "dtc-parser.y" ++#line 435 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.data) = empty_data; ++ (yyval.data) = data_add_marker((yyvsp[-1].data), LABEL, (yyvsp[0].labelref)); + } ++#line 1963 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 75: +- +-/* Line 1806 of yacc.c */ +-#line 448 "dtc-parser.y" ++#line 442 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.data) = data_append_byte((yyvsp[(1) - (2)].data), (yyvsp[(2) - (2)].byte)); ++ (yyval.nodelist) = NULL; + } ++#line 1971 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 76: +- +-/* Line 1806 of yacc.c */ +-#line 452 "dtc-parser.y" ++#line 446 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.data) = data_add_marker((yyvsp[(1) - (2)].data), LABEL, (yyvsp[(2) - (2)].labelref)); ++ (yyval.nodelist) = chain_node((yyvsp[-1].node), (yyvsp[0].nodelist)); + } ++#line 1979 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 77: +- +-/* Line 1806 of yacc.c */ +-#line 459 "dtc-parser.y" ++#line 450 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.nodelist) = NULL; ++ ERROR(&(yylsp[0]), "Properties must precede subnodes"); ++ YYERROR; + } ++#line 1988 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 78: +- +-/* Line 1806 of yacc.c */ +-#line 463 "dtc-parser.y" ++#line 458 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.nodelist) = chain_node((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].nodelist)); ++ (yyval.node) = name_node((yyvsp[0].node), (yyvsp[-1].propnodename)); + } ++#line 1996 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 79: +- +-/* Line 1806 of yacc.c */ +-#line 467 "dtc-parser.y" ++#line 462 "dtc-parser.y" /* yacc.c:1646 */ + { +- ERROR(&(yylsp[(2) - (2)]), "Properties must precede subnodes"); +- YYERROR; ++ (yyval.node) = name_node(build_node_delete(), (yyvsp[-1].propnodename)); + } ++#line 2004 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 80: +- +-/* Line 1806 of yacc.c */ +-#line 475 "dtc-parser.y" ++#line 466 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.node) = name_node((yyvsp[(2) - (2)].node), (yyvsp[(1) - (2)].propnodename)); ++ add_label(&(yyvsp[0].node)->labels, (yyvsp[-1].labelref)); ++ (yyval.node) = (yyvsp[0].node); + } ++#line 2013 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 81: + +-/* Line 1806 of yacc.c */ +-#line 479 "dtc-parser.y" +- { +- (yyval.node) = name_node(build_node_delete(), (yyvsp[(2) - (3)].propnodename)); +- } +- break; +- +- case 82: +- +-/* Line 1806 of yacc.c */ +-#line 483 "dtc-parser.y" +- { +- add_label(&(yyvsp[(2) - (2)].node)->labels, (yyvsp[(1) - (2)].labelref)); +- (yyval.node) = (yyvsp[(2) - (2)].node); +- } +- break; +- +- +- +-/* Line 1806 of yacc.c */ +-#line 2235 "dtc-parser.tab.c" ++#line 2017 "dtc-parser.tab.c" /* yacc.c:1646 */ + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires +@@ -2254,7 +2036,7 @@ yyreduce: + *++yyvsp = yyval; + *++yylsp = yyloc; + +- /* Now `shift' the result of the reduction. Determine what state ++ /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + +@@ -2269,9 +2051,9 @@ yyreduce: + goto yynewstate; + + +-/*------------------------------------. +-| yyerrlab -- here on detecting error | +-`------------------------------------*/ ++/*--------------------------------------. ++| yyerrlab -- here on detecting error. | ++`--------------------------------------*/ + yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ +@@ -2322,20 +2104,20 @@ yyerrlab: + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an +- error, discard it. */ ++ error, discard it. */ + + if (yychar <= YYEOF) +- { +- /* Return failure if at end of input. */ +- if (yychar == YYEOF) +- YYABORT; +- } ++ { ++ /* Return failure if at end of input. */ ++ if (yychar == YYEOF) ++ YYABORT; ++ } + else +- { +- yydestruct ("Error: discarding", +- yytoken, &yylval, &yylloc); +- yychar = YYEMPTY; +- } ++ { ++ yydestruct ("Error: discarding", ++ yytoken, &yylval, &yylloc); ++ yychar = YYEMPTY; ++ } + } + + /* Else will try to reuse lookahead token after shifting the error +@@ -2355,7 +2137,7 @@ yyerrorlab: + goto yyerrorlab; + + yyerror_range[1] = yylsp[1-yylen]; +- /* Do not reclaim the symbols of the rule which action triggered ++ /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; +@@ -2368,35 +2150,37 @@ yyerrorlab: + | yyerrlab1 -- common code for both syntax error and YYERROR. | + `-------------------------------------------------------------*/ + yyerrlab1: +- yyerrstatus = 3; /* Each real token shifted decrements this. */ ++ yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) +- { +- yyn += YYTERROR; +- if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) +- { +- yyn = yytable[yyn]; +- if (0 < yyn) +- break; +- } +- } ++ { ++ yyn += YYTERROR; ++ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) ++ { ++ yyn = yytable[yyn]; ++ if (0 < yyn) ++ break; ++ } ++ } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) +- YYABORT; ++ YYABORT; + + yyerror_range[1] = *yylsp; + yydestruct ("Error: popping", +- yystos[yystate], yyvsp, yylsp); ++ yystos[yystate], yyvsp, yylsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + ++ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; ++ YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + /* Using YYLLOC is tempting, but would change the location of +@@ -2425,7 +2209,7 @@ yyabortlab: + yyresult = 1; + goto yyreturn; + +-#if !defined(yyoverflow) || YYERROR_VERBOSE ++#if !defined yyoverflow || YYERROR_VERBOSE + /*-------------------------------------------------. + | yyexhaustedlab -- memory exhaustion comes here. | + `-------------------------------------------------*/ +@@ -2444,14 +2228,14 @@ yyreturn: + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc); + } +- /* Do not reclaim the symbols of the rule which action triggered ++ /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", +- yystos[*yyssp], yyvsp, yylsp); ++ yystos[*yyssp], yyvsp, yylsp); + YYPOPSTACK (1); + } + #ifndef yyoverflow +@@ -2462,18 +2246,12 @@ yyreturn: + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + #endif +- /* Make sure YYID is used. */ +- return YYID (yyresult); ++ return yyresult; + } +- +- +- +-/* Line 2067 of yacc.c */ +-#line 489 "dtc-parser.y" ++#line 472 "dtc-parser.y" /* yacc.c:1906 */ + + + void yyerror(char const *s) + { + ERROR(&yylloc, "%s", s); + } +- +--- a/scripts/dtc/dtc-parser.tab.h_shipped ++++ b/scripts/dtc/dtc-parser.tab.h_shipped +@@ -1,19 +1,19 @@ +-/* A Bison parser, made by GNU Bison 2.5. */ ++/* A Bison parser, made by GNU Bison 3.0.2. */ + + /* Bison interface for Yacc-like parsers in C +- +- Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. +- ++ ++ Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. ++ + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +- ++ + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU 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 . */ + +@@ -26,50 +26,55 @@ + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. +- ++ + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + ++#ifndef YY_YY_DTC_PARSER_TAB_H_INCLUDED ++# define YY_YY_DTC_PARSER_TAB_H_INCLUDED ++/* Debug traces. */ ++#ifndef YYDEBUG ++# define YYDEBUG 0 ++#endif ++#if YYDEBUG ++extern int yydebug; ++#endif + +-/* Tokens. */ ++/* Token type. */ + #ifndef YYTOKENTYPE + # define YYTOKENTYPE +- /* Put the tokens into the symbol table, so that GDB and other debuggers +- know about them. */ +- enum yytokentype { +- DT_V1 = 258, +- DT_PLUGIN = 259, +- DT_MEMRESERVE = 260, +- DT_LSHIFT = 261, +- DT_RSHIFT = 262, +- DT_LE = 263, +- DT_GE = 264, +- DT_EQ = 265, +- DT_NE = 266, +- DT_AND = 267, +- DT_OR = 268, +- DT_BITS = 269, +- DT_DEL_PROP = 270, +- DT_DEL_NODE = 271, +- DT_PROPNODENAME = 272, +- DT_LITERAL = 273, +- DT_CHAR_LITERAL = 274, +- DT_BYTE = 275, +- DT_STRING = 276, +- DT_LABEL = 277, +- DT_REF = 278, +- DT_INCBIN = 279 +- }; ++ enum yytokentype ++ { ++ DT_V1 = 258, ++ DT_MEMRESERVE = 259, ++ DT_LSHIFT = 260, ++ DT_RSHIFT = 261, ++ DT_LE = 262, ++ DT_GE = 263, ++ DT_EQ = 264, ++ DT_NE = 265, ++ DT_AND = 266, ++ DT_OR = 267, ++ DT_BITS = 268, ++ DT_DEL_PROP = 269, ++ DT_DEL_NODE = 270, ++ DT_PROPNODENAME = 271, ++ DT_LITERAL = 272, ++ DT_CHAR_LITERAL = 273, ++ DT_BYTE = 274, ++ DT_STRING = 275, ++ DT_LABEL = 276, ++ DT_REF = 277, ++ DT_INCBIN = 278 ++ }; + #endif + +- +- ++/* Value type. */ + #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +-typedef union YYSTYPE ++typedef union YYSTYPE YYSTYPE; ++union YYSTYPE + { +- +-/* Line 2068 of yacc.c */ +-#line 39 "dtc-parser.y" ++#line 38 "dtc-parser.y" /* yacc.c:1909 */ + + char *propnodename; + char *labelref; +@@ -87,32 +92,30 @@ typedef union YYSTYPE + struct node *nodelist; + struct reserve_info *re; + uint64_t integer; +- int is_plugin; +- +- + +-/* Line 2068 of yacc.c */ +-#line 96 "dtc-parser.tab.h" +-} YYSTYPE; ++#line 97 "dtc-parser.tab.h" /* yacc.c:1909 */ ++}; + # define YYSTYPE_IS_TRIVIAL 1 +-# define yystype YYSTYPE /* obsolescent; will be withdrawn */ + # define YYSTYPE_IS_DECLARED 1 + #endif + +-extern YYSTYPE yylval; +- ++/* Location type. */ + #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +-typedef struct YYLTYPE ++typedef struct YYLTYPE YYLTYPE; ++struct YYLTYPE + { + int first_line; + int first_column; + int last_line; + int last_column; +-} YYLTYPE; +-# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ ++}; + # define YYLTYPE_IS_DECLARED 1 + # define YYLTYPE_IS_TRIVIAL 1 + #endif + ++ ++extern YYSTYPE yylval; + extern YYLTYPE yylloc; ++int yyparse (void); + ++#endif /* !YY_YY_DTC_PARSER_TAB_H_INCLUDED */ +--- a/scripts/dtc/dtc-parser.y ++++ b/scripts/dtc/dtc-parser.y +@@ -19,7 +19,6 @@ + */ + %{ + #include +-#include + + #include "dtc.h" + #include "srcpos.h" +@@ -53,11 +52,9 @@ extern bool treesource_error; + struct node *nodelist; + struct reserve_info *re; + uint64_t integer; +- int is_plugin; + } + + %token DT_V1 +-%token DT_PLUGIN + %token DT_MEMRESERVE + %token DT_LSHIFT DT_RSHIFT DT_LE DT_GE DT_EQ DT_NE DT_AND DT_OR + %token DT_BITS +@@ -74,7 +71,6 @@ extern bool treesource_error; + + %type propdata + %type propdataprefix +-%type plugindecl + %type memreserve + %type memreserves + %type arrayprefix +@@ -105,23 +101,10 @@ extern bool treesource_error; + %% + + sourcefile: +- DT_V1 ';' plugindecl memreserves devicetree ++ DT_V1 ';' memreserves devicetree + { +- $5->is_plugin = $3; +- $5->is_root = 1; +- the_boot_info = build_boot_info($4, $5, +- guess_boot_cpuid($5)); +- } +- ; +- +-plugindecl: +- /* empty */ +- { +- $$ = 0; +- } +- | DT_PLUGIN ';' +- { +- $$ = 1; ++ the_boot_info = build_boot_info($3, $4, ++ guess_boot_cpuid($4)); + } + ; + +--- a/scripts/dtc/dtc.c ++++ b/scripts/dtc/dtc.c +@@ -29,7 +29,6 @@ int reservenum; /* Number of memory res + int minsize; /* Minimum blob size */ + int padsize; /* Additional padding to blob */ + int phandle_format = PHANDLE_BOTH; /* Use linux,phandle or phandle properties */ +-int symbol_fixup_support = 0; + + static void fill_fullpaths(struct node *tree, const char *prefix) + { +@@ -52,7 +51,7 @@ static void fill_fullpaths(struct node * + #define FDT_VERSION(version) _FDT_VERSION(version) + #define _FDT_VERSION(version) #version + static const char usage_synopsis[] = "dtc [options] "; +-static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:hv@"; ++static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:hv"; + static struct option const usage_long_opts[] = { + {"quiet", no_argument, NULL, 'q'}, + {"in-format", a_argument, NULL, 'I'}, +@@ -70,7 +69,6 @@ static struct option const usage_long_op + {"phandle", a_argument, NULL, 'H'}, + {"warning", a_argument, NULL, 'W'}, + {"error", a_argument, NULL, 'E'}, +- {"symbols", a_argument, NULL, '@'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, + {NULL, no_argument, NULL, 0x0}, +@@ -101,7 +99,6 @@ static const char * const usage_opts_hel + "\t\tboth - Both \"linux,phandle\" and \"phandle\" properties", + "\n\tEnable/disable warnings (prefix with \"no-\")", + "\n\tEnable/disable errors (prefix with \"no-\")", +- "\n\tSymbols and Fixups support", + "\n\tPrint this help and exit", + "\n\tPrint version and exit", + NULL, +@@ -189,9 +186,7 @@ int main(int argc, char *argv[]) + case 'E': + parse_checks_option(false, true, optarg); + break; +- case '@': +- symbol_fixup_support = 1; +- break; ++ + case 'h': + usage(NULL); + default: +--- a/scripts/dtc/dtc.h ++++ b/scripts/dtc/dtc.h +@@ -54,7 +54,6 @@ extern int reservenum; /* Number of mem + extern int minsize; /* Minimum blob size */ + extern int padsize; /* Additional padding to blob */ + extern int phandle_format; /* Use linux,phandle or phandle properties */ +-extern int symbol_fixup_support;/* enable symbols & fixup support */ + + #define PHANDLE_LEGACY 0x1 + #define PHANDLE_EPAPR 0x2 +@@ -133,25 +132,6 @@ struct label { + struct label *next; + }; + +-struct fixup_entry { +- int offset; +- struct node *node; +- struct property *prop; +- struct fixup_entry *next; +-}; +- +-struct fixup { +- char *ref; +- struct fixup_entry *entries; +- struct fixup *next; +-}; +- +-struct symbol { +- struct label *label; +- struct node *node; +- struct symbol *next; +-}; +- + struct property { + bool deleted; + char *name; +@@ -178,12 +158,6 @@ struct node { + int addr_cells, size_cells; + + struct label *labels; +- +- int is_root; +- int is_plugin; +- struct fixup *fixups; +- struct symbol *symbols; +- struct fixup_entry *local_fixups; + }; + + #define for_each_label_withdel(l0, l) \ +@@ -207,18 +181,6 @@ struct node { + for_each_child_withdel(n, c) \ + if (!(c)->deleted) + +-#define for_each_fixup(n, f) \ +- for ((f) = (n)->fixups; (f); (f) = (f)->next) +- +-#define for_each_fixup_entry(f, fe) \ +- for ((fe) = (f)->entries; (fe); (fe) = (fe)->next) +- +-#define for_each_symbol(n, s) \ +- for ((s) = (n)->symbols; (s); (s) = (s)->next) +- +-#define for_each_local_fixup_entry(n, fe) \ +- for ((fe) = (n)->local_fixups; (fe); (fe) = (fe)->next) +- + void add_label(struct label **labels, char *label); + void delete_labels(struct label **labels); + +--- a/scripts/dtc/flattree.c ++++ b/scripts/dtc/flattree.c +@@ -262,12 +262,6 @@ static void flatten_tree(struct node *tr + struct property *prop; + struct node *child; + bool seen_name_prop = false; +- struct symbol *sym; +- struct fixup *f; +- struct fixup_entry *fe; +- char *name, *s; +- const char *fullpath; +- int namesz, nameoff, vallen; + + if (tree->deleted) + return; +@@ -282,6 +276,8 @@ static void flatten_tree(struct node *tr + emit->align(etarget, sizeof(cell_t)); + + for_each_property(tree, prop) { ++ int nameoff; ++ + if (streq(prop->name, "name")) + seen_name_prop = true; + +@@ -314,139 +310,6 @@ static void flatten_tree(struct node *tr + flatten_tree(child, emit, etarget, strbuf, vi); + } + +- if (!symbol_fixup_support) +- goto no_symbols; +- +- /* add the symbol nodes (if any) */ +- if (tree->symbols) { +- +- emit->beginnode(etarget, NULL); +- emit->string(etarget, "__symbols__", 0); +- emit->align(etarget, sizeof(cell_t)); +- +- for_each_symbol(tree, sym) { +- +- vallen = strlen(sym->node->fullpath); +- +- nameoff = stringtable_insert(strbuf, sym->label->label); +- +- emit->property(etarget, NULL); +- emit->cell(etarget, vallen + 1); +- emit->cell(etarget, nameoff); +- +- if ((vi->flags & FTF_VARALIGN) && vallen >= 8) +- emit->align(etarget, 8); +- +- emit->string(etarget, sym->node->fullpath, +- strlen(sym->node->fullpath)); +- emit->align(etarget, sizeof(cell_t)); +- } +- +- emit->endnode(etarget, NULL); +- } +- +- /* add the fixup nodes */ +- if (tree->fixups) { +- +- /* emit the external fixups */ +- emit->beginnode(etarget, NULL); +- emit->string(etarget, "__fixups__", 0); +- emit->align(etarget, sizeof(cell_t)); +- +- for_each_fixup(tree, f) { +- +- namesz = 0; +- for_each_fixup_entry(f, fe) { +- fullpath = fe->node->fullpath; +- if (fullpath[0] == '\0') +- fullpath = "/"; +- namesz += strlen(fullpath) + 1; +- namesz += strlen(fe->prop->name) + 1; +- namesz += 32; /* space for : + '\0' */ +- } +- +- name = xmalloc(namesz); +- +- s = name; +- for_each_fixup_entry(f, fe) { +- fullpath = fe->node->fullpath; +- if (fullpath[0] == '\0') +- fullpath = "/"; +- snprintf(s, name + namesz - s, "%s:%s:%d", +- fullpath, +- fe->prop->name, fe->offset); +- s += strlen(s) + 1; +- } +- +- nameoff = stringtable_insert(strbuf, f->ref); +- vallen = s - name - 1; +- +- emit->property(etarget, NULL); +- emit->cell(etarget, vallen + 1); +- emit->cell(etarget, nameoff); +- +- if ((vi->flags & FTF_VARALIGN) && vallen >= 8) +- emit->align(etarget, 8); +- +- emit->string(etarget, name, vallen); +- emit->align(etarget, sizeof(cell_t)); +- +- free(name); +- } +- +- emit->endnode(etarget, tree->labels); +- } +- +- /* add the local fixup property */ +- if (tree->local_fixups) { +- +- /* emit the external fixups */ +- emit->beginnode(etarget, NULL); +- emit->string(etarget, "__local_fixups__", 0); +- emit->align(etarget, sizeof(cell_t)); +- +- namesz = 0; +- for_each_local_fixup_entry(tree, fe) { +- fullpath = fe->node->fullpath; +- if (fullpath[0] == '\0') +- fullpath = "/"; +- namesz += strlen(fullpath) + 1; +- namesz += strlen(fe->prop->name) + 1; +- namesz += 32; /* space for : + '\0' */ +- } +- +- name = xmalloc(namesz); +- +- s = name; +- for_each_local_fixup_entry(tree, fe) { +- fullpath = fe->node->fullpath; +- if (fullpath[0] == '\0') +- fullpath = "/"; +- snprintf(s, name + namesz - s, "%s:%s:%d", +- fullpath, fe->prop->name, +- fe->offset); +- s += strlen(s) + 1; +- } +- +- nameoff = stringtable_insert(strbuf, "fixup"); +- vallen = s - name - 1; +- +- emit->property(etarget, NULL); +- emit->cell(etarget, vallen + 1); +- emit->cell(etarget, nameoff); +- +- if ((vi->flags & FTF_VARALIGN) && vallen >= 8) +- emit->align(etarget, 8); +- +- emit->string(etarget, name, vallen); +- emit->align(etarget, sizeof(cell_t)); +- +- free(name); +- +- emit->endnode(etarget, tree->labels); +- } +- +-no_symbols: + emit->endnode(etarget, tree->labels); + } + +--- a/scripts/dtc/version_gen.h ++++ b/scripts/dtc/version_gen.h +@@ -1 +1 @@ +-#define DTC_VERSION "DTC 1.4.1-g9d3649bd-dirty" ++#define DTC_VERSION "DTC 1.4.1-g9d3649bd" diff --git a/target/linux/brcm2708/patches-4.4/0176-bcm2835-mmc-Only-claim-one-DMA-channel.patch b/target/linux/brcm2708/patches-4.4/0176-bcm2835-mmc-Only-claim-one-DMA-channel.patch deleted file mode 100644 index 8f224faf88..0000000000 --- a/target/linux/brcm2708/patches-4.4/0176-bcm2835-mmc-Only-claim-one-DMA-channel.patch +++ /dev/null @@ -1,170 +0,0 @@ -From af80d75abc7604cd9eb1788b0171148d000db09d Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Tue, 8 Mar 2016 09:49:16 +0000 -Subject: [PATCH 176/180] bcm2835-mmc: Only claim one DMA channel - -With both MMC controllers enabled there are few DMA channels left. The -bcm2835-mmc driver only uses DMA in one direction at a time, so it -doesn't need to claim two channels. - -See: https://github.com/raspberrypi/linux/issues/1327 - -Signed-off-by: Phil Elwell ---- - arch/arm/boot/dts/bcm2708_common.dtsi | 5 +-- - drivers/mmc/host/bcm2835-mmc.c | 69 +++++++++++++++++++++++++---------- - 2 files changed, 51 insertions(+), 23 deletions(-) - ---- a/arch/arm/boot/dts/bcm2708_common.dtsi -+++ b/arch/arm/boot/dts/bcm2708_common.dtsi -@@ -232,9 +232,8 @@ - reg = <0x7e300000 0x100>; - interrupts = <2 30>; - clocks = <&clk_mmc>; -- dmas = <&dma 11>, -- <&dma 11>; -- dma-names = "tx", "rx"; -+ dmas = <&dma 11>; -+ dma-names = "rx-tx"; - brcm,overclock-50 = <0>; - status = "disabled"; - }; ---- a/drivers/mmc/host/bcm2835-mmc.c -+++ b/drivers/mmc/host/bcm2835-mmc.c -@@ -108,8 +108,9 @@ struct bcm2835_host { - 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_chan *dma_chan_rxtx; /* DMA channel for reads and writes */ -+ struct dma_slave_config dma_cfg_rx; -+ struct dma_slave_config dma_cfg_tx; - struct dma_async_tx_descriptor *tx_desc; /* descriptor */ - - bool have_dma; -@@ -342,7 +343,7 @@ static void bcm2835_mmc_dma_complete(voi - - if (host->data && !(host->data->flags & MMC_DATA_WRITE)) { - /* otherwise handled in SDHCI IRQ */ -- dma_chan = host->dma_chan_rx; -+ dma_chan = host->dma_chan_rxtx; - dir_data = DMA_FROM_DEVICE; - - dma_unmap_sg(dma_chan->device->dev, -@@ -493,16 +494,21 @@ static void bcm2835_mmc_transfer_dma(str - if (host->blocks == 0) - return; - -+ dma_chan = host->dma_chan_rxtx; - 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; - } - -+ /* The parameters have already been validated, so this will not fail */ -+ (void)dmaengine_slave_config(dma_chan, -+ (dir_data == DMA_FROM_DEVICE) ? -+ &host->dma_cfg_rx : -+ &host->dma_cfg_tx); -+ - BUG_ON(!dma_chan->device); - BUG_ON(!dma_chan->device->dev); - BUG_ON(!host->data->sg); -@@ -936,7 +942,7 @@ static void bcm2835_mmc_data_irq(struct - if (host->data->flags & MMC_DATA_WRITE) { - /* IRQ handled here */ - -- dma_chan = host->dma_chan_tx; -+ dma_chan = host->dma_chan_rxtx; - dir_data = DMA_TO_DEVICE; - dma_unmap_sg(dma_chan->device->dev, - host->data->sg, host->data->sg_len, -@@ -1316,28 +1322,47 @@ static int bcm2835_mmc_add_host(struct b - 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", -+ if (IS_ERR_OR_NULL(host->dma_chan_rxtx)) { -+ dev_err(dev, "%s: Unable to initialise DMA channel. Falling back to PIO\n", - DRIVER_NAME); - host->have_dma = false; - } else { -- dev_info(dev, "DMA channels allocated"); -- host->have_dma = true; -+ dev_info(dev, "DMA channel allocated"); - - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - cfg.slave_id = 11; /* DREQ channel */ - -+ /* Validate the slave configurations */ -+ - cfg.direction = DMA_MEM_TO_DEV; - cfg.src_addr = 0; - cfg.dst_addr = host->bus_addr + SDHCI_BUFFER; -- ret = dmaengine_slave_config(host->dma_chan_tx, &cfg); - -- cfg.direction = DMA_DEV_TO_MEM; -- cfg.src_addr = host->bus_addr + SDHCI_BUFFER; -- cfg.dst_addr = 0; -- ret = dmaengine_slave_config(host->dma_chan_rx, &cfg); -+ ret = dmaengine_slave_config(host->dma_chan_rxtx, &cfg); -+ -+ if (ret == 0) { -+ host->dma_cfg_tx = cfg; -+ -+ cfg.direction = DMA_DEV_TO_MEM; -+ cfg.src_addr = host->bus_addr + SDHCI_BUFFER; -+ cfg.dst_addr = 0; -+ -+ ret = dmaengine_slave_config(host->dma_chan_rxtx, &cfg); -+ } -+ -+ if (ret == 0) { -+ host->dma_cfg_rx = cfg; -+ -+ host->use_dma = true; -+ } else { -+ pr_err("%s: unable to configure DMA channel. " -+ "Faling back to PIO\n", -+ mmc_hostname(mmc)); -+ dma_release_channel(host->dma_chan_rxtx); -+ host->dma_chan_rxtx = NULL; -+ host->use_dma = false; -+ } - } - #endif - mmc->max_segs = 128; -@@ -1416,16 +1441,20 @@ static int bcm2835_mmc_probe(struct plat - - #ifndef FORCE_PIO - if (node) { -- host->dma_chan_tx = dma_request_slave_channel(dev, "tx"); -- host->dma_chan_rx = dma_request_slave_channel(dev, "rx"); -+ host->dma_chan_rxtx = dma_request_slave_channel(dev, "rx-tx"); -+ if (!host->dma_chan_rxtx) -+ host->dma_chan_rxtx = -+ dma_request_slave_channel(dev, "tx"); -+ if (!host->dma_chan_rxtx) -+ host->dma_chan_rxtx = -+ dma_request_slave_channel(dev, "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); -+ host->dma_chan_rxtx = dma_request_channel(mask, NULL, NULL); - } - #endif - clk = devm_clk_get(dev, NULL); diff --git a/target/linux/brcm2708/patches-4.4/0177-config-rebuild-with-savedefconfig.patch b/target/linux/brcm2708/patches-4.4/0177-config-rebuild-with-savedefconfig.patch deleted file mode 100644 index 34eedc1076..0000000000 --- a/target/linux/brcm2708/patches-4.4/0177-config-rebuild-with-savedefconfig.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 0b65dbd40ad6b3d7cc979e8b7817f74b823f66ef Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 8 Mar 2016 17:08:39 +0000 -Subject: [PATCH 177/180] config: rebuild with savedefconfig - ---- - arch/arm/configs/bcm2709_defconfig | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - ---- a/arch/arm/configs/bcm2709_defconfig -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -593,7 +593,6 @@ CONFIG_SERIAL_AMBA_PL011_CONSOLE=y - CONFIG_SERIAL_OF_PLATFORM=y - CONFIG_TTY_PRINTK=y - CONFIG_HW_RANDOM=y --CONFIG_HW_RANDOM_BCM2835=y - CONFIG_RAW_DRIVER=y - CONFIG_I2C=y - CONFIG_I2C_CHARDEV=m -@@ -1112,7 +1111,7 @@ CONFIG_EXTCON=m - CONFIG_EXTCON_ARIZONA=m - CONFIG_IIO=m - CONFIG_IIO_BUFFER=y --CONFIG_IIO_BUFFER_CB=y -+CONFIG_IIO_BUFFER_CB=m - CONFIG_IIO_KFIFO_BUF=m - CONFIG_MCP320X=m - CONFIG_DHT11=m diff --git a/target/linux/brcm2708/patches-4.4/0177-scripts-dtc-Update-to-upstream-version-1.4.1.patch b/target/linux/brcm2708/patches-4.4/0177-scripts-dtc-Update-to-upstream-version-1.4.1.patch new file mode 100644 index 0000000000..1057e83e3f --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0177-scripts-dtc-Update-to-upstream-version-1.4.1.patch @@ -0,0 +1,2736 @@ +From 8ed747466ab2fac41d870582b149385d3e8c2d43 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 10 Aug 2015 09:49:15 +0100 +Subject: [PATCH 177/232] scripts/dtc: Update to upstream version 1.4.1 + +Includes the new localfixups format. + +Signed-off-by: Phil Elwell +--- + scripts/dtc/checks.c | 105 ++++- + scripts/dtc/dtc-lexer.l | 5 + + scripts/dtc/dtc-lexer.lex.c_shipped | 490 ++++++++++++------------ + scripts/dtc/dtc-parser.tab.c_shipped | 722 ++++++++++++++++++----------------- + scripts/dtc/dtc-parser.tab.h_shipped | 46 +-- + scripts/dtc/dtc-parser.y | 22 +- + scripts/dtc/dtc.c | 9 +- + scripts/dtc/dtc.h | 40 ++ + scripts/dtc/flattree.c | 202 ++++++++++ + scripts/dtc/version_gen.h | 2 +- + 10 files changed, 1021 insertions(+), 622 deletions(-) + +--- a/scripts/dtc/checks.c ++++ b/scripts/dtc/checks.c +@@ -458,6 +458,8 @@ static void fixup_phandle_references(str + struct node *node, struct property *prop) + { + struct marker *m = prop->val.markers; ++ struct fixup *f, **fp; ++ struct fixup_entry *fe, **fep; + struct node *refnode; + cell_t phandle; + +@@ -466,11 +468,69 @@ static void fixup_phandle_references(str + + refnode = get_node_by_ref(dt, m->ref); + if (! refnode) { +- FAIL(c, "Reference to non-existent node or label \"%s\"\n", +- m->ref); ++ if (!dt->is_plugin) { ++ FAIL(c, "Reference to non-existent node or label \"%s\"\n", ++ m->ref); ++ continue; ++ } ++ ++ /* allocate fixup entry */ ++ fe = xmalloc(sizeof(*fe)); ++ ++ fe->node = node; ++ fe->prop = prop; ++ fe->offset = m->offset; ++ fe->next = NULL; ++ ++ /* search for an already existing fixup */ ++ for_each_fixup(dt, f) ++ if (strcmp(f->ref, m->ref) == 0) ++ break; ++ ++ /* no fixup found, add new */ ++ if (f == NULL) { ++ f = xmalloc(sizeof(*f)); ++ f->ref = m->ref; ++ f->entries = NULL; ++ f->next = NULL; ++ ++ /* add it to the tree */ ++ fp = &dt->fixups; ++ while (*fp) ++ fp = &(*fp)->next; ++ *fp = f; ++ } ++ ++ /* and now append fixup entry */ ++ fep = &f->entries; ++ while (*fep) ++ fep = &(*fep)->next; ++ *fep = fe; ++ ++ /* mark the entry as unresolved */ ++ *((cell_t *)(prop->val.val + m->offset)) = ++ cpu_to_fdt32(0xdeadbeef); + continue; + } + ++ /* if it's a local reference, we need to record it */ ++ if (symbol_fixup_support) { ++ ++ /* allocate a new local fixup entry */ ++ fe = xmalloc(sizeof(*fe)); ++ ++ fe->node = node; ++ fe->prop = prop; ++ fe->offset = m->offset; ++ fe->next = NULL; ++ ++ /* append it to the local fixups */ ++ fep = &dt->local_fixups; ++ while (*fep) ++ fep = &(*fep)->next; ++ *fep = fe; ++ } ++ + phandle = get_node_phandle(dt, refnode); + *((cell_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle); + } +@@ -652,6 +712,45 @@ static void check_obsolete_chosen_interr + } + TREE_WARNING(obsolete_chosen_interrupt_controller, NULL); + ++static void check_auto_label_phandles(struct check *c, struct node *dt, ++ struct node *node) ++{ ++ struct label *l; ++ struct symbol *s, **sp; ++ int has_label; ++ ++ if (!symbol_fixup_support) ++ return; ++ ++ has_label = 0; ++ for_each_label(node->labels, l) { ++ has_label = 1; ++ break; ++ } ++ ++ if (!has_label) ++ return; ++ ++ /* force allocation of a phandle for this node */ ++ (void)get_node_phandle(dt, node); ++ ++ /* add the symbol */ ++ for_each_label(node->labels, l) { ++ ++ s = xmalloc(sizeof(*s)); ++ s->label = l; ++ s->node = node; ++ s->next = NULL; ++ ++ /* add it to the symbols list */ ++ sp = &dt->symbols; ++ while (*sp) ++ sp = &((*sp)->next); ++ *sp = s; ++ } ++} ++NODE_WARNING(auto_label_phandles, NULL); ++ + static struct check *check_table[] = { + &duplicate_node_names, &duplicate_property_names, + &node_name_chars, &node_name_format, &property_name_chars, +@@ -670,6 +769,8 @@ static struct check *check_table[] = { + &avoid_default_addr_size, + &obsolete_chosen_interrupt_controller, + ++ &auto_label_phandles, ++ + &always_fail, + }; + +--- a/scripts/dtc/dtc-lexer.l ++++ b/scripts/dtc/dtc-lexer.l +@@ -113,6 +113,11 @@ static void lexical_error(const char *fm + return DT_V1; + } + ++<*>"/plugin/" { ++ DPRINT("Keyword: /plugin/\n"); ++ return DT_PLUGIN; ++ } ++ + <*>"/memreserve/" { + DPRINT("Keyword: /memreserve/\n"); + BEGIN_DEFAULT(); +--- a/scripts/dtc/dtc-lexer.lex.c_shipped ++++ b/scripts/dtc/dtc-lexer.lex.c_shipped +@@ -9,7 +9,7 @@ + #define FLEX_SCANNER + #define YY_FLEX_MAJOR_VERSION 2 + #define YY_FLEX_MINOR_VERSION 5 +-#define YY_FLEX_SUBMINOR_VERSION 39 ++#define YY_FLEX_SUBMINOR_VERSION 35 + #if YY_FLEX_SUBMINOR_VERSION > 0 + #define FLEX_BETA + #endif +@@ -162,12 +162,7 @@ typedef unsigned int flex_uint32_t; + typedef struct yy_buffer_state *YY_BUFFER_STATE; + #endif + +-#ifndef YY_TYPEDEF_YY_SIZE_T +-#define YY_TYPEDEF_YY_SIZE_T +-typedef size_t yy_size_t; +-#endif +- +-extern yy_size_t yyleng; ++extern int yyleng; + + extern FILE *yyin, *yyout; + +@@ -176,7 +171,6 @@ extern FILE *yyin, *yyout; + #define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) +- #define YY_LINENO_REWIND_TO(ptr) + + /* Return all but the first "n" matched characters back to the input stream. */ + #define yyless(n) \ +@@ -194,6 +188,11 @@ extern FILE *yyin, *yyout; + + #define unput(c) yyunput( c, (yytext_ptr) ) + ++#ifndef YY_TYPEDEF_YY_SIZE_T ++#define YY_TYPEDEF_YY_SIZE_T ++typedef size_t yy_size_t; ++#endif ++ + #ifndef YY_STRUCT_YY_BUFFER_STATE + #define YY_STRUCT_YY_BUFFER_STATE + struct yy_buffer_state +@@ -211,7 +210,7 @@ struct yy_buffer_state + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ +- yy_size_t yy_n_chars; ++ int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to +@@ -281,8 +280,8 @@ static YY_BUFFER_STATE * yy_buffer_stack + + /* yy_hold_char holds the character lost when yytext is formed. */ + static char yy_hold_char; +-static yy_size_t yy_n_chars; /* number of characters read into yy_ch_buf */ +-yy_size_t yyleng; ++static int yy_n_chars; /* number of characters read into yy_ch_buf */ ++int yyleng; + + /* Points to current character in buffer. */ + static char *yy_c_buf_p = (char *) 0; +@@ -310,7 +309,7 @@ static void yy_init_buffer (YY_BUFFER_ST + + YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); + YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); +-YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ); ++YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); + + void *yyalloc (yy_size_t ); + void *yyrealloc (void *,yy_size_t ); +@@ -342,7 +341,7 @@ void yyfree (void * ); + + /* Begin user sect3 */ + +-#define yywrap() 1 ++#define yywrap(n) 1 + #define YY_SKIP_YYWRAP + + typedef unsigned char YY_CHAR; +@@ -373,8 +372,8 @@ static void yy_fatal_error (yyconst char + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +-#define YY_NUM_RULES 30 +-#define YY_END_OF_BUFFER 31 ++#define YY_NUM_RULES 31 ++#define YY_END_OF_BUFFER 32 + /* This struct is not used in this scanner, + but its presence is necessary. */ + struct yy_trans_info +@@ -382,25 +381,26 @@ struct yy_trans_info + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +-static yyconst flex_int16_t yy_accept[159] = ++static yyconst flex_int16_t yy_accept[166] = + { 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 31, 29, +- 18, 18, 29, 29, 29, 29, 29, 29, 29, 29, +- 29, 29, 29, 29, 29, 29, 15, 16, 16, 29, +- 16, 10, 10, 18, 26, 0, 3, 0, 27, 12, +- 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, +- 21, 23, 25, 24, 22, 0, 9, 28, 0, 0, +- 0, 14, 14, 16, 16, 16, 10, 10, 10, 0, +- 12, 0, 11, 0, 0, 0, 20, 0, 0, 0, +- 0, 0, 0, 0, 0, 16, 10, 10, 10, 0, +- 13, 19, 0, 0, 0, 0, 0, 0, 0, 0, +- +- 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 16, 6, 0, 0, 0, 0, 0, 0, 2, +- 0, 0, 0, 0, 0, 0, 0, 0, 4, 17, +- 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, +- 5, 8, 0, 0, 0, 0, 7, 0 ++ 0, 0, 0, 0, 0, 0, 0, 0, 32, 30, ++ 19, 19, 30, 30, 30, 30, 30, 30, 30, 30, ++ 30, 30, 30, 30, 30, 30, 16, 17, 17, 30, ++ 17, 11, 11, 19, 27, 0, 3, 0, 28, 13, ++ 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, ++ 0, 22, 24, 26, 25, 23, 0, 10, 29, 0, ++ 0, 0, 15, 15, 17, 17, 17, 11, 11, 11, ++ 0, 13, 0, 12, 0, 0, 0, 21, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 17, 11, 11, ++ 11, 0, 14, 20, 0, 0, 0, 0, 0, 0, ++ ++ 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 17, 7, 0, 0, 0, ++ 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 4, 18, 0, 0, 5, 2, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 1, 0, 0, 0, 0, 6, 9, 0, ++ 0, 0, 0, 8, 0 + } ; + + static yyconst flex_int32_t yy_ec[256] = +@@ -416,9 +416,9 @@ static yyconst flex_int32_t yy_ec[256] = + 22, 22, 22, 22, 24, 22, 22, 25, 22, 22, + 1, 26, 27, 1, 22, 1, 21, 28, 29, 30, + +- 31, 21, 22, 22, 32, 22, 22, 33, 34, 35, +- 36, 37, 22, 38, 39, 40, 41, 42, 22, 25, +- 43, 22, 44, 45, 46, 1, 1, 1, 1, 1, ++ 31, 21, 32, 22, 33, 22, 22, 34, 35, 36, ++ 37, 38, 22, 39, 40, 41, 42, 43, 22, 25, ++ 44, 22, 45, 46, 47, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +@@ -435,163 +435,165 @@ static yyconst flex_int32_t yy_ec[256] = + 1, 1, 1, 1, 1 + } ; + +-static yyconst flex_int32_t yy_meta[47] = ++static yyconst flex_int32_t yy_meta[48] = + { 0, + 1, 1, 1, 1, 1, 1, 2, 3, 1, 2, + 2, 2, 4, 5, 5, 5, 6, 1, 1, 1, + 7, 8, 8, 8, 8, 1, 1, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, +- 8, 8, 8, 3, 1, 4 ++ 8, 8, 8, 8, 3, 1, 4 + } ; + +-static yyconst flex_int16_t yy_base[173] = ++static yyconst flex_int16_t yy_base[180] = + { 0, +- 0, 383, 34, 382, 65, 381, 37, 105, 387, 391, +- 54, 111, 367, 110, 109, 109, 112, 41, 366, 104, +- 367, 338, 124, 117, 0, 144, 391, 0, 121, 0, +- 135, 155, 140, 179, 391, 160, 391, 379, 391, 0, +- 368, 141, 391, 167, 370, 376, 346, 103, 342, 345, +- 391, 391, 391, 391, 391, 358, 391, 391, 175, 342, +- 338, 391, 355, 0, 185, 339, 184, 347, 346, 0, +- 0, 322, 175, 357, 175, 363, 352, 324, 330, 323, +- 332, 326, 201, 324, 329, 322, 391, 333, 181, 309, +- 391, 341, 340, 313, 320, 338, 178, 311, 146, 317, +- +- 314, 315, 335, 331, 303, 300, 309, 299, 308, 188, +- 336, 335, 391, 305, 320, 281, 283, 271, 203, 288, +- 281, 271, 266, 264, 245, 242, 208, 104, 391, 391, +- 244, 218, 204, 219, 206, 224, 201, 212, 204, 229, +- 215, 208, 207, 200, 219, 391, 233, 221, 200, 181, +- 391, 391, 149, 122, 86, 41, 391, 391, 245, 251, +- 259, 263, 267, 273, 280, 284, 292, 300, 304, 310, +- 318, 326 ++ 0, 393, 35, 392, 66, 391, 38, 107, 397, 401, ++ 55, 113, 377, 112, 111, 111, 114, 42, 376, 106, ++ 377, 347, 126, 120, 0, 147, 401, 0, 124, 0, ++ 137, 158, 170, 163, 401, 153, 401, 389, 401, 0, ++ 378, 120, 401, 131, 380, 386, 355, 139, 351, 355, ++ 351, 401, 401, 401, 401, 401, 367, 401, 401, 185, ++ 350, 346, 401, 364, 0, 185, 347, 189, 356, 355, ++ 0, 0, 330, 180, 366, 141, 372, 361, 332, 338, ++ 331, 341, 334, 326, 205, 331, 337, 329, 401, 341, ++ 167, 316, 401, 349, 348, 320, 328, 346, 180, 318, ++ ++ 324, 209, 324, 320, 322, 342, 338, 309, 306, 315, ++ 305, 315, 312, 192, 342, 341, 401, 293, 306, 282, ++ 268, 252, 255, 203, 285, 282, 272, 268, 252, 233, ++ 232, 239, 208, 107, 401, 401, 238, 211, 401, 211, ++ 212, 208, 228, 203, 215, 207, 233, 222, 212, 211, ++ 203, 227, 401, 237, 225, 204, 185, 401, 401, 149, ++ 128, 88, 42, 401, 401, 253, 259, 267, 271, 275, ++ 281, 288, 292, 300, 308, 312, 318, 326, 334 + } ; + +-static yyconst flex_int16_t yy_def[173] = ++static yyconst flex_int16_t yy_def[180] = + { 0, +- 158, 1, 1, 3, 158, 5, 1, 1, 158, 158, +- 158, 158, 158, 159, 160, 161, 158, 158, 158, 158, +- 162, 158, 158, 158, 163, 162, 158, 164, 165, 164, +- 164, 158, 158, 158, 158, 159, 158, 159, 158, 166, +- 158, 161, 158, 161, 167, 168, 158, 158, 158, 158, +- 158, 158, 158, 158, 158, 162, 158, 158, 158, 158, +- 158, 158, 162, 164, 165, 164, 158, 158, 158, 169, +- 166, 170, 161, 167, 167, 168, 158, 158, 158, 158, +- 158, 158, 158, 158, 158, 164, 158, 158, 169, 170, +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- +- 158, 164, 158, 158, 158, 158, 158, 158, 158, 171, +- 158, 164, 158, 158, 158, 158, 158, 158, 171, 158, +- 171, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- 172, 158, 158, 158, 172, 158, 172, 158, 158, 158, +- 158, 158, 158, 158, 158, 158, 158, 0, 158, 158, +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- 158, 158 ++ 165, 1, 1, 3, 165, 5, 1, 1, 165, 165, ++ 165, 165, 165, 166, 167, 168, 165, 165, 165, 165, ++ 169, 165, 165, 165, 170, 169, 165, 171, 172, 171, ++ 171, 165, 165, 165, 165, 166, 165, 166, 165, 173, ++ 165, 168, 165, 168, 174, 175, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 169, 165, 165, 165, ++ 165, 165, 165, 169, 171, 172, 171, 165, 165, 165, ++ 176, 173, 177, 168, 174, 174, 175, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 171, 165, 165, ++ 176, 177, 165, 165, 165, 165, 165, 165, 165, 165, ++ ++ 165, 165, 165, 165, 171, 165, 165, 165, 165, 165, ++ 165, 165, 165, 178, 165, 171, 165, 165, 165, 165, ++ 165, 165, 165, 178, 165, 178, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 179, 165, 165, ++ 165, 179, 165, 179, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 0, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165, 165 + } ; + +-static yyconst flex_int16_t yy_nxt[438] = ++static yyconst flex_int16_t yy_nxt[449] = + { 0, + 10, 11, 12, 11, 13, 14, 10, 15, 16, 10, + 10, 10, 17, 10, 10, 10, 10, 18, 19, 20, + 21, 21, 21, 21, 21, 10, 10, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, +- 21, 21, 21, 10, 22, 10, 24, 25, 25, 25, +- 32, 33, 33, 157, 26, 34, 34, 34, 51, 52, +- 27, 26, 26, 26, 26, 10, 11, 12, 11, 13, +- 14, 28, 15, 16, 28, 28, 28, 24, 28, 28, +- 28, 10, 18, 19, 20, 29, 29, 29, 29, 29, +- 30, 10, 29, 29, 29, 29, 29, 29, 29, 29, +- +- 29, 29, 29, 29, 29, 29, 29, 29, 10, 22, +- 10, 23, 34, 34, 34, 37, 39, 43, 32, 33, +- 33, 45, 54, 55, 46, 59, 45, 64, 156, 46, +- 64, 64, 64, 79, 44, 38, 59, 57, 134, 47, +- 135, 48, 80, 49, 47, 50, 48, 99, 61, 43, +- 50, 110, 41, 67, 67, 67, 60, 63, 63, 63, +- 57, 155, 68, 69, 63, 37, 44, 66, 67, 67, +- 67, 63, 63, 63, 63, 73, 59, 68, 69, 70, +- 34, 34, 34, 43, 75, 38, 154, 92, 83, 83, +- 83, 64, 44, 120, 64, 64, 64, 67, 67, 67, +- +- 44, 57, 99, 68, 69, 107, 68, 69, 120, 127, +- 108, 153, 152, 121, 83, 83, 83, 133, 133, 133, +- 146, 133, 133, 133, 146, 140, 140, 140, 121, 141, +- 140, 140, 140, 151, 141, 158, 150, 149, 148, 144, +- 147, 143, 142, 139, 147, 36, 36, 36, 36, 36, +- 36, 36, 36, 40, 138, 137, 136, 40, 40, 42, +- 42, 42, 42, 42, 42, 42, 42, 56, 56, 56, +- 56, 62, 132, 62, 64, 131, 130, 64, 129, 64, +- 64, 65, 128, 158, 65, 65, 65, 65, 71, 127, +- 71, 71, 74, 74, 74, 74, 74, 74, 74, 74, +- +- 76, 76, 76, 76, 76, 76, 76, 76, 89, 126, +- 89, 90, 125, 90, 90, 124, 90, 90, 119, 119, +- 119, 119, 119, 119, 119, 119, 145, 145, 145, 145, +- 145, 145, 145, 145, 123, 122, 59, 59, 118, 117, +- 116, 115, 114, 113, 45, 112, 108, 111, 109, 106, +- 105, 104, 46, 103, 91, 87, 102, 101, 100, 98, +- 97, 96, 95, 94, 93, 77, 75, 91, 88, 87, +- 86, 57, 85, 84, 57, 82, 81, 78, 77, 75, +- 72, 158, 58, 57, 53, 35, 158, 31, 23, 23, +- 9, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- 158, 158, 158, 158, 158, 158, 158 ++ 21, 21, 21, 21, 10, 22, 10, 24, 25, 25, ++ 25, 32, 33, 33, 164, 26, 34, 34, 34, 52, ++ 53, 27, 26, 26, 26, 26, 10, 11, 12, 11, ++ 13, 14, 28, 15, 16, 28, 28, 28, 24, 28, ++ 28, 28, 10, 18, 19, 20, 29, 29, 29, 29, ++ 29, 30, 10, 29, 29, 29, 29, 29, 29, 29, ++ ++ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, ++ 10, 22, 10, 23, 34, 34, 34, 37, 39, 43, ++ 32, 33, 33, 45, 55, 56, 46, 60, 43, 45, ++ 65, 163, 46, 65, 65, 65, 44, 38, 60, 74, ++ 58, 47, 141, 48, 142, 44, 49, 47, 50, 48, ++ 76, 51, 62, 94, 50, 41, 44, 51, 37, 61, ++ 64, 64, 64, 58, 34, 34, 34, 64, 162, 80, ++ 67, 68, 68, 68, 64, 64, 64, 64, 38, 81, ++ 69, 70, 71, 68, 68, 68, 60, 161, 43, 69, ++ 70, 65, 69, 70, 65, 65, 65, 125, 85, 85, ++ ++ 85, 58, 68, 68, 68, 44, 102, 110, 125, 133, ++ 102, 69, 70, 111, 114, 160, 159, 126, 85, 85, ++ 85, 140, 140, 140, 140, 140, 140, 153, 126, 147, ++ 147, 147, 153, 148, 147, 147, 147, 158, 148, 165, ++ 157, 156, 155, 151, 150, 149, 146, 154, 145, 144, ++ 143, 139, 154, 36, 36, 36, 36, 36, 36, 36, ++ 36, 40, 138, 137, 136, 40, 40, 42, 42, 42, ++ 42, 42, 42, 42, 42, 57, 57, 57, 57, 63, ++ 135, 63, 65, 134, 165, 65, 133, 65, 65, 66, ++ 132, 131, 66, 66, 66, 66, 72, 130, 72, 72, ++ ++ 75, 75, 75, 75, 75, 75, 75, 75, 77, 77, ++ 77, 77, 77, 77, 77, 77, 91, 129, 91, 92, ++ 128, 92, 92, 127, 92, 92, 124, 124, 124, 124, ++ 124, 124, 124, 124, 152, 152, 152, 152, 152, 152, ++ 152, 152, 60, 60, 123, 122, 121, 120, 119, 118, ++ 117, 45, 116, 111, 115, 113, 112, 109, 108, 107, ++ 46, 106, 93, 89, 105, 104, 103, 101, 100, 99, ++ 98, 97, 96, 95, 78, 76, 93, 90, 89, 88, ++ 58, 87, 86, 58, 84, 83, 82, 79, 78, 76, ++ 73, 165, 59, 58, 54, 35, 165, 31, 23, 23, ++ ++ 9, 165, 165, 165, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165 + } ; + +-static yyconst flex_int16_t yy_chk[438] = ++static yyconst flex_int16_t yy_chk[449] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +- 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, +- 7, 7, 7, 156, 3, 11, 11, 11, 18, 18, +- 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, ++ 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, ++ 3, 7, 7, 7, 163, 3, 11, 11, 11, 18, ++ 18, 3, 3, 3, 3, 3, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, +- 5, 8, 12, 12, 12, 14, 15, 16, 8, 8, +- 8, 17, 20, 20, 17, 23, 24, 29, 155, 24, +- 29, 29, 29, 48, 16, 14, 31, 29, 128, 17, +- 128, 17, 48, 17, 24, 17, 24, 99, 24, 42, +- 24, 99, 15, 33, 33, 33, 23, 26, 26, 26, +- 26, 154, 33, 33, 26, 36, 42, 31, 32, 32, +- 32, 26, 26, 26, 26, 44, 59, 32, 32, 32, +- 34, 34, 34, 73, 75, 36, 153, 75, 59, 59, +- 59, 65, 44, 110, 65, 65, 65, 67, 67, 67, +- +- 73, 65, 83, 89, 89, 97, 67, 67, 119, 127, +- 97, 150, 149, 110, 83, 83, 83, 133, 133, 133, +- 141, 127, 127, 127, 145, 136, 136, 136, 119, 136, +- 140, 140, 140, 148, 140, 147, 144, 143, 142, 139, +- 141, 138, 137, 135, 145, 159, 159, 159, 159, 159, +- 159, 159, 159, 160, 134, 132, 131, 160, 160, 161, +- 161, 161, 161, 161, 161, 161, 161, 162, 162, 162, +- 162, 163, 126, 163, 164, 125, 124, 164, 123, 164, +- 164, 165, 122, 121, 165, 165, 165, 165, 166, 120, +- 166, 166, 167, 167, 167, 167, 167, 167, 167, 167, +- +- 168, 168, 168, 168, 168, 168, 168, 168, 169, 118, +- 169, 170, 117, 170, 170, 116, 170, 170, 171, 171, +- 171, 171, 171, 171, 171, 171, 172, 172, 172, 172, +- 172, 172, 172, 172, 115, 114, 112, 111, 109, 108, +- 107, 106, 105, 104, 103, 102, 101, 100, 98, 96, +- 95, 94, 93, 92, 90, 88, 86, 85, 84, 82, +- 81, 80, 79, 78, 77, 76, 74, 72, 69, 68, +- 66, 63, 61, 60, 56, 50, 49, 47, 46, 45, ++ 5, 5, 5, 8, 12, 12, 12, 14, 15, 16, ++ 8, 8, 8, 17, 20, 20, 17, 23, 42, 24, ++ 29, 162, 24, 29, 29, 29, 16, 14, 31, 44, ++ 29, 17, 134, 17, 134, 42, 17, 24, 17, 24, ++ 76, 17, 24, 76, 24, 15, 44, 24, 36, 23, ++ 26, 26, 26, 26, 34, 34, 34, 26, 161, 48, ++ 31, 32, 32, 32, 26, 26, 26, 26, 36, 48, ++ 32, 32, 32, 33, 33, 33, 60, 160, 74, 91, ++ 91, 66, 33, 33, 66, 66, 66, 114, 60, 60, ++ ++ 60, 66, 68, 68, 68, 74, 85, 99, 124, 133, ++ 102, 68, 68, 99, 102, 157, 156, 114, 85, 85, ++ 85, 133, 133, 133, 140, 140, 140, 148, 124, 143, ++ 143, 143, 152, 143, 147, 147, 147, 155, 147, 154, ++ 151, 150, 149, 146, 145, 144, 142, 148, 141, 138, ++ 137, 132, 152, 166, 166, 166, 166, 166, 166, 166, ++ 166, 167, 131, 130, 129, 167, 167, 168, 168, 168, ++ 168, 168, 168, 168, 168, 169, 169, 169, 169, 170, ++ 128, 170, 171, 127, 126, 171, 125, 171, 171, 172, ++ 123, 122, 172, 172, 172, 172, 173, 121, 173, 173, ++ ++ 174, 174, 174, 174, 174, 174, 174, 174, 175, 175, ++ 175, 175, 175, 175, 175, 175, 176, 120, 176, 177, ++ 119, 177, 177, 118, 177, 177, 178, 178, 178, 178, ++ 178, 178, 178, 178, 179, 179, 179, 179, 179, 179, ++ 179, 179, 116, 115, 113, 112, 111, 110, 109, 108, ++ 107, 106, 105, 104, 103, 101, 100, 98, 97, 96, ++ 95, 94, 92, 90, 88, 87, 86, 84, 83, 82, ++ 81, 80, 79, 78, 77, 75, 73, 70, 69, 67, ++ 64, 62, 61, 57, 51, 50, 49, 47, 46, 45, + 41, 38, 22, 21, 19, 13, 9, 6, 4, 2, +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, + +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, +- 158, 158, 158, 158, 158, 158, 158 ++ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, ++ 165, 165, 165, 165, 165, 165, 165, 165 + } ; + + static yy_state_type yy_last_accepting_state; +@@ -662,7 +664,7 @@ static int dts_version = 1; + static void push_input_file(const char *filename); + static bool pop_input_file(void); + static void lexical_error(const char *fmt, ...); +-#line 666 "dtc-lexer.lex.c" ++#line 668 "dtc-lexer.lex.c" + + #define INITIAL 0 + #define BYTESTRING 1 +@@ -704,7 +706,7 @@ FILE *yyget_out (void ); + + void yyset_out (FILE * out_str ); + +-yy_size_t yyget_leng (void ); ++int yyget_leng (void ); + + char *yyget_text (void ); + +@@ -853,6 +855,10 @@ YY_DECL + register char *yy_cp, *yy_bp; + register int yy_act; + ++#line 68 "dtc-lexer.l" ++ ++#line 861 "dtc-lexer.lex.c" ++ + if ( !(yy_init) ) + { + (yy_init) = 1; +@@ -879,11 +885,6 @@ YY_DECL + yy_load_buffer_state( ); + } + +- { +-#line 68 "dtc-lexer.l" +- +-#line 886 "dtc-lexer.lex.c" +- + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); +@@ -901,7 +902,7 @@ YY_DECL + yy_match: + do + { +- register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; ++ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; +@@ -910,13 +911,13 @@ yy_match: + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; +- if ( yy_current_state >= 159 ) ++ if ( yy_current_state >= 166 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } +- while ( yy_current_state != 158 ); ++ while ( yy_current_state != 165 ); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + +@@ -1007,23 +1008,31 @@ case 5: + YY_RULE_SETUP + #line 116 "dtc-lexer.l" + { ++ DPRINT("Keyword: /plugin/\n"); ++ return DT_PLUGIN; ++ } ++ YY_BREAK ++case 6: ++YY_RULE_SETUP ++#line 121 "dtc-lexer.l" ++{ + DPRINT("Keyword: /memreserve/\n"); + BEGIN_DEFAULT(); + return DT_MEMRESERVE; + } + YY_BREAK +-case 6: ++case 7: + YY_RULE_SETUP +-#line 122 "dtc-lexer.l" ++#line 127 "dtc-lexer.l" + { + DPRINT("Keyword: /bits/\n"); + BEGIN_DEFAULT(); + return DT_BITS; + } + YY_BREAK +-case 7: ++case 8: + YY_RULE_SETUP +-#line 128 "dtc-lexer.l" ++#line 133 "dtc-lexer.l" + { + DPRINT("Keyword: /delete-property/\n"); + DPRINT("\n"); +@@ -1031,9 +1040,9 @@ YY_RULE_SETUP + return DT_DEL_PROP; + } + YY_BREAK +-case 8: ++case 9: + YY_RULE_SETUP +-#line 135 "dtc-lexer.l" ++#line 140 "dtc-lexer.l" + { + DPRINT("Keyword: /delete-node/\n"); + DPRINT("\n"); +@@ -1041,9 +1050,9 @@ YY_RULE_SETUP + return DT_DEL_NODE; + } + YY_BREAK +-case 9: ++case 10: + YY_RULE_SETUP +-#line 142 "dtc-lexer.l" ++#line 147 "dtc-lexer.l" + { + DPRINT("Label: %s\n", yytext); + yylval.labelref = xstrdup(yytext); +@@ -1051,9 +1060,9 @@ YY_RULE_SETUP + return DT_LABEL; + } + YY_BREAK +-case 10: ++case 11: + YY_RULE_SETUP +-#line 149 "dtc-lexer.l" ++#line 154 "dtc-lexer.l" + { + char *e; + DPRINT("Integer Literal: '%s'\n", yytext); +@@ -1073,10 +1082,10 @@ YY_RULE_SETUP + return DT_LITERAL; + } + YY_BREAK +-case 11: +-/* rule 11 can match eol */ ++case 12: ++/* rule 12 can match eol */ + YY_RULE_SETUP +-#line 168 "dtc-lexer.l" ++#line 173 "dtc-lexer.l" + { + struct data d; + DPRINT("Character literal: %s\n", yytext); +@@ -1098,18 +1107,18 @@ YY_RULE_SETUP + return DT_CHAR_LITERAL; + } + YY_BREAK +-case 12: ++case 13: + YY_RULE_SETUP +-#line 189 "dtc-lexer.l" ++#line 194 "dtc-lexer.l" + { /* label reference */ + DPRINT("Ref: %s\n", yytext+1); + yylval.labelref = xstrdup(yytext+1); + return DT_REF; + } + YY_BREAK +-case 13: ++case 14: + YY_RULE_SETUP +-#line 195 "dtc-lexer.l" ++#line 200 "dtc-lexer.l" + { /* new-style path reference */ + yytext[yyleng-1] = '\0'; + DPRINT("Ref: %s\n", yytext+2); +@@ -1117,27 +1126,27 @@ YY_RULE_SETUP + return DT_REF; + } + YY_BREAK +-case 14: ++case 15: + YY_RULE_SETUP +-#line 202 "dtc-lexer.l" ++#line 207 "dtc-lexer.l" + { + yylval.byte = strtol(yytext, NULL, 16); + DPRINT("Byte: %02x\n", (int)yylval.byte); + return DT_BYTE; + } + YY_BREAK +-case 15: ++case 16: + YY_RULE_SETUP +-#line 208 "dtc-lexer.l" ++#line 213 "dtc-lexer.l" + { + DPRINT("/BYTESTRING\n"); + BEGIN_DEFAULT(); + return ']'; + } + YY_BREAK +-case 16: ++case 17: + YY_RULE_SETUP +-#line 214 "dtc-lexer.l" ++#line 219 "dtc-lexer.l" + { + DPRINT("PropNodeName: %s\n", yytext); + yylval.propnodename = xstrdup((yytext[0] == '\\') ? +@@ -1146,75 +1155,75 @@ YY_RULE_SETUP + return DT_PROPNODENAME; + } + YY_BREAK +-case 17: ++case 18: + YY_RULE_SETUP +-#line 222 "dtc-lexer.l" ++#line 227 "dtc-lexer.l" + { + DPRINT("Binary Include\n"); + return DT_INCBIN; + } + YY_BREAK +-case 18: +-/* rule 18 can match eol */ +-YY_RULE_SETUP +-#line 227 "dtc-lexer.l" +-/* eat whitespace */ +- YY_BREAK + case 19: + /* rule 19 can match eol */ + YY_RULE_SETUP +-#line 228 "dtc-lexer.l" +-/* eat C-style comments */ ++#line 232 "dtc-lexer.l" ++/* eat whitespace */ + YY_BREAK + case 20: + /* rule 20 can match eol */ + YY_RULE_SETUP +-#line 229 "dtc-lexer.l" +-/* eat C++-style comments */ ++#line 233 "dtc-lexer.l" ++/* eat C-style comments */ + YY_BREAK + case 21: ++/* rule 21 can match eol */ + YY_RULE_SETUP +-#line 231 "dtc-lexer.l" +-{ return DT_LSHIFT; }; ++#line 234 "dtc-lexer.l" ++/* eat C++-style comments */ + YY_BREAK + case 22: + YY_RULE_SETUP +-#line 232 "dtc-lexer.l" +-{ return DT_RSHIFT; }; ++#line 236 "dtc-lexer.l" ++{ return DT_LSHIFT; }; + YY_BREAK + case 23: + YY_RULE_SETUP +-#line 233 "dtc-lexer.l" +-{ return DT_LE; }; ++#line 237 "dtc-lexer.l" ++{ return DT_RSHIFT; }; + YY_BREAK + case 24: + YY_RULE_SETUP +-#line 234 "dtc-lexer.l" +-{ return DT_GE; }; ++#line 238 "dtc-lexer.l" ++{ return DT_LE; }; + YY_BREAK + case 25: + YY_RULE_SETUP +-#line 235 "dtc-lexer.l" +-{ return DT_EQ; }; ++#line 239 "dtc-lexer.l" ++{ return DT_GE; }; + YY_BREAK + case 26: + YY_RULE_SETUP +-#line 236 "dtc-lexer.l" +-{ return DT_NE; }; ++#line 240 "dtc-lexer.l" ++{ return DT_EQ; }; + YY_BREAK + case 27: + YY_RULE_SETUP +-#line 237 "dtc-lexer.l" +-{ return DT_AND; }; ++#line 241 "dtc-lexer.l" ++{ return DT_NE; }; + YY_BREAK + case 28: + YY_RULE_SETUP +-#line 238 "dtc-lexer.l" +-{ return DT_OR; }; ++#line 242 "dtc-lexer.l" ++{ return DT_AND; }; + YY_BREAK + case 29: + YY_RULE_SETUP +-#line 240 "dtc-lexer.l" ++#line 243 "dtc-lexer.l" ++{ return DT_OR; }; ++ YY_BREAK ++case 30: ++YY_RULE_SETUP ++#line 245 "dtc-lexer.l" + { + DPRINT("Char: %c (\\x%02x)\n", yytext[0], + (unsigned)yytext[0]); +@@ -1230,12 +1239,12 @@ YY_RULE_SETUP + return yytext[0]; + } + YY_BREAK +-case 30: ++case 31: + YY_RULE_SETUP +-#line 255 "dtc-lexer.l" ++#line 260 "dtc-lexer.l" + ECHO; + YY_BREAK +-#line 1239 "dtc-lexer.lex.c" ++#line 1248 "dtc-lexer.lex.c" + + case YY_END_OF_BUFFER: + { +@@ -1365,7 +1374,6 @@ ECHO; + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +- } /* end of user's declarations */ + } /* end of yylex */ + + /* yy_get_next_buffer - try to read in a new buffer +@@ -1421,21 +1429,21 @@ static int yy_get_next_buffer (void) + + else + { +- yy_size_t num_to_read = ++ int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ +- YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; ++ YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { +- yy_size_t new_size = b->yy_buf_size * 2; ++ int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; +@@ -1466,7 +1474,7 @@ static int yy_get_next_buffer (void) + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), +- (yy_n_chars), num_to_read ); ++ (yy_n_chars), (size_t) num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } +@@ -1528,7 +1536,7 @@ static int yy_get_next_buffer (void) + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; +- if ( yy_current_state >= 159 ) ++ if ( yy_current_state >= 166 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; +@@ -1556,13 +1564,13 @@ static int yy_get_next_buffer (void) + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; +- if ( yy_current_state >= 159 ) ++ if ( yy_current_state >= 166 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; +- yy_is_jam = (yy_current_state == 158); ++ yy_is_jam = (yy_current_state == 165); + +- return yy_is_jam ? 0 : yy_current_state; ++ return yy_is_jam ? 0 : yy_current_state; + } + + #ifndef YY_NO_INPUT +@@ -1589,7 +1597,7 @@ static int yy_get_next_buffer (void) + + else + { /* need more input */ +- yy_size_t offset = (yy_c_buf_p) - (yytext_ptr); ++ int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) +@@ -1863,7 +1871,7 @@ void yypop_buffer_state (void) + */ + static void yyensure_buffer_stack (void) + { +- yy_size_t num_to_alloc; ++ int num_to_alloc; + + if (!(yy_buffer_stack)) { + +@@ -1960,12 +1968,12 @@ YY_BUFFER_STATE yy_scan_string (yyconst + * + * @return the newly allocated buffer state object. + */ +-YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len ) ++YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) + { + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; +- yy_size_t i; ++ int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; +@@ -2047,7 +2055,7 @@ FILE *yyget_out (void) + /** Get the length of the current token. + * + */ +-yy_size_t yyget_leng (void) ++int yyget_leng (void) + { + return yyleng; + } +@@ -2195,7 +2203,7 @@ void yyfree (void * ptr ) + + #define YYTABLES_NAME "yytables" + +-#line 254 "dtc-lexer.l" ++#line 260 "dtc-lexer.l" + + + +--- a/scripts/dtc/dtc-parser.tab.c_shipped ++++ b/scripts/dtc/dtc-parser.tab.c_shipped +@@ -65,6 +65,7 @@ + #line 20 "dtc-parser.y" /* yacc.c:339 */ + + #include ++#include + + #include "dtc.h" + #include "srcpos.h" +@@ -80,7 +81,7 @@ extern void yyerror(char const *s); + extern struct boot_info *the_boot_info; + extern bool treesource_error; + +-#line 84 "dtc-parser.tab.c" /* yacc.c:339 */ ++#line 85 "dtc-parser.tab.c" /* yacc.c:339 */ + + # ifndef YY_NULLPTR + # if defined __cplusplus && 201103L <= __cplusplus +@@ -116,26 +117,27 @@ extern int yydebug; + enum yytokentype + { + DT_V1 = 258, +- DT_MEMRESERVE = 259, +- DT_LSHIFT = 260, +- DT_RSHIFT = 261, +- DT_LE = 262, +- DT_GE = 263, +- DT_EQ = 264, +- DT_NE = 265, +- DT_AND = 266, +- DT_OR = 267, +- DT_BITS = 268, +- DT_DEL_PROP = 269, +- DT_DEL_NODE = 270, +- DT_PROPNODENAME = 271, +- DT_LITERAL = 272, +- DT_CHAR_LITERAL = 273, +- DT_BYTE = 274, +- DT_STRING = 275, +- DT_LABEL = 276, +- DT_REF = 277, +- DT_INCBIN = 278 ++ DT_PLUGIN = 259, ++ DT_MEMRESERVE = 260, ++ DT_LSHIFT = 261, ++ DT_RSHIFT = 262, ++ DT_LE = 263, ++ DT_GE = 264, ++ DT_EQ = 265, ++ DT_NE = 266, ++ DT_AND = 267, ++ DT_OR = 268, ++ DT_BITS = 269, ++ DT_DEL_PROP = 270, ++ DT_DEL_NODE = 271, ++ DT_PROPNODENAME = 272, ++ DT_LITERAL = 273, ++ DT_CHAR_LITERAL = 274, ++ DT_BYTE = 275, ++ DT_STRING = 276, ++ DT_LABEL = 277, ++ DT_REF = 278, ++ DT_INCBIN = 279 + }; + #endif + +@@ -144,7 +146,7 @@ extern int yydebug; + typedef union YYSTYPE YYSTYPE; + union YYSTYPE + { +-#line 38 "dtc-parser.y" /* yacc.c:355 */ ++#line 39 "dtc-parser.y" /* yacc.c:355 */ + + char *propnodename; + char *labelref; +@@ -162,8 +164,9 @@ union YYSTYPE + struct node *nodelist; + struct reserve_info *re; + uint64_t integer; ++ bool is_plugin; + +-#line 167 "dtc-parser.tab.c" /* yacc.c:355 */ ++#line 170 "dtc-parser.tab.c" /* yacc.c:355 */ + }; + # define YYSTYPE_IS_TRIVIAL 1 + # define YYSTYPE_IS_DECLARED 1 +@@ -192,7 +195,7 @@ int yyparse (void); + + /* Copy the second part of user declarations. */ + +-#line 196 "dtc-parser.tab.c" /* yacc.c:358 */ ++#line 199 "dtc-parser.tab.c" /* yacc.c:358 */ + + #ifdef short + # undef short +@@ -439,18 +442,18 @@ union yyalloc + #define YYLAST 136 + + /* YYNTOKENS -- Number of terminals. */ +-#define YYNTOKENS 47 ++#define YYNTOKENS 48 + /* YYNNTS -- Number of nonterminals. */ +-#define YYNNTS 28 ++#define YYNNTS 29 + /* YYNRULES -- Number of rules. */ +-#define YYNRULES 80 ++#define YYNRULES 82 + /* YYNSTATES -- Number of states. */ +-#define YYNSTATES 144 ++#define YYNSTATES 147 + + /* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned + by yylex, with out-of-bounds checking. */ + #define YYUNDEFTOK 2 +-#define YYMAXUTOK 278 ++#define YYMAXUTOK 279 + + #define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) +@@ -462,16 +465,16 @@ static const yytype_uint8 yytranslate[] + 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, 46, 2, 2, 2, 44, 40, 2, +- 32, 34, 43, 41, 33, 42, 2, 25, 2, 2, +- 2, 2, 2, 2, 2, 2, 2, 2, 37, 24, +- 35, 28, 29, 36, 2, 2, 2, 2, 2, 2, ++ 2, 2, 2, 47, 2, 2, 2, 45, 41, 2, ++ 33, 35, 44, 42, 34, 43, 2, 26, 2, 2, ++ 2, 2, 2, 2, 2, 2, 2, 2, 38, 25, ++ 36, 29, 30, 37, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +- 2, 30, 2, 31, 39, 2, 2, 2, 2, 2, ++ 2, 31, 2, 32, 40, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +- 2, 2, 2, 26, 38, 27, 45, 2, 2, 2, ++ 2, 2, 2, 27, 39, 28, 46, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +@@ -486,22 +489,22 @@ static const yytype_uint8 yytranslate[] + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, +- 15, 16, 17, 18, 19, 20, 21, 22, 23 ++ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 + }; + + #if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ + static const yytype_uint16 yyrline[] = + { +- 0, 104, 104, 113, 116, 123, 127, 135, 139, 144, +- 155, 165, 180, 188, 191, 198, 202, 206, 210, 218, +- 222, 226, 230, 234, 250, 260, 268, 271, 275, 282, +- 298, 303, 322, 336, 343, 344, 345, 352, 356, 357, +- 361, 362, 366, 367, 371, 372, 376, 377, 381, 382, +- 386, 387, 388, 392, 393, 394, 395, 396, 400, 401, +- 402, 406, 407, 408, 412, 413, 414, 415, 419, 420, +- 421, 422, 427, 430, 434, 442, 445, 449, 457, 461, +- 465 ++ 0, 108, 108, 118, 121, 129, 132, 139, 143, 151, ++ 155, 160, 171, 181, 196, 204, 207, 214, 218, 222, ++ 226, 234, 238, 242, 246, 250, 266, 276, 284, 287, ++ 291, 298, 314, 319, 338, 352, 359, 360, 361, 368, ++ 372, 373, 377, 378, 382, 383, 387, 388, 392, 393, ++ 397, 398, 402, 403, 404, 408, 409, 410, 411, 412, ++ 416, 417, 418, 422, 423, 424, 428, 429, 430, 431, ++ 435, 436, 437, 438, 443, 446, 450, 458, 461, 465, ++ 473, 477, 481 + }; + #endif + +@@ -510,19 +513,19 @@ static const yytype_uint16 yyrline[] = + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ + static const char *const yytname[] = + { +- "$end", "error", "$undefined", "DT_V1", "DT_MEMRESERVE", "DT_LSHIFT", +- "DT_RSHIFT", "DT_LE", "DT_GE", "DT_EQ", "DT_NE", "DT_AND", "DT_OR", +- "DT_BITS", "DT_DEL_PROP", "DT_DEL_NODE", "DT_PROPNODENAME", "DT_LITERAL", +- "DT_CHAR_LITERAL", "DT_BYTE", "DT_STRING", "DT_LABEL", "DT_REF", +- "DT_INCBIN", "';'", "'/'", "'{'", "'}'", "'='", "'>'", "'['", "']'", +- "'('", "','", "')'", "'<'", "'?'", "':'", "'|'", "'^'", "'&'", "'+'", +- "'-'", "'*'", "'%'", "'~'", "'!'", "$accept", "sourcefile", +- "memreserves", "memreserve", "devicetree", "nodedef", "proplist", +- "propdef", "propdata", "propdataprefix", "arrayprefix", "integer_prim", +- "integer_expr", "integer_trinary", "integer_or", "integer_and", +- "integer_bitor", "integer_bitxor", "integer_bitand", "integer_eq", +- "integer_rela", "integer_shift", "integer_add", "integer_mul", +- "integer_unary", "bytestring", "subnodes", "subnode", YY_NULLPTR ++ "$end", "error", "$undefined", "DT_V1", "DT_PLUGIN", "DT_MEMRESERVE", ++ "DT_LSHIFT", "DT_RSHIFT", "DT_LE", "DT_GE", "DT_EQ", "DT_NE", "DT_AND", ++ "DT_OR", "DT_BITS", "DT_DEL_PROP", "DT_DEL_NODE", "DT_PROPNODENAME", ++ "DT_LITERAL", "DT_CHAR_LITERAL", "DT_BYTE", "DT_STRING", "DT_LABEL", ++ "DT_REF", "DT_INCBIN", "';'", "'/'", "'{'", "'}'", "'='", "'>'", "'['", ++ "']'", "'('", "','", "')'", "'<'", "'?'", "':'", "'|'", "'^'", "'&'", ++ "'+'", "'-'", "'*'", "'%'", "'~'", "'!'", "$accept", "sourcefile", ++ "plugindecl", "memreserves", "memreserve", "devicetree", "nodedef", ++ "proplist", "propdef", "propdata", "propdataprefix", "arrayprefix", ++ "integer_prim", "integer_expr", "integer_trinary", "integer_or", ++ "integer_and", "integer_bitor", "integer_bitxor", "integer_bitand", ++ "integer_eq", "integer_rela", "integer_shift", "integer_add", ++ "integer_mul", "integer_unary", "bytestring", "subnodes", "subnode", YY_NULLPTR + }; + #endif + +@@ -533,16 +536,16 @@ static const yytype_uint16 yytoknum[] = + { + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, +- 275, 276, 277, 278, 59, 47, 123, 125, 61, 62, +- 91, 93, 40, 44, 41, 60, 63, 58, 124, 94, +- 38, 43, 45, 42, 37, 126, 33 ++ 275, 276, 277, 278, 279, 59, 47, 123, 125, 61, ++ 62, 91, 93, 40, 44, 41, 60, 63, 58, 124, ++ 94, 38, 43, 45, 42, 37, 126, 33 + }; + # endif + +-#define YYPACT_NINF -81 ++#define YYPACT_NINF -84 + + #define yypact_value_is_default(Yystate) \ +- (!!((Yystate) == (-81))) ++ (!!((Yystate) == (-84))) + + #define YYTABLE_NINF -1 + +@@ -553,21 +556,21 @@ static const yytype_uint16 yytoknum[] = + STATE-NUM. */ + static const yytype_int8 yypact[] = + { +- 16, -11, 21, 10, -81, 25, 10, 19, 10, -81, +- -81, -9, 25, -81, 2, 51, -81, -9, -9, -9, +- -81, 1, -81, -6, 50, 14, 28, 29, 36, 3, +- 58, 44, -3, -81, 47, -81, -81, 65, 68, 2, +- 2, -81, -81, -81, -81, -9, -9, -9, -9, -9, +- -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, +- -9, -9, -9, -9, -81, 63, 69, 2, -81, -81, +- 50, 57, 14, 28, 29, 36, 3, 3, 58, 58, +- 58, 58, 44, 44, -3, -3, -81, -81, -81, 79, +- 80, -8, 63, -81, 72, 63, -81, -81, -9, 76, +- 77, -81, -81, -81, -81, -81, 78, -81, -81, -81, +- -81, -81, 35, 4, -81, -81, -81, -81, 86, -81, +- -81, -81, 73, -81, -81, 33, 71, 84, 39, -81, +- -81, -81, -81, -81, 41, -81, -81, -81, 25, -81, +- 74, 25, 75, -81 ++ 15, -12, 35, 42, -84, 27, 9, -84, 24, 9, ++ 43, 9, -84, -84, -10, 24, -84, 60, 44, -84, ++ -10, -10, -10, -84, 55, -84, -7, 52, 53, 51, ++ 54, 10, 2, 38, 37, -4, -84, 68, -84, -84, ++ 71, 73, 60, 60, -84, -84, -84, -84, -10, -10, ++ -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, ++ -10, -10, -10, -10, -10, -10, -10, -84, 56, 72, ++ 60, -84, -84, 52, 61, 53, 51, 54, 10, 2, ++ 2, 38, 38, 38, 38, 37, 37, -4, -4, -84, ++ -84, -84, 81, 83, 34, 56, -84, 74, 56, -84, ++ -84, -10, 76, 78, -84, -84, -84, -84, -84, 79, ++ -84, -84, -84, -84, -84, -6, 3, -84, -84, -84, ++ -84, 87, -84, -84, -84, 75, -84, -84, 32, 70, ++ 86, 36, -84, -84, -84, -84, -84, 47, -84, -84, ++ -84, 24, -84, 77, 24, 80, -84 + }; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. +@@ -575,37 +578,37 @@ static const yytype_int8 yypact[] = + means the default is an error. */ + static const yytype_uint8 yydefact[] = + { +- 0, 0, 0, 3, 1, 0, 0, 0, 3, 34, +- 35, 0, 0, 6, 0, 2, 4, 0, 0, 0, +- 68, 0, 37, 38, 40, 42, 44, 46, 48, 50, +- 53, 60, 63, 67, 0, 13, 7, 0, 0, 0, +- 0, 69, 70, 71, 36, 0, 0, 0, 0, 0, ++ 0, 0, 0, 3, 1, 0, 5, 4, 0, 0, ++ 0, 5, 36, 37, 0, 0, 8, 0, 2, 6, ++ 0, 0, 0, 70, 0, 39, 40, 42, 44, 46, ++ 48, 50, 52, 55, 62, 65, 69, 0, 15, 9, ++ 0, 0, 0, 0, 71, 72, 73, 38, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 5, 75, 0, 0, 10, 8, +- 41, 0, 43, 45, 47, 49, 51, 52, 56, 57, +- 55, 54, 58, 59, 61, 62, 65, 64, 66, 0, +- 0, 0, 0, 14, 0, 75, 11, 9, 0, 0, +- 0, 16, 26, 78, 18, 80, 0, 77, 76, 39, +- 17, 79, 0, 0, 12, 25, 15, 27, 0, 19, +- 28, 22, 0, 72, 30, 0, 0, 0, 0, 33, +- 32, 20, 31, 29, 0, 73, 74, 21, 0, 24, +- 0, 0, 0, 23 ++ 0, 0, 0, 0, 0, 0, 0, 7, 77, 0, ++ 0, 12, 10, 43, 0, 45, 47, 49, 51, 53, ++ 54, 58, 59, 57, 56, 60, 61, 63, 64, 67, ++ 66, 68, 0, 0, 0, 0, 16, 0, 77, 13, ++ 11, 0, 0, 0, 18, 28, 80, 20, 82, 0, ++ 79, 78, 41, 19, 81, 0, 0, 14, 27, 17, ++ 29, 0, 21, 30, 24, 0, 74, 32, 0, 0, ++ 0, 0, 35, 34, 22, 33, 31, 0, 75, 76, ++ 23, 0, 26, 0, 0, 0, 25 + }; + + /* YYPGOTO[NTERM-NUM]. */ + static const yytype_int8 yypgoto[] = + { +- -81, -81, 100, 104, -81, -38, -81, -80, -81, -81, +- -81, -5, 66, 13, -81, 70, 67, 81, 64, 82, +- 37, 27, 34, 38, -14, -81, 22, 24 ++ -84, -84, -84, 98, 101, -84, -41, -84, -83, -84, ++ -84, -84, -8, 63, 12, -84, 66, 67, 65, 69, ++ 82, 29, 18, 25, 26, -17, -84, 20, 28 + }; + + /* YYDEFGOTO[NTERM-NUM]. */ + static const yytype_int16 yydefgoto[] = + { +- -1, 2, 7, 8, 15, 36, 65, 93, 112, 113, +- 125, 20, 21, 22, 23, 24, 25, 26, 27, 28, +- 29, 30, 31, 32, 33, 128, 94, 95 ++ -1, 2, 6, 10, 11, 18, 39, 68, 96, 115, ++ 116, 128, 23, 24, 25, 26, 27, 28, 29, 30, ++ 31, 32, 33, 34, 35, 36, 131, 97, 98 + }; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If +@@ -613,87 +616,87 @@ static const yytype_int16 yydefgoto[] = + number is the opposite. If YYTABLE_NINF, syntax error. */ + static const yytype_uint8 yytable[] = + { +- 12, 68, 69, 41, 42, 43, 45, 34, 9, 10, +- 53, 54, 104, 3, 5, 107, 101, 118, 35, 1, +- 102, 4, 61, 11, 119, 120, 121, 122, 35, 97, +- 46, 6, 55, 17, 123, 44, 18, 19, 56, 124, +- 62, 63, 9, 10, 14, 51, 52, 86, 87, 88, +- 9, 10, 48, 103, 129, 130, 115, 11, 135, 116, +- 136, 47, 131, 57, 58, 11, 37, 49, 117, 50, +- 137, 64, 38, 39, 138, 139, 40, 89, 90, 91, +- 78, 79, 80, 81, 92, 59, 60, 66, 76, 77, +- 67, 82, 83, 96, 98, 99, 100, 84, 85, 106, +- 110, 111, 114, 126, 134, 127, 133, 141, 16, 143, +- 13, 109, 71, 74, 72, 70, 105, 108, 0, 0, +- 132, 0, 0, 0, 0, 0, 0, 0, 0, 73, +- 0, 0, 75, 140, 0, 0, 142 ++ 15, 71, 72, 44, 45, 46, 48, 37, 12, 13, ++ 56, 57, 107, 3, 8, 110, 118, 121, 1, 119, ++ 54, 55, 64, 14, 122, 123, 124, 125, 120, 100, ++ 49, 9, 58, 20, 126, 4, 21, 22, 59, 127, ++ 65, 66, 12, 13, 60, 61, 5, 89, 90, 91, ++ 12, 13, 7, 106, 132, 133, 138, 14, 139, 104, ++ 40, 38, 134, 105, 50, 14, 41, 42, 140, 17, ++ 43, 92, 93, 94, 81, 82, 83, 84, 95, 62, ++ 63, 141, 142, 79, 80, 85, 86, 38, 87, 88, ++ 47, 52, 51, 67, 69, 53, 70, 99, 102, 101, ++ 103, 113, 109, 114, 117, 129, 136, 137, 130, 19, ++ 16, 144, 74, 112, 73, 146, 76, 75, 111, 0, ++ 135, 77, 0, 108, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 143, 0, 78, 145 + }; + + static const yytype_int16 yycheck[] = + { +- 5, 39, 40, 17, 18, 19, 12, 12, 17, 18, +- 7, 8, 92, 24, 4, 95, 24, 13, 26, 3, +- 28, 0, 25, 32, 20, 21, 22, 23, 26, 67, +- 36, 21, 29, 42, 30, 34, 45, 46, 35, 35, +- 43, 44, 17, 18, 25, 9, 10, 61, 62, 63, +- 17, 18, 38, 91, 21, 22, 21, 32, 19, 24, +- 21, 11, 29, 5, 6, 32, 15, 39, 33, 40, +- 31, 24, 21, 22, 33, 34, 25, 14, 15, 16, +- 53, 54, 55, 56, 21, 41, 42, 22, 51, 52, +- 22, 57, 58, 24, 37, 16, 16, 59, 60, 27, +- 24, 24, 24, 17, 20, 32, 35, 33, 8, 34, +- 6, 98, 46, 49, 47, 45, 92, 95, -1, -1, +- 125, -1, -1, -1, -1, -1, -1, -1, -1, 48, +- -1, -1, 50, 138, -1, -1, 141 ++ 8, 42, 43, 20, 21, 22, 13, 15, 18, 19, ++ 8, 9, 95, 25, 5, 98, 22, 14, 3, 25, ++ 10, 11, 26, 33, 21, 22, 23, 24, 34, 70, ++ 37, 22, 30, 43, 31, 0, 46, 47, 36, 36, ++ 44, 45, 18, 19, 6, 7, 4, 64, 65, 66, ++ 18, 19, 25, 94, 22, 23, 20, 33, 22, 25, ++ 16, 27, 30, 29, 12, 33, 22, 23, 32, 26, ++ 26, 15, 16, 17, 56, 57, 58, 59, 22, 42, ++ 43, 34, 35, 54, 55, 60, 61, 27, 62, 63, ++ 35, 40, 39, 25, 23, 41, 23, 25, 17, 38, ++ 17, 25, 28, 25, 25, 18, 36, 21, 33, 11, ++ 9, 34, 49, 101, 48, 35, 51, 50, 98, -1, ++ 128, 52, -1, 95, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, 141, -1, 53, 144 + }; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ + static const yytype_uint8 yystos[] = + { +- 0, 3, 48, 24, 0, 4, 21, 49, 50, 17, +- 18, 32, 58, 50, 25, 51, 49, 42, 45, 46, +- 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, +- 68, 69, 70, 71, 58, 26, 52, 15, 21, 22, +- 25, 71, 71, 71, 34, 12, 36, 11, 38, 39, +- 40, 9, 10, 7, 8, 29, 35, 5, 6, 41, +- 42, 25, 43, 44, 24, 53, 22, 22, 52, 52, +- 62, 59, 63, 64, 65, 66, 67, 67, 68, 68, +- 68, 68, 69, 69, 70, 70, 71, 71, 71, 14, +- 15, 16, 21, 54, 73, 74, 24, 52, 37, 16, +- 16, 24, 28, 52, 54, 74, 27, 54, 73, 60, +- 24, 24, 55, 56, 24, 21, 24, 33, 13, 20, +- 21, 22, 23, 30, 35, 57, 17, 32, 72, 21, +- 22, 29, 58, 35, 20, 19, 21, 31, 33, 34, +- 58, 33, 58, 34 ++ 0, 3, 49, 25, 0, 4, 50, 25, 5, 22, ++ 51, 52, 18, 19, 33, 60, 52, 26, 53, 51, ++ 43, 46, 47, 60, 61, 62, 63, 64, 65, 66, ++ 67, 68, 69, 70, 71, 72, 73, 60, 27, 54, ++ 16, 22, 23, 26, 73, 73, 73, 35, 13, 37, ++ 12, 39, 40, 41, 10, 11, 8, 9, 30, 36, ++ 6, 7, 42, 43, 26, 44, 45, 25, 55, 23, ++ 23, 54, 54, 64, 61, 65, 66, 67, 68, 69, ++ 69, 70, 70, 70, 70, 71, 71, 72, 72, 73, ++ 73, 73, 15, 16, 17, 22, 56, 75, 76, 25, ++ 54, 38, 17, 17, 25, 29, 54, 56, 76, 28, ++ 56, 75, 62, 25, 25, 57, 58, 25, 22, 25, ++ 34, 14, 21, 22, 23, 24, 31, 36, 59, 18, ++ 33, 74, 22, 23, 30, 60, 36, 21, 20, 22, ++ 32, 34, 35, 60, 34, 60, 35 + }; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ + static const yytype_uint8 yyr1[] = + { +- 0, 47, 48, 49, 49, 50, 50, 51, 51, 51, +- 51, 51, 52, 53, 53, 54, 54, 54, 54, 55, +- 55, 55, 55, 55, 55, 55, 56, 56, 56, 57, +- 57, 57, 57, 57, 58, 58, 58, 59, 60, 60, +- 61, 61, 62, 62, 63, 63, 64, 64, 65, 65, +- 66, 66, 66, 67, 67, 67, 67, 67, 68, 68, +- 68, 69, 69, 69, 70, 70, 70, 70, 71, 71, +- 71, 71, 72, 72, 72, 73, 73, 73, 74, 74, +- 74 ++ 0, 48, 49, 50, 50, 51, 51, 52, 52, 53, ++ 53, 53, 53, 53, 54, 55, 55, 56, 56, 56, ++ 56, 57, 57, 57, 57, 57, 57, 57, 58, 58, ++ 58, 59, 59, 59, 59, 59, 60, 60, 60, 61, ++ 62, 62, 63, 63, 64, 64, 65, 65, 66, 66, ++ 67, 67, 68, 68, 68, 69, 69, 69, 69, 69, ++ 70, 70, 70, 71, 71, 71, 72, 72, 72, 72, ++ 73, 73, 73, 73, 74, 74, 74, 75, 75, 75, ++ 76, 76, 76 + }; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ + static const yytype_uint8 yyr2[] = + { +- 0, 2, 4, 0, 2, 4, 2, 2, 3, 4, +- 3, 4, 5, 0, 2, 4, 2, 3, 2, 2, +- 3, 4, 2, 9, 5, 2, 0, 2, 2, 3, +- 1, 2, 2, 2, 1, 1, 3, 1, 1, 5, +- 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, +- 1, 3, 3, 1, 3, 3, 3, 3, 3, 3, +- 1, 3, 3, 1, 3, 3, 3, 1, 1, 2, +- 2, 2, 0, 2, 2, 0, 2, 2, 2, 3, +- 2 ++ 0, 2, 5, 0, 2, 0, 2, 4, 2, 2, ++ 3, 4, 3, 4, 5, 0, 2, 4, 2, 3, ++ 2, 2, 3, 4, 2, 9, 5, 2, 0, 2, ++ 2, 3, 1, 2, 2, 2, 1, 1, 3, 1, ++ 1, 5, 1, 3, 1, 3, 1, 3, 1, 3, ++ 1, 3, 1, 3, 3, 1, 3, 3, 3, 3, ++ 3, 3, 1, 3, 3, 1, 3, 3, 3, 1, ++ 1, 2, 2, 2, 0, 2, 2, 0, 2, 2, ++ 2, 3, 2 + }; + + +@@ -1463,65 +1466,82 @@ yyreduce: + switch (yyn) + { + case 2: +-#line 105 "dtc-parser.y" /* yacc.c:1646 */ ++#line 109 "dtc-parser.y" /* yacc.c:1646 */ + { ++ (yyvsp[0].node)->is_plugin = (yyvsp[-2].is_plugin); + the_boot_info = build_boot_info((yyvsp[-1].re), (yyvsp[0].node), + guess_boot_cpuid((yyvsp[0].node))); + } +-#line 1472 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1476 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 3: +-#line 113 "dtc-parser.y" /* yacc.c:1646 */ ++#line 118 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.re) = NULL; ++ (yyval.is_plugin) = false; + } +-#line 1480 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1484 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 4: +-#line 117 "dtc-parser.y" /* yacc.c:1646 */ ++#line 122 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.re) = chain_reserve_entry((yyvsp[-1].re), (yyvsp[0].re)); ++ (yyval.is_plugin) = true; + } +-#line 1488 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1492 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 5: +-#line 124 "dtc-parser.y" /* yacc.c:1646 */ ++#line 129 "dtc-parser.y" /* yacc.c:1646 */ + { +- (yyval.re) = build_reserve_entry((yyvsp[-2].integer), (yyvsp[-1].integer)); ++ (yyval.re) = NULL; + } +-#line 1496 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1500 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 6: +-#line 128 "dtc-parser.y" /* yacc.c:1646 */ ++#line 133 "dtc-parser.y" /* yacc.c:1646 */ ++ { ++ (yyval.re) = chain_reserve_entry((yyvsp[-1].re), (yyvsp[0].re)); ++ } ++#line 1508 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ break; ++ ++ case 7: ++#line 140 "dtc-parser.y" /* yacc.c:1646 */ ++ { ++ (yyval.re) = build_reserve_entry((yyvsp[-2].integer), (yyvsp[-1].integer)); ++ } ++#line 1516 "dtc-parser.tab.c" /* yacc.c:1646 */ ++ break; ++ ++ case 8: ++#line 144 "dtc-parser.y" /* yacc.c:1646 */ + { + add_label(&(yyvsp[0].re)->labels, (yyvsp[-1].labelref)); + (yyval.re) = (yyvsp[0].re); + } +-#line 1505 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1525 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 7: +-#line 136 "dtc-parser.y" /* yacc.c:1646 */ ++ case 9: ++#line 152 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.node) = name_node((yyvsp[0].node), ""); + } +-#line 1513 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1533 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 8: +-#line 140 "dtc-parser.y" /* yacc.c:1646 */ ++ case 10: ++#line 156 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.node) = merge_nodes((yyvsp[-2].node), (yyvsp[0].node)); + } +-#line 1521 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1541 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 9: +-#line 145 "dtc-parser.y" /* yacc.c:1646 */ ++ case 11: ++#line 161 "dtc-parser.y" /* yacc.c:1646 */ + { + struct node *target = get_node_by_ref((yyvsp[-3].node), (yyvsp[-1].labelref)); + +@@ -1532,11 +1552,11 @@ yyreduce: + ERROR(&(yylsp[-1]), "Label or path %s not found", (yyvsp[-1].labelref)); + (yyval.node) = (yyvsp[-3].node); + } +-#line 1536 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1556 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 10: +-#line 156 "dtc-parser.y" /* yacc.c:1646 */ ++ case 12: ++#line 172 "dtc-parser.y" /* yacc.c:1646 */ + { + struct node *target = get_node_by_ref((yyvsp[-2].node), (yyvsp[-1].labelref)); + +@@ -1546,11 +1566,11 @@ yyreduce: + ERROR(&(yylsp[-1]), "Label or path %s not found", (yyvsp[-1].labelref)); + (yyval.node) = (yyvsp[-2].node); + } +-#line 1550 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1570 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 11: +-#line 166 "dtc-parser.y" /* yacc.c:1646 */ ++ case 13: ++#line 182 "dtc-parser.y" /* yacc.c:1646 */ + { + struct node *target = get_node_by_ref((yyvsp[-3].node), (yyvsp[-1].labelref)); + +@@ -1562,100 +1582,100 @@ yyreduce: + + (yyval.node) = (yyvsp[-3].node); + } +-#line 1566 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1586 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 12: +-#line 181 "dtc-parser.y" /* yacc.c:1646 */ ++ case 14: ++#line 197 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.node) = build_node((yyvsp[-3].proplist), (yyvsp[-2].nodelist)); + } +-#line 1574 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1594 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 13: +-#line 188 "dtc-parser.y" /* yacc.c:1646 */ ++ case 15: ++#line 204 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.proplist) = NULL; + } +-#line 1582 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1602 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 14: +-#line 192 "dtc-parser.y" /* yacc.c:1646 */ ++ case 16: ++#line 208 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.proplist) = chain_property((yyvsp[0].prop), (yyvsp[-1].proplist)); + } +-#line 1590 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1610 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 15: +-#line 199 "dtc-parser.y" /* yacc.c:1646 */ ++ case 17: ++#line 215 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.prop) = build_property((yyvsp[-3].propnodename), (yyvsp[-1].data)); + } +-#line 1598 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1618 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 16: +-#line 203 "dtc-parser.y" /* yacc.c:1646 */ ++ case 18: ++#line 219 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.prop) = build_property((yyvsp[-1].propnodename), empty_data); + } +-#line 1606 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1626 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 17: +-#line 207 "dtc-parser.y" /* yacc.c:1646 */ ++ case 19: ++#line 223 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.prop) = build_property_delete((yyvsp[-1].propnodename)); + } +-#line 1614 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1634 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 18: +-#line 211 "dtc-parser.y" /* yacc.c:1646 */ ++ case 20: ++#line 227 "dtc-parser.y" /* yacc.c:1646 */ + { + add_label(&(yyvsp[0].prop)->labels, (yyvsp[-1].labelref)); + (yyval.prop) = (yyvsp[0].prop); + } +-#line 1623 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1643 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 19: +-#line 219 "dtc-parser.y" /* yacc.c:1646 */ ++ case 21: ++#line 235 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.data) = data_merge((yyvsp[-1].data), (yyvsp[0].data)); + } +-#line 1631 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1651 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 20: +-#line 223 "dtc-parser.y" /* yacc.c:1646 */ ++ case 22: ++#line 239 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.data) = data_merge((yyvsp[-2].data), (yyvsp[-1].array).data); + } +-#line 1639 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1659 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 21: +-#line 227 "dtc-parser.y" /* yacc.c:1646 */ ++ case 23: ++#line 243 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.data) = data_merge((yyvsp[-3].data), (yyvsp[-1].data)); + } +-#line 1647 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1667 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 22: +-#line 231 "dtc-parser.y" /* yacc.c:1646 */ ++ case 24: ++#line 247 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.data) = data_add_marker((yyvsp[-1].data), REF_PATH, (yyvsp[0].labelref)); + } +-#line 1655 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1675 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 23: +-#line 235 "dtc-parser.y" /* yacc.c:1646 */ ++ case 25: ++#line 251 "dtc-parser.y" /* yacc.c:1646 */ + { + FILE *f = srcfile_relative_open((yyvsp[-5].data).val, NULL); + struct data d; +@@ -1671,11 +1691,11 @@ yyreduce: + (yyval.data) = data_merge((yyvsp[-8].data), d); + fclose(f); + } +-#line 1675 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1695 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 24: +-#line 251 "dtc-parser.y" /* yacc.c:1646 */ ++ case 26: ++#line 267 "dtc-parser.y" /* yacc.c:1646 */ + { + FILE *f = srcfile_relative_open((yyvsp[-1].data).val, NULL); + struct data d = empty_data; +@@ -1685,43 +1705,43 @@ yyreduce: + (yyval.data) = data_merge((yyvsp[-4].data), d); + fclose(f); + } +-#line 1689 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1709 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 25: +-#line 261 "dtc-parser.y" /* yacc.c:1646 */ ++ case 27: ++#line 277 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.data) = data_add_marker((yyvsp[-1].data), LABEL, (yyvsp[0].labelref)); + } +-#line 1697 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1717 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 26: +-#line 268 "dtc-parser.y" /* yacc.c:1646 */ ++ case 28: ++#line 284 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.data) = empty_data; + } +-#line 1705 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1725 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 27: +-#line 272 "dtc-parser.y" /* yacc.c:1646 */ ++ case 29: ++#line 288 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.data) = (yyvsp[-1].data); + } +-#line 1713 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1733 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 28: +-#line 276 "dtc-parser.y" /* yacc.c:1646 */ ++ case 30: ++#line 292 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.data) = data_add_marker((yyvsp[-1].data), LABEL, (yyvsp[0].labelref)); + } +-#line 1721 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1741 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 29: +-#line 283 "dtc-parser.y" /* yacc.c:1646 */ ++ case 31: ++#line 299 "dtc-parser.y" /* yacc.c:1646 */ + { + unsigned long long bits; + +@@ -1737,20 +1757,20 @@ yyreduce: + (yyval.array).data = empty_data; + (yyval.array).bits = bits; + } +-#line 1741 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1761 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 30: +-#line 299 "dtc-parser.y" /* yacc.c:1646 */ ++ case 32: ++#line 315 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.array).data = empty_data; + (yyval.array).bits = 32; + } +-#line 1750 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1770 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 31: +-#line 304 "dtc-parser.y" /* yacc.c:1646 */ ++ case 33: ++#line 320 "dtc-parser.y" /* yacc.c:1646 */ + { + if ((yyvsp[-1].array).bits < 64) { + uint64_t mask = (1ULL << (yyvsp[-1].array).bits) - 1; +@@ -1769,11 +1789,11 @@ yyreduce: + + (yyval.array).data = data_append_integer((yyvsp[-1].array).data, (yyvsp[0].integer), (yyvsp[-1].array).bits); + } +-#line 1773 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1793 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 32: +-#line 323 "dtc-parser.y" /* yacc.c:1646 */ ++ case 34: ++#line 339 "dtc-parser.y" /* yacc.c:1646 */ + { + uint64_t val = ~0ULL >> (64 - (yyvsp[-1].array).bits); + +@@ -1787,233 +1807,233 @@ yyreduce: + + (yyval.array).data = data_append_integer((yyvsp[-1].array).data, val, (yyvsp[-1].array).bits); + } +-#line 1791 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1811 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 33: +-#line 337 "dtc-parser.y" /* yacc.c:1646 */ ++ case 35: ++#line 353 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.array).data = data_add_marker((yyvsp[-1].array).data, LABEL, (yyvsp[0].labelref)); + } +-#line 1799 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1819 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 36: +-#line 346 "dtc-parser.y" /* yacc.c:1646 */ ++ case 38: ++#line 362 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.integer) = (yyvsp[-1].integer); + } +-#line 1807 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1827 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 39: +-#line 357 "dtc-parser.y" /* yacc.c:1646 */ ++ case 41: ++#line 373 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-4].integer) ? (yyvsp[-2].integer) : (yyvsp[0].integer); } +-#line 1813 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1833 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 41: +-#line 362 "dtc-parser.y" /* yacc.c:1646 */ ++ case 43: ++#line 378 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-2].integer) || (yyvsp[0].integer); } +-#line 1819 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1839 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 43: +-#line 367 "dtc-parser.y" /* yacc.c:1646 */ ++ case 45: ++#line 383 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-2].integer) && (yyvsp[0].integer); } +-#line 1825 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1845 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 45: +-#line 372 "dtc-parser.y" /* yacc.c:1646 */ ++ case 47: ++#line 388 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-2].integer) | (yyvsp[0].integer); } +-#line 1831 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1851 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 47: +-#line 377 "dtc-parser.y" /* yacc.c:1646 */ ++ case 49: ++#line 393 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-2].integer) ^ (yyvsp[0].integer); } +-#line 1837 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1857 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 49: +-#line 382 "dtc-parser.y" /* yacc.c:1646 */ ++ case 51: ++#line 398 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-2].integer) & (yyvsp[0].integer); } +-#line 1843 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1863 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 51: +-#line 387 "dtc-parser.y" /* yacc.c:1646 */ ++ case 53: ++#line 403 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-2].integer) == (yyvsp[0].integer); } +-#line 1849 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1869 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 52: +-#line 388 "dtc-parser.y" /* yacc.c:1646 */ ++ case 54: ++#line 404 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-2].integer) != (yyvsp[0].integer); } +-#line 1855 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1875 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 54: +-#line 393 "dtc-parser.y" /* yacc.c:1646 */ ++ case 56: ++#line 409 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-2].integer) < (yyvsp[0].integer); } +-#line 1861 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1881 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 55: +-#line 394 "dtc-parser.y" /* yacc.c:1646 */ ++ case 57: ++#line 410 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-2].integer) > (yyvsp[0].integer); } +-#line 1867 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1887 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 56: +-#line 395 "dtc-parser.y" /* yacc.c:1646 */ ++ case 58: ++#line 411 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-2].integer) <= (yyvsp[0].integer); } +-#line 1873 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1893 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 57: +-#line 396 "dtc-parser.y" /* yacc.c:1646 */ ++ case 59: ++#line 412 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-2].integer) >= (yyvsp[0].integer); } +-#line 1879 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1899 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 58: +-#line 400 "dtc-parser.y" /* yacc.c:1646 */ ++ case 60: ++#line 416 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-2].integer) << (yyvsp[0].integer); } +-#line 1885 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1905 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 59: +-#line 401 "dtc-parser.y" /* yacc.c:1646 */ ++ case 61: ++#line 417 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-2].integer) >> (yyvsp[0].integer); } +-#line 1891 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1911 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 61: +-#line 406 "dtc-parser.y" /* yacc.c:1646 */ ++ case 63: ++#line 422 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-2].integer) + (yyvsp[0].integer); } +-#line 1897 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1917 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 62: +-#line 407 "dtc-parser.y" /* yacc.c:1646 */ ++ case 64: ++#line 423 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-2].integer) - (yyvsp[0].integer); } +-#line 1903 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1923 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 64: +-#line 412 "dtc-parser.y" /* yacc.c:1646 */ ++ case 66: ++#line 428 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-2].integer) * (yyvsp[0].integer); } +-#line 1909 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1929 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 65: +-#line 413 "dtc-parser.y" /* yacc.c:1646 */ ++ case 67: ++#line 429 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-2].integer) / (yyvsp[0].integer); } +-#line 1915 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1935 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 66: +-#line 414 "dtc-parser.y" /* yacc.c:1646 */ ++ case 68: ++#line 430 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = (yyvsp[-2].integer) % (yyvsp[0].integer); } +-#line 1921 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1941 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 69: +-#line 420 "dtc-parser.y" /* yacc.c:1646 */ ++ case 71: ++#line 436 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = -(yyvsp[0].integer); } +-#line 1927 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1947 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 70: +-#line 421 "dtc-parser.y" /* yacc.c:1646 */ ++ case 72: ++#line 437 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = ~(yyvsp[0].integer); } +-#line 1933 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1953 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 71: +-#line 422 "dtc-parser.y" /* yacc.c:1646 */ ++ case 73: ++#line 438 "dtc-parser.y" /* yacc.c:1646 */ + { (yyval.integer) = !(yyvsp[0].integer); } +-#line 1939 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1959 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 72: +-#line 427 "dtc-parser.y" /* yacc.c:1646 */ ++ case 74: ++#line 443 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.data) = empty_data; + } +-#line 1947 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1967 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 73: +-#line 431 "dtc-parser.y" /* yacc.c:1646 */ ++ case 75: ++#line 447 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.data) = data_append_byte((yyvsp[-1].data), (yyvsp[0].byte)); + } +-#line 1955 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1975 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 74: +-#line 435 "dtc-parser.y" /* yacc.c:1646 */ ++ case 76: ++#line 451 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.data) = data_add_marker((yyvsp[-1].data), LABEL, (yyvsp[0].labelref)); + } +-#line 1963 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1983 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 75: +-#line 442 "dtc-parser.y" /* yacc.c:1646 */ ++ case 77: ++#line 458 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.nodelist) = NULL; + } +-#line 1971 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1991 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 76: +-#line 446 "dtc-parser.y" /* yacc.c:1646 */ ++ case 78: ++#line 462 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.nodelist) = chain_node((yyvsp[-1].node), (yyvsp[0].nodelist)); + } +-#line 1979 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 1999 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 77: +-#line 450 "dtc-parser.y" /* yacc.c:1646 */ ++ case 79: ++#line 466 "dtc-parser.y" /* yacc.c:1646 */ + { + ERROR(&(yylsp[0]), "Properties must precede subnodes"); + YYERROR; + } +-#line 1988 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 2008 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 78: +-#line 458 "dtc-parser.y" /* yacc.c:1646 */ ++ case 80: ++#line 474 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.node) = name_node((yyvsp[0].node), (yyvsp[-1].propnodename)); + } +-#line 1996 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 2016 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 79: +-#line 462 "dtc-parser.y" /* yacc.c:1646 */ ++ case 81: ++#line 478 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.node) = name_node(build_node_delete(), (yyvsp[-1].propnodename)); + } +-#line 2004 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 2024 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + +- case 80: +-#line 466 "dtc-parser.y" /* yacc.c:1646 */ ++ case 82: ++#line 482 "dtc-parser.y" /* yacc.c:1646 */ + { + add_label(&(yyvsp[0].node)->labels, (yyvsp[-1].labelref)); + (yyval.node) = (yyvsp[0].node); + } +-#line 2013 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 2033 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + +-#line 2017 "dtc-parser.tab.c" /* yacc.c:1646 */ ++#line 2037 "dtc-parser.tab.c" /* yacc.c:1646 */ + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires +@@ -2248,7 +2268,7 @@ yyreturn: + #endif + return yyresult; + } +-#line 472 "dtc-parser.y" /* yacc.c:1906 */ ++#line 488 "dtc-parser.y" /* yacc.c:1906 */ + + + void yyerror(char const *s) +--- a/scripts/dtc/dtc-parser.tab.h_shipped ++++ b/scripts/dtc/dtc-parser.tab.h_shipped +@@ -46,26 +46,27 @@ extern int yydebug; + enum yytokentype + { + DT_V1 = 258, +- DT_MEMRESERVE = 259, +- DT_LSHIFT = 260, +- DT_RSHIFT = 261, +- DT_LE = 262, +- DT_GE = 263, +- DT_EQ = 264, +- DT_NE = 265, +- DT_AND = 266, +- DT_OR = 267, +- DT_BITS = 268, +- DT_DEL_PROP = 269, +- DT_DEL_NODE = 270, +- DT_PROPNODENAME = 271, +- DT_LITERAL = 272, +- DT_CHAR_LITERAL = 273, +- DT_BYTE = 274, +- DT_STRING = 275, +- DT_LABEL = 276, +- DT_REF = 277, +- DT_INCBIN = 278 ++ DT_PLUGIN = 259, ++ DT_MEMRESERVE = 260, ++ DT_LSHIFT = 261, ++ DT_RSHIFT = 262, ++ DT_LE = 263, ++ DT_GE = 264, ++ DT_EQ = 265, ++ DT_NE = 266, ++ DT_AND = 267, ++ DT_OR = 268, ++ DT_BITS = 269, ++ DT_DEL_PROP = 270, ++ DT_DEL_NODE = 271, ++ DT_PROPNODENAME = 272, ++ DT_LITERAL = 273, ++ DT_CHAR_LITERAL = 274, ++ DT_BYTE = 275, ++ DT_STRING = 276, ++ DT_LABEL = 277, ++ DT_REF = 278, ++ DT_INCBIN = 279 + }; + #endif + +@@ -74,7 +75,7 @@ extern int yydebug; + typedef union YYSTYPE YYSTYPE; + union YYSTYPE + { +-#line 38 "dtc-parser.y" /* yacc.c:1909 */ ++#line 39 "dtc-parser.y" /* yacc.c:1909 */ + + char *propnodename; + char *labelref; +@@ -92,8 +93,9 @@ union YYSTYPE + struct node *nodelist; + struct reserve_info *re; + uint64_t integer; ++ bool is_plugin; + +-#line 97 "dtc-parser.tab.h" /* yacc.c:1909 */ ++#line 99 "dtc-parser.tab.h" /* yacc.c:1909 */ + }; + # define YYSTYPE_IS_TRIVIAL 1 + # define YYSTYPE_IS_DECLARED 1 +--- a/scripts/dtc/dtc-parser.y ++++ b/scripts/dtc/dtc-parser.y +@@ -19,6 +19,7 @@ + */ + %{ + #include ++#include + + #include "dtc.h" + #include "srcpos.h" +@@ -52,9 +53,11 @@ extern bool treesource_error; + struct node *nodelist; + struct reserve_info *re; + uint64_t integer; ++ bool is_plugin; + } + + %token DT_V1 ++%token DT_PLUGIN + %token DT_MEMRESERVE + %token DT_LSHIFT DT_RSHIFT DT_LE DT_GE DT_EQ DT_NE DT_AND DT_OR + %token DT_BITS +@@ -71,6 +74,7 @@ extern bool treesource_error; + + %type propdata + %type propdataprefix ++%type plugindecl + %type memreserve + %type memreserves + %type arrayprefix +@@ -101,10 +105,22 @@ extern bool treesource_error; + %% + + sourcefile: +- DT_V1 ';' memreserves devicetree ++ DT_V1 ';' plugindecl memreserves devicetree + { +- the_boot_info = build_boot_info($3, $4, +- guess_boot_cpuid($4)); ++ $5->is_plugin = $3; ++ the_boot_info = build_boot_info($4, $5, ++ guess_boot_cpuid($5)); ++ } ++ ; ++ ++plugindecl: ++ /* empty */ ++ { ++ $$ = false; ++ } ++ | DT_PLUGIN ';' ++ { ++ $$ = true; + } + ; + +--- a/scripts/dtc/dtc.c ++++ b/scripts/dtc/dtc.c +@@ -29,6 +29,7 @@ int reservenum; /* Number of memory res + int minsize; /* Minimum blob size */ + int padsize; /* Additional padding to blob */ + int phandle_format = PHANDLE_BOTH; /* Use linux,phandle or phandle properties */ ++int symbol_fixup_support = 0; + + static void fill_fullpaths(struct node *tree, const char *prefix) + { +@@ -51,7 +52,7 @@ static void fill_fullpaths(struct node * + #define FDT_VERSION(version) _FDT_VERSION(version) + #define _FDT_VERSION(version) #version + static const char usage_synopsis[] = "dtc [options] "; +-static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:hv"; ++static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:@hv"; + static struct option const usage_long_opts[] = { + {"quiet", no_argument, NULL, 'q'}, + {"in-format", a_argument, NULL, 'I'}, +@@ -69,6 +70,7 @@ static struct option const usage_long_op + {"phandle", a_argument, NULL, 'H'}, + {"warning", a_argument, NULL, 'W'}, + {"error", a_argument, NULL, 'E'}, ++ {"symbols", no_argument, NULL, '@'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, + {NULL, no_argument, NULL, 0x0}, +@@ -99,6 +101,7 @@ static const char * const usage_opts_hel + "\t\tboth - Both \"linux,phandle\" and \"phandle\" properties", + "\n\tEnable/disable warnings (prefix with \"no-\")", + "\n\tEnable/disable errors (prefix with \"no-\")", ++ "\n\tEnable symbols/fixup support", + "\n\tPrint this help and exit", + "\n\tPrint version and exit", + NULL, +@@ -186,7 +189,9 @@ int main(int argc, char *argv[]) + case 'E': + parse_checks_option(false, true, optarg); + break; +- ++ case '@': ++ symbol_fixup_support = 1; ++ break; + case 'h': + usage(NULL); + default: +--- a/scripts/dtc/dtc.h ++++ b/scripts/dtc/dtc.h +@@ -54,6 +54,7 @@ extern int reservenum; /* Number of mem + extern int minsize; /* Minimum blob size */ + extern int padsize; /* Additional padding to blob */ + extern int phandle_format; /* Use linux,phandle or phandle properties */ ++extern int symbol_fixup_support;/* enable symbols & fixup support */ + + #define PHANDLE_LEGACY 0x1 + #define PHANDLE_EPAPR 0x2 +@@ -132,6 +133,26 @@ struct label { + struct label *next; + }; + ++struct fixup_entry { ++ int offset; ++ struct node *node; ++ struct property *prop; ++ struct fixup_entry *next; ++ bool local_fixup_generated; ++}; ++ ++struct fixup { ++ char *ref; ++ struct fixup_entry *entries; ++ struct fixup *next; ++}; ++ ++struct symbol { ++ struct label *label; ++ struct node *node; ++ struct symbol *next; ++}; ++ + struct property { + bool deleted; + char *name; +@@ -158,6 +179,13 @@ struct node { + int addr_cells, size_cells; + + struct label *labels; ++ ++ struct symbol *symbols; ++ struct fixup_entry *local_fixups; ++ bool emit_local_fixup_node; ++ ++ bool is_plugin; ++ struct fixup *fixups; + }; + + #define for_each_label_withdel(l0, l) \ +@@ -181,6 +209,18 @@ struct node { + for_each_child_withdel(n, c) \ + if (!(c)->deleted) + ++#define for_each_fixup(n, f) \ ++ for ((f) = (n)->fixups; (f); (f) = (f)->next) ++ ++#define for_each_fixup_entry(f, fe) \ ++ for ((fe) = (f)->entries; (fe); (fe) = (fe)->next) ++ ++#define for_each_symbol(n, s) \ ++ for ((s) = (n)->symbols; (s); (s) = (s)->next) ++ ++#define for_each_local_fixup_entry(n, fe) \ ++ for ((fe) = (n)->local_fixups; (fe); (fe) = (fe)->next) ++ + void add_label(struct label **labels, char *label); + void delete_labels(struct label **labels); + +--- a/scripts/dtc/flattree.c ++++ b/scripts/dtc/flattree.c +@@ -255,6 +255,204 @@ static int stringtable_insert(struct dat + return i; + } + ++static void emit_local_fixups(struct node *tree, struct emitter *emit, ++ void *etarget, struct data *strbuf, struct version_info *vi, ++ struct node *node) ++{ ++ struct fixup_entry *fe, *fen; ++ struct node *child; ++ int nameoff, count; ++ cell_t *buf; ++ struct data d; ++ ++ if (node->emit_local_fixup_node) { ++ ++ /* emit the external fixups (do not emit /) */ ++ if (node != tree) { ++ emit->beginnode(etarget, NULL); ++ emit->string(etarget, node->name, 0); ++ emit->align(etarget, sizeof(cell_t)); ++ } ++ ++ for_each_local_fixup_entry(tree, fe) { ++ if (fe->node != node || fe->local_fixup_generated) ++ continue; ++ ++ /* count the number of fixup entries */ ++ count = 0; ++ for_each_local_fixup_entry(tree, fen) { ++ if (fen->prop != fe->prop) ++ continue; ++ fen->local_fixup_generated = true; ++ count++; ++ } ++ ++ /* allocate buffer */ ++ buf = xmalloc(count * sizeof(cell_t)); ++ ++ /* collect all the offsets in buffer */ ++ count = 0; ++ for_each_local_fixup_entry(tree, fen) { ++ if (fen->prop != fe->prop) ++ continue; ++ fen->local_fixup_generated = true; ++ buf[count++] = cpu_to_fdt32(fen->offset); ++ } ++ d = empty_data; ++ d.len = count * sizeof(cell_t); ++ d.val = (char *)buf; ++ ++ nameoff = stringtable_insert(strbuf, fe->prop->name); ++ emit->property(etarget, fe->prop->labels); ++ emit->cell(etarget, count * sizeof(cell_t)); ++ emit->cell(etarget, nameoff); ++ ++ if ((vi->flags & FTF_VARALIGN) && ++ (count * sizeof(cell_t)) >= 8) ++ emit->align(etarget, 8); ++ ++ emit->data(etarget, d); ++ emit->align(etarget, sizeof(cell_t)); ++ ++ free(buf); ++ } ++ } ++ ++ for_each_child(node, child) ++ emit_local_fixups(tree, emit, etarget, strbuf, vi, child); ++ ++ if (node->emit_local_fixup_node && node != tree) ++ emit->endnode(etarget, tree->labels); ++} ++ ++static void emit_symbols_node(struct node *tree, struct emitter *emit, ++ void *etarget, struct data *strbuf, ++ struct version_info *vi) ++{ ++ struct symbol *sym; ++ int nameoff, vallen; ++ ++ /* do nothing if no symbols */ ++ if (!tree->symbols) ++ return; ++ ++ emit->beginnode(etarget, NULL); ++ emit->string(etarget, "__symbols__", 0); ++ emit->align(etarget, sizeof(cell_t)); ++ ++ for_each_symbol(tree, sym) { ++ ++ vallen = strlen(sym->node->fullpath); ++ ++ nameoff = stringtable_insert(strbuf, sym->label->label); ++ ++ emit->property(etarget, NULL); ++ emit->cell(etarget, vallen + 1); ++ emit->cell(etarget, nameoff); ++ ++ if ((vi->flags & FTF_VARALIGN) && vallen >= 8) ++ emit->align(etarget, 8); ++ ++ emit->string(etarget, sym->node->fullpath, ++ strlen(sym->node->fullpath)); ++ emit->align(etarget, sizeof(cell_t)); ++ } ++ ++ emit->endnode(etarget, NULL); ++} ++ ++static void emit_local_fixups_node(struct node *tree, struct emitter *emit, ++ void *etarget, struct data *strbuf, ++ struct version_info *vi) ++{ ++ struct fixup_entry *fe; ++ struct node *node; ++ ++ /* do nothing if no local fixups */ ++ if (!tree->local_fixups) ++ return; ++ ++ /* mark all nodes that need a local fixup generated (and parents) */ ++ for_each_local_fixup_entry(tree, fe) { ++ node = fe->node; ++ while (node != NULL && !node->emit_local_fixup_node) { ++ node->emit_local_fixup_node = true; ++ node = node->parent; ++ } ++ } ++ ++ /* emit the local fixups node now */ ++ emit->beginnode(etarget, NULL); ++ emit->string(etarget, "__local_fixups__", 0); ++ emit->align(etarget, sizeof(cell_t)); ++ ++ emit_local_fixups(tree, emit, etarget, strbuf, vi, tree); ++ ++ emit->endnode(etarget, tree->labels); ++} ++ ++static void emit_fixups_node(struct node *tree, struct emitter *emit, ++ void *etarget, struct data *strbuf, ++ struct version_info *vi) ++{ ++ struct fixup *f; ++ struct fixup_entry *fe; ++ char *name, *s; ++ const char *fullpath; ++ int namesz, nameoff, vallen; ++ ++ /* do nothing if no fixups */ ++ if (!tree->fixups) ++ return; ++ ++ /* emit the external fixups */ ++ emit->beginnode(etarget, NULL); ++ emit->string(etarget, "__fixups__", 0); ++ emit->align(etarget, sizeof(cell_t)); ++ ++ for_each_fixup(tree, f) { ++ ++ namesz = 0; ++ for_each_fixup_entry(f, fe) { ++ fullpath = fe->node->fullpath; ++ if (fullpath[0] == '\0') ++ fullpath = "/"; ++ namesz += strlen(fullpath) + 1; ++ namesz += strlen(fe->prop->name) + 1; ++ namesz += 32; /* space for : + '\0' */ ++ } ++ ++ name = xmalloc(namesz); ++ ++ s = name; ++ for_each_fixup_entry(f, fe) { ++ fullpath = fe->node->fullpath; ++ if (fullpath[0] == '\0') ++ fullpath = "/"; ++ snprintf(s, name + namesz - s, "%s:%s:%d", fullpath, ++ fe->prop->name, fe->offset); ++ s += strlen(s) + 1; ++ } ++ ++ nameoff = stringtable_insert(strbuf, f->ref); ++ vallen = s - name - 1; ++ ++ emit->property(etarget, NULL); ++ emit->cell(etarget, vallen + 1); ++ emit->cell(etarget, nameoff); ++ ++ if ((vi->flags & FTF_VARALIGN) && vallen >= 8) ++ emit->align(etarget, 8); ++ ++ emit->string(etarget, name, vallen); ++ emit->align(etarget, sizeof(cell_t)); ++ ++ free(name); ++ } ++ ++ emit->endnode(etarget, tree->labels); ++} ++ + static void flatten_tree(struct node *tree, struct emitter *emit, + void *etarget, struct data *strbuf, + struct version_info *vi) +@@ -310,6 +508,10 @@ static void flatten_tree(struct node *tr + flatten_tree(child, emit, etarget, strbuf, vi); + } + ++ emit_symbols_node(tree, emit, etarget, strbuf, vi); ++ emit_local_fixups_node(tree, emit, etarget, strbuf, vi); ++ emit_fixups_node(tree, emit, etarget, strbuf, vi); ++ + emit->endnode(etarget, tree->labels); + } + +--- a/scripts/dtc/version_gen.h ++++ b/scripts/dtc/version_gen.h +@@ -1 +1 @@ +-#define DTC_VERSION "DTC 1.4.1-g9d3649bd" ++#define DTC_VERSION "DTC 1.4.1-g25efc119" diff --git a/target/linux/brcm2708/patches-4.4/0178-config-Add-module-for-mcp3422-ADC.patch b/target/linux/brcm2708/patches-4.4/0178-config-Add-module-for-mcp3422-ADC.patch deleted file mode 100644 index d6c1a75fa8..0000000000 --- a/target/linux/brcm2708/patches-4.4/0178-config-Add-module-for-mcp3422-ADC.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 1e19acaf4b88ee6dcbe8843a07a04edf177be7c3 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 8 Mar 2016 17:06:33 +0000 -Subject: [PATCH 178/180] config: Add module for mcp3422 ADC - ---- - arch/arm/configs/bcm2709_defconfig | 1 + - arch/arm/configs/bcmrpi_defconfig | 1 + - 2 files changed, 2 insertions(+) - ---- a/arch/arm/configs/bcm2709_defconfig -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -1114,6 +1114,7 @@ CONFIG_IIO_BUFFER=y - CONFIG_IIO_BUFFER_CB=m - CONFIG_IIO_KFIFO_BUF=m - CONFIG_MCP320X=m -+CONFIG_MCP3422=m - CONFIG_DHT11=m - CONFIG_PWM_BCM2835=m - CONFIG_RASPBERRYPI_FIRMWARE=y ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -1121,6 +1121,7 @@ CONFIG_IIO_BUFFER=y - CONFIG_IIO_BUFFER_CB=m - CONFIG_IIO_KFIFO_BUF=m - CONFIG_MCP320X=m -+CONFIG_MCP3422=m - CONFIG_DHT11=m - CONFIG_PWM_BCM2835=m - CONFIG_RASPBERRYPI_FIRMWARE=y diff --git a/target/linux/brcm2708/patches-4.4/0178-configfs-implement-binary-attributes.patch b/target/linux/brcm2708/patches-4.4/0178-configfs-implement-binary-attributes.patch new file mode 100644 index 0000000000..27c83ea866 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0178-configfs-implement-binary-attributes.patch @@ -0,0 +1,639 @@ +From 7499c16ca9e47899ff65070af57588a8f541f707 Mon Sep 17 00:00:00 2001 +From: Pantelis Antoniou +Date: Thu, 22 Oct 2015 23:30:04 +0300 +Subject: [PATCH 178/232] configfs: implement binary attributes + +ConfigFS lacked binary attributes up until now. This patch +introduces support for binary attributes in a somewhat similar +manner of sysfs binary attributes albeit with changes that +fit the configfs usage model. + +Problems that configfs binary attributes fix are everything that +requires a binary blob as part of the configuration of a resource, +such as bitstream loading for FPGAs, DTBs for dynamically created +devices etc. + +Look at Documentation/filesystems/configfs/configfs.txt for internals +and howto use them. + +This patch is against linux-next as of today that contains +Christoph's configfs rework. + +Signed-off-by: Pantelis Antoniou +[hch: folded a fix from Geert Uytterhoeven ] +[hch: a few tiny updates based on review feedback] +Signed-off-by: Christoph Hellwig +--- + Documentation/filesystems/configfs/configfs.txt | 57 +++++- + fs/configfs/configfs_internal.h | 14 +- + fs/configfs/dir.c | 18 +- + fs/configfs/file.c | 255 +++++++++++++++++++++++- + fs/configfs/inode.c | 2 +- + include/linux/configfs.h | 50 +++++ + 6 files changed, 374 insertions(+), 22 deletions(-) + +--- a/Documentation/filesystems/configfs/configfs.txt ++++ b/Documentation/filesystems/configfs/configfs.txt +@@ -51,15 +51,27 @@ configfs tree is always there, whether m + An item is created via mkdir(2). The item's attributes will also + appear at this time. readdir(3) can determine what the attributes are, + read(2) can query their default values, and write(2) can store new +-values. Like sysfs, attributes should be ASCII text files, preferably +-with only one value per file. The same efficiency caveats from sysfs +-apply. Don't mix more than one attribute in one attribute file. +- +-Like sysfs, configfs expects write(2) to store the entire buffer at +-once. When writing to configfs attributes, userspace processes should +-first read the entire file, modify the portions they wish to change, and +-then write the entire buffer back. Attribute files have a maximum size +-of one page (PAGE_SIZE, 4096 on i386). ++values. Don't mix more than one attribute in one attribute file. ++ ++There are two types of configfs attributes: ++ ++* Normal attributes, which similar to sysfs attributes, are small ASCII text ++files, with a maximum size of one page (PAGE_SIZE, 4096 on i386). Preferably ++only one value per file should be used, and the same caveats from sysfs apply. ++Configfs expects write(2) to store the entire buffer at once. When writing to ++normal configfs attributes, userspace processes should first read the entire ++file, modify the portions they wish to change, and then write the entire ++buffer back. ++ ++* Binary attributes, which are somewhat similar to sysfs binary attributes, ++but with a few slight changes to semantics. The PAGE_SIZE limitation does not ++apply, but the whole binary item must fit in single kernel vmalloc'ed buffer. ++The write(2) calls from user space are buffered, and the attributes' ++write_bin_attribute method will be invoked on the final close, therefore it is ++imperative for user-space to check the return code of close(2) in order to ++verify that the operation finished successfully. ++To avoid a malicious user OOMing the kernel, there's a per-binary attribute ++maximum buffer value. + + When an item needs to be destroyed, remove it with rmdir(2). An + item cannot be destroyed if any other item has a link to it (via +@@ -171,6 +183,7 @@ among other things. For that, it needs + struct configfs_item_operations *ct_item_ops; + struct configfs_group_operations *ct_group_ops; + struct configfs_attribute **ct_attrs; ++ struct configfs_bin_attribute **ct_bin_attrs; + }; + + The most basic function of a config_item_type is to define what +@@ -201,6 +214,32 @@ be called whenever userspace asks for a + attribute is writable and provides a ->store method, that method will be + be called whenever userspace asks for a write(2) on the attribute. + ++[struct configfs_bin_attribute] ++ ++ struct configfs_attribute { ++ struct configfs_attribute cb_attr; ++ void *cb_private; ++ size_t cb_max_size; ++ }; ++ ++The binary attribute is used when the one needs to use binary blob to ++appear as the contents of a file in the item's configfs directory. ++To do so add the binary attribute to the NULL-terminated array ++config_item_type->ct_bin_attrs, and the item appears in configfs, the ++attribute file will appear with the configfs_bin_attribute->cb_attr.ca_name ++filename. configfs_bin_attribute->cb_attr.ca_mode specifies the file ++permissions. ++The cb_private member is provided for use by the driver, while the ++cb_max_size member specifies the maximum amount of vmalloc buffer ++to be used. ++ ++If binary attribute is readable and the config_item provides a ++ct_item_ops->read_bin_attribute() method, that method will be called ++whenever userspace asks for a read(2) on the attribute. The converse ++will happen for write(2). The reads/writes are bufferred so only a ++single read/write will occur; the attributes' need not concern itself ++with it. ++ + [struct config_group] + + A config_item cannot live in a vacuum. The only way one can be created +--- a/fs/configfs/configfs_internal.h ++++ b/fs/configfs/configfs_internal.h +@@ -53,13 +53,14 @@ struct configfs_dirent { + #define CONFIGFS_ROOT 0x0001 + #define CONFIGFS_DIR 0x0002 + #define CONFIGFS_ITEM_ATTR 0x0004 ++#define CONFIGFS_ITEM_BIN_ATTR 0x0008 + #define CONFIGFS_ITEM_LINK 0x0020 + #define CONFIGFS_USET_DIR 0x0040 + #define CONFIGFS_USET_DEFAULT 0x0080 + #define CONFIGFS_USET_DROPPING 0x0100 + #define CONFIGFS_USET_IN_MKDIR 0x0200 + #define CONFIGFS_USET_CREATING 0x0400 +-#define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR) ++#define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR | CONFIGFS_ITEM_BIN_ATTR) + + extern struct mutex configfs_symlink_mutex; + extern spinlock_t configfs_dirent_lock; +@@ -72,6 +73,8 @@ extern struct inode * configfs_new_inode + extern int configfs_create(struct dentry *, umode_t mode, void (*init)(struct inode *)); + + extern int configfs_create_file(struct config_item *, const struct configfs_attribute *); ++extern int configfs_create_bin_file(struct config_item *, ++ const struct configfs_bin_attribute *); + extern int configfs_make_dirent(struct configfs_dirent *, + struct dentry *, void *, umode_t, int); + extern int configfs_dirent_is_ready(struct configfs_dirent *); +@@ -88,7 +91,7 @@ extern void configfs_release_fs(void); + extern struct rw_semaphore configfs_rename_sem; + extern const struct file_operations configfs_dir_operations; + extern const struct file_operations configfs_file_operations; +-extern const struct file_operations bin_fops; ++extern const struct file_operations configfs_bin_file_operations; + extern const struct inode_operations configfs_dir_inode_operations; + extern const struct inode_operations configfs_root_inode_operations; + extern const struct inode_operations configfs_symlink_inode_operations; +@@ -119,6 +122,13 @@ static inline struct configfs_attribute + return ((struct configfs_attribute *) sd->s_element); + } + ++static inline struct configfs_bin_attribute *to_bin_attr(struct dentry *dentry) ++{ ++ struct configfs_attribute *attr = to_attr(dentry); ++ ++ return container_of(attr, struct configfs_bin_attribute, cb_attr); ++} ++ + static inline struct config_item *configfs_get_config_item(struct dentry *dentry) + { + struct config_item * item = NULL; +--- a/fs/configfs/dir.c ++++ b/fs/configfs/dir.c +@@ -255,6 +255,12 @@ static void configfs_init_file(struct in + inode->i_fop = &configfs_file_operations; + } + ++static void configfs_init_bin_file(struct inode *inode) ++{ ++ inode->i_size = 0; ++ inode->i_fop = &configfs_bin_file_operations; ++} ++ + static void init_symlink(struct inode * inode) + { + inode->i_op = &configfs_symlink_inode_operations; +@@ -423,7 +429,9 @@ static int configfs_attach_attr(struct c + spin_unlock(&configfs_dirent_lock); + + error = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG, +- configfs_init_file); ++ (sd->s_type & CONFIGFS_ITEM_BIN_ATTR) ? ++ configfs_init_bin_file : ++ configfs_init_file); + if (error) { + configfs_put(sd); + return error; +@@ -583,6 +591,7 @@ static int populate_attrs(struct config_ + { + struct config_item_type *t = item->ci_type; + struct configfs_attribute *attr; ++ struct configfs_bin_attribute *bin_attr; + int error = 0; + int i; + +@@ -594,6 +603,13 @@ static int populate_attrs(struct config_ + break; + } + } ++ if (t->ct_bin_attrs) { ++ for (i = 0; (bin_attr = t->ct_bin_attrs[i]) != NULL; i++) { ++ error = configfs_create_bin_file(item, bin_attr); ++ if (error) ++ break; ++ } ++ } + + if (error) + detach_attrs(item); +--- a/fs/configfs/file.c ++++ b/fs/configfs/file.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -48,6 +49,10 @@ struct configfs_buffer { + struct configfs_item_operations * ops; + struct mutex mutex; + int needs_read_fill; ++ bool read_in_progress; ++ bool write_in_progress; ++ char *bin_buffer; ++ int bin_buffer_size; + }; + + +@@ -123,6 +128,87 @@ out: + return retval; + } + ++/** ++ * configfs_read_bin_file - read a binary attribute. ++ * @file: file pointer. ++ * @buf: buffer to fill. ++ * @count: number of bytes to read. ++ * @ppos: starting offset in file. ++ * ++ * Userspace wants to read a binary attribute file. The attribute ++ * descriptor is in the file's ->d_fsdata. The target item is in the ++ * directory's ->d_fsdata. ++ * ++ * We check whether we need to refill the buffer. If so we will ++ * call the attributes' attr->read() twice. The first time we ++ * will pass a NULL as a buffer pointer, which the attributes' method ++ * will use to return the size of the buffer required. If no error ++ * occurs we will allocate the buffer using vmalloc and call ++ * attr->read() again passing that buffer as an argument. ++ * Then we just copy to user-space using simple_read_from_buffer. ++ */ ++ ++static ssize_t ++configfs_read_bin_file(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct configfs_buffer *buffer = file->private_data; ++ struct dentry *dentry = file->f_path.dentry; ++ struct config_item *item = to_item(dentry->d_parent); ++ struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry); ++ ssize_t retval = 0; ++ ssize_t len = min_t(size_t, count, PAGE_SIZE); ++ ++ mutex_lock(&buffer->mutex); ++ ++ /* we don't support switching read/write modes */ ++ if (buffer->write_in_progress) { ++ retval = -ETXTBSY; ++ goto out; ++ } ++ buffer->read_in_progress = 1; ++ ++ if (buffer->needs_read_fill) { ++ /* perform first read with buf == NULL to get extent */ ++ len = bin_attr->read(item, NULL, 0); ++ if (len <= 0) { ++ retval = len; ++ goto out; ++ } ++ ++ /* do not exceed the maximum value */ ++ if (bin_attr->cb_max_size && len > bin_attr->cb_max_size) { ++ retval = -EFBIG; ++ goto out; ++ } ++ ++ buffer->bin_buffer = vmalloc(len); ++ if (buffer->bin_buffer == NULL) { ++ retval = -ENOMEM; ++ goto out; ++ } ++ buffer->bin_buffer_size = len; ++ ++ /* perform second read to fill buffer */ ++ len = bin_attr->read(item, buffer->bin_buffer, len); ++ if (len < 0) { ++ retval = len; ++ vfree(buffer->bin_buffer); ++ buffer->bin_buffer_size = 0; ++ buffer->bin_buffer = NULL; ++ goto out; ++ } ++ ++ buffer->needs_read_fill = 0; ++ } ++ ++ retval = simple_read_from_buffer(buf, count, ppos, buffer->bin_buffer, ++ buffer->bin_buffer_size); ++out: ++ mutex_unlock(&buffer->mutex); ++ return retval; ++} ++ + + /** + * fill_write_buffer - copy buffer from userspace. +@@ -209,10 +295,80 @@ configfs_write_file(struct file *file, c + return len; + } + +-static int check_perm(struct inode * inode, struct file * file) ++/** ++ * configfs_write_bin_file - write a binary attribute. ++ * @file: file pointer ++ * @buf: data to write ++ * @count: number of bytes ++ * @ppos: starting offset ++ * ++ * Writing to a binary attribute file is similar to a normal read. ++ * We buffer the consecutive writes (binary attribute files do not ++ * support lseek) in a continuously growing buffer, but we don't ++ * commit until the close of the file. ++ */ ++ ++static ssize_t ++configfs_write_bin_file(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct configfs_buffer *buffer = file->private_data; ++ struct dentry *dentry = file->f_path.dentry; ++ struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry); ++ void *tbuf = NULL; ++ ssize_t len; ++ ++ mutex_lock(&buffer->mutex); ++ ++ /* we don't support switching read/write modes */ ++ if (buffer->read_in_progress) { ++ len = -ETXTBSY; ++ goto out; ++ } ++ buffer->write_in_progress = 1; ++ ++ /* buffer grows? */ ++ if (*ppos + count > buffer->bin_buffer_size) { ++ ++ if (bin_attr->cb_max_size && ++ *ppos + count > bin_attr->cb_max_size) { ++ len = -EFBIG; ++ } ++ ++ tbuf = vmalloc(*ppos + count); ++ if (tbuf == NULL) { ++ len = -ENOMEM; ++ goto out; ++ } ++ ++ /* copy old contents */ ++ if (buffer->bin_buffer) { ++ memcpy(tbuf, buffer->bin_buffer, ++ buffer->bin_buffer_size); ++ vfree(buffer->bin_buffer); ++ } ++ ++ /* clear the new area */ ++ memset(tbuf + buffer->bin_buffer_size, 0, ++ *ppos + count - buffer->bin_buffer_size); ++ buffer->bin_buffer = tbuf; ++ buffer->bin_buffer_size = *ppos + count; ++ } ++ ++ len = simple_write_to_buffer(buffer->bin_buffer, ++ buffer->bin_buffer_size, ppos, buf, count); ++ if (len > 0) ++ *ppos += len; ++out: ++ mutex_unlock(&buffer->mutex); ++ return len; ++} ++ ++static int check_perm(struct inode * inode, struct file * file, int type) + { + struct config_item *item = configfs_get_config_item(file->f_path.dentry->d_parent); + struct configfs_attribute * attr = to_attr(file->f_path.dentry); ++ struct configfs_bin_attribute *bin_attr = NULL; + struct configfs_buffer * buffer; + struct configfs_item_operations * ops = NULL; + int error = 0; +@@ -220,6 +376,9 @@ static int check_perm(struct inode * ino + if (!item || !attr) + goto Einval; + ++ if (type & CONFIGFS_ITEM_BIN_ATTR) ++ bin_attr = to_bin_attr(file->f_path.dentry); ++ + /* Grab the module reference for this attribute if we have one */ + if (!try_module_get(attr->ca_owner)) { + error = -ENODEV; +@@ -236,9 +395,14 @@ static int check_perm(struct inode * ino + * and we must have a store method. + */ + if (file->f_mode & FMODE_WRITE) { +- if (!(inode->i_mode & S_IWUGO) || !attr->store) ++ if (!(inode->i_mode & S_IWUGO)) + goto Eaccess; + ++ if ((type & CONFIGFS_ITEM_ATTR) && !attr->store) ++ goto Eaccess; ++ ++ if ((type & CONFIGFS_ITEM_BIN_ATTR) && !bin_attr->write) ++ goto Eaccess; + } + + /* File needs read support. +@@ -246,7 +410,13 @@ static int check_perm(struct inode * ino + * must be a show method for it. + */ + if (file->f_mode & FMODE_READ) { +- if (!(inode->i_mode & S_IRUGO) || !attr->show) ++ if (!(inode->i_mode & S_IRUGO)) ++ goto Eaccess; ++ ++ if ((type & CONFIGFS_ITEM_ATTR) && !attr->show) ++ goto Eaccess; ++ ++ if ((type & CONFIGFS_ITEM_BIN_ATTR) && !bin_attr->read) + goto Eaccess; + } + +@@ -260,6 +430,8 @@ static int check_perm(struct inode * ino + } + mutex_init(&buffer->mutex); + buffer->needs_read_fill = 1; ++ buffer->read_in_progress = 0; ++ buffer->write_in_progress = 0; + buffer->ops = ops; + file->private_data = buffer; + goto Done; +@@ -277,12 +449,7 @@ static int check_perm(struct inode * ino + return error; + } + +-static int configfs_open_file(struct inode * inode, struct file * filp) +-{ +- return check_perm(inode,filp); +-} +- +-static int configfs_release(struct inode * inode, struct file * filp) ++static int configfs_release(struct inode *inode, struct file *filp) + { + struct config_item * item = to_item(filp->f_path.dentry->d_parent); + struct configfs_attribute * attr = to_attr(filp->f_path.dentry); +@@ -303,6 +470,47 @@ static int configfs_release(struct inode + return 0; + } + ++static int configfs_open_file(struct inode *inode, struct file *filp) ++{ ++ return check_perm(inode, filp, CONFIGFS_ITEM_ATTR); ++} ++ ++static int configfs_open_bin_file(struct inode *inode, struct file *filp) ++{ ++ return check_perm(inode, filp, CONFIGFS_ITEM_BIN_ATTR); ++} ++ ++static int configfs_release_bin_file(struct inode *inode, struct file *filp) ++{ ++ struct configfs_buffer *buffer = filp->private_data; ++ struct dentry *dentry = filp->f_path.dentry; ++ struct config_item *item = to_item(dentry->d_parent); ++ struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry); ++ ssize_t len = 0; ++ int ret; ++ ++ buffer->read_in_progress = 0; ++ ++ if (buffer->write_in_progress) { ++ buffer->write_in_progress = 0; ++ ++ len = bin_attr->write(item, buffer->bin_buffer, ++ buffer->bin_buffer_size); ++ ++ /* vfree on NULL is safe */ ++ vfree(buffer->bin_buffer); ++ buffer->bin_buffer = NULL; ++ buffer->bin_buffer_size = 0; ++ buffer->needs_read_fill = 1; ++ } ++ ++ ret = configfs_release(inode, filp); ++ if (len < 0) ++ return len; ++ return ret; ++} ++ ++ + const struct file_operations configfs_file_operations = { + .read = configfs_read_file, + .write = configfs_write_file, +@@ -311,6 +519,14 @@ const struct file_operations configfs_fi + .release = configfs_release, + }; + ++const struct file_operations configfs_bin_file_operations = { ++ .read = configfs_read_bin_file, ++ .write = configfs_write_bin_file, ++ .llseek = NULL, /* bin file is not seekable */ ++ .open = configfs_open_bin_file, ++ .release = configfs_release_bin_file, ++}; ++ + /** + * configfs_create_file - create an attribute file for an item. + * @item: item we're creating for. +@@ -332,3 +548,24 @@ int configfs_create_file(struct config_i + return error; + } + ++/** ++ * configfs_create_bin_file - create a binary attribute file for an item. ++ * @item: item we're creating for. ++ * @attr: atrribute descriptor. ++ */ ++ ++int configfs_create_bin_file(struct config_item *item, ++ const struct configfs_bin_attribute *bin_attr) ++{ ++ struct dentry *dir = item->ci_dentry; ++ struct configfs_dirent *parent_sd = dir->d_fsdata; ++ umode_t mode = (bin_attr->cb_attr.ca_mode & S_IALLUGO) | S_IFREG; ++ int error = 0; ++ ++ mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_NORMAL); ++ error = configfs_make_dirent(parent_sd, NULL, (void *) bin_attr, mode, ++ CONFIGFS_ITEM_BIN_ATTR); ++ mutex_unlock(&dir->d_inode->i_mutex); ++ ++ return error; ++} +--- a/fs/configfs/inode.c ++++ b/fs/configfs/inode.c +@@ -218,7 +218,7 @@ const unsigned char * configfs_get_name( + if (sd->s_type & (CONFIGFS_DIR | CONFIGFS_ITEM_LINK)) + return sd->s_dentry->d_name.name; + +- if (sd->s_type & CONFIGFS_ITEM_ATTR) { ++ if (sd->s_type & (CONFIGFS_ITEM_ATTR | CONFIGFS_ITEM_BIN_ATTR)) { + attr = sd->s_element; + return attr->ca_name; + } +--- a/include/linux/configfs.h ++++ b/include/linux/configfs.h +@@ -51,6 +51,7 @@ struct module; + struct configfs_item_operations; + struct configfs_group_operations; + struct configfs_attribute; ++struct configfs_bin_attribute; + struct configfs_subsystem; + + struct config_item { +@@ -84,6 +85,7 @@ struct config_item_type { + struct configfs_item_operations *ct_item_ops; + struct configfs_group_operations *ct_group_ops; + struct configfs_attribute **ct_attrs; ++ struct configfs_bin_attribute **ct_bin_attrs; + }; + + /** +@@ -154,6 +156,54 @@ static struct configfs_attribute _pfx##a + .store = _pfx##_name##_store, \ + } + ++struct file; ++struct vm_area_struct; ++ ++struct configfs_bin_attribute { ++ struct configfs_attribute cb_attr; /* std. attribute */ ++ void *cb_private; /* for user */ ++ size_t cb_max_size; /* max core size */ ++ ssize_t (*read)(struct config_item *, void *, size_t); ++ ssize_t (*write)(struct config_item *, const void *, size_t); ++}; ++ ++#define CONFIGFS_BIN_ATTR(_pfx, _name, _priv, _maxsz) \ ++static struct configfs_bin_attribute _pfx##attr_##_name = { \ ++ .cb_attr = { \ ++ .ca_name = __stringify(_name), \ ++ .ca_mode = S_IRUGO | S_IWUSR, \ ++ .ca_owner = THIS_MODULE, \ ++ }, \ ++ .cb_private = _priv, \ ++ .cb_max_size = _maxsz, \ ++ .read = _pfx##_name##_read, \ ++ .write = _pfx##_name##_write, \ ++} ++ ++#define CONFIGFS_BIN_ATTR_RO(_pfx, _name, _priv, _maxsz) \ ++static struct configfs_attribute _pfx##attr_##_name = { \ ++ .cb_attr = { \ ++ .ca_name = __stringify(_name), \ ++ .ca_mode = S_IRUGO, \ ++ .ca_owner = THIS_MODULE, \ ++ }, \ ++ .cb_private = _priv, \ ++ .cb_max_size = _maxsz, \ ++ .read = _pfx##_name##_read, \ ++} ++ ++#define CONFIGFS_BIN_ATTR_WO(_pfx, _name, _priv, _maxsz) \ ++static struct configfs_attribute _pfx##attr_##_name = { \ ++ .cb_attr = { \ ++ .ca_name = __stringify(_name), \ ++ .ca_mode = S_IWUSR, \ ++ .ca_owner = THIS_MODULE, \ ++ }, \ ++ .cb_private = _priv, \ ++ .cb_max_size = _maxsz, \ ++ .write = _pfx##_name##_write, \ ++} ++ + /* + * If allow_link() exists, the item can symlink(2) out to other + * items. If the item is a group, it may support mkdir(2). diff --git a/target/linux/brcm2708/patches-4.4/0179-OF-DT-Overlay-configfs-interface.patch b/target/linux/brcm2708/patches-4.4/0179-OF-DT-Overlay-configfs-interface.patch new file mode 100644 index 0000000000..cb7eadb622 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0179-OF-DT-Overlay-configfs-interface.patch @@ -0,0 +1,408 @@ +From 54ca6e08ce89f5e33de78db7deff727c12513397 Mon Sep 17 00:00:00 2001 +From: Pantelis Antoniou +Date: Wed, 3 Dec 2014 13:23:28 +0200 +Subject: [PATCH 179/232] OF: DT-Overlay configfs interface + +This is a port of Pantelis Antoniou's v3 port that makes use of the +new upstreamed configfs support for binary attributes. + +Original commit message: + +Add a runtime interface to using configfs for generic device tree overlay +usage. With it its possible to use device tree overlays without having +to use a per-platform overlay manager. + +Please see Documentation/devicetree/configfs-overlays.txt for more info. + +Changes since v2: +- Removed ifdef CONFIG_OF_OVERLAY (since for now it's required) +- Created a documentation entry +- Slight rewording in Kconfig + +Changes since v1: +- of_resolve() -> of_resolve_phandles(). + +Originally-signed-off-by: Pantelis Antoniou +Signed-off-by: Phil Elwell +--- + Documentation/devicetree/configfs-overlays.txt | 31 +++ + drivers/of/Kconfig | 7 + + drivers/of/Makefile | 1 + + drivers/of/configfs.c | 314 +++++++++++++++++++++++++ + 4 files changed, 353 insertions(+) + create mode 100644 Documentation/devicetree/configfs-overlays.txt + create mode 100644 drivers/of/configfs.c + +--- /dev/null ++++ b/Documentation/devicetree/configfs-overlays.txt +@@ -0,0 +1,31 @@ ++Howto use the configfs overlay interface. ++ ++A device-tree configfs entry is created in /config/device-tree/overlays ++and and it is manipulated using standard file system I/O. ++Note that this is a debug level interface, for use by developers and ++not necessarily something accessed by normal users due to the ++security implications of having direct access to the kernel's device tree. ++ ++* To create an overlay you mkdir the directory: ++ ++ # mkdir /config/device-tree/overlays/foo ++ ++* Either you echo the overlay firmware file to the path property file. ++ ++ # echo foo.dtbo >/config/device-tree/overlays/foo/path ++ ++* Or you cat the contents of the overlay to the dtbo file ++ ++ # cat foo.dtbo >/config/device-tree/overlays/foo/dtbo ++ ++The overlay file will be applied, and devices will be created/destroyed ++as required. ++ ++To remove it simply rmdir the directory. ++ ++ # rmdir /config/device-tree/overlays/foo ++ ++The rationalle of the dual interface (firmware & direct copy) is that each is ++better suited to different use patterns. The firmware interface is what's ++intended to be used by hardware managers in the kernel, while the copy interface ++make sense for developers (since it avoids problems with namespaces). +--- a/drivers/of/Kconfig ++++ b/drivers/of/Kconfig +@@ -112,4 +112,11 @@ config OF_OVERLAY + While this option is selected automatically when needed, you can + enable it manually to improve device tree unit test coverage. + ++config OF_CONFIGFS ++ bool "Device Tree Overlay ConfigFS interface" ++ select CONFIGFS_FS ++ select OF_OVERLAY ++ help ++ Enable a simple user-space driven DT overlay interface. ++ + endif # OF +--- a/drivers/of/Makefile ++++ b/drivers/of/Makefile +@@ -1,4 +1,5 @@ + obj-y = base.o device.o platform.o ++obj-$(CONFIG_OF_CONFIGFS) += configfs.o + obj-$(CONFIG_OF_DYNAMIC) += dynamic.o + obj-$(CONFIG_OF_FLATTREE) += fdt.o + obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o +--- /dev/null ++++ b/drivers/of/configfs.c +@@ -0,0 +1,314 @@ ++/* ++ * Configfs entries for device-tree ++ * ++ * Copyright (C) 2013 - Pantelis Antoniou ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "of_private.h" ++ ++struct cfs_overlay_item { ++ struct config_item item; ++ ++ char path[PATH_MAX]; ++ ++ const struct firmware *fw; ++ struct device_node *overlay; ++ int ov_id; ++ ++ void *dtbo; ++ int dtbo_size; ++}; ++ ++static int create_overlay(struct cfs_overlay_item *overlay, void *blob) ++{ ++ int err; ++ ++ /* unflatten the tree */ ++ of_fdt_unflatten_tree(blob, &overlay->overlay); ++ if (overlay->overlay == NULL) { ++ pr_err("%s: failed to unflatten tree\n", __func__); ++ err = -EINVAL; ++ goto out_err; ++ } ++ pr_debug("%s: unflattened OK\n", __func__); ++ ++ /* mark it as detached */ ++ of_node_set_flag(overlay->overlay, OF_DETACHED); ++ ++ /* perform resolution */ ++ err = of_resolve_phandles(overlay->overlay); ++ if (err != 0) { ++ pr_err("%s: Failed to resolve tree\n", __func__); ++ goto out_err; ++ } ++ pr_debug("%s: resolved OK\n", __func__); ++ ++ err = of_overlay_create(overlay->overlay); ++ if (err < 0) { ++ pr_err("%s: Failed to create overlay (err=%d)\n", ++ __func__, err); ++ goto out_err; ++ } ++ overlay->ov_id = err; ++ ++out_err: ++ return err; ++} ++ ++static inline struct cfs_overlay_item *to_cfs_overlay_item( ++ struct config_item *item) ++{ ++ return item ? container_of(item, struct cfs_overlay_item, item) : NULL; ++} ++ ++static ssize_t cfs_overlay_item_path_show(struct config_item *item, ++ char *page) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ return sprintf(page, "%s\n", overlay->path); ++} ++ ++static ssize_t cfs_overlay_item_path_store(struct config_item *item, ++ const char *page, size_t count) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ const char *p = page; ++ char *s; ++ int err; ++ ++ /* if it's set do not allow changes */ ++ if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) ++ return -EPERM; ++ ++ /* copy to path buffer (and make sure it's always zero terminated */ ++ count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p); ++ overlay->path[sizeof(overlay->path) - 1] = '\0'; ++ ++ /* strip trailing newlines */ ++ s = overlay->path + strlen(overlay->path); ++ while (s > overlay->path && *--s == '\n') ++ *s = '\0'; ++ ++ pr_debug("%s: path is '%s'\n", __func__, overlay->path); ++ ++ err = request_firmware(&overlay->fw, overlay->path, NULL); ++ if (err != 0) ++ goto out_err; ++ ++ err = create_overlay(overlay, (void *)overlay->fw->data); ++ if (err != 0) ++ goto out_err; ++ ++ return count; ++ ++out_err: ++ ++ release_firmware(overlay->fw); ++ overlay->fw = NULL; ++ ++ overlay->path[0] = '\0'; ++ return err; ++} ++ ++static ssize_t cfs_overlay_item_status_show(struct config_item *item, ++ char *page) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ ++ return sprintf(page, "%s\n", ++ overlay->ov_id >= 0 ? "applied" : "unapplied"); ++} ++ ++CONFIGFS_ATTR(cfs_overlay_item_, path); ++CONFIGFS_ATTR_RO(cfs_overlay_item_, status); ++ ++static struct configfs_attribute *cfs_overlay_attrs[] = { ++ &cfs_overlay_item_attr_path, ++ &cfs_overlay_item_attr_status, ++ NULL, ++}; ++ ++ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, ++ void *buf, size_t max_count) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ ++ pr_debug("%s: buf=%p max_count=%u\n", __func__, ++ buf, max_count); ++ ++ if (overlay->dtbo == NULL) ++ return 0; ++ ++ /* copy if buffer provided */ ++ if (buf != NULL) { ++ /* the buffer must be large enough */ ++ if (overlay->dtbo_size > max_count) ++ return -ENOSPC; ++ ++ memcpy(buf, overlay->dtbo, overlay->dtbo_size); ++ } ++ ++ return overlay->dtbo_size; ++} ++ ++ssize_t cfs_overlay_item_dtbo_write(struct config_item *item, ++ const void *buf, size_t count) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ int err; ++ ++ /* if it's set do not allow changes */ ++ if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) ++ return -EPERM; ++ ++ /* copy the contents */ ++ overlay->dtbo = kmemdup(buf, count, GFP_KERNEL); ++ if (overlay->dtbo == NULL) ++ return -ENOMEM; ++ ++ overlay->dtbo_size = count; ++ ++ err = create_overlay(overlay, overlay->dtbo); ++ if (err != 0) ++ goto out_err; ++ ++ return count; ++ ++out_err: ++ kfree(overlay->dtbo); ++ overlay->dtbo = NULL; ++ overlay->dtbo_size = 0; ++ ++ return err; ++} ++ ++CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M); ++ ++static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = { ++ &cfs_overlay_item_attr_dtbo, ++ NULL, ++}; ++ ++static void cfs_overlay_release(struct config_item *item) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ ++ if (overlay->ov_id >= 0) ++ of_overlay_destroy(overlay->ov_id); ++ if (overlay->fw) ++ release_firmware(overlay->fw); ++ /* kfree with NULL is safe */ ++ kfree(overlay->dtbo); ++ kfree(overlay); ++} ++ ++static struct configfs_item_operations cfs_overlay_item_ops = { ++ .release = cfs_overlay_release, ++}; ++ ++static struct config_item_type cfs_overlay_type = { ++ .ct_item_ops = &cfs_overlay_item_ops, ++ .ct_attrs = cfs_overlay_attrs, ++ .ct_bin_attrs = cfs_overlay_bin_attrs, ++ .ct_owner = THIS_MODULE, ++}; ++ ++static struct config_item *cfs_overlay_group_make_item( ++ struct config_group *group, const char *name) ++{ ++ struct cfs_overlay_item *overlay; ++ ++ overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); ++ if (!overlay) ++ return ERR_PTR(-ENOMEM); ++ overlay->ov_id = -1; ++ ++ config_item_init_type_name(&overlay->item, name, &cfs_overlay_type); ++ return &overlay->item; ++} ++ ++static void cfs_overlay_group_drop_item(struct config_group *group, ++ struct config_item *item) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ ++ config_item_put(&overlay->item); ++} ++ ++static struct configfs_group_operations overlays_ops = { ++ .make_item = cfs_overlay_group_make_item, ++ .drop_item = cfs_overlay_group_drop_item, ++}; ++ ++static struct config_item_type overlays_type = { ++ .ct_group_ops = &overlays_ops, ++ .ct_owner = THIS_MODULE, ++}; ++ ++static struct configfs_group_operations of_cfs_ops = { ++ /* empty - we don't allow anything to be created */ ++}; ++ ++static struct config_item_type of_cfs_type = { ++ .ct_group_ops = &of_cfs_ops, ++ .ct_owner = THIS_MODULE, ++}; ++ ++struct config_group of_cfs_overlay_group; ++ ++struct config_group *of_cfs_def_groups[] = { ++ &of_cfs_overlay_group, ++ NULL ++}; ++ ++static struct configfs_subsystem of_cfs_subsys = { ++ .su_group = { ++ .cg_item = { ++ .ci_namebuf = "device-tree", ++ .ci_type = &of_cfs_type, ++ }, ++ .default_groups = of_cfs_def_groups, ++ }, ++ .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex), ++}; ++ ++static int __init of_cfs_init(void) ++{ ++ int ret; ++ ++ pr_info("%s\n", __func__); ++ ++ config_group_init(&of_cfs_subsys.su_group); ++ config_group_init_type_name(&of_cfs_overlay_group, "overlays", ++ &overlays_type); ++ ++ ret = configfs_register_subsystem(&of_cfs_subsys); ++ if (ret != 0) { ++ pr_err("%s: failed to register subsys\n", __func__); ++ goto out; ++ } ++ pr_info("%s: OK\n", __func__); ++out: ++ return ret; ++} ++late_initcall(of_cfs_init); diff --git a/target/linux/brcm2708/patches-4.4/0179-Pi3-DT-Add-pull-ups-on-the-UART-RX-lines.patch b/target/linux/brcm2708/patches-4.4/0179-Pi3-DT-Add-pull-ups-on-the-UART-RX-lines.patch deleted file mode 100644 index 794a7bb4d5..0000000000 --- a/target/linux/brcm2708/patches-4.4/0179-Pi3-DT-Add-pull-ups-on-the-UART-RX-lines.patch +++ /dev/null @@ -1,40 +0,0 @@ -From e064caf725672cb95cc8327f4dc55d3de257cd74 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Tue, 8 Mar 2016 16:18:57 +0000 -Subject: [PATCH 179/180] Pi3 DT: Add pull-ups on the UART RX lines - -Signed-off-by: Phil Elwell ---- - arch/arm/boot/dts/bcm2710-rpi-3-b.dts | 4 ++-- - arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts | 2 +- - 2 files changed, 3 insertions(+), 3 deletions(-) - ---- a/arch/arm/boot/dts/bcm2710-rpi-3-b.dts -+++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts -@@ -53,13 +53,13 @@ - uart0_pins: uart0_pins { - brcm,pins = <32 33>; - brcm,function = <7>; /* alt3=UART0 */ -- brcm,pull = <0 0>; -+ brcm,pull = <0 2>; - }; - - uart1_pins: uart1_pins { - brcm,pins = <14 15>; - brcm,function = <2>; /* alt5=UART1 */ -- brcm,pull = <0 0>; -+ brcm,pull = <0 2>; - }; - }; - ---- a/arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts -+++ b/arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts -@@ -46,7 +46,7 @@ - uart1_pins: uart1_pins { - brcm,pins = <32 33>; - brcm,function = <2>; /* alt5=UART1 */ -- brcm,pull = <0 0>; -+ brcm,pull = <0 2>; - }; - }; - }; diff --git a/target/linux/brcm2708/patches-4.4/0180-Protect-__release_resource-against-resources-without.patch b/target/linux/brcm2708/patches-4.4/0180-Protect-__release_resource-against-resources-without.patch new file mode 100644 index 0000000000..936cd00cb8 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0180-Protect-__release_resource-against-resources-without.patch @@ -0,0 +1,28 @@ +From c841449374b2acd024ae8971bbd2474c6ba27309 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 13 Mar 2015 12:43:36 +0000 +Subject: [PATCH 180/232] Protect __release_resource against resources without + parents + +Without this patch, removing a device tree overlay can crash here. + +Signed-off-by: Phil Elwell +--- + kernel/resource.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/kernel/resource.c ++++ b/kernel/resource.c +@@ -237,6 +237,12 @@ static int __release_resource(struct res + { + struct resource *tmp, **p; + ++ if (!old->parent) { ++ WARN(old->sibling, "sibling but no parent"); ++ if (old->sibling) ++ return -EINVAL; ++ return 0; ++ } + p = &old->parent->child; + for (;;) { + tmp = *p; diff --git a/target/linux/brcm2708/patches-4.4/0181-BCM270X_DT-Add-a-.dtbo-target-use-for-overlays.patch b/target/linux/brcm2708/patches-4.4/0181-BCM270X_DT-Add-a-.dtbo-target-use-for-overlays.patch new file mode 100644 index 0000000000..e60c266250 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0181-BCM270X_DT-Add-a-.dtbo-target-use-for-overlays.patch @@ -0,0 +1,193 @@ +From 420e1b8c5cec3bbccd4c260ff836a71e54df084e Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 13 Mar 2015 20:00:21 +0000 +Subject: [PATCH 181/232] BCM270X_DT: Add a .dtbo target, use for overlays + +Change the filenames and extensions to keep the pre-DDT style of +overlay (-overlay.dtb) distinct from new ones that use a +different style of local fixups (.dtbo), and to match other +platforms. + +The RPi firmware uses the DDTK trailer atom to choose which type of +overlay to use for each kernel. + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/.gitignore | 2 +- + arch/arm/boot/dts/overlays/Makefile | 135 +++++++++++++++++------------------- + scripts/Makefile.lib | 10 +++ + 3 files changed, 76 insertions(+), 71 deletions(-) + +--- a/arch/arm/boot/.gitignore ++++ b/arch/arm/boot/.gitignore +@@ -3,4 +3,4 @@ zImage + xipImage + bootpImage + uImage +-*.dtb ++*.dtb* +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -12,78 +12,73 @@ ifeq ($(CONFIG_ARCH_BCM2835),y) + RPI_DT_OVERLAYS=y + endif + +-dtb-$(RPI_DT_OVERLAYS) += ads7846-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += at86rf233-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += bmp085_i2c-sensor-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += dwc2-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += dwc-otg-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += dht11-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += enc28j60-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += gpio-ir-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += gpio-poweroff-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-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) += hy28a-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += hy28b-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += i2c-rtc-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += i2c-gpio-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += i2c0-bcm2708-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += i2c1-bcm2708-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += i2s-mmap-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) += mcp2515-can0-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += mcp2515-can1-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += mmc-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += mz61581-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += pi3-disable-bt-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += pi3-miniuart-bt-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += piscreen-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += piscreen2r-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += pitft28-capacitive-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += pitft28-resistive-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += pwm-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += pwm-2chan-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += raspidac3-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += rpi-backlight-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += rpi-dac-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += rpi-display-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += rpi-ft5406-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += rpi-proto-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += rpi-sense-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += sdhost-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += sdio-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += sdtweak-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += smi-dev-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += smi-nand-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += smi-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += spi1-1cs-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += spi1-2cs-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += spi1-3cs-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += spi2-1cs-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += spi2-2cs-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += spi2-3cs-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += spi-gpio35-39-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += tinylcd35-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += uart1-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += vc4-kms-v3d-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += vga666-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += w1-gpio-overlay.dtb +-dtb-$(RPI_DT_OVERLAYS) += w1-gpio-pullup-overlay.dtb ++dtbo-$(RPI_DT_OVERLAYS) += ads7846.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += at86rf233.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += bmp085_i2c-sensor.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += dwc2.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += dwc-otg.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += dht11.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += enc28j60.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += gpio-ir.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += gpio-poweroff.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += hifiberry-amp.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += hifiberry-dac.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += hifiberry-dacplus.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += hifiberry-digi.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += hy28a.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += hy28b.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += i2c-rtc.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += i2c-gpio.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += i2c0-bcm2708.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += i2c1-bcm2708.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += i2s-mmap.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += iqaudio-dac.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += iqaudio-dacplus.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += lirc-rpi.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += mcp2515-can0.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += mcp2515-can1.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += mmc.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += mz61581.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += pi3-disable-bt.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += pi3-miniuart-bt.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += piscreen.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += piscreen2r.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += pitft28-capacitive.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += pitft28-resistive.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += pps-gpio.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += pwm.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += pwm-2chan.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += raspidac3.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += rpi-backlight.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += rpi-dac.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += rpi-display.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += rpi-ft5406.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += rpi-proto.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += rpi-sense.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += sdhost.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += sdio.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += sdtweak.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += smi-dev.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += smi-nand.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += smi.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += spi1-1cs.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += spi1-2cs.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += spi1-3cs.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += spi2-1cs.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += spi2-2cs.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += spi2-3cs.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += spi-gpio35-39.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += tinylcd35.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += uart1.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += vc4-kms-v3d.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += vga666.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += w1-gpio.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += w1-gpio-pullup.dtbo + + targets += dtbs dtbs_install +-targets += $(dtb-y) ++targets += $(dtbo-y) + + endif + +-always := $(dtb-y) +-clean-files := *.dtb +- +-# Enable fixups to support overlays on BCM2708 platforms +-ifeq ($(RPI_DT_OVERLAYS),y) +- DTC_FLAGS ?= -@ +-endif ++always := $(dtbo-y) ++clean-files := *.dtbo +--- a/scripts/Makefile.lib ++++ b/scripts/Makefile.lib +@@ -292,6 +292,16 @@ cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \ + $(obj)/%.dtb: $(src)/%.dts FORCE + $(call if_changed_dep,dtc) + ++quiet_cmd_dtco = DTCO $@ ++cmd_dtco = $(CPP) $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; \ ++ $(objtree)/scripts/dtc/dtc -@ -O dtb -o $@ -b 0 \ ++ -i $(dir $<) $(DTC_FLAGS) \ ++ -d $(depfile).dtc.tmp $(dtc-tmp) ; \ ++ cat $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile) ++ ++$(obj)/%.dtbo: $(src)/%-overlay.dts FORCE ++ $(call if_changed_dep,dtco) ++ + dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp) + + # Bzip2 diff --git a/target/linux/brcm2708/patches-4.4/0182-scripts-knlinfo-Decode-DDTK-atom.patch b/target/linux/brcm2708/patches-4.4/0182-scripts-knlinfo-Decode-DDTK-atom.patch new file mode 100644 index 0000000000..13a223b07d --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0182-scripts-knlinfo-Decode-DDTK-atom.patch @@ -0,0 +1,31 @@ +From e56b2ead4021e94eb15f8dd7240c607fe61ce57f Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 29 May 2015 11:18:58 +0100 +Subject: [PATCH 182/232] scripts/knlinfo: Decode DDTK atom + +Show the DDTK atom as being a boolean. + +Signed-off-by: Phil Elwell +--- + scripts/knlinfo | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/scripts/knlinfo ++++ b/scripts/knlinfo +@@ -16,6 +16,7 @@ my $trailer_magic = 'RPTL'; + + my %atom_formats = + ( ++ 'DDTK' => \&format_bool, + 'DTOK' => \&format_bool, + 'KVer' => \&format_string, + '270X' => \&format_bool, +@@ -148,7 +149,7 @@ sub format_atom + sub format_bool + { + my ($data) = @_; +- return unpack('V', $data) ? 'true' : 'false'; ++ return unpack('V', $data) ? 'y' : 'n'; + } + + sub format_int diff --git a/target/linux/brcm2708/patches-4.4/0183-Enable-Dynamic-Device-Tree-for-bcmrpi_defconfig-and-.patch b/target/linux/brcm2708/patches-4.4/0183-Enable-Dynamic-Device-Tree-for-bcmrpi_defconfig-and-.patch new file mode 100644 index 0000000000..694b1f7b25 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0183-Enable-Dynamic-Device-Tree-for-bcmrpi_defconfig-and-.patch @@ -0,0 +1,40 @@ +From aa7d45428dba9267190fa17a424db693628856f6 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 29 May 2015 11:48:59 +0100 +Subject: [PATCH 183/232] Enable Dynamic Device Tree for bcmrpi_defconfig and + bcm2709_defconfig + +Signed-off-by: Phil Elwell +--- + arch/arm/configs/bcm2709_defconfig | 2 +- + arch/arm/configs/bcmrpi_defconfig | 1 + + 2 files changed, 2 insertions(+), 1 deletion(-) + +--- a/arch/arm/configs/bcm2709_defconfig ++++ b/arch/arm/configs/bcm2709_defconfig +@@ -403,6 +403,7 @@ CONFIG_MTD=m + CONFIG_MTD_BLOCK=m + CONFIG_MTD_NAND=m + CONFIG_MTD_UBI=m ++CONFIG_OF_CONFIGFS=y + CONFIG_ZRAM=m + CONFIG_ZRAM_LZ4_COMPRESS=y + CONFIG_BLK_DEV_LOOP=y +@@ -1161,7 +1162,6 @@ 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 +--- a/arch/arm/configs/bcmrpi_defconfig ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -396,6 +396,7 @@ CONFIG_MTD=m + CONFIG_MTD_BLOCK=m + CONFIG_MTD_NAND=m + CONFIG_MTD_UBI=m ++CONFIG_OF_OVERLAY=y + CONFIG_ZRAM=m + CONFIG_ZRAM_LZ4_COMPRESS=y + CONFIG_BLK_DEV_LOOP=y diff --git a/target/linux/brcm2708/patches-4.4/0184-SQUASH-Add-CONFIG_OF_CONFIGFS-to-bcmrpi_defconfig.patch b/target/linux/brcm2708/patches-4.4/0184-SQUASH-Add-CONFIG_OF_CONFIGFS-to-bcmrpi_defconfig.patch new file mode 100644 index 0000000000..21d8e0de6a --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0184-SQUASH-Add-CONFIG_OF_CONFIGFS-to-bcmrpi_defconfig.patch @@ -0,0 +1,29 @@ +From 293a0e05a554b39c3d3587f0631c495485ec903b Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Sun, 13 Mar 2016 16:14:44 +0000 +Subject: [PATCH 184/232] SQUASH: Add CONFIG_OF_CONFIGFS to bcmrpi_defconfig + +Signed-off-by: Phil Elwell +--- + arch/arm/configs/bcmrpi_defconfig | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/arch/arm/configs/bcmrpi_defconfig ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -396,7 +396,7 @@ CONFIG_MTD=m + CONFIG_MTD_BLOCK=m + CONFIG_MTD_NAND=m + CONFIG_MTD_UBI=m +-CONFIG_OF_OVERLAY=y ++CONFIG_OF_CONFIGFS=y + CONFIG_ZRAM=m + CONFIG_ZRAM_LZ4_COMPRESS=y + CONFIG_BLK_DEV_LOOP=y +@@ -1169,7 +1169,6 @@ 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 diff --git a/target/linux/brcm2708/patches-4.4/0185-dts-kbuild-dtbs_install-installs-.dtbo-files-too.patch b/target/linux/brcm2708/patches-4.4/0185-dts-kbuild-dtbs_install-installs-.dtbo-files-too.patch new file mode 100644 index 0000000000..b87aab5aec --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0185-dts-kbuild-dtbs_install-installs-.dtbo-files-too.patch @@ -0,0 +1,42 @@ +From e948b371fbb724b7d715dee41c0db5ef5a028a13 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 14 Mar 2016 16:56:54 +0000 +Subject: [PATCH 185/232] dts, kbuild: dtbs_install installs .dtbo files too + +Signed-off-by: Phil Elwell +--- + scripts/Makefile.dtbinst | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +--- a/scripts/Makefile.dtbinst ++++ b/scripts/Makefile.dtbinst +@@ -29,6 +29,7 @@ ifeq ("$(dtbinst-root)", "$(obj)") + endif + + dtbinst-files := $(dtb-y) ++dtboinst-files := $(dtbo-y) + dtbinst-dirs := $(dts-dirs) + + # Helper targets for Installing DTBs into the boot directory +@@ -37,15 +38,18 @@ quiet_cmd_dtb_install = INSTALL $< + + install-dir = $(patsubst $(dtbinst-root)%,$(INSTALL_DTBS_PATH)%,$(obj)) + +-$(dtbinst-files) $(dtbinst-dirs): | __dtbs_install_prep ++$(dtbinst-files) $(dtboinst-files) $(dtbinst-dirs): | __dtbs_install_prep + + $(dtbinst-files): %.dtb: $(obj)/%.dtb + $(call cmd,dtb_install,$(install-dir)) + ++$(dtboinst-files): %.dtbo: $(obj)/%.dtbo ++ $(call cmd,dtb_install,$(install-dir)) ++ + $(dtbinst-dirs): + $(Q)$(MAKE) $(dtbinst)=$(obj)/$@ + +-PHONY += $(dtbinst-files) $(dtbinst-dirs) +-__dtbs_install: $(dtbinst-files) $(dtbinst-dirs) ++PHONY += $(dtbinst-files) $(dtboinst-files) $(dtbinst-dirs) ++__dtbs_install: $(dtbinst-files) $(dtboinst-files) $(dtbinst-dirs) + + .PHONY: $(PHONY) diff --git a/target/linux/brcm2708/patches-4.4/0186-bcm2835-sdhost-Workaround-for-slow-sectors.patch b/target/linux/brcm2708/patches-4.4/0186-bcm2835-sdhost-Workaround-for-slow-sectors.patch new file mode 100644 index 0000000000..26f64a1811 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0186-bcm2835-sdhost-Workaround-for-slow-sectors.patch @@ -0,0 +1,118 @@ +From c215c0d855343400b53ae2ec8b015a3f4a3eb27e Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 15 Mar 2016 14:10:29 +0000 +Subject: [PATCH 186/232] bcm2835-sdhost: Workaround for "slow" sectors + +Some cards have been seen to cause timeouts after certain sectors are +read. This workaround enforces a minimum delay between the stop after +reading one of those sectors and a subsequent data command. + +Using CMD23 (SET_BLOCK_COUNT) avoids this problem, so good cards will +not be penalised by this workaround. + +Signed-off-by: Phil Elwell +--- + drivers/mmc/host/bcm2835-sdhost.c | 50 +++++++++++++++++++++++++++++++++++---- + 1 file changed, 46 insertions(+), 4 deletions(-) + +--- a/drivers/mmc/host/bcm2835-sdhost.c ++++ b/drivers/mmc/host/bcm2835-sdhost.c +@@ -202,9 +202,12 @@ struct bcm2835_host { + int max_delay; /* maximum length of time spent waiting */ + struct timeval stop_time; /* when the last stop was issued */ + u32 delay_after_stop; /* minimum time between stop and subsequent data transfer */ ++ u32 delay_after_this_stop; /* minimum time between this stop and subsequent data transfer */ + u32 overclock_50; /* frequency to use when 50MHz is requested (in MHz) */ + u32 overclock; /* Current frequency if overclocked, else zero */ + u32 pio_limit; /* Maximum block count for PIO (0 = always DMA) */ ++ ++ u32 sectors; /* Cached card size in sectors */ + }; + + #if ENABLE_LOG +@@ -425,6 +428,7 @@ static void bcm2835_sdhost_reset_interna + bcm2835_sdhost_set_power(host, true); + mdelay(10); + host->clock = 0; ++ host->sectors = 0; + bcm2835_sdhost_write(host, host->hcfg, SDHCFG); + bcm2835_sdhost_write(host, host->cdiv, SDCDIV); + mmiowb(); +@@ -880,6 +884,24 @@ static void bcm2835_sdhost_prepare_data( + host->flush_fifo = 0; + host->data->bytes_xfered = 0; + ++ if (!host->sectors && host->mmc->card) { ++ struct mmc_card *card = host->mmc->card; ++ if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) { ++ /* ++ * The EXT_CSD sector count is in number of 512 byte ++ * sectors. ++ */ ++ host->sectors = card->ext_csd.sectors; ++ } else { ++ /* ++ * The CSD capacity field is in units of read_blkbits. ++ * set_capacity takes units of 512 bytes. ++ */ ++ host->sectors = card->csd.capacity << ++ (card->csd.read_blkbits - 9); ++ } ++ } ++ + if (!host->dma_desc) { + /* Use PIO */ + int flags = SG_MITER_ATOMIC; +@@ -989,7 +1011,7 @@ bool bcm2835_sdhost_send_command(struct + + if (cmd->data) { + log_event("CMDD", cmd->data->blocks, cmd->data->blksz); +- if (host->delay_after_stop) { ++ if (host->delay_after_this_stop) { + struct timeval now; + int time_since_stop; + do_gettimeofday(&now); +@@ -998,12 +1020,32 @@ bool bcm2835_sdhost_send_command(struct + /* Possibly less than one second */ + time_since_stop = time_since_stop * 1000000 + + (now.tv_usec - host->stop_time.tv_usec); +- if (time_since_stop < host->delay_after_stop) +- udelay(host->delay_after_stop - ++ if (time_since_stop < ++ host->delay_after_this_stop) ++ udelay(host->delay_after_this_stop - + time_since_stop); + } + } + ++ host->delay_after_this_stop = host->delay_after_stop; ++ if ((cmd->data->flags & MMC_DATA_READ) && !host->use_sbc) { ++ /* See if read crosses one of the hazardous sectors */ ++ u32 first_blk, last_blk; ++ ++ /* Intentionally include the following sector because ++ without CMD23/SBC the read may run on. */ ++ first_blk = host->mrq->cmd->arg; ++ last_blk = first_blk + cmd->data->blocks; ++ ++ if (((last_blk >= (host->sectors - 64)) && ++ (first_blk <= (host->sectors - 64))) || ++ ((last_blk >= (host->sectors - 32)) && ++ (first_blk <= (host->sectors - 32)))) { ++ host->delay_after_this_stop = ++ max(250u, host->delay_after_stop); ++ } ++ } ++ + if (cmd->data->flags & MMC_DATA_WRITE) + sdcmd |= SDCMD_WRITE_CMD; + if (cmd->data->flags & MMC_DATA_READ) +@@ -1078,7 +1120,7 @@ static void bcm2835_sdhost_transfer_comp + if (!host->use_busy) + bcm2835_sdhost_finish_command(host, NULL); + +- if (host->delay_after_stop) ++ if (host->delay_after_this_stop) + do_gettimeofday(&host->stop_time); + } + } else { diff --git a/target/linux/brcm2708/patches-4.4/0187-BCM270X_DT-Add-labels-to-spidev-nodes.patch b/target/linux/brcm2708/patches-4.4/0187-BCM270X_DT-Add-labels-to-spidev-nodes.patch new file mode 100644 index 0000000000..f1b0bece86 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0187-BCM270X_DT-Add-labels-to-spidev-nodes.patch @@ -0,0 +1,114 @@ +From 605effb19bd81578339b66e642ff5e5d4f86babd Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 15 Mar 2016 15:49:16 +0000 +Subject: [PATCH 187/232] BCM270X_DT: Add labels to spidev nodes + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 4 ++-- + arch/arm/boot/dts/bcm2708-rpi-b.dts | 4 ++-- + arch/arm/boot/dts/bcm2708-rpi-cm.dts | 4 ++-- + arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 4 ++-- + arch/arm/boot/dts/bcm2710-rpi-3-b.dts | 4 ++-- + 5 files changed, 10 insertions(+), 10 deletions(-) + +--- a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts +@@ -59,7 +59,7 @@ + pinctrl-0 = <&spi0_pins &spi0_cs_pins>; + cs-gpios = <&gpio 8 1>, <&gpio 7 1>; + +- spidev@0{ ++ spidev0: spidev@0{ + compatible = "spidev"; + reg = <0>; /* CE0 */ + #address-cells = <1>; +@@ -67,7 +67,7 @@ + spi-max-frequency = <500000>; + }; + +- spidev@1{ ++ spidev1: spidev@1{ + compatible = "spidev"; + reg = <1>; /* CE1 */ + #address-cells = <1>; +--- a/arch/arm/boot/dts/bcm2708-rpi-b.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts +@@ -59,7 +59,7 @@ + pinctrl-0 = <&spi0_pins &spi0_cs_pins>; + cs-gpios = <&gpio 8 1>, <&gpio 7 1>; + +- spidev@0{ ++ spidev0: spidev@0{ + compatible = "spidev"; + reg = <0>; /* CE0 */ + #address-cells = <1>; +@@ -67,7 +67,7 @@ + spi-max-frequency = <500000>; + }; + +- spidev@1{ ++ spidev1: spidev@1{ + compatible = "spidev"; + reg = <1>; /* CE1 */ + #address-cells = <1>; +--- a/arch/arm/boot/dts/bcm2708-rpi-cm.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-cm.dts +@@ -42,7 +42,7 @@ + pinctrl-0 = <&spi0_pins &spi0_cs_pins>; + cs-gpios = <&gpio 8 1>, <&gpio 7 1>; + +- spidev@0{ ++ spidev0: spidev@0{ + compatible = "spidev"; + reg = <0>; /* CE0 */ + #address-cells = <1>; +@@ -50,7 +50,7 @@ + spi-max-frequency = <500000>; + }; + +- spidev@1{ ++ spidev1: spidev@1{ + compatible = "spidev"; + reg = <1>; /* CE1 */ + #address-cells = <1>; +--- a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts ++++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts +@@ -59,7 +59,7 @@ + pinctrl-0 = <&spi0_pins &spi0_cs_pins>; + cs-gpios = <&gpio 8 1>, <&gpio 7 1>; + +- spidev@0{ ++ spidev0: spidev@0{ + compatible = "spidev"; + reg = <0>; /* CE0 */ + #address-cells = <1>; +@@ -67,7 +67,7 @@ + spi-max-frequency = <500000>; + }; + +- spidev@1{ ++ spidev1: spidev@1{ + compatible = "spidev"; + reg = <1>; /* CE1 */ + #address-cells = <1>; +--- a/arch/arm/boot/dts/bcm2710-rpi-3-b.dts ++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts +@@ -110,7 +110,7 @@ + pinctrl-0 = <&spi0_pins &spi0_cs_pins>; + cs-gpios = <&gpio 8 1>, <&gpio 7 1>; + +- spidev@0{ ++ spidev0: spidev@0{ + compatible = "spidev"; + reg = <0>; /* CE0 */ + #address-cells = <1>; +@@ -118,7 +118,7 @@ + spi-max-frequency = <500000>; + }; + +- spidev@1{ ++ spidev1: spidev@1{ + compatible = "spidev"; + reg = <1>; /* CE1 */ + #address-cells = <1>; diff --git a/target/linux/brcm2708/patches-4.4/0188-BCM270X_DT-Use-spidev-labels-in-overlays.patch b/target/linux/brcm2708/patches-4.4/0188-BCM270X_DT-Use-spidev-labels-in-overlays.patch new file mode 100644 index 0000000000..29c1695120 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0188-BCM270X_DT-Use-spidev-labels-in-overlays.patch @@ -0,0 +1,628 @@ +From 666d717a56639ca89b6bba18a325b1c530683fac Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 15 Mar 2016 16:27:26 +0000 +Subject: [PATCH 188/232] BCM270X_DT: Use spidev labels in overlays + +--- + arch/arm/boot/dts/overlays/ads7846-overlay.dts | 22 ++++++++++------- + arch/arm/boot/dts/overlays/at86rf233-overlay.dts | 11 +++++---- + arch/arm/boot/dts/overlays/enc28j60-overlay.dts | 11 +++++---- + arch/arm/boot/dts/overlays/hy28a-overlay.dts | 22 ++++++++++------- + arch/arm/boot/dts/overlays/hy28b-overlay.dts | 22 ++++++++++------- + .../arm/boot/dts/overlays/mcp2515-can0-overlay.dts | 16 ++++++++----- + .../arm/boot/dts/overlays/mcp2515-can1-overlay.dts | 16 ++++++++----- + arch/arm/boot/dts/overlays/mz61581-overlay.dts | 22 ++++++++++------- + arch/arm/boot/dts/overlays/piscreen-overlay.dts | 22 ++++++++++------- + arch/arm/boot/dts/overlays/piscreen2r-overlay.dts | 22 ++++++++++------- + .../dts/overlays/pitft28-capacitive-overlay.dts | 17 +++++++------ + .../dts/overlays/pitft28-resistive-overlay.dts | 24 ++++++++++++------- + arch/arm/boot/dts/overlays/rpi-display-overlay.dts | 22 ++++++++++------- + arch/arm/boot/dts/overlays/tinylcd35-overlay.dts | 28 +++++++++++++--------- + 14 files changed, 174 insertions(+), 103 deletions(-) + +--- a/arch/arm/boot/dts/overlays/ads7846-overlay.dts ++++ b/arch/arm/boot/dts/overlays/ads7846-overlay.dts +@@ -13,18 +13,24 @@ + target = <&spi0>; + __overlay__ { + status = "okay"; ++ }; ++ }; + +- spidev@0{ +- status = "disabled"; +- }; ++ fragment@1 { ++ target = <&spidev0>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; + +- spidev@1{ +- status = "disabled"; +- }; ++ fragment@2 { ++ target = <&spidev1>; ++ __overlay__ { ++ status = "disabled"; + }; + }; + +- fragment@1 { ++ fragment@3 { + target = <&gpio>; + __overlay__ { + ads7846_pins: ads7846_pins { +@@ -35,7 +41,7 @@ + }; + }; + +- fragment@2 { ++ fragment@4 { + target = <&spi0>; + __overlay__ { + /* needed to avoid dtc warning */ +--- a/arch/arm/boot/dts/overlays/at86rf233-overlay.dts ++++ b/arch/arm/boot/dts/overlays/at86rf233-overlay.dts +@@ -14,10 +14,6 @@ + + status = "okay"; + +- spidev@0{ +- status = "disabled"; +- }; +- + lowpan0: at86rf233@0 { + compatible = "atmel,at86rf233"; + reg = <0>; +@@ -32,6 +28,13 @@ + }; + + fragment@1 { ++ target = <&spidev0>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ ++ fragment@2 { + target = <&gpio>; + __overlay__ { + lowpan0_pins: lowpan0_pins { +--- a/arch/arm/boot/dts/overlays/enc28j60-overlay.dts ++++ b/arch/arm/boot/dts/overlays/enc28j60-overlay.dts +@@ -14,10 +14,6 @@ + + status = "okay"; + +- spidev@0{ +- status = "disabled"; +- }; +- + eth1: enc28j60@0{ + compatible = "microchip,enc28j60"; + reg = <0>; /* CE0 */ +@@ -32,6 +28,13 @@ + }; + + fragment@1 { ++ target = <&spidev0>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ ++ fragment@2 { + target = <&gpio>; + __overlay__ { + eth1_pins: eth1_pins { +--- a/arch/arm/boot/dts/overlays/hy28a-overlay.dts ++++ b/arch/arm/boot/dts/overlays/hy28a-overlay.dts +@@ -13,18 +13,24 @@ + target = <&spi0>; + __overlay__ { + status = "okay"; ++ }; ++ }; + +- spidev@0{ +- status = "disabled"; +- }; ++ fragment@1 { ++ target = <&spidev0>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; + +- spidev@1{ +- status = "disabled"; +- }; ++ fragment@2 { ++ target = <&spidev1>; ++ __overlay__ { ++ status = "disabled"; + }; + }; + +- fragment@1 { ++ fragment@3 { + target = <&gpio>; + __overlay__ { + hy28a_pins: hy28a_pins { +@@ -34,7 +40,7 @@ + }; + }; + +- fragment@2 { ++ fragment@4 { + target = <&spi0>; + __overlay__ { + /* needed to avoid dtc warning */ +--- a/arch/arm/boot/dts/overlays/hy28b-overlay.dts ++++ b/arch/arm/boot/dts/overlays/hy28b-overlay.dts +@@ -13,18 +13,24 @@ + target = <&spi0>; + __overlay__ { + status = "okay"; ++ }; ++ }; + +- spidev@0{ +- status = "disabled"; +- }; ++ fragment@1 { ++ target = <&spidev0>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; + +- spidev@1{ +- status = "disabled"; +- }; ++ fragment@2 { ++ target = <&spidev1>; ++ __overlay__ { ++ status = "disabled"; + }; + }; + +- fragment@1 { ++ fragment@3 { + target = <&gpio>; + __overlay__ { + hy28b_pins: hy28b_pins { +@@ -34,7 +40,7 @@ + }; + }; + +- fragment@2 { ++ fragment@4 { + target = <&spi0>; + __overlay__ { + /* needed to avoid dtc warning */ +--- a/arch/arm/boot/dts/overlays/mcp2515-can0-overlay.dts ++++ b/arch/arm/boot/dts/overlays/mcp2515-can0-overlay.dts +@@ -12,14 +12,18 @@ + target = <&spi0>; + __overlay__ { + status = "okay"; +- spidev@0{ +- status = "disabled"; +- }; + }; + }; + +- /* the interrupt pin of the can-controller */ + fragment@1 { ++ target = <&spidev0>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ ++ /* the interrupt pin of the can-controller */ ++ fragment@2 { + target = <&gpio>; + __overlay__ { + can0_pins: can0_pins { +@@ -30,7 +34,7 @@ + }; + + /* the clock/oscillator of the can-controller */ +- fragment@2 { ++ fragment@3 { + target-path = "/clocks"; + __overlay__ { + /* external oscillator of mcp2515 on SPI0.0 */ +@@ -43,7 +47,7 @@ + }; + + /* the spi config of the can-controller itself binding everything together */ +- fragment@3 { ++ fragment@4 { + target = <&spi0>; + __overlay__ { + /* needed to avoid dtc warning */ +--- a/arch/arm/boot/dts/overlays/mcp2515-can1-overlay.dts ++++ b/arch/arm/boot/dts/overlays/mcp2515-can1-overlay.dts +@@ -12,14 +12,18 @@ + target = <&spi0>; + __overlay__ { + status = "okay"; +- spidev@1{ +- status = "disabled"; +- }; + }; + }; + +- /* the interrupt pin of the can-controller */ + fragment@1 { ++ target = <&spidev1>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ ++ /* the interrupt pin of the can-controller */ ++ fragment@2 { + target = <&gpio>; + __overlay__ { + can1_pins: can1_pins { +@@ -30,7 +34,7 @@ + }; + + /* the clock/oscillator of the can-controller */ +- fragment@2 { ++ fragment@3 { + target-path = "/clocks"; + __overlay__ { + /* external oscillator of mcp2515 on spi0.1 */ +@@ -43,7 +47,7 @@ + }; + + /* the spi config of the can-controller itself binding everything together */ +- fragment@3 { ++ fragment@4 { + target = <&spi0>; + __overlay__ { + /* needed to avoid dtc warning */ +--- a/arch/arm/boot/dts/overlays/mz61581-overlay.dts ++++ b/arch/arm/boot/dts/overlays/mz61581-overlay.dts +@@ -13,18 +13,24 @@ + target = <&spi0>; + __overlay__ { + status = "okay"; ++ }; ++ }; + +- spidev@0{ +- status = "disabled"; +- }; ++ fragment@1 { ++ target = <&spidev0>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; + +- spidev@1{ +- status = "disabled"; +- }; ++ fragment@2 { ++ target = <&spidev1>; ++ __overlay__ { ++ status = "disabled"; + }; + }; + +- fragment@1 { ++ fragment@3 { + target = <&gpio>; + __overlay__ { + mz61581_pins: mz61581_pins { +@@ -34,7 +40,7 @@ + }; + }; + +- fragment@2 { ++ fragment@4 { + target = <&spi0>; + __overlay__ { + /* needed to avoid dtc warning */ +--- a/arch/arm/boot/dts/overlays/piscreen-overlay.dts ++++ b/arch/arm/boot/dts/overlays/piscreen-overlay.dts +@@ -13,18 +13,24 @@ + target = <&spi0>; + __overlay__ { + status = "okay"; ++ }; ++ }; + +- spidev@0{ +- status = "disabled"; +- }; ++ fragment@1 { ++ target = <&spidev0>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; + +- spidev@1{ +- status = "disabled"; +- }; ++ fragment@2 { ++ target = <&spidev1>; ++ __overlay__ { ++ status = "disabled"; + }; + }; + +- fragment@1 { ++ fragment@3 { + target = <&gpio>; + __overlay__ { + piscreen_pins: piscreen_pins { +@@ -34,7 +40,7 @@ + }; + }; + +- fragment@2 { ++ fragment@4 { + target = <&spi0>; + __overlay__ { + /* needed to avoid dtc warning */ +--- a/arch/arm/boot/dts/overlays/piscreen2r-overlay.dts ++++ b/arch/arm/boot/dts/overlays/piscreen2r-overlay.dts +@@ -13,18 +13,24 @@ + target = <&spi0>; + __overlay__ { + status = "okay"; ++ }; ++ }; + +- spidev@0{ +- status = "disabled"; +- }; ++ fragment@1 { ++ target = <&spidev0>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; + +- spidev@1{ +- status = "disabled"; +- }; ++ fragment@2 { ++ target = <&spidev1>; ++ __overlay__ { ++ status = "disabled"; + }; + }; + +- fragment@1 { ++ fragment@3 { + target = <&gpio>; + __overlay__ { + piscreen2_pins: piscreen2_pins { +@@ -34,7 +40,7 @@ + }; + }; + +- fragment@2 { ++ fragment@4 { + target = <&spi0>; + __overlay__ { + /* needed to avoid dtc warning */ +--- a/arch/arm/boot/dts/overlays/pitft28-capacitive-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pitft28-capacitive-overlay.dts +@@ -13,14 +13,17 @@ + target = <&spi0>; + __overlay__ { + status = "okay"; +- +- spidev@0{ +- status = "disabled"; +- }; + }; + }; + +- fragment@1 { ++ fragment@1 { ++ target = <&spidev0>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ ++ fragment@2 { + target = <&gpio>; + __overlay__ { + pitft_pins: pitft_pins { +@@ -31,7 +34,7 @@ + }; + }; + +- fragment@2 { ++ fragment@3 { + target = <&spi0>; + __overlay__ { + /* needed to avoid dtc warning */ +@@ -55,7 +58,7 @@ + }; + }; + +- fragment@3 { ++ fragment@4 { + target = <&i2c1>; + __overlay__ { + /* needed to avoid dtc warning */ +--- a/arch/arm/boot/dts/overlays/pitft28-resistive-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pitft28-resistive-overlay.dts +@@ -13,18 +13,24 @@ + target = <&spi0>; + __overlay__ { + status = "okay"; ++ }; ++ }; + +- spidev@0{ +- status = "disabled"; +- }; ++ fragment@1 { ++ target = <&spidev0>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; + +- spidev@1{ +- status = "disabled"; +- }; ++ fragment@2 { ++ target = <&spidev1>; ++ __overlay__ { ++ status = "disabled"; + }; + }; + +- fragment@1 { ++ fragment@3 { + target = <&gpio>; + __overlay__ { + pitft_pins: pitft_pins { +@@ -35,7 +41,7 @@ + }; + }; + +- fragment@2 { ++ fragment@4 { + target = <&spi0>; + __overlay__ { + /* needed to avoid dtc warning */ +@@ -95,7 +101,7 @@ + }; + }; + +- fragment@3 { ++ fragment@5 { + target-path = "/soc"; + __overlay__ { + backlight { +--- a/arch/arm/boot/dts/overlays/rpi-display-overlay.dts ++++ b/arch/arm/boot/dts/overlays/rpi-display-overlay.dts +@@ -13,18 +13,24 @@ + target = <&spi0>; + __overlay__ { + status = "okay"; ++ }; ++ }; + +- spidev@0{ +- status = "disabled"; +- }; ++ fragment@1 { ++ target = <&spidev0>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; + +- spidev@1{ +- status = "disabled"; +- }; ++ fragment@2 { ++ target = <&spidev1>; ++ __overlay__ { ++ status = "disabled"; + }; + }; + +- fragment@1 { ++ fragment@3 { + target = <&gpio>; + __overlay__ { + rpi_display_pins: rpi_display_pins { +@@ -35,7 +41,7 @@ + }; + }; + +- fragment@2 { ++ fragment@4 { + target = <&spi0>; + __overlay__ { + /* needed to avoid dtc warning */ +--- a/arch/arm/boot/dts/overlays/tinylcd35-overlay.dts ++++ b/arch/arm/boot/dts/overlays/tinylcd35-overlay.dts +@@ -30,18 +30,24 @@ + target = <&spi0>; + __overlay__ { + status = "okay"; ++ }; ++ }; + +- spidev@0{ +- status = "disabled"; +- }; ++ fragment@1 { ++ target = <&spidev0>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; + +- spidev@1{ +- status = "disabled"; +- }; ++ fragment@2 { ++ target = <&spidev1>; ++ __overlay__ { ++ status = "disabled"; + }; + }; + +- fragment@1 { ++ fragment@3 { + target = <&gpio>; + __overlay__ { + tinylcd35_pins: tinylcd35_pins { +@@ -60,7 +66,7 @@ + }; + }; + +- fragment@2 { ++ fragment@4 { + target = <&spi0>; + __overlay__ { + /* needed to avoid dtc warning */ +@@ -124,7 +130,7 @@ + + /* RTC */ + +- fragment@3 { ++ fragment@5 { + target = <&i2c1>; + __overlay__ { + #address-cells = <1>; +@@ -138,7 +144,7 @@ + }; + }; + +- fragment@4 { ++ fragment@6 { + target = <&i2c1>; + __overlay__ { + #address-cells = <1>; +@@ -156,7 +162,7 @@ + * Values for input event code is found under the + * 'Keys and buttons' heading in include/uapi/linux/input.h + */ +- fragment@5 { ++ fragment@7 { + target-path = "/soc"; + __overlay__ { + keypad: keypad { diff --git a/target/linux/brcm2708/patches-4.4/0189-BCM270X_DT-Build-and-document-the-wittypi-overlay.patch b/target/linux/brcm2708/patches-4.4/0189-BCM270X_DT-Build-and-document-the-wittypi-overlay.patch new file mode 100644 index 0000000000..d5606e54df --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0189-BCM270X_DT-Build-and-document-the-wittypi-overlay.patch @@ -0,0 +1,50 @@ +From 3259cf06132d0987314a37f39b0b18750b127eba Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 15 Mar 2016 16:41:37 +0000 +Subject: [PATCH 189/232] BCM270X_DT: Build and document the wittypi overlay + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 8 ++++++++ + arch/arm/boot/dts/overlays/wittypi-overlay.dts | 2 +- + 3 files changed, 10 insertions(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -74,6 +74,7 @@ dtbo-$(RPI_DT_OVERLAYS) += vc4-kms-v3d.d + dtbo-$(RPI_DT_OVERLAYS) += vga666.dtbo + dtbo-$(RPI_DT_OVERLAYS) += w1-gpio.dtbo + dtbo-$(RPI_DT_OVERLAYS) += w1-gpio-pullup.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += wittypi.dtbo + + targets += dtbs dtbs_install + targets += $(dtbo-y) +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -920,6 +920,14 @@ Params: gpiopin GPIO for + extpullup GPIO for external pullup (default "5") + + ++Name: wittypi ++Info: Configures the wittypi RTC module. ++Load: dtoverlay=wittypi,= ++Params: led_gpio GPIO for LED (default "17") ++ led_trigger Choose which activity the LED tracks (default ++ "default-on") ++ ++ + Troubleshooting + =============== + +--- a/arch/arm/boot/dts/overlays/wittypi-overlay.dts ++++ b/arch/arm/boot/dts/overlays/wittypi-overlay.dts +@@ -37,7 +37,7 @@ + }; + + __overrides__ { +- led_gpio = <&wittypi_led>,"gpios:4"; ++ led_gpio = <&wittypi_led>,"gpios:4"; + led_trigger = <&wittypi_led>,"linux,default-trigger"; + }; + diff --git a/target/linux/brcm2708/patches-4.4/0190-scripts-dtc-Fix-UMR-causing-corrupt-dtbo-overlay-fil.patch b/target/linux/brcm2708/patches-4.4/0190-scripts-dtc-Fix-UMR-causing-corrupt-dtbo-overlay-fil.patch new file mode 100644 index 0000000000..b6525b564a --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0190-scripts-dtc-Fix-UMR-causing-corrupt-dtbo-overlay-fil.patch @@ -0,0 +1,27 @@ +From cd57bc1baa3d03746b6bf78996cbd46460558c28 Mon Sep 17 00:00:00 2001 +From: Matthias Reichl +Date: Tue, 15 Mar 2016 21:13:39 +0100 +Subject: [PATCH 190/232] scripts/dtc: Fix UMR causing corrupt dtbo overlay + files + +struct fixup_entry is allocated from the heap but it's member +local_fixup_generated was never initialized. This lead to +corrupted dtbo files. + +Fix this by initializing local_fixup_generated to false. + +Signed-off-by: Matthias Reichl +--- + scripts/dtc/checks.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/scripts/dtc/checks.c ++++ b/scripts/dtc/checks.c +@@ -523,6 +523,7 @@ static void fixup_phandle_references(str + fe->prop = prop; + fe->offset = m->offset; + fe->next = NULL; ++ fe->local_fixup_generated = false; + + /* append it to the local fixups */ + fep = &dt->local_fixups; diff --git a/target/linux/brcm2708/patches-4.4/0191-BCM270X_DT-Add-dtparam-for-uart1.patch b/target/linux/brcm2708/patches-4.4/0191-BCM270X_DT-Add-dtparam-for-uart1.patch new file mode 100644 index 0000000000..a7009cf2aa --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0191-BCM270X_DT-Add-dtparam-for-uart1.patch @@ -0,0 +1,77 @@ +From d171758951c497d0cd557040884ffb4efd231c3c Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 16 Mar 2016 08:35:06 +0000 +Subject: [PATCH 191/232] BCM270X_DT: Add dtparam for uart1 + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 1 + + arch/arm/boot/dts/bcm2708-rpi-b.dts | 1 + + arch/arm/boot/dts/bcm2708-rpi-cm.dts | 1 + + arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 1 + + arch/arm/boot/dts/bcm2710-rpi-3-b.dts | 1 + + arch/arm/boot/dts/overlays/README | 3 +++ + 6 files changed, 8 insertions(+) + +--- a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts +@@ -120,6 +120,7 @@ + __overrides__ { + uart0 = <&uart0>,"status"; + uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; ++ uart1 = <&uart1>,"status"; + i2s = <&i2s>,"status"; + spi = <&spi0>,"status"; + i2c0 = <&i2c0>,"status"; +--- a/arch/arm/boot/dts/bcm2708-rpi-b.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts +@@ -114,6 +114,7 @@ + __overrides__ { + uart0 = <&uart0>,"status"; + uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; ++ uart1 = <&uart1>,"status"; + i2s = <&i2s>,"status"; + spi = <&spi0>,"status"; + i2c0 = <&i2c0>,"status"; +--- a/arch/arm/boot/dts/bcm2708-rpi-cm.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-cm.dts +@@ -89,6 +89,7 @@ + __overrides__ { + uart0 = <&uart0>,"status"; + uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; ++ uart1 = <&uart1>,"status"; + i2s = <&i2s>,"status"; + spi = <&spi0>,"status"; + i2c0 = <&i2c0>,"status"; +--- a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts ++++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts +@@ -120,6 +120,7 @@ + __overrides__ { + uart0 = <&uart0>,"status"; + uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; ++ uart1 = <&uart1>,"status"; + i2s = <&i2s>,"status"; + spi = <&spi0>,"status"; + i2c0 = <&i2c0>,"status"; +--- a/arch/arm/boot/dts/bcm2710-rpi-3-b.dts ++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts +@@ -171,6 +171,7 @@ + __overrides__ { + uart0 = <&uart0>,"status"; + uart0_clkrate = <&clk_uart0>,"clock-frequency:0"; ++ uart1 = <&uart1>,"status"; + i2s = <&i2s>,"status"; + spi = <&spi0>,"status"; + i2c0 = <&i2c0>,"status"; +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -124,6 +124,9 @@ Params: + + uart0 Set to "off" to disable uart0 (default "on") + ++ uart1 Set to "on" or "off" to enable or disable uart1 ++ (default varies) ++ + watchdog Set to "on" to enable the hardware watchdog + (default "off") + diff --git a/target/linux/brcm2708/patches-4.4/0192-dwc-overlay-Use-label-so-overrides-can-apply.patch b/target/linux/brcm2708/patches-4.4/0192-dwc-overlay-Use-label-so-overrides-can-apply.patch new file mode 100644 index 0000000000..fa8983c694 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0192-dwc-overlay-Use-label-so-overrides-can-apply.patch @@ -0,0 +1,34 @@ +From 916634f9a59abd7f0de793ac5b523a89c882dbdf Mon Sep 17 00:00:00 2001 +From: Przemek Rudy +Date: Fri, 11 Mar 2016 22:41:26 +0100 +Subject: [PATCH 192/232] dwc-overlay: Use label so overrides can apply. + +--- + arch/arm/boot/dts/overlays/dwc2-overlay.dts | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/arch/arm/boot/dts/overlays/dwc2-overlay.dts ++++ b/arch/arm/boot/dts/overlays/dwc2-overlay.dts +@@ -8,7 +8,7 @@ + target = <&usb>; + #address-cells = <1>; + #size-cells = <1>; +- __overlay__ { ++ dwc2_usb: __overlay__ { + compatible = "brcm,bcm2835-usb"; + reg = <0x7e980000 0x10000>; + interrupts = <1 9>; +@@ -21,9 +21,9 @@ + }; + + __overrides__ { +- dr_mode = <&usb>, "dr_mode"; +- g-np-tx-fifo-size = <&usb>,"g-np-tx-fifo-size:0"; +- g-rx-fifo-size = <&usb>,"g-rx-fifo-size:0"; +- g-tx-fifo-size = <&usb>,"g-tx-fifo-size:0"; ++ dr_mode = <&dwc2_usb>, "dr_mode"; ++ g-np-tx-fifo-size = <&dwc2_usb>,"g-np-tx-fifo-size:0"; ++ g-rx-fifo-size = <&dwc2_usb>,"g-rx-fifo-size:0"; ++ g-tx-fifo-size = <&dwc2_usb>,"g-tx-fifo-size:0"; + }; + }; diff --git a/target/linux/brcm2708/patches-4.4/0193-drm-vc4-Add-a-debugfs-node-for-tracking-execution-st.patch b/target/linux/brcm2708/patches-4.4/0193-drm-vc4-Add-a-debugfs-node-for-tracking-execution-st.patch new file mode 100644 index 0000000000..8b5832409a --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0193-drm-vc4-Add-a-debugfs-node-for-tracking-execution-st.patch @@ -0,0 +1,56 @@ +From b30fecff47b19f93b4d20706bc6c6e6c0b972344 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Fri, 22 Jan 2016 13:06:39 -0800 +Subject: [PATCH 193/232] drm/vc4: Add a debugfs node for tracking execution + state. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_debugfs.c | 1 + + drivers/gpu/drm/vc4/vc4_drv.h | 1 + + drivers/gpu/drm/vc4/vc4_gem.c | 14 ++++++++++++++ + 3 files changed, 16 insertions(+) + +--- a/drivers/gpu/drm/vc4/vc4_debugfs.c ++++ b/drivers/gpu/drm/vc4/vc4_debugfs.c +@@ -17,6 +17,7 @@ + + static const struct drm_info_list vc4_debugfs_list[] = { + {"bo_stats", vc4_bo_stats_debugfs, 0}, ++ {"gem_exec", vc4_gem_exec_debugfs, 0}, + {"hdmi_regs", vc4_hdmi_debugfs_regs, 0}, + {"hvs_regs", vc4_hvs_debugfs_regs, 0}, + {"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0}, +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -403,6 +403,7 @@ void vc4_job_handle_completed(struct vc4 + int vc4_queue_seqno_cb(struct drm_device *dev, + struct vc4_seqno_cb *cb, uint64_t seqno, + void (*func)(struct vc4_seqno_cb *cb)); ++int vc4_gem_exec_debugfs(struct seq_file *m, void *arg); + + /* vc4_hdmi.c */ + extern struct platform_driver vc4_hdmi_driver; +--- a/drivers/gpu/drm/vc4/vc4_gem.c ++++ b/drivers/gpu/drm/vc4/vc4_gem.c +@@ -31,6 +31,20 @@ + #include "vc4_regs.h" + #include "vc4_trace.h" + ++#ifdef CONFIG_DEBUG_FS ++int vc4_gem_exec_debugfs(struct seq_file *m, void *unused) ++{ ++ struct drm_info_node *node = (struct drm_info_node *)m->private; ++ struct drm_device *dev = node->minor->dev; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ seq_printf(m, "Emitted seqno: 0x%016llx\n", vc4->emit_seqno); ++ seq_printf(m, "Finished seqno: 0x%016llx\n", vc4->finished_seqno); ++ ++ return 0; ++} ++#endif /* CONFIG_DEBUG_FS */ ++ + static void + vc4_queue_hangcheck(struct drm_device *dev) + { diff --git a/target/linux/brcm2708/patches-4.4/0194-drm-vc4-Include-vc4_drm.h-in-uapi-in-downstream-buil.patch b/target/linux/brcm2708/patches-4.4/0194-drm-vc4-Include-vc4_drm.h-in-uapi-in-downstream-buil.patch new file mode 100644 index 0000000000..599a38a13a --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0194-drm-vc4-Include-vc4_drm.h-in-uapi-in-downstream-buil.patch @@ -0,0 +1,21 @@ +From 64da292638fcacae979a5e8b3239969ec4275eb9 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 25 Jan 2016 13:03:33 -0800 +Subject: [PATCH 194/232] drm/vc4: Include vc4_drm.h in uapi in downstream + build. + +Signed-off-by: Eric Anholt +--- + include/uapi/drm/Kbuild | 1 + + 1 file changed, 1 insertion(+) + +--- a/include/uapi/drm/Kbuild ++++ b/include/uapi/drm/Kbuild +@@ -14,6 +14,7 @@ header-y += radeon_drm.h + header-y += savage_drm.h + header-y += sis_drm.h + header-y += tegra_drm.h ++header-y += vc4_drm.h + header-y += via_drm.h + header-y += vmwgfx_drm.h + header-y += msm_drm.h diff --git a/target/linux/brcm2708/patches-4.4/0195-drm-vc4-Validate-that-WAIT_BO-padding-is-cleared.patch b/target/linux/brcm2708/patches-4.4/0195-drm-vc4-Validate-that-WAIT_BO-padding-is-cleared.patch new file mode 100644 index 0000000000..ea2a610f86 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0195-drm-vc4-Validate-that-WAIT_BO-padding-is-cleared.patch @@ -0,0 +1,25 @@ +From 4b02ed240cad8575c197d01d083a10e398f768bd Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 25 Jan 2016 13:05:00 -0800 +Subject: [PATCH 195/232] drm/vc4: Validate that WAIT_BO padding is cleared. + +This is ABI future-proofing if we ever want to extend the pad to mean +something. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_gem.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/gpu/drm/vc4/vc4_gem.c ++++ b/drivers/gpu/drm/vc4/vc4_gem.c +@@ -761,6 +761,9 @@ vc4_wait_bo_ioctl(struct drm_device *dev + struct drm_gem_object *gem_obj; + struct vc4_bo *bo; + ++ if (args->pad != 0) ++ return -EINVAL; ++ + gem_obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (!gem_obj) { + DRM_ERROR("Failed to look up GEM BO %d\n", args->handle); diff --git a/target/linux/brcm2708/patches-4.4/0196-drm-vc4-Fix-the-clear-color-for-the-first-tile-rende.patch b/target/linux/brcm2708/patches-4.4/0196-drm-vc4-Fix-the-clear-color-for-the-first-tile-rende.patch new file mode 100644 index 0000000000..bc4c81286a --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0196-drm-vc4-Fix-the-clear-color-for-the-first-tile-rende.patch @@ -0,0 +1,49 @@ +From 7ff02c91259682b40b5ce8a90f114925d71572c0 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 25 Jan 2016 13:52:41 -0800 +Subject: [PATCH 196/232] drm/vc4: Fix the clear color for the first tile + rendered. + +Apparently in hardware (as opposed to simulation), the clear colors +need to be uploaded before the render config, otherwise they won't +take effect. Fixes igt's vc4_wait_bo/used-bo-* subtests. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_render_cl.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_render_cl.c ++++ b/drivers/gpu/drm/vc4/vc4_render_cl.c +@@ -321,15 +321,6 @@ static int vc4_create_rcl_bo(struct drm_ + list_add_tail(&to_vc4_bo(&setup->rcl->base)->unref_head, + &exec->unref_list); + +- rcl_u8(setup, VC4_PACKET_TILE_RENDERING_MODE_CONFIG); +- rcl_u32(setup, +- (setup->color_write ? (setup->color_write->paddr + +- args->color_write.offset) : +- 0)); +- rcl_u16(setup, args->width); +- rcl_u16(setup, args->height); +- rcl_u16(setup, args->color_write.bits); +- + /* The tile buffer gets cleared when the previous tile is stored. If + * the clear values changed between frames, then the tile buffer has + * stale clear values in it, so we have to do a store in None mode (no +@@ -349,6 +340,15 @@ static int vc4_create_rcl_bo(struct drm_ + rcl_u32(setup, 0); /* no address, since we're in None mode */ + } + ++ rcl_u8(setup, VC4_PACKET_TILE_RENDERING_MODE_CONFIG); ++ rcl_u32(setup, ++ (setup->color_write ? (setup->color_write->paddr + ++ args->color_write.offset) : ++ 0)); ++ rcl_u16(setup, args->width); ++ rcl_u16(setup, args->height); ++ rcl_u16(setup, args->color_write.bits); ++ + for (y = min_y_tile; y <= max_y_tile; y++) { + for (x = min_x_tile; x <= max_x_tile; x++) { + bool first = (x == min_x_tile && y == min_y_tile); diff --git a/target/linux/brcm2708/patches-4.4/0197-drm-vc4-Return-an-ERR_PTR-from-BO-creation-instead-o.patch b/target/linux/brcm2708/patches-4.4/0197-drm-vc4-Return-an-ERR_PTR-from-BO-creation-instead-o.patch new file mode 100644 index 0000000000..9043036868 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0197-drm-vc4-Return-an-ERR_PTR-from-BO-creation-instead-o.patch @@ -0,0 +1,137 @@ +From d6211c47e6671faf74389dbcbe29454c5997defb Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 25 Jan 2016 14:13:12 -0800 +Subject: [PATCH 197/232] drm/vc4: Return an ERR_PTR from BO creation instead + of NULL. + +Fixes igt vc4_create_bo/create-bo-0 by returning -EINVAL from the +ioctl instead of -ENOMEM. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_bo.c | 23 +++++++++++++---------- + drivers/gpu/drm/vc4/vc4_gem.c | 4 ++-- + drivers/gpu/drm/vc4/vc4_irq.c | 2 +- + drivers/gpu/drm/vc4/vc4_render_cl.c | 4 ++-- + drivers/gpu/drm/vc4/vc4_validate.c | 4 ++-- + 5 files changed, 20 insertions(+), 17 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_bo.c ++++ b/drivers/gpu/drm/vc4/vc4_bo.c +@@ -213,10 +213,10 @@ struct vc4_bo *vc4_bo_create(struct drm_ + size_t size = roundup(unaligned_size, PAGE_SIZE); + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct drm_gem_cma_object *cma_obj; +- int pass; ++ int pass, ret; + + if (size == 0) +- return NULL; ++ return ERR_PTR(-EINVAL); + + /* First, try to get a vc4_bo from the kernel BO cache. */ + if (from_cache) { +@@ -247,14 +247,17 @@ struct vc4_bo *vc4_bo_create(struct drm_ + * unreferenced BOs to the cache, and then + * free the cache. + */ +- vc4_wait_for_seqno(dev, vc4->emit_seqno, ~0ull, true); ++ ret = vc4_wait_for_seqno(dev, vc4->emit_seqno, ~0ull, ++ true); ++ if (ret) ++ return ERR_PTR(ret); + vc4_job_handle_completed(vc4); + vc4_bo_cache_purge(dev); + break; + case 3: + DRM_ERROR("Failed to allocate from CMA:\n"); + vc4_bo_stats_dump(vc4); +- return NULL; ++ return ERR_PTR(-ENOMEM); + } + } + +@@ -276,8 +279,8 @@ int vc4_dumb_create(struct drm_file *fil + args->size = args->pitch * args->height; + + bo = vc4_bo_create(dev, args->size, false); +- if (!bo) +- return -ENOMEM; ++ if (IS_ERR(bo)) ++ return PTR_ERR(bo); + + ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); + drm_gem_object_unreference_unlocked(&bo->base.base); +@@ -460,8 +463,8 @@ int vc4_create_bo_ioctl(struct drm_devic + * get zeroed, and that might leak data between users. + */ + bo = vc4_bo_create(dev, args->size, false); +- if (!bo) +- return -ENOMEM; ++ if (IS_ERR(bo)) ++ return PTR_ERR(bo); + + ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); + drm_gem_object_unreference_unlocked(&bo->base.base); +@@ -513,8 +516,8 @@ vc4_create_shader_bo_ioctl(struct drm_de + } + + bo = vc4_bo_create(dev, args->size, true); +- if (!bo) +- return -ENOMEM; ++ if (IS_ERR(bo)) ++ return PTR_ERR(bo); + + ret = copy_from_user(bo->base.vaddr, + (void __user *)(uintptr_t)args->data, +--- a/drivers/gpu/drm/vc4/vc4_gem.c ++++ b/drivers/gpu/drm/vc4/vc4_gem.c +@@ -593,9 +593,9 @@ vc4_get_bcl(struct drm_device *dev, stru + } + + bo = vc4_bo_create(dev, exec_size, true); +- if (!bo) { ++ if (IS_ERR(bo)) { + DRM_ERROR("Couldn't allocate BO for binning\n"); +- ret = PTR_ERR(exec->exec_bo); ++ ret = PTR_ERR(bo); + goto fail; + } + exec->exec_bo = &bo->base; +--- a/drivers/gpu/drm/vc4/vc4_irq.c ++++ b/drivers/gpu/drm/vc4/vc4_irq.c +@@ -57,7 +57,7 @@ vc4_overflow_mem_work(struct work_struct + struct vc4_bo *bo; + + bo = vc4_bo_create(dev, 256 * 1024, true); +- if (!bo) { ++ if (IS_ERR(bo)) { + DRM_ERROR("Couldn't allocate binner overflow mem\n"); + return; + } +--- a/drivers/gpu/drm/vc4/vc4_render_cl.c ++++ b/drivers/gpu/drm/vc4/vc4_render_cl.c +@@ -316,8 +316,8 @@ static int vc4_create_rcl_bo(struct drm_ + size += xtiles * ytiles * loop_body_size; + + setup->rcl = &vc4_bo_create(dev, size, true)->base; +- if (!setup->rcl) +- return -ENOMEM; ++ if (IS_ERR(setup->rcl)) ++ return PTR_ERR(setup->rcl); + list_add_tail(&to_vc4_bo(&setup->rcl->base)->unref_head, + &exec->unref_list); + +--- a/drivers/gpu/drm/vc4/vc4_validate.c ++++ b/drivers/gpu/drm/vc4/vc4_validate.c +@@ -401,8 +401,8 @@ validate_tile_binning_config(VALIDATE_AR + tile_bo = vc4_bo_create(dev, exec->tile_alloc_offset + tile_alloc_size, + true); + exec->tile_bo = &tile_bo->base; +- if (!exec->tile_bo) +- return -ENOMEM; ++ if (IS_ERR(exec->tile_bo)) ++ return PTR_ERR(exec->tile_bo); + list_add_tail(&tile_bo->unref_head, &exec->unref_list); + + /* tile alloc address. */ diff --git a/target/linux/brcm2708/patches-4.4/0198-drm-vc4-Fix-ERESTARTSYS-error-return-from-BO-waits.patch b/target/linux/brcm2708/patches-4.4/0198-drm-vc4-Fix-ERESTARTSYS-error-return-from-BO-waits.patch new file mode 100644 index 0000000000..5ce96be4e0 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0198-drm-vc4-Fix-ERESTARTSYS-error-return-from-BO-waits.patch @@ -0,0 +1,32 @@ +From 39e77e9d67511ac9bfbb00ab471fc7b0374dcdf6 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 25 Jan 2016 14:32:41 -0800 +Subject: [PATCH 198/232] drm/vc4: Fix -ERESTARTSYS error return from BO waits. + +This caused the wait ioctls to claim that waiting had completed when +we actually got interrupted by a signal before it was done. Fixes +broken rendering throttling that produced serious lag in X window +dragging. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_gem.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_gem.c ++++ b/drivers/gpu/drm/vc4/vc4_gem.c +@@ -352,12 +352,10 @@ vc4_wait_for_seqno(struct drm_device *de + finish_wait(&vc4->job_wait_queue, &wait); + trace_vc4_wait_for_seqno_end(dev, seqno); + +- if (ret && ret != -ERESTARTSYS) { ++ if (ret && ret != -ERESTARTSYS) + DRM_ERROR("timeout waiting for render thread idle\n"); +- return ret; +- } + +- return 0; ++ return ret; + } + + static void diff --git a/target/linux/brcm2708/patches-4.4/0199-drm-vc4-Drop-error-message-on-seqno-wait-timeouts.patch b/target/linux/brcm2708/patches-4.4/0199-drm-vc4-Drop-error-message-on-seqno-wait-timeouts.patch new file mode 100644 index 0000000000..34bcc248d1 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0199-drm-vc4-Drop-error-message-on-seqno-wait-timeouts.patch @@ -0,0 +1,27 @@ +From a9221f160c28301850d5368d283c751b18950f2a Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 25 Jan 2016 14:33:50 -0800 +Subject: [PATCH 199/232] drm/vc4: Drop error message on seqno wait timeouts. + +These ioctls end up getting exposed to userspace, and having normal +user requests print DRM errors is obviously wrong. The message was +originally to give us some idea of what happened when a hang occurred, +but we have a DRM_INFO from reset for that. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_gem.c | 3 --- + 1 file changed, 3 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_gem.c ++++ b/drivers/gpu/drm/vc4/vc4_gem.c +@@ -352,9 +352,6 @@ vc4_wait_for_seqno(struct drm_device *de + finish_wait(&vc4->job_wait_queue, &wait); + trace_vc4_wait_for_seqno_end(dev, seqno); + +- if (ret && ret != -ERESTARTSYS) +- DRM_ERROR("timeout waiting for render thread idle\n"); +- + return ret; + } + diff --git a/target/linux/brcm2708/patches-4.4/0200-BCM270X_DT-Add-1-bit-SDIO-using-minimal-pins.patch b/target/linux/brcm2708/patches-4.4/0200-BCM270X_DT-Add-1-bit-SDIO-using-minimal-pins.patch new file mode 100644 index 0000000000..3aea5f67e6 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0200-BCM270X_DT-Add-1-bit-SDIO-using-minimal-pins.patch @@ -0,0 +1,81 @@ +From 19d839cafdd5ddb1233df5be821d702e655d2d16 Mon Sep 17 00:00:00 2001 +From: campag +Date: Wed, 24 Feb 2016 16:45:42 +0000 +Subject: [PATCH 200/232] BCM270X_DT: Add 1-bit SDIO using minimal pins... + +... for that mode: GPIOs 22-25. +--- + arch/arm/boot/dts/overlays/README | 21 ++++++++++++++ + arch/arm/boot/dts/overlays/sdio-1bit-overlay.dts | 36 ++++++++++++++++++++++++ + 2 files changed, 57 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/sdio-1bit-overlay.dts + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -709,6 +709,27 @@ Params: overclock_50 SD Clock + bus_width Set the SDIO host bus width (default 4 bits) + + ++Name: sdio-1bit ++Info: Selects the bcm2835-sdhost SD/MMC driver, optionally with overclock, ++ and enables 1-bit SDIO via GPIOs 22-25. ++Load: dtoverlay=sdio-1bit,= ++Params: overclock_50 SD Clock (in MHz) to use when the MMC framework ++ requests 50MHz ++ ++ sdio_overclock SDIO Clock (in MHz) to use when the MMC ++ framework requests 50MHz ++ ++ force_pio Disable DMA support (default off) ++ ++ pio_limit Number of blocks above which to use DMA ++ (default 1) ++ ++ debug Enable debug output (default off) ++ ++ poll_once Disable SDIO-device polling every second ++ (default on: polling once at boot-time) ++ ++ + Name: sdtweak + Info: Tunes the bcm2835-sdhost SD/MMC driver + Load: dtoverlay=sdtweak,= +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/sdio-1bit-overlay.dts +@@ -0,0 +1,36 @@ ++/* Enable 1-bit SDIO from MMC interface via GPIOs 22-25. Includes sdhost overlay. */ ++ ++/include/ "sdhost-overlay.dts" ++ ++/{ ++ compatible = "brcm,bcm2708"; ++ ++ fragment@3 { ++ target = <&mmc>; ++ sdio_mmc: __overlay__ { ++ compatible = "brcm,bcm2835-mmc"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdio_pins>; ++ non-removable; ++ bus-width = <1>; ++ brcm,overclock-50 = <0>; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@4 { ++ target = <&gpio>; ++ __overlay__ { ++ sdio_pins: sdio_pins { ++ brcm,pins = <22 23 24 25>; ++ brcm,function = <7 7 7 7>; /* ALT3 = SD1 */ ++ brcm,pull = <0 2 2 2>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ poll_once = <&sdio_mmc>,"non-removable?"; ++ sdio_overclock = <&sdio_mmc>,"brcm,overclock-50:0"; ++ }; ++}; diff --git a/target/linux/brcm2708/patches-4.4/0202-Add-overlay-and-enable-support-for-QCA7000-board.patch b/target/linux/brcm2708/patches-4.4/0202-Add-overlay-and-enable-support-for-QCA7000-board.patch new file mode 100644 index 0000000000..e2d168b9c1 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0202-Add-overlay-and-enable-support-for-QCA7000-board.patch @@ -0,0 +1,127 @@ +From 76d3e26b821b2490619f4762b5f604e77636fdb1 Mon Sep 17 00:00:00 2001 +From: Michael Heimpold +Date: Fri, 29 Jan 2016 12:00:37 +0100 +Subject: [PATCH 202/232] Add overlay and enable support for QCA7000 board + +This adds a device tree overlay for the QCA7000 which can be used +when attaching an I2SE's PLC Stamp micro EVK to the Raspberry Pi. + +This Evaluation Board embeds a QCA7000 chip, a Homeplug Green PHY +powerline chip from Qualcomm/Atheros for the Internet of Things. + +This patch also enables the required QCA7000 driver module +in the default configurations. + +Signed-off-by: Stefan Wahren +Signed-off-by: Michael Heimpold +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 8 ++++ + arch/arm/boot/dts/overlays/qca7000-overlay.dts | 52 ++++++++++++++++++++++++++ + arch/arm/configs/bcm2709_defconfig | 1 + + arch/arm/configs/bcmrpi_defconfig | 1 + + 5 files changed, 63 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/qca7000-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -48,6 +48,7 @@ dtbo-$(RPI_DT_OVERLAYS) += pitft28-resis + dtbo-$(RPI_DT_OVERLAYS) += pps-gpio.dtbo + dtbo-$(RPI_DT_OVERLAYS) += pwm.dtbo + dtbo-$(RPI_DT_OVERLAYS) += pwm-2chan.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += qca7000.dtbo + dtbo-$(RPI_DT_OVERLAYS) += raspidac3.dtbo + dtbo-$(RPI_DT_OVERLAYS) += rpi-backlight.dtbo + dtbo-$(RPI_DT_OVERLAYS) += rpi-dac.dtbo +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -625,6 +625,14 @@ Params: pin Output p + clock PWM clock frequency (informational) + + ++Name: qca7000 ++Info: I2SE's Evaluation Board for PLC Stamp micro ++Load: dtoverlay=qca7000,= ++Params: int_pin GPIO pin for interrupt signal (default 23) ++ ++ speed SPI bus speed (default 12 MHz) ++ ++ + Name: raspidac3 + Info: Configures the RaspiDAV Rev.3x audio card + Load: dtoverlay=raspidac3 +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/qca7000-overlay.dts +@@ -0,0 +1,52 @@ ++// Overlay for the Qualcomm Atheros QCA7000 on I2SE's PLC Stamp micro EVK ++// Visit: https://www.i2se.com/product/plc-stamp-micro-evk for details ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ status = "okay"; ++ ++ spidev@0 { ++ status = "disabled"; ++ }; ++ ++ eth1: qca7000@0 { ++ compatible = "qca,qca7000"; ++ reg = <0>; /* CE0 */ ++ pinctrl-names = "default"; ++ pinctrl-0 = <ð1_pins>; ++ interrupt-parent = <&gpio>; ++ interrupts = <23 0x1>; /* rising edge */ ++ spi-max-frequency = <12000000>; ++ status = "okay"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ eth1_pins: eth1_pins { ++ brcm,pins = <23>; ++ brcm,function = <0>; /* in */ ++ brcm,pull = <0>; /* none */ ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ int_pin = <ð1>, "interrupts:0", ++ <ð1_pins>, "brcm,pins:0"; ++ speed = <ð1>, "spi-max-frequency:0"; ++ }; ++}; +--- a/arch/arm/configs/bcm2709_defconfig ++++ b/arch/arm/configs/bcm2709_defconfig +@@ -446,6 +446,7 @@ CONFIG_NETCONSOLE=m + CONFIG_TUN=m + CONFIG_VETH=m + CONFIG_ENC28J60=m ++CONFIG_QCA7000=m + CONFIG_MDIO_BITBANG=m + CONFIG_PPP=m + CONFIG_PPP_BSDCOMP=m +--- a/arch/arm/configs/bcmrpi_defconfig ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -439,6 +439,7 @@ CONFIG_NETCONSOLE=m + CONFIG_TUN=m + CONFIG_VETH=m + CONFIG_ENC28J60=m ++CONFIG_QCA7000=m + CONFIG_MDIO_BITBANG=m + CONFIG_PPP=m + CONFIG_PPP_BSDCOMP=m diff --git a/target/linux/brcm2708/patches-4.4/0203-serial-Take-care-starting-a-hung-up-tty-s-port.patch b/target/linux/brcm2708/patches-4.4/0203-serial-Take-care-starting-a-hung-up-tty-s-port.patch new file mode 100644 index 0000000000..5ab6d2e509 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0203-serial-Take-care-starting-a-hung-up-tty-s-port.patch @@ -0,0 +1,28 @@ +From c9276ae7cfecbd1df477ff31116682e72dd6d8f7 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 9 Mar 2016 13:28:24 +0000 +Subject: [PATCH 203/232] serial: Take care starting a hung-up tty's port + +tty_port_hangup sets a port's tty field to NULL (holding the port lock), +but uart_tx_stopped, called from __uart_start (with the port lock), +uses the tty field without checking for NULL. + +Change uart_tx_stopped to treat a NULL tty field as another stopped +indication. + +Signed-off-by: Phil Elwell +--- + include/linux/serial_core.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/serial_core.h ++++ b/include/linux/serial_core.h +@@ -397,7 +397,7 @@ int uart_resume_port(struct uart_driver + static inline int uart_tx_stopped(struct uart_port *port) + { + struct tty_struct *tty = port->state->port.tty; +- if (tty->stopped || port->hw_stopped) ++ if (!tty || tty->stopped || port->hw_stopped) + return 1; + return 0; + } diff --git a/target/linux/brcm2708/patches-4.4/0204-pi3-miniuart-bt-overlay-Correct-and-clarify-info.patch b/target/linux/brcm2708/patches-4.4/0204-pi3-miniuart-bt-overlay-Correct-and-clarify-info.patch new file mode 100644 index 0000000000..656b9010d7 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0204-pi3-miniuart-bt-overlay-Correct-and-clarify-info.patch @@ -0,0 +1,41 @@ +From b7bbcabcce3f31bca25f17d68fb9eb0e2e045184 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 17 Mar 2016 10:16:16 +0000 +Subject: [PATCH 204/232] pi3-miniuart-bt-overlay: Correct and clarify info + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/overlays/README | 6 ++++-- + arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts | 6 ++++-- + 2 files changed, 8 insertions(+), 4 deletions(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -511,8 +511,10 @@ Name: pi3-miniuart-bt + Info: Switch Pi3 Bluetooth function to use the mini-UART (ttyS0) and restore + UART0/ttyAMA0 over GPIOs 14 & 15. Note that this may reduce the maximum + usable baudrate. +- N.B. It is also necessary to edit /lib/systemd/system/hciuart.server +- and replace ttyAMA0 with ttyS0. ++ N.B. It is also necessary to edit /lib/systemd/system/hciuart.service ++ and replace ttyAMA0 with ttyS0, unless you have a system with udev rules ++ that create /dev/serial0 and /dev/serial1, in which case use ++ /dev/serial1 instead because it will always be correct. + Load: dtoverlay=pi3-miniuart-bt + Params: + +--- a/arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts +@@ -5,8 +5,10 @@ + UART0/ttyAMA0 over GPIOs 14 & 15. Note that this may reduce the maximum + usable baudrate. + +- It is also necessary to edit /lib/systemd/system/hciuart.server and +- replace ttyAMA0 with ttyS0. ++ It is also necessary to edit /lib/systemd/system/hciuart.service and ++ replace ttyAMA0 with ttyS0, unless you have a system with udev rules ++ that create /dev/serial0 and /dev/serial1, in which case use /dev/serial1 ++ instead because it will always be correct. + + If cmdline.txt uses the alias serial0 to refer to the user-accessable port + then the firmware will replace with the appropriate port whether or not diff --git a/target/linux/brcm2708/patches-4.4/0205-pwm-overlays-Params-must-have-in-overlay-targets.patch b/target/linux/brcm2708/patches-4.4/0205-pwm-overlays-Params-must-have-in-overlay-targets.patch new file mode 100644 index 0000000000..188239d39a --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0205-pwm-overlays-Params-must-have-in-overlay-targets.patch @@ -0,0 +1,52 @@ +From 05a2610389127e4cfcc2b9afdec83e1176b205bc Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 17 Mar 2016 10:41:56 +0000 +Subject: [PATCH 205/232] pwm overlays: Params must have in-overlay targets + +--- + arch/arm/boot/dts/overlays/pwm-2chan-overlay.dts | 9 ++++++++- + arch/arm/boot/dts/overlays/pwm-overlay.dts | 9 ++++++++- + 2 files changed, 16 insertions(+), 2 deletions(-) + +--- a/arch/arm/boot/dts/overlays/pwm-2chan-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pwm-2chan-overlay.dts +@@ -36,11 +36,18 @@ N.B.: + }; + }; + ++ fragment@2 { ++ target = <&clk_pwm>; ++ frag2: __overlay__ { ++ clock-frequency = <100000000>; ++ }; ++ }; ++ + __overrides__ { + pin = <&pwm_pins>,"brcm,pins:0"; + pin2 = <&pwm_pins>,"brcm,pins:4"; + func = <&pwm_pins>,"brcm,function:0"; + func2 = <&pwm_pins>,"brcm,function:4"; +- clock = <&clk_pwm>,"clock-frequency:0"; ++ clock = <&frag2>,"clock-frequency:0"; + }; + }; +--- a/arch/arm/boot/dts/overlays/pwm-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pwm-overlay.dts +@@ -34,9 +34,16 @@ N.B.: + }; + }; + ++ fragment@2 { ++ target = <&clk_pwm>; ++ frag2: __overlay__ { ++ clock-frequency = <100000000>; ++ }; ++ }; ++ + __overrides__ { + pin = <&pwm_pins>,"brcm,pins:0"; + func = <&pwm_pins>,"brcm,function:0"; +- clock = <&clk_pwm>,"clock-frequency:0"; ++ clock = <&frag2>,"clock-frequency:0"; + }; + }; diff --git a/target/linux/brcm2708/patches-4.4/0206-BCM270X_DT-Switch-Compute-Module-to-MMC.patch b/target/linux/brcm2708/patches-4.4/0206-BCM270X_DT-Switch-Compute-Module-to-MMC.patch new file mode 100644 index 0000000000..e89917b178 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0206-BCM270X_DT-Switch-Compute-Module-to-MMC.patch @@ -0,0 +1,53 @@ +From 985c06bd528e49fd44155c3da0c6107c46d947cc Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 18 Mar 2016 13:06:29 +0000 +Subject: [PATCH 206/232] BCM270X_DT: Switch Compute Module to MMC + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/bcm2708-rpi-cm.dtsi | 17 ++++++++--------- + 1 file changed, 8 insertions(+), 9 deletions(-) + +--- a/arch/arm/boot/dts/bcm2708-rpi-cm.dtsi ++++ b/arch/arm/boot/dts/bcm2708-rpi-cm.dtsi +@@ -8,9 +8,9 @@ + }; + + &gpio { +- sdhost_pins: sdhost_pins { ++ mmc_pins: mmc_pins { + brcm,pins = <48 49 50 51 52 53>; +- brcm,function = <4>; /* alt0 */ ++ brcm,function = <7>; /* alt3 */ + }; + }; + +@@ -22,12 +22,14 @@ + }; + }; + +-&sdhost { ++ ++&mmc { + pinctrl-names = "default"; +- pinctrl-0 = <&sdhost_pins>; +- bus-width = <4>; ++ pinctrl-0 = <&mmc_pins>; + non-removable; ++ bus-width = <4>; + status = "okay"; ++ brcm,overclock-50 = <0>; + }; + + &fb { +@@ -45,9 +47,6 @@ + audio = <&audio>,"status"; + watchdog = <&watchdog>,"status"; + random = <&random>,"status"; +- sd_overclock = <&sdhost>,"brcm,overclock-50:0"; +- sd_force_pio = <&sdhost>,"brcm,force-pio?"; +- sd_pio_limit = <&sdhost>,"brcm,pio-limit:0"; +- sd_debug = <&sdhost>,"brcm,debug"; ++ sd_overclock = <&mmc>,"brcm,overclock-50:0"; + }; + }; diff --git a/target/linux/brcm2708/patches-4.4/0207-dwc_otg-Don-t-free-qh-align-buffers-in-atomic-contex.patch b/target/linux/brcm2708/patches-4.4/0207-dwc_otg-Don-t-free-qh-align-buffers-in-atomic-contex.patch new file mode 100644 index 0000000000..201cdc834a --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0207-dwc_otg-Don-t-free-qh-align-buffers-in-atomic-contex.patch @@ -0,0 +1,44 @@ +From 4558f488edf41daddd4bceed975f3af8cee89033 Mon Sep 17 00:00:00 2001 +From: P33M +Date: Fri, 18 Mar 2016 17:38:37 +0000 +Subject: [PATCH 207/232] dwc_otg: Don't free qh align buffers in atomic + context + +--- + drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c ++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c +@@ -56,6 +56,9 @@ void dwc_otg_hcd_qh_free(dwc_otg_hcd_t * + { + dwc_otg_qtd_t *qtd, *qtd_tmp; + dwc_irqflags_t flags; ++ uint32_t buf_size = 0; ++ uint8_t *align_buf_virt = NULL; ++ dwc_dma_t align_buf_dma; + + /* Free each QTD in the QTD list */ + DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); +@@ -67,17 +70,19 @@ void dwc_otg_hcd_qh_free(dwc_otg_hcd_t * + if (hcd->core_if->dma_desc_enable) { + dwc_otg_hcd_qh_free_ddma(hcd, qh); + } else if (qh->dw_align_buf) { +- uint32_t buf_size; + if (qh->ep_type == UE_ISOCHRONOUS) { + buf_size = 4096; + } else { + buf_size = hcd->core_if->core_params->max_transfer_size; + } +- DWC_DMA_FREE(buf_size, qh->dw_align_buf, qh->dw_align_buf_dma); ++ align_buf_virt = qh->dw_align_buf; ++ align_buf_dma = qh->dw_align_buf_dma; + } + + DWC_FREE(qh); + DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ if (align_buf_virt) ++ DWC_DMA_FREE(buf_size, align_buf_virt, align_buf_dma); + return; + } + diff --git a/target/linux/brcm2708/patches-4.4/0208-dwc_otg-Enable-the-hack-for-Split-Interrupt-transact.patch b/target/linux/brcm2708/patches-4.4/0208-dwc_otg-Enable-the-hack-for-Split-Interrupt-transact.patch new file mode 100644 index 0000000000..88000fddfb --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0208-dwc_otg-Enable-the-hack-for-Split-Interrupt-transact.patch @@ -0,0 +1,28 @@ +From 7956536a3d78ba0ef8ec990651b315664ed70f90 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Mon, 21 Mar 2016 15:38:38 +0000 +Subject: [PATCH 208/232] dwc_otg: Enable the hack for Split Interrupt + transactions by default + +dwc_otg.fiq_fsm_mask=0xF has long been a suggestion for users with audio stutters or other USB bandwidth issues. +So far we are aware of many success stories but no failure caused by this setting. +Make it a default to learn more. + +See: https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=70437 + +Signed-off-by: popcornmix +--- + drivers/usb/host/dwc_otg/dwc_otg_driver.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/usb/host/dwc_otg/dwc_otg_driver.c ++++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.c +@@ -247,7 +247,7 @@ bool fiq_fsm_enable = true; + //Bulk split-transaction NAK holdoff in microframes + uint16_t nak_holdoff = 8; + +-unsigned short fiq_fsm_mask = 0x07; ++unsigned short fiq_fsm_mask = 0x0F; + + /** + * This function shows the Driver Version. diff --git a/target/linux/brcm2708/patches-4.4/0209-BCM270X_DT-Remove-explicit-claiming-of-UART-pins.patch b/target/linux/brcm2708/patches-4.4/0209-BCM270X_DT-Remove-explicit-claiming-of-UART-pins.patch new file mode 100644 index 0000000000..d99119e8bd --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0209-BCM270X_DT-Remove-explicit-claiming-of-UART-pins.patch @@ -0,0 +1,89 @@ +From adc10c5da2b83bbc8c893e3a745b43890f27e2ac Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Sat, 19 Mar 2016 16:51:37 +0000 +Subject: [PATCH 209/232] BCM270X_DT: Remove explicit claiming of UART pins + +It is convenient to be able to map a different function to the UART +pins (e.g. DPI for vga666) without having to disable the UART first. + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/bcm2710-rpi-3-b.dts | 6 +++--- + .../boot/dts/overlays/pi3-disable-bt-overlay.dts | 10 ++++----- + .../boot/dts/overlays/pi3-miniuart-bt-overlay.dts | 25 +++++++++++----------- + 3 files changed, 20 insertions(+), 21 deletions(-) + +--- a/arch/arm/boot/dts/bcm2710-rpi-3-b.dts ++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts +@@ -57,9 +57,9 @@ + }; + + uart1_pins: uart1_pins { +- brcm,pins = <14 15>; +- brcm,function = <2>; /* alt5=UART1 */ +- brcm,pull = <0 2>; ++ brcm,pins; ++ brcm,function; ++ brcm,pull; + }; + }; + +--- a/arch/arm/boot/dts/overlays/pi3-disable-bt-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pi3-disable-bt-overlay.dts +@@ -28,13 +28,11 @@ + }; + + fragment@2 { +- target = <&gpio>; ++ target = <&uart0_pins>; + __overlay__ { +- uart0_pins: uart0_pins { +- brcm,pins = <14 15>; +- brcm,function = <4>; /* alt0 */ +- brcm,pull = <0 2>; +- }; ++ brcm,pins; ++ brcm,function; ++ brcm,pull; + }; + }; + +--- a/arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts +@@ -37,23 +37,24 @@ + }; + + fragment@2 { +- target = <&gpio>; ++ target = <&uart0_pins>; + __overlay__ { +- uart0_pins: uart0_pins { +- brcm,pins = <14 15>; +- brcm,function = <4>; /* alt0 */ +- brcm,pull = <0 2>; +- }; +- +- uart1_pins: uart1_pins { +- brcm,pins = <32 33>; +- brcm,function = <2>; /* alt5=UART1 */ +- brcm,pull = <0 2>; +- }; ++ brcm,pins; ++ brcm,function; ++ brcm,pull; + }; + }; + + fragment@3 { ++ target = <&uart1_pins>; ++ __overlay__ { ++ brcm,pins = <32 33>; ++ brcm,function = <2>; /* alt5=UART1 */ ++ brcm,pull = <0 2>; ++ }; ++ }; ++ ++ fragment@4 { + target-path = "/aliases"; + __overlay__ { + serial0 = "/soc/uart@7e201000"; diff --git a/target/linux/brcm2708/patches-4.4/0210-lirc_rpi-Lower-IR-reception-error-to-debug.patch b/target/linux/brcm2708/patches-4.4/0210-lirc_rpi-Lower-IR-reception-error-to-debug.patch new file mode 100644 index 0000000000..d7c0722095 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0210-lirc_rpi-Lower-IR-reception-error-to-debug.patch @@ -0,0 +1,21 @@ +From 79e3ee1d325bf1aea9e255127dd132265b734efe Mon Sep 17 00:00:00 2001 +From: Rodrigo Freire +Date: Tue, 22 Mar 2016 12:40:33 -0300 +Subject: [PATCH 210/232] lirc_rpi: Lower IR reception error to debug + +Lowers a IR reception error condition message to KERNEL_DEBUG +--- + drivers/staging/media/lirc/lirc_rpi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/staging/media/lirc/lirc_rpi.c ++++ b/drivers/staging/media/lirc/lirc_rpi.c +@@ -271,7 +271,7 @@ static irqreturn_t irq_handler(int i, vo + data = PULSE_MASK; /* really long time */ + if (!(signal^sense)) { + /* sanity check */ +- printk(KERN_WARNING LIRC_DRIVER_NAME ++ printk(KERN_DEBUG 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); diff --git a/target/linux/brcm2708/patches-4.4/0211-vchiq_arm-Access-the-dequeue_pending-flag-locked.patch b/target/linux/brcm2708/patches-4.4/0211-vchiq_arm-Access-the-dequeue_pending-flag-locked.patch new file mode 100644 index 0000000000..77158ea793 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0211-vchiq_arm-Access-the-dequeue_pending-flag-locked.patch @@ -0,0 +1,58 @@ +From 26eb219f0645eb9848887ca25c0a073a9a40d852 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 23 Mar 2016 14:16:25 +0000 +Subject: [PATCH 211/232] vchiq_arm: Access the dequeue_pending flag locked + +Reading through this code looking for another problem (now found in userland) +the use of dequeue_pending outside a lock didn't seem safe. + +Signed-off-by: Phil Elwell +--- + .../misc/vc04_services/interface/vchiq_arm/vchiq_arm.c | 17 ++++++++++++----- + 1 file changed, 12 insertions(+), 5 deletions(-) + +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c +@@ -279,6 +279,7 @@ service_callback(VCHIQ_REASON_T reason, + USER_SERVICE_T *user_service; + VCHIQ_SERVICE_T *service; + VCHIQ_INSTANCE_T instance; ++ int skip_completion = 0; + DEBUG_INITIALISE(g_state.local) + + DEBUG_TRACE(SERVICE_CALLBACK_LINE); +@@ -345,9 +346,6 @@ service_callback(VCHIQ_REASON_T reason, + 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 +@@ -356,13 +354,22 @@ service_callback(VCHIQ_REASON_T reason, + 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; ++ skip_completion = 1; + } + ++ spin_unlock(&msg_queue_spinlock); ++ ++ up(&user_service->insert_event); ++ + header = NULL; + } ++ ++ if (skip_completion) { ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ return VCHIQ_SUCCESS; ++ } ++ + DEBUG_TRACE(SERVICE_CALLBACK_LINE); + + return add_completion(instance, reason, header, user_service, diff --git a/target/linux/brcm2708/patches-4.4/0212-BCM270X_DT-Add-pi3-act-led-overlay.patch b/target/linux/brcm2708/patches-4.4/0212-BCM270X_DT-Add-pi3-act-led-overlay.patch new file mode 100644 index 0000000000..c3f0ea0930 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0212-BCM270X_DT-Add-pi3-act-led-overlay.patch @@ -0,0 +1,94 @@ +From 10cd986960d2ad1fd881fa3338af40e612076ba4 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 23 Mar 2016 15:57:14 +0000 +Subject: [PATCH 212/232] BCM270X_DT: Add pi3-act-led overlay + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 19 +++++++++++++++ + arch/arm/boot/dts/overlays/pi3-act-led-overlay.dts | 27 ++++++++++++++++++++++ + 3 files changed, 47 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/pi3-act-led-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -39,6 +39,7 @@ dtbo-$(RPI_DT_OVERLAYS) += mcp2515-can0. + dtbo-$(RPI_DT_OVERLAYS) += mcp2515-can1.dtbo + dtbo-$(RPI_DT_OVERLAYS) += mmc.dtbo + dtbo-$(RPI_DT_OVERLAYS) += mz61581.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += pi3-act-led.dtbo + dtbo-$(RPI_DT_OVERLAYS) += pi3-disable-bt.dtbo + dtbo-$(RPI_DT_OVERLAYS) += pi3-miniuart-bt.dtbo + dtbo-$(RPI_DT_OVERLAYS) += piscreen.dtbo +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -136,12 +136,14 @@ Params: + + act_led_activelow Set to "on" to invert the sense of the LED + (default "off") ++ N.B. For Pi3 see pi3-act-led overlay. + + act_led_gpio Set which GPIO to use for the activity LED + (in case you want to connect it to an external + device) + (default "16" on a non-Plus board, "47" on a + Plus or Pi 2) ++ N.B. For Pi3 see pi3-act-led overlay. + + pwr_led_trigger + pwr_led_activelow +@@ -499,6 +501,23 @@ Params: speed Display + [ The pcf8563-rtc overlay has been deleted. See i2c-rtc. ] + + ++Name: pi3-act-led ++Info: Pi3 uses a GPIO expander to drive the LEDs which can only be accessed ++ from the VPU. There is a special driver for this with a separate DT ++ node, which has the unfortunate consequence of breaking the ++ act_led_gpio and act_led_activelow dtparams. ++ This overlay changes the GPIO controller back to the standard one and ++ restores the dtparams. ++Load: dtoverlay=pi3-act-led,= ++Params: activelow Set to "on" to invert the sense of the LED ++ (default "off") ++ ++ gpio Set which GPIO to use for the activity LED ++ (in case you want to connect it to an external ++ device) ++ REQUIRED ++ ++ + Name: pi3-disable-bt + Info: Disable Pi3 Bluetooth and restore UART0/ttyAMA0 over GPIOs 14 & 15 + N.B. To disable the systemd service that initialises the modem so it +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/pi3-act-led-overlay.dts +@@ -0,0 +1,27 @@ ++/dts-v1/; ++/plugin/; ++ ++/* Pi3 uses a GPIO expander to drive the LEDs which can only be accessed ++ from the VPU. There is a special driver for this with a separate DT node, ++ which has the unfortunate consequence of breaking the act_led_gpio and ++ act_led_activelow dtparams. ++ ++ This overlay changes the GPIO controller back to the standard one and ++ restores the dtparams. ++*/ ++ ++/{ ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&act_led>; ++ frag0: __overlay__ { ++ gpios = <&gpio 0 0>; ++ }; ++ }; ++ ++ __overrides__ { ++ gpio = <&frag0>,"gpios:4"; ++ activelow = <&frag0>,"gpios:8"; ++ }; ++}; diff --git a/target/linux/brcm2708/patches-4.4/0213-vchiq_arm-Service-callbacks-must-not-fail.patch b/target/linux/brcm2708/patches-4.4/0213-vchiq_arm-Service-callbacks-must-not-fail.patch new file mode 100644 index 0000000000..4979218fc0 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0213-vchiq_arm-Service-callbacks-must-not-fail.patch @@ -0,0 +1,26 @@ +From 2faaa2ccef9e4c595bd26f14285c225ceea6097e Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 23 Mar 2016 20:53:47 +0000 +Subject: [PATCH 213/232] vchiq_arm: Service callbacks must not fail + +Service callbacks are not allowed to return an error. The internal callback +that delivers events and messages to user tasks does not enqueue them if +the service is closing, but this is not an error and should not be +reported as such. + +Signed-off-by: Phil Elwell +--- + drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c +@@ -224,7 +224,7 @@ add_completion(VCHIQ_INSTANCE_T instance + } else if (instance->closing) { + vchiq_log_info(vchiq_arm_log_level, + "service_callback closing"); +- return VCHIQ_ERROR; ++ return VCHIQ_SUCCESS; + } + DEBUG_TRACE(SERVICE_CALLBACK_LINE); + } diff --git a/target/linux/brcm2708/patches-4.4/0214-Add-configs-and-overlay-for-PCA9548-I2C-mux.patch b/target/linux/brcm2708/patches-4.4/0214-Add-configs-and-overlay-for-PCA9548-I2C-mux.patch new file mode 100644 index 0000000000..cb41cceee4 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0214-Add-configs-and-overlay-for-PCA9548-I2C-mux.patch @@ -0,0 +1,133 @@ +From 09e64d48934eb812bab8667dcce81d0387092644 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <6by9@users.noreply.github.com> +Date: Thu, 17 Mar 2016 18:16:16 +0000 +Subject: [PATCH 214/232] Add configs and overlay for PCA9548 I2C mux + +Adds kernel configs for I2C muxes and a dt overlay for PCA9548 +that adds the 8 muxed I2C buses and mux device. +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 6 ++ + .../boot/dts/overlays/i2c-mux-pca9548a-overlay.dts | 67 ++++++++++++++++++++++ + arch/arm/configs/bcm2709_defconfig | 2 + + arch/arm/configs/bcmrpi_defconfig | 2 + + 5 files changed, 78 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/i2c-mux-pca9548a-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -29,6 +29,7 @@ dtbo-$(RPI_DT_OVERLAYS) += hy28a.dtbo + dtbo-$(RPI_DT_OVERLAYS) += hy28b.dtbo + dtbo-$(RPI_DT_OVERLAYS) += i2c-rtc.dtbo + dtbo-$(RPI_DT_OVERLAYS) += i2c-gpio.dtbo ++dtbo-$(RPI_DT_OVERLAYS) += i2c-mux-pca9548a.dtbo + dtbo-$(RPI_DT_OVERLAYS) += i2c0-bcm2708.dtbo + dtbo-$(RPI_DT_OVERLAYS) += i2c1-bcm2708.dtbo + dtbo-$(RPI_DT_OVERLAYS) += i2s-mmap.dtbo +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -360,6 +360,12 @@ Params: i2c_gpio_sda GPIO use + (default "2" = ~100kHz) + + ++Name: i2c-mux-pca9548a ++Info: Adds support for an NXP PCA9548A I2C multiplexer on i2c_arm ++Load: dtoverlay=i2c-mux-pca9548a,= ++Params: addr I2C address of PCA9548A (default 0x70) ++ ++ + Name: i2c-rtc + Info: Adds support for a number of I2C Real Time Clock devices + Load: dtoverlay=i2c-rtc,= +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/i2c-mux-pca9548a-overlay.dts +@@ -0,0 +1,67 @@ ++// Definitions for NXP PCA9548A I2C mux on ARM I2C bus. ++/dts-v1/; ++/plugin/; ++ ++/{ ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&i2c_arm>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ i2cmux: mux@70 { ++ compatible = "nxp,pca9548"; ++ reg = <0x70>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ i2c@0 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0>; ++ }; ++ i2c@1 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <1>; ++ }; ++ i2c@2 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <2>; ++ }; ++ i2c@3 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <3>; ++ }; ++ i2c@4 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <4>; ++ }; ++ i2c@5 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <5>; ++ }; ++ i2c@6 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <6>; ++ }; ++ i2c@7 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <7>; ++ }; ++ }; ++ }; ++ }; ++ __overrides__ { ++ addr = <&i2cmux>,"reg:0"; ++ }; ++}; +--- a/arch/arm/configs/bcm2709_defconfig ++++ b/arch/arm/configs/bcm2709_defconfig +@@ -600,6 +600,8 @@ CONFIG_I2C=y + CONFIG_I2C_CHARDEV=m + CONFIG_I2C_BCM2708=m + CONFIG_I2C_GPIO=m ++CONFIG_I2C_MUX=m ++CONFIG_I2C_MUX_PCA954x=m + CONFIG_SPI=y + CONFIG_SPI_BCM2835=m + CONFIG_SPI_BCM2835AUX=m +--- a/arch/arm/configs/bcmrpi_defconfig ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -593,6 +593,8 @@ CONFIG_I2C=y + CONFIG_I2C_CHARDEV=m + CONFIG_I2C_BCM2708=m + CONFIG_I2C_GPIO=m ++CONFIG_I2C_MUX=m ++CONFIG_I2C_MUX_PCA954x=m + CONFIG_SPI=y + CONFIG_SPI_BCM2835=m + CONFIG_SPI_BCM2835AUX=m diff --git a/target/linux/brcm2708/patches-4.4/0215-BCM270X_DT-Add-DS1339-to-i2c-rtc-overlay.patch b/target/linux/brcm2708/patches-4.4/0215-BCM270X_DT-Add-DS1339-to-i2c-rtc-overlay.patch new file mode 100644 index 0000000000..d27966fc9f --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0215-BCM270X_DT-Add-DS1339-to-i2c-rtc-overlay.patch @@ -0,0 +1,58 @@ +From 6ec2115ccc87288132b345ab2cd05aed2d3beb97 Mon Sep 17 00:00:00 2001 +From: Nicolas Boullis +Date: Wed, 23 Mar 2016 23:40:15 +0100 +Subject: [PATCH 215/232] BCM270X_DT: Add DS1339 to i2c-rtc overlay + +--- + arch/arm/boot/dts/overlays/README | 4 ++++ + arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts | 8 ++++++++ + 2 files changed, 12 insertions(+) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -371,6 +371,8 @@ Info: Adds support for a number of I2C + Load: dtoverlay=i2c-rtc,= + Params: ds1307 Select the DS1307 device + ++ ds1339 Select the DS1339 device ++ + ds3231 Select the DS3231 device + + mcp7941x Select the MCP7941x device +@@ -381,6 +383,8 @@ Params: ds1307 Select t + + pcf8563 Select the PCF8563 device + ++ trickle-resistor-ohms Resistor value for trickle charge (DS1339-only) ++ + + Name: i2c0-bcm2708 + Info: Enable the i2c_bcm2708 driver for the i2c0 bus +--- a/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts +@@ -17,6 +17,12 @@ + reg = <0x68>; + status = "disable"; + }; ++ ds1339: ds1339@68 { ++ compatible = "dallas,ds1339"; ++ trickle-resistor-ohms = <0>; ++ reg = <0x68>; ++ status = "disable"; ++ }; + mcp7941x: mcp7941x@6f { + compatible = "microchip,mcp7941x"; + reg = <0x6f>; +@@ -46,10 +52,12 @@ + }; + __overrides__ { + ds1307 = <&ds1307>,"status"; ++ ds1339 = <&ds1339>,"status"; + ds3231 = <&ds3231>,"status"; + mcp7941x = <&mcp7941x>,"status"; + pcf2127 = <&pcf2127>,"status"; + pcf8523 = <&pcf8523>,"status"; + pcf8563 = <&pcf8563>,"status"; ++ trickle-resistor-ohms = <&ds1339>,"trickle-resistor-ohms:0"; + }; + }; diff --git a/target/linux/brcm2708/patches-4.4/0216-copy_from_user-CPU_SW_DOMAIN_PAN-compatibility.patch b/target/linux/brcm2708/patches-4.4/0216-copy_from_user-CPU_SW_DOMAIN_PAN-compatibility.patch new file mode 100644 index 0000000000..5ead9b2338 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0216-copy_from_user-CPU_SW_DOMAIN_PAN-compatibility.patch @@ -0,0 +1,53 @@ +From a160443a0d967ea6394cd0d1b7a0ab8ef2da3ff1 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 29 Mar 2016 15:32:30 +0100 +Subject: [PATCH 216/232] copy_from_user: CPU_SW_DOMAIN_PAN compatibility + +The downstream copy_from_user acceleration must also play nice with +CONFIG_CPU_SW_DOMAIN_PAN. + +See: https://github.com/raspberrypi/linux/issues/1381 + +Signed-off-by: Phil Elwell +--- + arch/arm/lib/uaccess_with_memcpy.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +--- a/arch/arm/lib/uaccess_with_memcpy.c ++++ b/arch/arm/lib/uaccess_with_memcpy.c +@@ -186,6 +186,7 @@ out: + unsigned long noinline + __copy_from_user_memcpy(void *to, const void __user *from, unsigned long n) + { ++ unsigned long ua_flags; + int atomic; + + if (unlikely(segment_eq(get_fs(), KERNEL_DS))) { +@@ -217,7 +218,9 @@ __copy_from_user_memcpy(void *to, const + if (tocopy > n) + tocopy = n; + ++ ua_flags = uaccess_save_and_enable(); + memcpy(to, (const void *)from, tocopy); ++ uaccess_restore(ua_flags); + to += tocopy; + from += tocopy; + n -= tocopy; +@@ -261,9 +264,14 @@ arm_copy_from_user(void *to, const void + * 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); ++ if (n < COPY_TO_USER_THRESHOLD) { ++ unsigned long ua_flags = uaccess_save_and_enable(); ++ n = __copy_from_user_std(to, from, n); ++ uaccess_restore(ua_flags); ++ } else { ++ n = __copy_from_user_memcpy(to, from, n); ++ } ++ return n; + } + + static unsigned long noinline diff --git a/target/linux/brcm2708/patches-4.4/0217-bcm2835-sdhost-Adjust-to-core-clock-changes.patch b/target/linux/brcm2708/patches-4.4/0217-bcm2835-sdhost-Adjust-to-core-clock-changes.patch new file mode 100644 index 0000000000..f671944f15 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0217-bcm2835-sdhost-Adjust-to-core-clock-changes.patch @@ -0,0 +1,339 @@ +From 4b89d07fd299a0f4e25321920cb74416ba2e638e Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 30 Mar 2016 16:33:09 +0100 +Subject: [PATCH 217/232] bcm2835-sdhost: Adjust to core clock changes + +The SDHOST block uses the core clock, so previously it has been +necessary to prevent the core clock from changing in order to maintain +performance and prevent accidental SD bus overclocking. + +With this patch the sdhost driver is notified of clock changes, allowing +it to delay them while an SD access is outstanding and to delay new SD +accesses while the clock is changing. This feature is disabled in the +case where the core frequency can never change. + +Now that the driver copes with changes to the core clock, it is safe +to disable the io_is_busy feature of the on-demand governor. + +Signed-off-by: Phil Elwell +--- + drivers/mmc/host/Kconfig | 1 + + drivers/mmc/host/bcm2835-sdhost.c | 140 ++++++++++++++++++++++++++++++++------ + 2 files changed, 119 insertions(+), 22 deletions(-) + +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -36,6 +36,7 @@ config MMC_BCM2835_PIO_DMA_BARRIER + config MMC_BCM2835_SDHOST + tristate "Support for the SDHost controller on BCM2708/9" + depends on MACH_BCM2708 || MACH_BCM2709 || ARCH_BCM2835 ++ depends on RASPBERRYPI_FIRMWARE + help + This selects the SDHost controller on BCM2835/6. + +--- a/drivers/mmc/host/bcm2835-sdhost.c ++++ b/drivers/mmc/host/bcm2835-sdhost.c +@@ -50,6 +50,10 @@ + #include + #include + #include ++#include ++#include ++#include ++ + + #define DRIVER_NAME "sdhost-bcm2835" + +@@ -136,6 +140,8 @@ + + #define MHZ 1000000 + ++#define RPI_FIRMWARE_CLOCK_CORE 4 ++ + + struct bcm2835_host { + spinlock_t lock; +@@ -151,7 +157,9 @@ struct bcm2835_host { + + bool slow_card; /* Force 11-bit divisor */ + +- unsigned int max_clk; /* Max possible freq */ ++ unsigned int max_clk; /* Max src clock freq */ ++ unsigned int min_clk; /* Min src clock freq */ ++ unsigned int cur_clk; /* Current src clock freq */ + + struct tasklet_struct finish_tasklet; /* Tasklet structures */ + +@@ -183,6 +191,7 @@ struct bcm2835_host { + unsigned int use_sbc:1; /* Send CMD23 */ + + unsigned int debug:1; /* Enable debug output */ ++ unsigned int variable_clock:1; /* The core clock may change */ + + /*DMA part*/ + struct dma_chan *dma_chan_rxtx; /* DMA channel for reads and writes */ +@@ -208,6 +217,9 @@ struct bcm2835_host { + u32 pio_limit; /* Maximum block count for PIO (0 = always DMA) */ + + u32 sectors; /* Cached card size in sectors */ ++ ++ struct notifier_block cpufreq_nb; /* The cpufreq callback list item */ ++ struct semaphore cpufreq_semaphore; /* Interlock between SD activity and cpufreq changes */ + }; + + #if ENABLE_LOG +@@ -227,6 +239,10 @@ static u32 sdhost_log_idx; + static spinlock_t log_lock; + static void __iomem *timer_base; + ++static int bcm2835_sdhost_cpufreq_callback(struct notifier_block *nb, ++ unsigned long action, void *data); ++static unsigned int get_core_clock(unsigned int mode); ++ + #define LOG_ENTRIES (256*1) + #define LOG_SIZE (sizeof(LOG_ENTRY_T)*LOG_ENTRIES) + +@@ -448,20 +464,14 @@ static void bcm2835_sdhost_reset(struct + + static void bcm2835_sdhost_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); + +-static void bcm2835_sdhost_init(struct bcm2835_host *host, int soft) ++static void bcm2835_sdhost_init(struct bcm2835_host *host) + { +- pr_debug("bcm2835_sdhost_init(%d)\n", soft); ++ pr_debug("bcm2835_sdhost_init()\n"); + + /* Set interrupt enables */ + host->hcfg = SDHCFG_BUSY_IRPT_EN; + + bcm2835_sdhost_reset_internal(host); +- +- if (soft) { +- /* force clock reconfiguration */ +- host->clock = 0; +- bcm2835_sdhost_set_ios(host->mmc, &host->mmc->ios); +- } + } + + static void bcm2835_sdhost_wait_transfer_complete(struct bcm2835_host *host) +@@ -1499,10 +1509,10 @@ static irqreturn_t bcm2835_sdhost_irq(in + return result; + } + +-void bcm2835_sdhost_set_clock(struct bcm2835_host *host, unsigned int clock) ++void bcm2835_sdhost_set_clock(struct bcm2835_host *host) + { + int div = 0; /* Initialized for compiler warning */ +- unsigned int input_clock = clock; ++ unsigned int clock = host->clock; + + if (host->debug) + pr_info("%s: set_clock(%d)\n", mmc_hostname(host->mmc), clock); +@@ -1543,17 +1553,17 @@ void bcm2835_sdhost_set_clock(struct bcm + return; + } + +- div = host->max_clk / clock; ++ div = host->cur_clk / clock; + if (div < 2) + div = 2; +- if ((host->max_clk / div) > clock) ++ if ((host->cur_clk / div) > clock) + div++; + div -= 2; + + if (div > SDCDIV_MAX_CDIV) + div = SDCDIV_MAX_CDIV; + +- clock = host->max_clk / (div + 2); ++ clock = host->cur_clk / (div + 2); + host->mmc->actual_clock = clock; + + /* Calibrate some delays */ +@@ -1561,7 +1571,7 @@ void bcm2835_sdhost_set_clock(struct bcm + host->ns_per_fifo_word = (1000000000/clock) * + ((host->mmc->caps & MMC_CAP_4_BIT_DATA) ? 8 : 32); + +- if (clock > input_clock) { ++ if (clock > host->clock) { + /* Save the closest value, to make it easier + to reduce in the event of error */ + host->overclock_50 = (clock/MHZ); +@@ -1587,9 +1597,9 @@ void bcm2835_sdhost_set_clock(struct bcm + bcm2835_sdhost_write(host, host->mmc->actual_clock/2, SDTOUT); + + if (host->debug) +- pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x (actual clock %d)\n", +- mmc_hostname(host->mmc), input_clock, +- host->max_clk, host->cdiv, host->mmc->actual_clock); ++ pr_info("%s: clock=%d -> cur_clk=%d, cdiv=%x (actual clock %d)\n", ++ mmc_hostname(host->mmc), host->clock, ++ host->cur_clk, host->cdiv, host->mmc->actual_clock); + } + + static void bcm2835_sdhost_request(struct mmc_host *mmc, struct mmc_request *mrq) +@@ -1638,6 +1648,13 @@ static void bcm2835_sdhost_request(struc + (mrq->data->blocks > host->pio_limit)) + bcm2835_sdhost_prepare_dma(host, mrq->data); + ++ if (host->variable_clock && ++ (down_killable(&host->cpufreq_semaphore) != 0)) { ++ mrq->cmd->error = -EINTR; ++ mmc_request_done(mmc, mrq); ++ return; ++ } ++ + spin_lock_irqsave(&host->lock, flags); + + WARN_ON(host->mrq != NULL); +@@ -1687,6 +1704,52 @@ static void bcm2835_sdhost_request(struc + spin_unlock_irqrestore(&host->lock, flags); + } + ++static int bcm2835_sdhost_cpufreq_callback(struct notifier_block *nb, ++ unsigned long action, void *data) ++{ ++ struct cpufreq_freqs *freq = data; ++ struct bcm2835_host *host; ++ ++ host = container_of(nb, struct bcm2835_host, cpufreq_nb); ++ ++ if (freq->cpu == 0) { ++ switch (action) { ++ case CPUFREQ_PRECHANGE: ++ if (down_killable(&host->cpufreq_semaphore) != 0) ++ return NOTIFY_BAD; ++ break; ++ case CPUFREQ_POSTCHANGE: ++ if (freq->new > freq->old) ++ host->cur_clk = host->max_clk; ++ else ++ host->cur_clk = host->min_clk; ++ bcm2835_sdhost_set_clock(host); ++ up(&host->cpufreq_semaphore); ++ break; ++ default: ++ break; ++ } ++ } ++ return NOTIFY_OK; ++} ++ ++static unsigned int get_core_clock(unsigned int mode) ++{ ++ struct rpi_firmware *fw = rpi_firmware_get(NULL); ++ struct { ++ u32 id; ++ u32 val; ++ } packet; ++ int ret; ++ ++ packet.id = RPI_FIRMWARE_CLOCK_CORE; ++ ret = rpi_firmware_property(fw, mode, &packet, sizeof(packet)); ++ if (ret) ++ return 0; ++ ++ return packet.val; ++} ++ + static void bcm2835_sdhost_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) + { + +@@ -1700,13 +1763,16 @@ static void bcm2835_sdhost_set_ios(struc + ios->clock, ios->power_mode, ios->bus_width, + ios->timing, ios->signal_voltage, ios->drv_type); + ++ if (ios->clock && !host->cur_clk) ++ host->cur_clk = get_core_clock(RPI_FIRMWARE_GET_CLOCK_RATE); ++ + spin_lock_irqsave(&host->lock, flags); + + log_event("IOS<", ios->clock, 0); + + if (!ios->clock || ios->clock != host->clock) { +- bcm2835_sdhost_set_clock(host, ios->clock); + host->clock = ios->clock; ++ bcm2835_sdhost_set_clock(host); + } + + /* set bus width */ +@@ -1795,7 +1861,7 @@ static void bcm2835_sdhost_tasklet_finis + host->overclock_50--; + pr_warn("%s: reducing overclock due to errors\n", + mmc_hostname(host->mmc)); +- bcm2835_sdhost_set_clock(host,50*MHZ); ++ bcm2835_sdhost_set_clock(host); + mrq->cmd->error = -EILSEQ; + mrq->cmd->retries = 1; + } +@@ -1813,6 +1879,9 @@ static void bcm2835_sdhost_tasklet_finis + + spin_unlock_irqrestore(&host->lock, flags); + ++ if (host->variable_clock) ++ up(&host->cpufreq_semaphore); ++ + if (terminate_chan) + { + int err = dmaengine_terminate_all(terminate_chan); +@@ -1915,10 +1984,10 @@ int bcm2835_sdhost_add_host(struct bcm28 + setup_timer(&host->timer, bcm2835_sdhost_timeout, + (unsigned long)host); + +- bcm2835_sdhost_init(host, 0); ++ bcm2835_sdhost_init(host); + + ret = request_irq(host->irq, bcm2835_sdhost_irq, 0 /*IRQF_SHARED*/, +- mmc_hostname(mmc), host); ++ mmc_hostname(mmc), host); + if (ret) { + pr_err("%s: failed to request IRQ %d: %d\n", + mmc_hostname(mmc), host->irq, ret); +@@ -1953,6 +2022,7 @@ static int bcm2835_sdhost_probe(struct p + struct bcm2835_host *host; + struct mmc_host *mmc; + const __be32 *addr; ++ unsigned int max_clk; + int ret; + + pr_debug("bcm2835_sdhost_probe\n"); +@@ -2062,6 +2132,28 @@ static int bcm2835_sdhost_probe(struct p + if (ret) + goto err; + ++ /* Query the core clock frequencies */ ++ host->min_clk = get_core_clock(RPI_FIRMWARE_GET_MIN_CLOCK_RATE); ++ max_clk = get_core_clock(RPI_FIRMWARE_GET_MAX_CLOCK_RATE); ++ if (max_clk != host->max_clk) { ++ pr_warn("%s: Expected max clock %d, found %d\n", ++ mmc_hostname(mmc), host->max_clk, max_clk); ++ host->max_clk = max_clk; ++ } ++ ++ if (host->min_clk != host->max_clk) { ++ host->cpufreq_nb.notifier_call = ++ bcm2835_sdhost_cpufreq_callback; ++ sema_init(&host->cpufreq_semaphore, 1); ++ cpufreq_register_notifier(&host->cpufreq_nb, ++ CPUFREQ_TRANSITION_NOTIFIER); ++ host->variable_clock = 1; ++ host->cur_clk = 0; /* Get this later */ ++ } else { ++ host->variable_clock = 0; ++ host->cur_clk = host->max_clk; ++ } ++ + platform_set_drvdata(pdev, host); + + pr_debug("bcm2835_sdhost_probe -> OK\n"); +@@ -2081,6 +2173,10 @@ static int bcm2835_sdhost_remove(struct + + pr_debug("bcm2835_sdhost_remove\n"); + ++ if (host->variable_clock) ++ cpufreq_unregister_notifier(&host->cpufreq_nb, ++ CPUFREQ_TRANSITION_NOTIFIER); ++ + mmc_remove_host(host->mmc); + + bcm2835_sdhost_set_power(host, false); diff --git a/target/linux/brcm2708/patches-4.4/0218-BCM270X_DT-Document-hazards-of-sdhost-overlay.patch b/target/linux/brcm2708/patches-4.4/0218-BCM270X_DT-Document-hazards-of-sdhost-overlay.patch new file mode 100644 index 0000000000..1686969631 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0218-BCM270X_DT-Document-hazards-of-sdhost-overlay.patch @@ -0,0 +1,34 @@ +From 73f4b796019a7e9b7314851657aebd5f92f3ff75 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 30 Mar 2016 17:07:15 +0100 +Subject: [PATCH 218/232] BCM270X_DT: Document hazards of sdhost overlay + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/overlays/README | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -712,7 +712,11 @@ Params: + + + Name: sdhost +-Info: Selects the bcm2835-sdhost SD/MMC driver, optionally with overclock ++Info: Selects the bcm2835-sdhost SD/MMC driver, optionally with overclock. ++ N.B. This overlay is designed for situations where the mmc driver is ++ the default, so it disables the other (mmc) interface - this will kill ++ WiFi on a Pi3. If this isn't what you want, either use the sdtweak ++ overlay or the new sd_* dtparams of the base DTBs. + Load: dtoverlay=sdhost,= + Params: overclock_50 Clock (in MHz) to use when the MMC framework + requests 50MHz +@@ -771,6 +775,8 @@ Params: overclock_50 SD Clock + + Name: sdtweak + Info: Tunes the bcm2835-sdhost SD/MMC driver ++ N.B. This functionality is now available via the sd_* dtparams in the ++ base DTB. + Load: dtoverlay=sdtweak,= + Params: overclock_50 Clock (in MHz) to use when the MMC framework + requests 50MHz diff --git a/target/linux/brcm2708/patches-4.4/0219-cpufreq-Temporarily-ignore-io_is_busy-1.patch b/target/linux/brcm2708/patches-4.4/0219-cpufreq-Temporarily-ignore-io_is_busy-1.patch new file mode 100644 index 0000000000..c6938b4206 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0219-cpufreq-Temporarily-ignore-io_is_busy-1.patch @@ -0,0 +1,31 @@ +From 2af1218a8a0220fec526f64d03977b8451afb4c8 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 30 Mar 2016 17:23:15 +0100 +Subject: [PATCH 219/232] cpufreq: Temporarily ignore io_is_busy=1 + +To speed testing of the new sdhost driver that adapts to changes in +core_freq, hack the on-demand governor to treat io_is_busy=1 as +io_is_busy=0. The io_is_busy feature can still be forced using +io_is_busy=2. + +Signed-off-by: Phil Elwell +--- + drivers/cpufreq/cpufreq_ondemand.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/cpufreq/cpufreq_ondemand.c ++++ b/drivers/cpufreq/cpufreq_ondemand.c +@@ -307,7 +307,12 @@ static ssize_t store_io_is_busy(struct d + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; +- od_tuners->io_is_busy = !!input; ++ // XXX temporary hack ++ if (input > 1) ++ input = 1; ++ else ++ input = 0; ++ od_tuners->io_is_busy = input; + + /* we need to re-evaluate prev_cpu_idle */ + for_each_online_cpu(j) { diff --git a/target/linux/brcm2708/patches-4.4/0220-Revert-cpufreq-Temporarily-ignore-io_is_busy-1.patch b/target/linux/brcm2708/patches-4.4/0220-Revert-cpufreq-Temporarily-ignore-io_is_busy-1.patch new file mode 100644 index 0000000000..423b801ce8 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0220-Revert-cpufreq-Temporarily-ignore-io_is_busy-1.patch @@ -0,0 +1,26 @@ +From c353af0f83220068c10f6593b1767576b9b6cc18 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 30 Mar 2016 20:18:38 +0100 +Subject: [PATCH 220/232] Revert "cpufreq: Temporarily ignore io_is_busy=1" + +This reverts commit 2af1218a8a0220fec526f64d03977b8451afb4c8. +--- + drivers/cpufreq/cpufreq_ondemand.c | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +--- a/drivers/cpufreq/cpufreq_ondemand.c ++++ b/drivers/cpufreq/cpufreq_ondemand.c +@@ -307,12 +307,7 @@ static ssize_t store_io_is_busy(struct d + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; +- // XXX temporary hack +- if (input > 1) +- input = 1; +- else +- input = 0; +- od_tuners->io_is_busy = input; ++ od_tuners->io_is_busy = !!input; + + /* we need to re-evaluate prev_cpu_idle */ + for_each_online_cpu(j) { diff --git a/target/linux/brcm2708/patches-4.4/0221-net-sched-add-skb_at_tc_ingress-helper.patch b/target/linux/brcm2708/patches-4.4/0221-net-sched-add-skb_at_tc_ingress-helper.patch new file mode 100644 index 0000000000..df8f3dc0e1 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0221-net-sched-add-skb_at_tc_ingress-helper.patch @@ -0,0 +1,50 @@ +From a436f21ba8e57517734c24d2b571d5aed888be36 Mon Sep 17 00:00:00 2001 +From: Daniel Borkmann +Date: Thu, 7 Jan 2016 15:50:22 +0100 +Subject: [PATCH 221/232] net, sched: add skb_at_tc_ingress helper + +Add a skb_at_tc_ingress() as this will be needed elsewhere as well and +can hide the ugly ifdef. + +Signed-off-by: Daniel Borkmann +Acked-by: Alexei Starovoitov +Signed-off-by: David S. Miller +--- + include/net/sch_generic.h | 9 +++++++++ + net/sched/cls_bpf.c | 6 +----- + 2 files changed, 10 insertions(+), 5 deletions(-) + +--- a/include/net/sch_generic.h ++++ b/include/net/sch_generic.h +@@ -408,6 +408,15 @@ bool tcf_destroy(struct tcf_proto *tp, b + void tcf_destroy_chain(struct tcf_proto __rcu **fl); + int skb_do_redirect(struct sk_buff *); + ++static inline bool skb_at_tc_ingress(const struct sk_buff *skb) ++{ ++#ifdef CONFIG_NET_CLS_ACT ++ return G_TC_AT(skb->tc_verd) & AT_INGRESS; ++#else ++ return false; ++#endif ++} ++ + /* Reset all TX qdiscs greater then index of a device. */ + static inline void qdisc_reset_all_tx_gt(struct net_device *dev, unsigned int i) + { +--- a/net/sched/cls_bpf.c ++++ b/net/sched/cls_bpf.c +@@ -79,12 +79,8 @@ static int cls_bpf_classify(struct sk_bu + struct tcf_result *res) + { + struct cls_bpf_head *head = rcu_dereference_bh(tp->root); ++ bool at_ingress = skb_at_tc_ingress(skb); + struct cls_bpf_prog *prog; +-#ifdef CONFIG_NET_CLS_ACT +- bool at_ingress = G_TC_AT(skb->tc_verd) & AT_INGRESS; +-#else +- bool at_ingress = false; +-#endif + int ret = -1; + + if (unlikely(!skb_mac_header_was_set(skb))) diff --git a/target/linux/brcm2708/patches-4.4/0222-net-fix-bridge-multicast-packet-checksum-validation.patch b/target/linux/brcm2708/patches-4.4/0222-net-fix-bridge-multicast-packet-checksum-validation.patch new file mode 100644 index 0000000000..b3f105be02 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0222-net-fix-bridge-multicast-packet-checksum-validation.patch @@ -0,0 +1,75 @@ +From 966e306c7690f118bedf2f5de94e22e487bd39ec Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Linus=20L=C3=BCssing?= +Date: Wed, 24 Feb 2016 04:21:42 +0100 +Subject: [PATCH 222/232] net: fix bridge multicast packet checksum validation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +We need to update the skb->csum after pulling the skb, otherwise +an unnecessary checksum (re)computation can ocure for IGMP/MLD packets +in the bridge code. Additionally this fixes the following splats for +network devices / bridge ports with support for and enabled RX checksum +offloading: + +[...] +[ 43.986968] eth0: hw csum failure +[ 43.990344] CPU: 3 PID: 0 Comm: swapper/3 Not tainted 4.4.0 #2 +[ 43.996193] Hardware name: BCM2709 +[ 43.999647] [<800204e0>] (unwind_backtrace) from [<8001cf14>] (show_stack+0x10/0x14) +[ 44.007432] [<8001cf14>] (show_stack) from [<801ab614>] (dump_stack+0x80/0x90) +[ 44.014695] [<801ab614>] (dump_stack) from [<802e4548>] (__skb_checksum_complete+0x6c/0xac) +[ 44.023090] [<802e4548>] (__skb_checksum_complete) from [<803a055c>] (ipv6_mc_validate_checksum+0x104/0x178) +[ 44.032959] [<803a055c>] (ipv6_mc_validate_checksum) from [<802e111c>] (skb_checksum_trimmed+0x130/0x188) +[ 44.042565] [<802e111c>] (skb_checksum_trimmed) from [<803a06e8>] (ipv6_mc_check_mld+0x118/0x338) +[ 44.051501] [<803a06e8>] (ipv6_mc_check_mld) from [<803b2c98>] (br_multicast_rcv+0x5dc/0xd00) +[ 44.060077] [<803b2c98>] (br_multicast_rcv) from [<803aa510>] (br_handle_frame_finish+0xac/0x51c) +[...] + +Fixes: 9afd85c9e455 ("net: Export IGMP/MLD message validation code") +Reported-by: Álvaro Fernández Rojas +Signed-off-by: Linus Lüssing +Signed-off-by: David S. Miller +--- + net/core/skbuff.c | 22 ++++++++++++++++++++-- + 1 file changed, 20 insertions(+), 2 deletions(-) + +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -2965,6 +2965,24 @@ int skb_append_pagefrags(struct sk_buff + EXPORT_SYMBOL_GPL(skb_append_pagefrags); + + /** ++ * skb_push_rcsum - push skb and update receive checksum ++ * @skb: buffer to update ++ * @len: length of data pulled ++ * ++ * This function performs an skb_push on the packet and updates ++ * the CHECKSUM_COMPLETE checksum. It should be used on ++ * receive path processing instead of skb_push unless you know ++ * that the checksum difference is zero (e.g., a valid IP header) ++ * or you are setting ip_summed to CHECKSUM_NONE. ++ */ ++static unsigned char *skb_push_rcsum(struct sk_buff *skb, unsigned len) ++{ ++ skb_push(skb, len); ++ skb_postpush_rcsum(skb, skb->data, len); ++ return skb->data; ++} ++ ++/** + * skb_pull_rcsum - pull skb and update receive checksum + * @skb: buffer to update + * @len: length of data pulled +@@ -4101,9 +4119,9 @@ struct sk_buff *skb_checksum_trimmed(str + if (!pskb_may_pull(skb_chk, offset)) + goto err; + +- __skb_pull(skb_chk, offset); ++ skb_pull_rcsum(skb_chk, offset); + ret = skb_chkf(skb_chk); +- __skb_push(skb_chk, offset); ++ skb_push_rcsum(skb_chk, offset); + + if (ret) + goto err; diff --git a/target/linux/brcm2708/patches-4.4/0223-bpf-add-skb_postpush_rcsum-and-fix-dev_forward_skb-o.patch b/target/linux/brcm2708/patches-4.4/0223-bpf-add-skb_postpush_rcsum-and-fix-dev_forward_skb-o.patch new file mode 100644 index 0000000000..1793d5e109 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0223-bpf-add-skb_postpush_rcsum-and-fix-dev_forward_skb-o.patch @@ -0,0 +1,124 @@ +From 7ce01cd3013b7c9f1b257a6033b6d1f67fed5b5c Mon Sep 17 00:00:00 2001 +From: Daniel Borkmann +Date: Thu, 7 Jan 2016 15:50:23 +0100 +Subject: [PATCH 223/232] bpf: add skb_postpush_rcsum and fix dev_forward_skb + occasions + +Add a small helper skb_postpush_rcsum() and fix up redirect locations +that need CHECKSUM_COMPLETE fixups on ingress. dev_forward_skb() expects +a proper csum that covers also Ethernet header, f.e. since 2c26d34bbcc0 +("net/core: Handle csum for CHECKSUM_COMPLETE VXLAN forwarding"), we +also do skb_postpull_rcsum() after pulling Ethernet header off via +eth_type_trans(). + +When using eBPF in a netns setup f.e. with vxlan in collect metadata mode, +I can trigger the following csum issue with an IPv6 setup: + + [ 505.144065] dummy1: hw csum failure + [...] + [ 505.144108] Call Trace: + [ 505.144112] [] dump_stack+0x44/0x5c + [ 505.144134] [] netdev_rx_csum_fault+0x3a/0x40 + [ 505.144142] [] __skb_checksum_complete+0xcf/0xe0 + [ 505.144149] [] nf_ip6_checksum+0xb2/0x120 + [ 505.144161] [] icmpv6_error+0x17e/0x328 [nf_conntrack_ipv6] + [ 505.144170] [] ? ip6t_do_table+0x2fa/0x645 [ip6_tables] + [ 505.144177] [] ? ipv6_get_l4proto+0x65/0xd0 [nf_conntrack_ipv6] + [ 505.144189] [] nf_conntrack_in+0xc2/0x5a0 [nf_conntrack] + [ 505.144196] [] ipv6_conntrack_in+0x1c/0x20 [nf_conntrack_ipv6] + [ 505.144204] [] nf_iterate+0x5d/0x70 + [ 505.144210] [] nf_hook_slow+0x66/0xc0 + [ 505.144218] [] ipv6_rcv+0x3f2/0x4f0 + [ 505.144225] [] ? ip6_make_skb+0x1b0/0x1b0 + [ 505.144232] [] __netif_receive_skb_core+0x36b/0x9a0 + [ 505.144239] [] ? __netif_receive_skb+0x18/0x60 + [ 505.144245] [] __netif_receive_skb+0x18/0x60 + [ 505.144252] [] process_backlog+0x9f/0x140 + [ 505.144259] [] net_rx_action+0x145/0x320 + [...] + +What happens is that on ingress, we push Ethernet header back in, either +from cls_bpf or right before skb_do_redirect(), but without updating csum. +The "hw csum failure" can be fixed by using the new skb_postpush_rcsum() +helper for the dev_forward_skb() case to correct the csum diff again. + +Thanks to Hannes Frederic Sowa for the csum_partial() idea! + +Fixes: 3896d655f4d4 ("bpf: introduce bpf_clone_redirect() helper") +Fixes: 27b29f63058d ("bpf: add bpf_redirect() helper") +Signed-off-by: Daniel Borkmann +Acked-by: Alexei Starovoitov +Signed-off-by: David S. Miller +--- + include/linux/skbuff.h | 17 +++++++++++++++++ + net/core/filter.c | 17 +++++++++++++---- + 2 files changed, 30 insertions(+), 4 deletions(-) + +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -2737,6 +2737,23 @@ static inline void skb_postpull_rcsum(st + + unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len); + ++static inline void skb_postpush_rcsum(struct sk_buff *skb, ++ const void *start, unsigned int len) ++{ ++ /* For performing the reverse operation to skb_postpull_rcsum(), ++ * we can instead of ... ++ * ++ * skb->csum = csum_add(skb->csum, csum_partial(start, len, 0)); ++ * ++ * ... just use this equivalent version here to save a few ++ * instructions. Feeding csum of 0 in csum_partial() and later ++ * on adding skb->csum is equivalent to feed skb->csum in the ++ * first place. ++ */ ++ if (skb->ip_summed == CHECKSUM_COMPLETE) ++ skb->csum = csum_partial(start, len, skb->csum); ++} ++ + /** + * pskb_trim_rcsum - trim received skb and update checksum + * @skb: buffer to trim +--- a/net/core/filter.c ++++ b/net/core/filter.c +@@ -1288,8 +1288,9 @@ static u64 bpf_skb_store_bytes(u64 r1, u + /* skb_store_bits cannot return -EFAULT here */ + skb_store_bits(skb, offset, ptr, len); + +- if (BPF_RECOMPUTE_CSUM(flags) && skb->ip_summed == CHECKSUM_COMPLETE) +- skb->csum = csum_add(skb->csum, csum_partial(ptr, len, 0)); ++ if (BPF_RECOMPUTE_CSUM(flags)) ++ skb_postpush_rcsum(skb, ptr, len); ++ + return 0; + } + +@@ -1415,8 +1416,12 @@ static u64 bpf_clone_redirect(u64 r1, u6 + if (unlikely(!skb2)) + return -ENOMEM; + +- if (BPF_IS_REDIRECT_INGRESS(flags)) ++ if (BPF_IS_REDIRECT_INGRESS(flags)) { ++ if (skb_at_tc_ingress(skb2)) ++ skb_postpush_rcsum(skb2, skb_mac_header(skb2), ++ skb2->mac_len); + return dev_forward_skb(dev, skb2); ++ } + + skb2->dev = dev; + skb_sender_cpu_clear(skb2); +@@ -1459,8 +1464,12 @@ int skb_do_redirect(struct sk_buff *skb) + return -EINVAL; + } + +- if (BPF_IS_REDIRECT_INGRESS(ri->flags)) ++ if (BPF_IS_REDIRECT_INGRESS(ri->flags)) { ++ if (skb_at_tc_ingress(skb)) ++ skb_postpush_rcsum(skb, skb_mac_header(skb), ++ skb->mac_len); + return dev_forward_skb(dev, skb); ++ } + + skb->dev = dev; + skb_sender_cpu_clear(skb); diff --git a/target/linux/brcm2708/patches-4.4/0224-bcm2835-sdhost-Precalc-divisors-and-overclocks.patch b/target/linux/brcm2708/patches-4.4/0224-bcm2835-sdhost-Precalc-divisors-and-overclocks.patch new file mode 100644 index 0000000000..14c2e8c861 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0224-bcm2835-sdhost-Precalc-divisors-and-overclocks.patch @@ -0,0 +1,270 @@ +From 20260462773366a5734e5268dae0a4c179a21a2d Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 31 Mar 2016 15:44:53 +0100 +Subject: [PATCH 224/232] bcm2835-sdhost: Precalc divisors and overclocks + +Recalculating the clock divisors when the core clock changes is wasteful +and makes it harder to manage the overclock settings. Instead, +precalculate them and only report significant changes. + +Signed-off-by: Phil Elwell +--- + drivers/mmc/host/bcm2835-sdhost.c | 152 ++++++++++++++++++++++---------------- + 1 file changed, 88 insertions(+), 64 deletions(-) + +--- a/drivers/mmc/host/bcm2835-sdhost.c ++++ b/drivers/mmc/host/bcm2835-sdhost.c +@@ -154,12 +154,15 @@ struct bcm2835_host { + u32 pio_timeout; /* In jiffies */ + + int clock; /* Current clock speed */ ++ int clocks[2]; + + bool slow_card; /* Force 11-bit divisor */ + + unsigned int max_clk; /* Max src clock freq */ +- unsigned int min_clk; /* Min src clock freq */ +- unsigned int cur_clk; /* Current src clock freq */ ++ unsigned int src_clks[2]; /* Min/max src clock freqs */ ++ unsigned int cur_clk_idx; /* Index of current clock */ ++ unsigned int next_clk_idx; /* Next clock index */ ++ unsigned int cdivs[2]; + + struct tasklet_struct finish_tasklet; /* Tasklet structures */ + +@@ -213,7 +216,7 @@ struct bcm2835_host { + u32 delay_after_stop; /* minimum time between stop and subsequent data transfer */ + u32 delay_after_this_stop; /* minimum time between this stop and subsequent data transfer */ + u32 overclock_50; /* frequency to use when 50MHz is requested (in MHz) */ +- u32 overclock; /* Current frequency if overclocked, else zero */ ++ u32 prev_overclock_50; + u32 pio_limit; /* Maximum block count for PIO (0 = always DMA) */ + + u32 sectors; /* Cached card size in sectors */ +@@ -1509,10 +1512,35 @@ static irqreturn_t bcm2835_sdhost_irq(in + return result; + } + ++static void bcm2835_sdhost_select_clock(struct bcm2835_host *host, int idx) ++{ ++ unsigned int clock = host->clocks[idx]; ++ unsigned int cdiv = host->cdivs[idx]; ++ ++ host->mmc->actual_clock = clock; ++ host->ns_per_fifo_word = (1000000000/clock) * ++ ((host->mmc->caps & MMC_CAP_4_BIT_DATA) ? 8 : 32); ++ ++ host->cdiv = cdiv; ++ bcm2835_sdhost_write(host, host->cdiv, SDCDIV); ++ ++ /* Set the timeout to 500ms */ ++ bcm2835_sdhost_write(host, clock/2, SDTOUT); ++ ++ host->cur_clk_idx = host->next_clk_idx = idx; ++ ++ if (host->debug) ++ pr_info("%s: clock=%d -> src_clk=%d, cdiv=%x (actual %d)\n", ++ mmc_hostname(host->mmc), host->clock, ++ host->src_clks[idx], host->cdiv, ++ host->mmc->actual_clock); ++} ++ + void bcm2835_sdhost_set_clock(struct bcm2835_host *host) + { + int div = 0; /* Initialized for compiler warning */ + unsigned int clock = host->clock; ++ int clk_idx; + + if (host->debug) + pr_info("%s: set_clock(%d)\n", mmc_hostname(host->mmc), clock); +@@ -1553,53 +1581,45 @@ void bcm2835_sdhost_set_clock(struct bcm + return; + } + +- div = host->cur_clk / clock; +- if (div < 2) +- div = 2; +- if ((host->cur_clk / div) > clock) +- div++; +- div -= 2; +- +- if (div > SDCDIV_MAX_CDIV) +- div = SDCDIV_MAX_CDIV; +- +- clock = host->cur_clk / (div + 2); +- host->mmc->actual_clock = clock; +- +- /* Calibrate some delays */ +- +- host->ns_per_fifo_word = (1000000000/clock) * +- ((host->mmc->caps & MMC_CAP_4_BIT_DATA) ? 8 : 32); ++ /* Calculate the clock divisors */ ++ for (clk_idx = 0; clk_idx <= host->variable_clock; clk_idx++) ++ { ++ unsigned int cur_clk = host->src_clks[clk_idx]; ++ unsigned int actual_clock; + +- if (clock > host->clock) { +- /* Save the closest value, to make it easier +- to reduce in the event of error */ +- host->overclock_50 = (clock/MHZ); +- +- if (clock != host->overclock) { +- pr_warn("%s: overclocking to %dHz\n", +- mmc_hostname(host->mmc), clock); +- host->overclock = clock; ++ div = cur_clk / clock; ++ if (div < 2) ++ div = 2; ++ if ((cur_clk / div) > clock) ++ div++; ++ div -= 2; ++ ++ if (div > SDCDIV_MAX_CDIV) ++ div = SDCDIV_MAX_CDIV; ++ actual_clock = cur_clk / (div + 2); ++ ++ host->cdivs[clk_idx] = div; ++ host->clocks[clk_idx] = actual_clock; ++ ++ if (host->overclock_50 != host->prev_overclock_50) { ++ const char *clk_name = ""; ++ if (host->variable_clock) ++ clk_name = clk_idx ? " (turbo)" : " (normal)"; ++ if (actual_clock > host->clock) ++ pr_info("%s: overclocking to %dHz%s\n", ++ mmc_hostname(host->mmc), ++ actual_clock, clk_name); ++ else if ((host->overclock_50 < 50) && (clk_idx == 0)) ++ pr_info("%s: cancelling overclock%s\n", ++ mmc_hostname(host->mmc), ++ host->variable_clock ? "s" : ""); + } + } +- else if (host->overclock) +- { +- host->overclock = 0; +- if (clock == 50 * MHZ) +- pr_warn("%s: cancelling overclock\n", +- mmc_hostname(host->mmc)); +- } + +- host->cdiv = div; +- bcm2835_sdhost_write(host, host->cdiv, SDCDIV); ++ if (host->clock == 50*MHZ) ++ host->prev_overclock_50 = host->overclock_50; + +- /* Set the timeout to 500ms */ +- bcm2835_sdhost_write(host, host->mmc->actual_clock/2, SDTOUT); +- +- if (host->debug) +- pr_info("%s: clock=%d -> cur_clk=%d, cdiv=%x (actual clock %d)\n", +- mmc_hostname(host->mmc), host->clock, +- host->cur_clk, host->cdiv, host->mmc->actual_clock); ++ bcm2835_sdhost_select_clock(host, host->cur_clk_idx); + } + + static void bcm2835_sdhost_request(struct mmc_host *mmc, struct mmc_request *mrq) +@@ -1657,6 +1677,9 @@ static void bcm2835_sdhost_request(struc + + spin_lock_irqsave(&host->lock, flags); + ++ if (host->next_clk_idx != host->cur_clk_idx) ++ bcm2835_sdhost_select_clock(host, host->next_clk_idx); ++ + WARN_ON(host->mrq != NULL); + host->mrq = mrq; + +@@ -1719,11 +1742,7 @@ static int bcm2835_sdhost_cpufreq_callba + return NOTIFY_BAD; + break; + case CPUFREQ_POSTCHANGE: +- if (freq->new > freq->old) +- host->cur_clk = host->max_clk; +- else +- host->cur_clk = host->min_clk; +- bcm2835_sdhost_set_clock(host); ++ host->next_clk_idx = (freq->new > freq->old); + up(&host->cpufreq_semaphore); + break; + default: +@@ -1763,8 +1782,11 @@ static void bcm2835_sdhost_set_ios(struc + ios->clock, ios->power_mode, ios->bus_width, + ios->timing, ios->signal_voltage, ios->drv_type); + +- if (ios->clock && !host->cur_clk) +- host->cur_clk = get_core_clock(RPI_FIRMWARE_GET_CLOCK_RATE); ++ if (ios->clock && (host->cur_clk_idx == -1)) { ++ unsigned int cur_clk = ++ get_core_clock(RPI_FIRMWARE_GET_CLOCK_RATE); ++ host->cur_clk_idx = (cur_clk == host->src_clks[0]) ? 0 : 1; ++ } + + spin_lock_irqsave(&host->lock, flags); + +@@ -1854,11 +1876,12 @@ static void bcm2835_sdhost_tasklet_finis + + /* Drop the overclock after any data corruption, or after any + error overclocked */ +- if (host->overclock) { ++ if (host->clock > 50*MHZ) { + if ((mrq->cmd && mrq->cmd->error) || + (mrq->data && mrq->data->error) || + (mrq->stop && mrq->stop->error)) { +- host->overclock_50--; ++ host->overclock_50 = (host->clock/MHZ) - 1; ++ + pr_warn("%s: reducing overclock due to errors\n", + mmc_hostname(host->mmc)); + bcm2835_sdhost_set_clock(host); +@@ -2022,7 +2045,7 @@ static int bcm2835_sdhost_probe(struct p + struct bcm2835_host *host; + struct mmc_host *mmc; + const __be32 *addr; +- unsigned int max_clk; ++ unsigned int max_clk, min_clk; + int ret; + + pr_debug("bcm2835_sdhost_probe\n"); +@@ -2128,12 +2151,8 @@ static int bcm2835_sdhost_probe(struct p + else + mmc->caps |= MMC_CAP_4_BIT_DATA; + +- ret = bcm2835_sdhost_add_host(host); +- if (ret) +- goto err; +- + /* Query the core clock frequencies */ +- host->min_clk = get_core_clock(RPI_FIRMWARE_GET_MIN_CLOCK_RATE); ++ min_clk = get_core_clock(RPI_FIRMWARE_GET_MIN_CLOCK_RATE); + max_clk = get_core_clock(RPI_FIRMWARE_GET_MAX_CLOCK_RATE); + if (max_clk != host->max_clk) { + pr_warn("%s: Expected max clock %d, found %d\n", +@@ -2141,19 +2160,24 @@ static int bcm2835_sdhost_probe(struct p + host->max_clk = max_clk; + } + +- if (host->min_clk != host->max_clk) { ++ host->src_clks[0] = min_clk; ++ host->cur_clk_idx = -1; ++ if (max_clk != min_clk) { ++ host->src_clks[1] = max_clk; + host->cpufreq_nb.notifier_call = + bcm2835_sdhost_cpufreq_callback; + sema_init(&host->cpufreq_semaphore, 1); + cpufreq_register_notifier(&host->cpufreq_nb, + CPUFREQ_TRANSITION_NOTIFIER); + host->variable_clock = 1; +- host->cur_clk = 0; /* Get this later */ + } else { + host->variable_clock = 0; +- host->cur_clk = host->max_clk; + } + ++ ret = bcm2835_sdhost_add_host(host); ++ if (ret) ++ goto err; ++ + platform_set_drvdata(pdev, host); + + pr_debug("bcm2835_sdhost_probe -> OK\n"); diff --git a/target/linux/brcm2708/patches-4.4/0225-Revert-bcm2835-sdhost-Precalc-divisors-and-overclock.patch b/target/linux/brcm2708/patches-4.4/0225-Revert-bcm2835-sdhost-Precalc-divisors-and-overclock.patch new file mode 100644 index 0000000000..e83ac3765b --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0225-Revert-bcm2835-sdhost-Precalc-divisors-and-overclock.patch @@ -0,0 +1,267 @@ +From 9524a87ecce1788c8f77d2b86deee65d11b4190d Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 4 Apr 2016 12:35:32 +0100 +Subject: [PATCH 225/232] Revert "bcm2835-sdhost: Precalc divisors and + overclocks" + +This reverts commit 20260462773366a5734e5268dae0a4c179a21a2d. +--- + drivers/mmc/host/bcm2835-sdhost.c | 152 ++++++++++++++++---------------------- + 1 file changed, 64 insertions(+), 88 deletions(-) + +--- a/drivers/mmc/host/bcm2835-sdhost.c ++++ b/drivers/mmc/host/bcm2835-sdhost.c +@@ -154,15 +154,12 @@ struct bcm2835_host { + u32 pio_timeout; /* In jiffies */ + + int clock; /* Current clock speed */ +- int clocks[2]; + + bool slow_card; /* Force 11-bit divisor */ + + unsigned int max_clk; /* Max src clock freq */ +- unsigned int src_clks[2]; /* Min/max src clock freqs */ +- unsigned int cur_clk_idx; /* Index of current clock */ +- unsigned int next_clk_idx; /* Next clock index */ +- unsigned int cdivs[2]; ++ unsigned int min_clk; /* Min src clock freq */ ++ unsigned int cur_clk; /* Current src clock freq */ + + struct tasklet_struct finish_tasklet; /* Tasklet structures */ + +@@ -216,7 +213,7 @@ struct bcm2835_host { + u32 delay_after_stop; /* minimum time between stop and subsequent data transfer */ + u32 delay_after_this_stop; /* minimum time between this stop and subsequent data transfer */ + u32 overclock_50; /* frequency to use when 50MHz is requested (in MHz) */ +- u32 prev_overclock_50; ++ u32 overclock; /* Current frequency if overclocked, else zero */ + u32 pio_limit; /* Maximum block count for PIO (0 = always DMA) */ + + u32 sectors; /* Cached card size in sectors */ +@@ -1512,35 +1509,10 @@ static irqreturn_t bcm2835_sdhost_irq(in + return result; + } + +-static void bcm2835_sdhost_select_clock(struct bcm2835_host *host, int idx) +-{ +- unsigned int clock = host->clocks[idx]; +- unsigned int cdiv = host->cdivs[idx]; +- +- host->mmc->actual_clock = clock; +- host->ns_per_fifo_word = (1000000000/clock) * +- ((host->mmc->caps & MMC_CAP_4_BIT_DATA) ? 8 : 32); +- +- host->cdiv = cdiv; +- bcm2835_sdhost_write(host, host->cdiv, SDCDIV); +- +- /* Set the timeout to 500ms */ +- bcm2835_sdhost_write(host, clock/2, SDTOUT); +- +- host->cur_clk_idx = host->next_clk_idx = idx; +- +- if (host->debug) +- pr_info("%s: clock=%d -> src_clk=%d, cdiv=%x (actual %d)\n", +- mmc_hostname(host->mmc), host->clock, +- host->src_clks[idx], host->cdiv, +- host->mmc->actual_clock); +-} +- + void bcm2835_sdhost_set_clock(struct bcm2835_host *host) + { + int div = 0; /* Initialized for compiler warning */ + unsigned int clock = host->clock; +- int clk_idx; + + if (host->debug) + pr_info("%s: set_clock(%d)\n", mmc_hostname(host->mmc), clock); +@@ -1581,45 +1553,53 @@ void bcm2835_sdhost_set_clock(struct bcm + return; + } + +- /* Calculate the clock divisors */ +- for (clk_idx = 0; clk_idx <= host->variable_clock; clk_idx++) +- { +- unsigned int cur_clk = host->src_clks[clk_idx]; +- unsigned int actual_clock; ++ div = host->cur_clk / clock; ++ if (div < 2) ++ div = 2; ++ if ((host->cur_clk / div) > clock) ++ div++; ++ div -= 2; ++ ++ if (div > SDCDIV_MAX_CDIV) ++ div = SDCDIV_MAX_CDIV; ++ ++ clock = host->cur_clk / (div + 2); ++ host->mmc->actual_clock = clock; ++ ++ /* Calibrate some delays */ ++ ++ host->ns_per_fifo_word = (1000000000/clock) * ++ ((host->mmc->caps & MMC_CAP_4_BIT_DATA) ? 8 : 32); + +- div = cur_clk / clock; +- if (div < 2) +- div = 2; +- if ((cur_clk / div) > clock) +- div++; +- div -= 2; +- +- if (div > SDCDIV_MAX_CDIV) +- div = SDCDIV_MAX_CDIV; +- actual_clock = cur_clk / (div + 2); +- +- host->cdivs[clk_idx] = div; +- host->clocks[clk_idx] = actual_clock; +- +- if (host->overclock_50 != host->prev_overclock_50) { +- const char *clk_name = ""; +- if (host->variable_clock) +- clk_name = clk_idx ? " (turbo)" : " (normal)"; +- if (actual_clock > host->clock) +- pr_info("%s: overclocking to %dHz%s\n", +- mmc_hostname(host->mmc), +- actual_clock, clk_name); +- else if ((host->overclock_50 < 50) && (clk_idx == 0)) +- pr_info("%s: cancelling overclock%s\n", +- mmc_hostname(host->mmc), +- host->variable_clock ? "s" : ""); ++ if (clock > host->clock) { ++ /* Save the closest value, to make it easier ++ to reduce in the event of error */ ++ host->overclock_50 = (clock/MHZ); ++ ++ if (clock != host->overclock) { ++ pr_warn("%s: overclocking to %dHz\n", ++ mmc_hostname(host->mmc), clock); ++ host->overclock = clock; + } + } ++ else if (host->overclock) ++ { ++ host->overclock = 0; ++ if (clock == 50 * MHZ) ++ pr_warn("%s: cancelling overclock\n", ++ mmc_hostname(host->mmc)); ++ } + +- if (host->clock == 50*MHZ) +- host->prev_overclock_50 = host->overclock_50; ++ host->cdiv = div; ++ bcm2835_sdhost_write(host, host->cdiv, SDCDIV); + +- bcm2835_sdhost_select_clock(host, host->cur_clk_idx); ++ /* Set the timeout to 500ms */ ++ bcm2835_sdhost_write(host, host->mmc->actual_clock/2, SDTOUT); ++ ++ if (host->debug) ++ pr_info("%s: clock=%d -> cur_clk=%d, cdiv=%x (actual clock %d)\n", ++ mmc_hostname(host->mmc), host->clock, ++ host->cur_clk, host->cdiv, host->mmc->actual_clock); + } + + static void bcm2835_sdhost_request(struct mmc_host *mmc, struct mmc_request *mrq) +@@ -1677,9 +1657,6 @@ static void bcm2835_sdhost_request(struc + + spin_lock_irqsave(&host->lock, flags); + +- if (host->next_clk_idx != host->cur_clk_idx) +- bcm2835_sdhost_select_clock(host, host->next_clk_idx); +- + WARN_ON(host->mrq != NULL); + host->mrq = mrq; + +@@ -1742,7 +1719,11 @@ static int bcm2835_sdhost_cpufreq_callba + return NOTIFY_BAD; + break; + case CPUFREQ_POSTCHANGE: +- host->next_clk_idx = (freq->new > freq->old); ++ if (freq->new > freq->old) ++ host->cur_clk = host->max_clk; ++ else ++ host->cur_clk = host->min_clk; ++ bcm2835_sdhost_set_clock(host); + up(&host->cpufreq_semaphore); + break; + default: +@@ -1782,11 +1763,8 @@ static void bcm2835_sdhost_set_ios(struc + ios->clock, ios->power_mode, ios->bus_width, + ios->timing, ios->signal_voltage, ios->drv_type); + +- if (ios->clock && (host->cur_clk_idx == -1)) { +- unsigned int cur_clk = +- get_core_clock(RPI_FIRMWARE_GET_CLOCK_RATE); +- host->cur_clk_idx = (cur_clk == host->src_clks[0]) ? 0 : 1; +- } ++ if (ios->clock && !host->cur_clk) ++ host->cur_clk = get_core_clock(RPI_FIRMWARE_GET_CLOCK_RATE); + + spin_lock_irqsave(&host->lock, flags); + +@@ -1876,12 +1854,11 @@ static void bcm2835_sdhost_tasklet_finis + + /* Drop the overclock after any data corruption, or after any + error overclocked */ +- if (host->clock > 50*MHZ) { ++ if (host->overclock) { + if ((mrq->cmd && mrq->cmd->error) || + (mrq->data && mrq->data->error) || + (mrq->stop && mrq->stop->error)) { +- host->overclock_50 = (host->clock/MHZ) - 1; +- ++ host->overclock_50--; + pr_warn("%s: reducing overclock due to errors\n", + mmc_hostname(host->mmc)); + bcm2835_sdhost_set_clock(host); +@@ -2045,7 +2022,7 @@ static int bcm2835_sdhost_probe(struct p + struct bcm2835_host *host; + struct mmc_host *mmc; + const __be32 *addr; +- unsigned int max_clk, min_clk; ++ unsigned int max_clk; + int ret; + + pr_debug("bcm2835_sdhost_probe\n"); +@@ -2151,8 +2128,12 @@ static int bcm2835_sdhost_probe(struct p + else + mmc->caps |= MMC_CAP_4_BIT_DATA; + ++ ret = bcm2835_sdhost_add_host(host); ++ if (ret) ++ goto err; ++ + /* Query the core clock frequencies */ +- min_clk = get_core_clock(RPI_FIRMWARE_GET_MIN_CLOCK_RATE); ++ host->min_clk = get_core_clock(RPI_FIRMWARE_GET_MIN_CLOCK_RATE); + max_clk = get_core_clock(RPI_FIRMWARE_GET_MAX_CLOCK_RATE); + if (max_clk != host->max_clk) { + pr_warn("%s: Expected max clock %d, found %d\n", +@@ -2160,24 +2141,19 @@ static int bcm2835_sdhost_probe(struct p + host->max_clk = max_clk; + } + +- host->src_clks[0] = min_clk; +- host->cur_clk_idx = -1; +- if (max_clk != min_clk) { +- host->src_clks[1] = max_clk; ++ if (host->min_clk != host->max_clk) { + host->cpufreq_nb.notifier_call = + bcm2835_sdhost_cpufreq_callback; + sema_init(&host->cpufreq_semaphore, 1); + cpufreq_register_notifier(&host->cpufreq_nb, + CPUFREQ_TRANSITION_NOTIFIER); + host->variable_clock = 1; ++ host->cur_clk = 0; /* Get this later */ + } else { + host->variable_clock = 0; ++ host->cur_clk = host->max_clk; + } + +- ret = bcm2835_sdhost_add_host(host); +- if (ret) +- goto err; +- + platform_set_drvdata(pdev, host); + + pr_debug("bcm2835_sdhost_probe -> OK\n"); diff --git a/target/linux/brcm2708/patches-4.4/0226-Revert-bcm2835-sdhost-Adjust-to-core-clock-changes.patch b/target/linux/brcm2708/patches-4.4/0226-Revert-bcm2835-sdhost-Adjust-to-core-clock-changes.patch new file mode 100644 index 0000000000..55f777f883 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0226-Revert-bcm2835-sdhost-Adjust-to-core-clock-changes.patch @@ -0,0 +1,327 @@ +From 28035a98dc9820c065dc64ad3016a1f71a9911f2 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 4 Apr 2016 12:35:51 +0100 +Subject: [PATCH 226/232] Revert "bcm2835-sdhost: Adjust to core clock changes" + +This reverts commit 4b89d07fd299a0f4e25321920cb74416ba2e638e. +--- + drivers/mmc/host/Kconfig | 1 - + drivers/mmc/host/bcm2835-sdhost.c | 140 ++++++-------------------------------- + 2 files changed, 22 insertions(+), 119 deletions(-) + +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -36,7 +36,6 @@ config MMC_BCM2835_PIO_DMA_BARRIER + config MMC_BCM2835_SDHOST + tristate "Support for the SDHost controller on BCM2708/9" + depends on MACH_BCM2708 || MACH_BCM2709 || ARCH_BCM2835 +- depends on RASPBERRYPI_FIRMWARE + help + This selects the SDHost controller on BCM2835/6. + +--- a/drivers/mmc/host/bcm2835-sdhost.c ++++ b/drivers/mmc/host/bcm2835-sdhost.c +@@ -50,10 +50,6 @@ + #include + #include + #include +-#include +-#include +-#include +- + + #define DRIVER_NAME "sdhost-bcm2835" + +@@ -140,8 +136,6 @@ + + #define MHZ 1000000 + +-#define RPI_FIRMWARE_CLOCK_CORE 4 +- + + struct bcm2835_host { + spinlock_t lock; +@@ -157,9 +151,7 @@ struct bcm2835_host { + + bool slow_card; /* Force 11-bit divisor */ + +- unsigned int max_clk; /* Max src clock freq */ +- unsigned int min_clk; /* Min src clock freq */ +- unsigned int cur_clk; /* Current src clock freq */ ++ unsigned int max_clk; /* Max possible freq */ + + struct tasklet_struct finish_tasklet; /* Tasklet structures */ + +@@ -191,7 +183,6 @@ struct bcm2835_host { + unsigned int use_sbc:1; /* Send CMD23 */ + + unsigned int debug:1; /* Enable debug output */ +- unsigned int variable_clock:1; /* The core clock may change */ + + /*DMA part*/ + struct dma_chan *dma_chan_rxtx; /* DMA channel for reads and writes */ +@@ -217,9 +208,6 @@ struct bcm2835_host { + u32 pio_limit; /* Maximum block count for PIO (0 = always DMA) */ + + u32 sectors; /* Cached card size in sectors */ +- +- struct notifier_block cpufreq_nb; /* The cpufreq callback list item */ +- struct semaphore cpufreq_semaphore; /* Interlock between SD activity and cpufreq changes */ + }; + + #if ENABLE_LOG +@@ -239,10 +227,6 @@ static u32 sdhost_log_idx; + static spinlock_t log_lock; + static void __iomem *timer_base; + +-static int bcm2835_sdhost_cpufreq_callback(struct notifier_block *nb, +- unsigned long action, void *data); +-static unsigned int get_core_clock(unsigned int mode); +- + #define LOG_ENTRIES (256*1) + #define LOG_SIZE (sizeof(LOG_ENTRY_T)*LOG_ENTRIES) + +@@ -464,14 +448,20 @@ static void bcm2835_sdhost_reset(struct + + static void bcm2835_sdhost_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); + +-static void bcm2835_sdhost_init(struct bcm2835_host *host) ++static void bcm2835_sdhost_init(struct bcm2835_host *host, int soft) + { +- pr_debug("bcm2835_sdhost_init()\n"); ++ pr_debug("bcm2835_sdhost_init(%d)\n", soft); + + /* Set interrupt enables */ + host->hcfg = SDHCFG_BUSY_IRPT_EN; + + bcm2835_sdhost_reset_internal(host); ++ ++ if (soft) { ++ /* force clock reconfiguration */ ++ host->clock = 0; ++ bcm2835_sdhost_set_ios(host->mmc, &host->mmc->ios); ++ } + } + + static void bcm2835_sdhost_wait_transfer_complete(struct bcm2835_host *host) +@@ -1509,10 +1499,10 @@ static irqreturn_t bcm2835_sdhost_irq(in + return result; + } + +-void bcm2835_sdhost_set_clock(struct bcm2835_host *host) ++void bcm2835_sdhost_set_clock(struct bcm2835_host *host, unsigned int clock) + { + int div = 0; /* Initialized for compiler warning */ +- unsigned int clock = host->clock; ++ unsigned int input_clock = clock; + + if (host->debug) + pr_info("%s: set_clock(%d)\n", mmc_hostname(host->mmc), clock); +@@ -1553,17 +1543,17 @@ void bcm2835_sdhost_set_clock(struct bcm + return; + } + +- div = host->cur_clk / clock; ++ div = host->max_clk / clock; + if (div < 2) + div = 2; +- if ((host->cur_clk / div) > clock) ++ if ((host->max_clk / div) > clock) + div++; + div -= 2; + + if (div > SDCDIV_MAX_CDIV) + div = SDCDIV_MAX_CDIV; + +- clock = host->cur_clk / (div + 2); ++ clock = host->max_clk / (div + 2); + host->mmc->actual_clock = clock; + + /* Calibrate some delays */ +@@ -1571,7 +1561,7 @@ void bcm2835_sdhost_set_clock(struct bcm + host->ns_per_fifo_word = (1000000000/clock) * + ((host->mmc->caps & MMC_CAP_4_BIT_DATA) ? 8 : 32); + +- if (clock > host->clock) { ++ if (clock > input_clock) { + /* Save the closest value, to make it easier + to reduce in the event of error */ + host->overclock_50 = (clock/MHZ); +@@ -1597,9 +1587,9 @@ void bcm2835_sdhost_set_clock(struct bcm + bcm2835_sdhost_write(host, host->mmc->actual_clock/2, SDTOUT); + + if (host->debug) +- pr_info("%s: clock=%d -> cur_clk=%d, cdiv=%x (actual clock %d)\n", +- mmc_hostname(host->mmc), host->clock, +- host->cur_clk, host->cdiv, host->mmc->actual_clock); ++ pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x (actual clock %d)\n", ++ mmc_hostname(host->mmc), input_clock, ++ host->max_clk, host->cdiv, host->mmc->actual_clock); + } + + static void bcm2835_sdhost_request(struct mmc_host *mmc, struct mmc_request *mrq) +@@ -1648,13 +1638,6 @@ static void bcm2835_sdhost_request(struc + (mrq->data->blocks > host->pio_limit)) + bcm2835_sdhost_prepare_dma(host, mrq->data); + +- if (host->variable_clock && +- (down_killable(&host->cpufreq_semaphore) != 0)) { +- mrq->cmd->error = -EINTR; +- mmc_request_done(mmc, mrq); +- return; +- } +- + spin_lock_irqsave(&host->lock, flags); + + WARN_ON(host->mrq != NULL); +@@ -1704,52 +1687,6 @@ static void bcm2835_sdhost_request(struc + spin_unlock_irqrestore(&host->lock, flags); + } + +-static int bcm2835_sdhost_cpufreq_callback(struct notifier_block *nb, +- unsigned long action, void *data) +-{ +- struct cpufreq_freqs *freq = data; +- struct bcm2835_host *host; +- +- host = container_of(nb, struct bcm2835_host, cpufreq_nb); +- +- if (freq->cpu == 0) { +- switch (action) { +- case CPUFREQ_PRECHANGE: +- if (down_killable(&host->cpufreq_semaphore) != 0) +- return NOTIFY_BAD; +- break; +- case CPUFREQ_POSTCHANGE: +- if (freq->new > freq->old) +- host->cur_clk = host->max_clk; +- else +- host->cur_clk = host->min_clk; +- bcm2835_sdhost_set_clock(host); +- up(&host->cpufreq_semaphore); +- break; +- default: +- break; +- } +- } +- return NOTIFY_OK; +-} +- +-static unsigned int get_core_clock(unsigned int mode) +-{ +- struct rpi_firmware *fw = rpi_firmware_get(NULL); +- struct { +- u32 id; +- u32 val; +- } packet; +- int ret; +- +- packet.id = RPI_FIRMWARE_CLOCK_CORE; +- ret = rpi_firmware_property(fw, mode, &packet, sizeof(packet)); +- if (ret) +- return 0; +- +- return packet.val; +-} +- + static void bcm2835_sdhost_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) + { + +@@ -1763,16 +1700,13 @@ static void bcm2835_sdhost_set_ios(struc + ios->clock, ios->power_mode, ios->bus_width, + ios->timing, ios->signal_voltage, ios->drv_type); + +- if (ios->clock && !host->cur_clk) +- host->cur_clk = get_core_clock(RPI_FIRMWARE_GET_CLOCK_RATE); +- + spin_lock_irqsave(&host->lock, flags); + + log_event("IOS<", ios->clock, 0); + + if (!ios->clock || ios->clock != host->clock) { ++ bcm2835_sdhost_set_clock(host, ios->clock); + host->clock = ios->clock; +- bcm2835_sdhost_set_clock(host); + } + + /* set bus width */ +@@ -1861,7 +1795,7 @@ static void bcm2835_sdhost_tasklet_finis + host->overclock_50--; + pr_warn("%s: reducing overclock due to errors\n", + mmc_hostname(host->mmc)); +- bcm2835_sdhost_set_clock(host); ++ bcm2835_sdhost_set_clock(host,50*MHZ); + mrq->cmd->error = -EILSEQ; + mrq->cmd->retries = 1; + } +@@ -1879,9 +1813,6 @@ static void bcm2835_sdhost_tasklet_finis + + spin_unlock_irqrestore(&host->lock, flags); + +- if (host->variable_clock) +- up(&host->cpufreq_semaphore); +- + if (terminate_chan) + { + int err = dmaengine_terminate_all(terminate_chan); +@@ -1984,10 +1915,10 @@ int bcm2835_sdhost_add_host(struct bcm28 + setup_timer(&host->timer, bcm2835_sdhost_timeout, + (unsigned long)host); + +- bcm2835_sdhost_init(host); ++ bcm2835_sdhost_init(host, 0); + + ret = request_irq(host->irq, bcm2835_sdhost_irq, 0 /*IRQF_SHARED*/, +- mmc_hostname(mmc), host); ++ mmc_hostname(mmc), host); + if (ret) { + pr_err("%s: failed to request IRQ %d: %d\n", + mmc_hostname(mmc), host->irq, ret); +@@ -2022,7 +1953,6 @@ static int bcm2835_sdhost_probe(struct p + struct bcm2835_host *host; + struct mmc_host *mmc; + const __be32 *addr; +- unsigned int max_clk; + int ret; + + pr_debug("bcm2835_sdhost_probe\n"); +@@ -2132,28 +2062,6 @@ static int bcm2835_sdhost_probe(struct p + if (ret) + goto err; + +- /* Query the core clock frequencies */ +- host->min_clk = get_core_clock(RPI_FIRMWARE_GET_MIN_CLOCK_RATE); +- max_clk = get_core_clock(RPI_FIRMWARE_GET_MAX_CLOCK_RATE); +- if (max_clk != host->max_clk) { +- pr_warn("%s: Expected max clock %d, found %d\n", +- mmc_hostname(mmc), host->max_clk, max_clk); +- host->max_clk = max_clk; +- } +- +- if (host->min_clk != host->max_clk) { +- host->cpufreq_nb.notifier_call = +- bcm2835_sdhost_cpufreq_callback; +- sema_init(&host->cpufreq_semaphore, 1); +- cpufreq_register_notifier(&host->cpufreq_nb, +- CPUFREQ_TRANSITION_NOTIFIER); +- host->variable_clock = 1; +- host->cur_clk = 0; /* Get this later */ +- } else { +- host->variable_clock = 0; +- host->cur_clk = host->max_clk; +- } +- + platform_set_drvdata(pdev, host); + + pr_debug("bcm2835_sdhost_probe -> OK\n"); +@@ -2173,10 +2081,6 @@ static int bcm2835_sdhost_remove(struct + + pr_debug("bcm2835_sdhost_remove\n"); + +- if (host->variable_clock) +- cpufreq_unregister_notifier(&host->cpufreq_nb, +- CPUFREQ_TRANSITION_NOTIFIER); +- + mmc_remove_host(host->mmc); + + bcm2835_sdhost_set_power(host, false); diff --git a/target/linux/brcm2708/patches-4.4/0227-bcm2835-sdhost-Firmware-manages-the-clock-divisor.patch b/target/linux/brcm2708/patches-4.4/0227-bcm2835-sdhost-Firmware-manages-the-clock-divisor.patch new file mode 100644 index 0000000000..828692dd9d --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0227-bcm2835-sdhost-Firmware-manages-the-clock-divisor.patch @@ -0,0 +1,226 @@ +From 08532d242d7702ae0add95096aa49c5e96e066e2 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 4 Apr 2016 16:03:18 +0100 +Subject: [PATCH 227/232] bcm2835-sdhost: Firmware manages the clock divisor + +The bcm2835-sdhost driver hands control of the CDIV clock divisor +register to matching firmware, allowing it to adjust to a changing +core clock. This removes the need to use the performance governor or +to enable io_is_busy on the on-demand governor in order to get the +best SD performance. + +N.B. As SD clocks must be an integer divisor of the core clock, it is +possible that the SD clock for "turbo" mode can be different (even +lower) than "normal" mode. + +Signed-off-by: Phil Elwell +--- + drivers/mmc/host/bcm2835-sdhost.c | 120 ++++++++++++++++++----------- + include/soc/bcm2835/raspberrypi-firmware.h | 1 + + 2 files changed, 74 insertions(+), 47 deletions(-) + +--- a/drivers/mmc/host/bcm2835-sdhost.c ++++ b/drivers/mmc/host/bcm2835-sdhost.c +@@ -50,6 +50,7 @@ + #include + #include + #include ++#include + + #define DRIVER_NAME "sdhost-bcm2835" + +@@ -183,6 +184,7 @@ struct bcm2835_host { + unsigned int use_sbc:1; /* Send CMD23 */ + + unsigned int debug:1; /* Enable debug output */ ++ unsigned int firmware_sets_cdiv:1; /* Let the firmware manage the clock */ + + /*DMA part*/ + struct dma_chan *dma_chan_rxtx; /* DMA channel for reads and writes */ +@@ -430,7 +432,7 @@ static void bcm2835_sdhost_reset_interna + host->clock = 0; + host->sectors = 0; + bcm2835_sdhost_write(host, host->hcfg, SDHCFG); +- bcm2835_sdhost_write(host, host->cdiv, SDCDIV); ++ bcm2835_sdhost_write(host, SDCDIV_MAX_CDIV, SDCDIV); + mmiowb(); + } + +@@ -1534,62 +1536,75 @@ void bcm2835_sdhost_set_clock(struct bcm + + host->mmc->actual_clock = 0; + +- if (clock < 100000) { +- /* Can't stop the clock, but make it as slow as possible +- * to show willing +- */ +- host->cdiv = SDCDIV_MAX_CDIV; +- bcm2835_sdhost_write(host, host->cdiv, SDCDIV); +- return; +- } +- +- div = host->max_clk / clock; +- if (div < 2) +- div = 2; +- if ((host->max_clk / div) > clock) +- div++; +- div -= 2; ++ if (host->firmware_sets_cdiv) { ++ u32 msg[3] = { clock, 0, 0 }; + +- if (div > SDCDIV_MAX_CDIV) +- div = SDCDIV_MAX_CDIV; ++ rpi_firmware_property(rpi_firmware_get(NULL), ++ RPI_FIRMWARE_SET_SDHOST_CLOCK, ++ &msg, sizeof(msg)); + +- clock = host->max_clk / (div + 2); +- host->mmc->actual_clock = clock; ++ clock = max(msg[1], msg[2]); ++ } else { ++ if (clock < 100000) { ++ /* Can't stop the clock, but make it as slow as ++ * possible to show willing ++ */ ++ host->cdiv = SDCDIV_MAX_CDIV; ++ bcm2835_sdhost_write(host, host->cdiv, SDCDIV); ++ return; ++ } ++ ++ div = host->max_clk / clock; ++ if (div < 2) ++ div = 2; ++ if ((host->max_clk / div) > clock) ++ div++; ++ div -= 2; ++ ++ if (div > SDCDIV_MAX_CDIV) ++ div = SDCDIV_MAX_CDIV; ++ ++ clock = host->max_clk / (div + 2); ++ ++ host->cdiv = div; ++ bcm2835_sdhost_write(host, host->cdiv, SDCDIV); ++ ++ if (host->debug) ++ pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x " ++ "(actual clock %d)\n", ++ mmc_hostname(host->mmc), input_clock, ++ host->max_clk, host->cdiv, ++ clock); ++ } + + /* Calibrate some delays */ + + host->ns_per_fifo_word = (1000000000/clock) * + ((host->mmc->caps & MMC_CAP_4_BIT_DATA) ? 8 : 32); + +- if (clock > input_clock) { +- /* Save the closest value, to make it easier +- to reduce in the event of error */ +- host->overclock_50 = (clock/MHZ); +- +- if (clock != host->overclock) { +- pr_warn("%s: overclocking to %dHz\n", +- mmc_hostname(host->mmc), clock); +- host->overclock = clock; ++ if (input_clock == 50 * MHZ) { ++ if (clock > input_clock) { ++ /* Save the closest value, to make it easier ++ to reduce in the event of error */ ++ host->overclock_50 = (clock/MHZ); ++ ++ if (clock != host->overclock) { ++ pr_warn("%s: overclocking to %dHz\n", ++ mmc_hostname(host->mmc), clock); ++ host->overclock = clock; ++ } ++ } else if (host->overclock) { ++ host->overclock = 0; ++ if (clock == 50 * MHZ) ++ pr_warn("%s: cancelling overclock\n", ++ mmc_hostname(host->mmc)); + } + } +- else if (host->overclock) +- { +- host->overclock = 0; +- if (clock == 50 * MHZ) +- pr_warn("%s: cancelling overclock\n", +- mmc_hostname(host->mmc)); +- } +- +- host->cdiv = div; +- bcm2835_sdhost_write(host, host->cdiv, SDCDIV); + + /* Set the timeout to 500ms */ +- bcm2835_sdhost_write(host, host->mmc->actual_clock/2, SDTOUT); ++ bcm2835_sdhost_write(host, clock/2, SDTOUT); + +- if (host->debug) +- pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x (actual clock %d)\n", +- mmc_hostname(host->mmc), input_clock, +- host->max_clk, host->cdiv, host->mmc->actual_clock); ++ host->mmc->actual_clock = clock; + } + + static void bcm2835_sdhost_request(struct mmc_host *mmc, struct mmc_request *mrq) +@@ -1704,11 +1719,6 @@ static void bcm2835_sdhost_set_ios(struc + + log_event("IOS<", ios->clock, 0); + +- if (!ios->clock || ios->clock != host->clock) { +- bcm2835_sdhost_set_clock(host, ios->clock); +- host->clock = ios->clock; +- } +- + /* set bus width */ + host->hcfg &= ~SDHCFG_WIDE_EXT_BUS; + if (ios->bus_width == MMC_BUS_WIDTH_4) +@@ -1721,6 +1731,11 @@ static void bcm2835_sdhost_set_ios(struc + + bcm2835_sdhost_write(host, host->hcfg, SDHCFG); + ++ if (!ios->clock || ios->clock != host->clock) { ++ bcm2835_sdhost_set_clock(host, ios->clock); ++ host->clock = ios->clock; ++ } ++ + mmiowb(); + + spin_unlock_irqrestore(&host->lock, flags); +@@ -1953,6 +1968,7 @@ static int bcm2835_sdhost_probe(struct p + struct bcm2835_host *host; + struct mmc_host *mmc; + const __be32 *addr; ++ u32 msg[3]; + int ret; + + pr_debug("bcm2835_sdhost_probe\n"); +@@ -2058,6 +2074,16 @@ static int bcm2835_sdhost_probe(struct p + else + mmc->caps |= MMC_CAP_4_BIT_DATA; + ++ msg[0] = 0; ++ msg[1] = ~0; ++ msg[2] = ~0; ++ ++ rpi_firmware_property(rpi_firmware_get(NULL), ++ RPI_FIRMWARE_SET_SDHOST_CLOCK, ++ &msg, sizeof(msg)); ++ ++ host->firmware_sets_cdiv = (msg[1] != ~0); ++ + ret = bcm2835_sdhost_add_host(host); + if (ret) + goto err; +--- a/include/soc/bcm2835/raspberrypi-firmware.h ++++ b/include/soc/bcm2835/raspberrypi-firmware.h +@@ -79,6 +79,7 @@ enum rpi_firmware_property_tag { + RPI_FIRMWARE_SET_VOLTAGE = 0x00038003, + RPI_FIRMWARE_SET_TURBO = 0x00038009, + RPI_FIRMWARE_SET_CUSTOMER_OTP = 0x00038021, ++ RPI_FIRMWARE_SET_SDHOST_CLOCK = 0x00038042, + + /* Dispmanx TAGS */ + RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001, diff --git a/target/linux/brcm2708/patches-4.4/0228-Revert-Revert-cpufreq-Temporarily-ignore-io_is_busy-.patch b/target/linux/brcm2708/patches-4.4/0228-Revert-Revert-cpufreq-Temporarily-ignore-io_is_busy-.patch new file mode 100644 index 0000000000..b6e678bcbe --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0228-Revert-Revert-cpufreq-Temporarily-ignore-io_is_busy-.patch @@ -0,0 +1,27 @@ +From 9f0a3f09ec2d80fed2781b341527ae6bd5666cd8 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Mon, 4 Apr 2016 19:52:27 +0100 +Subject: [PATCH 228/232] Revert "Revert "cpufreq: Temporarily ignore + io_is_busy=1"" + +This reverts commit c353af0f83220068c10f6593b1767576b9b6cc18. +--- + drivers/cpufreq/cpufreq_ondemand.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/cpufreq/cpufreq_ondemand.c ++++ b/drivers/cpufreq/cpufreq_ondemand.c +@@ -307,7 +307,12 @@ static ssize_t store_io_is_busy(struct d + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; +- od_tuners->io_is_busy = !!input; ++ // XXX temporary hack ++ if (input > 1) ++ input = 1; ++ else ++ input = 0; ++ od_tuners->io_is_busy = input; + + /* we need to re-evaluate prev_cpu_idle */ + for_each_online_cpu(j) { diff --git a/target/linux/brcm2708/patches-4.4/0229-config-Enabled-IPV6_SUBTREES.patch b/target/linux/brcm2708/patches-4.4/0229-config-Enabled-IPV6_SUBTREES.patch new file mode 100644 index 0000000000..314ea7fc2b --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0229-config-Enabled-IPV6_SUBTREES.patch @@ -0,0 +1,30 @@ +From ed825c0f1cfce352d446f33ef7f798134b8d6cb6 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Thu, 31 Mar 2016 16:49:52 +0100 +Subject: [PATCH 229/232] config: Enabled IPV6_SUBTREES + +--- + arch/arm/configs/bcm2709_defconfig | 1 + + arch/arm/configs/bcmrpi_defconfig | 1 + + 2 files changed, 2 insertions(+) + +--- a/arch/arm/configs/bcm2709_defconfig ++++ b/arch/arm/configs/bcm2709_defconfig +@@ -106,6 +106,7 @@ CONFIG_INET6_ESP=m + CONFIG_INET6_IPCOMP=m + CONFIG_IPV6_TUNNEL=m + CONFIG_IPV6_MULTIPLE_TABLES=y ++CONFIG_IPV6_SUBTREES=y + CONFIG_IPV6_MROUTE=y + CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y + CONFIG_IPV6_PIMSM_V2=y +--- a/arch/arm/configs/bcmrpi_defconfig ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -73,6 +73,7 @@ CONFIG_INET=y + CONFIG_IP_MULTICAST=y + CONFIG_IP_ADVANCED_ROUTER=y + CONFIG_IP_MULTIPLE_TABLES=y ++CONFIG_IPV6_SUBTREES=y + CONFIG_IP_ROUTE_MULTIPATH=y + CONFIG_IP_ROUTE_VERBOSE=y + CONFIG_IP_PNP=y diff --git a/target/linux/brcm2708/patches-4.4/0230-add-smsc95xx-packetsize-module_param.patch b/target/linux/brcm2708/patches-4.4/0230-add-smsc95xx-packetsize-module_param.patch new file mode 100644 index 0000000000..b4005c9d98 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0230-add-smsc95xx-packetsize-module_param.patch @@ -0,0 +1,42 @@ +From 3cea4c3fef10a33547949610d9a81ac9950b5be9 Mon Sep 17 00:00:00 2001 +From: Sam Nazarko +Date: Fri, 1 Apr 2016 17:27:21 +0100 +Subject: [PATCH 230/232] add smsc95xx packetsize module_param + +Signed-off-by: Sam Nazarko +--- + drivers/net/usb/smsc95xx.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +--- a/drivers/net/usb/smsc95xx.c ++++ b/drivers/net/usb/smsc95xx.c +@@ -83,6 +83,10 @@ static char *macaddr = ":"; + module_param(macaddr, charp, 0); + MODULE_PARM_DESC(macaddr, "MAC address"); + ++static int packetsize = 0; ++module_param(packetsize, int, 0644); ++MODULE_PARM_DESC(packetsize, "Override the RX URB packet size"); ++ + static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index, + u32 *data, int in_pm) + { +@@ -1006,13 +1010,13 @@ static int smsc95xx_reset(struct usbnet + + if (!turbo_mode) { + burst_cap = 0; +- dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE; ++ dev->rx_urb_size = packetsize ? packetsize : MAX_SINGLE_PACKET_SIZE; + } else if (dev->udev->speed == USB_SPEED_HIGH) { +- burst_cap = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE; +- dev->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE; ++ dev->rx_urb_size = packetsize ? packetsize : DEFAULT_HS_BURST_CAP_SIZE; ++ burst_cap = dev->rx_urb_size / HS_USB_PKT_SIZE; + } else { +- burst_cap = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE; +- dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE; ++ dev->rx_urb_size = packetsize ? packetsize : DEFAULT_FS_BURST_CAP_SIZE; ++ burst_cap = dev->rx_urb_size / FS_USB_PKT_SIZE; + } + + netif_dbg(dev, ifup, dev->net, "rx_urb_size=%ld\n", diff --git a/target/linux/brcm2708/patches-4.4/0231-reboot-Use-power-off-rather-than-busy-spinning-when-.patch b/target/linux/brcm2708/patches-4.4/0231-reboot-Use-power-off-rather-than-busy-spinning-when-.patch new file mode 100644 index 0000000000..f659a6809e --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0231-reboot-Use-power-off-rather-than-busy-spinning-when-.patch @@ -0,0 +1,25 @@ +From 3ce05d6e2e4994e06ec73ff95969ae189681a8e8 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Tue, 5 Apr 2016 19:40:12 +0100 +Subject: [PATCH 231/232] reboot: Use power off rather than busy spinning when + halt is requested + +--- + arch/arm/kernel/reboot.c | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +--- a/arch/arm/kernel/reboot.c ++++ b/arch/arm/kernel/reboot.c +@@ -102,11 +102,7 @@ void machine_shutdown(void) + */ + void machine_halt(void) + { +- local_irq_disable(); +- smp_send_stop(); +- +- local_irq_disable(); +- while (1); ++ machine_power_off(); + } + + /* diff --git a/target/linux/brcm2708/patches-4.4/0232-Revert-bcm2835-dma-Fix-dreq-not-set-for-slave-transf.patch b/target/linux/brcm2708/patches-4.4/0232-Revert-bcm2835-dma-Fix-dreq-not-set-for-slave-transf.patch new file mode 100644 index 0000000000..84b3777084 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0232-Revert-bcm2835-dma-Fix-dreq-not-set-for-slave-transf.patch @@ -0,0 +1,32 @@ +From ccb9ab7ba94d7b005f4fd820f1cb91caf73dbf89 Mon Sep 17 00:00:00 2001 +From: HiassofT +Date: Wed, 6 Apr 2016 21:45:01 +0200 +Subject: [PATCH 232/232] Revert "bcm2835-dma: Fix dreq not set for slave + transfers" + +This reverts commit 8ad957e866a1fe1450f663f2b00a57d7de44904c. + + + +DMA channels are set in devicetree, thus dreq will be set, + +and this commit is no longer needed. + + + +Signed-off-by: Matthias Reichl +--- + drivers/dma/bcm2835-dma.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/drivers/dma/bcm2835-dma.c ++++ b/drivers/dma/bcm2835-dma.c +@@ -679,8 +679,6 @@ static int bcm2835_dma_slave_config(stru + } + + c->cfg = *cfg; +- if (!c->dreq) +- c->dreq = cfg->slave_id; + + return 0; + } diff --git a/target/linux/brcm2708/patches-4.4/1001-smsc95xx-disable-hw-csum.patch b/target/linux/brcm2708/patches-4.4/1001-smsc95xx-disable-hw-csum.patch deleted file mode 100644 index eff1f0049c..0000000000 --- a/target/linux/brcm2708/patches-4.4/1001-smsc95xx-disable-hw-csum.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/drivers/net/usb/smsc95xx.c -+++ b/drivers/net/usb/smsc95xx.c -@@ -42,7 +42,7 @@ - #define LAN95XX_EEPROM_MAGIC (0x9500) - #define EEPROM_MAC_OFFSET (0x01) - #define DEFAULT_TX_CSUM_ENABLE (true) --#define DEFAULT_RX_CSUM_ENABLE (true) -+#define DEFAULT_RX_CSUM_ENABLE (false) - #define SMSC95XX_INTERNAL_PHY_ID (1) - #define SMSC95XX_TX_OVERHEAD (8) - #define SMSC95XX_TX_OVERHEAD_CSUM (12)