CONFIG_HAVE_UID16=y
CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
CONFIG_HW_CONSOLE=y
+CONFIG_HZ=100
+CONFIG_HZ_100=y
CONFIG_HZ_FIXED=0
CONFIG_I2C=y
# CONFIG_I2C_BCM2708 is not set
CONFIG_HIGHPTE=y
CONFIG_HOTPLUG_CPU=y
CONFIG_HW_CONSOLE=y
+CONFIG_HZ=100
+CONFIG_HZ_100=y
CONFIG_HZ_FIXED=0
CONFIG_I2C=y
# CONFIG_I2C_BCM2708 is not set
CONFIG_HOLES_IN_ZONE=y
CONFIG_HOTPLUG_CPU=y
CONFIG_HW_CONSOLE=y
+CONFIG_HZ=250
+CONFIG_HZ_250=y
CONFIG_I2C=y
# CONFIG_I2C_BCM2708 is not set
CONFIG_I2C_BOARDINFO=y
CONFIG_HOLES_IN_ZONE=y
CONFIG_HOTPLUG_CPU=y
CONFIG_HW_CONSOLE=y
+CONFIG_HZ=250
+CONFIG_HZ_250=y
CONFIG_I2C=y
# CONFIG_I2C_BCM2708 is not set
CONFIG_I2C_BOARDINFO=y
--- /dev/null
+From c182949e33dc3ac4d718386f97c75583bae0e46b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 28 Feb 2020 11:22:40 +0000
+Subject: [PATCH] ARM: dts: overlays: Create custom clocks in /
+
+Change [1] removes the simple-bus compatible string from the "/clocks"
+node, preventing any custom clocks placed there from being initialised.
+Rather than reinstate the compatible string and trigger DT warnings at
+kernel build time, change the overlays to instantiate those clocks under
+the root node ("/").
+
+See: https://github.com/raspberrypi/linux/issues/3481
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+[1] 4b2d24662126 ("ARM: dts: bcm283x: Remove simple-bus from fixed clocks")
+---
+ .../boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts | 2 +-
+ arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts | 2 +-
+ arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts | 2 +-
+ arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts | 2 +-
+ arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts | 2 +-
+ 5 files changed, 5 insertions(+), 5 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts
+@@ -9,7 +9,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target-path = "/clocks";
++ target-path = "/";
+ __overlay__ {
+ boss_osc: boss_osc {
+ compatible = "allo,dac-clk";
+--- a/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target-path = "/clocks";
++ target-path = "/";
+ __overlay__ {
+ dacpro_osc: dacpro_osc {
+ compatible = "hifiberry,dacpro-clk";
+--- a/arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target-path = "/clocks";
++ target-path = "/";
+ __overlay__ {
+ dacpro_osc: dacpro_osc {
+ compatible = "hifiberry,dacpro-clk";
+--- a/arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target-path = "/clocks";
++ target-path = "/";
+ __overlay__ {
+ dacpro_osc: dacpro_osc {
+ compatible = "hifiberry,dacpro-clk";
+--- a/arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts
+@@ -8,7 +8,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target-path = "/clocks";
++ target-path = "/";
+ __overlay__ {
+ dachd_osc: pll_dachd_osc {
+ compatible = "hifiberry,dachd-clk";
+++ /dev/null
-From c182949e33dc3ac4d718386f97c75583bae0e46b Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Fri, 28 Feb 2020 11:22:40 +0000
-Subject: [PATCH] ARM: dts: overlays: Create custom clocks in /
-
-Change [1] removes the simple-bus compatible string from the "/clocks"
-node, preventing any custom clocks placed there from being initialised.
-Rather than reinstate the compatible string and trigger DT warnings at
-kernel build time, change the overlays to instantiate those clocks under
-the root node ("/").
-
-See: https://github.com/raspberrypi/linux/issues/3481
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
-
-[1] 4b2d24662126 ("ARM: dts: bcm283x: Remove simple-bus from fixed clocks")
----
- .../boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts | 2 +-
- arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts | 2 +-
- arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts | 2 +-
- arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts | 2 +-
- arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts | 2 +-
- 5 files changed, 5 insertions(+), 5 deletions(-)
-
---- a/arch/arm/boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts
-@@ -9,7 +9,7 @@
- compatible = "brcm,bcm2835";
-
- fragment@0 {
-- target-path = "/clocks";
-+ target-path = "/";
- __overlay__ {
- boss_osc: boss_osc {
- compatible = "allo,dac-clk";
---- a/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts
-@@ -6,7 +6,7 @@
- compatible = "brcm,bcm2835";
-
- fragment@0 {
-- target-path = "/clocks";
-+ target-path = "/";
- __overlay__ {
- dacpro_osc: dacpro_osc {
- compatible = "hifiberry,dacpro-clk";
---- a/arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts
-@@ -6,7 +6,7 @@
- compatible = "brcm,bcm2835";
-
- fragment@0 {
-- target-path = "/clocks";
-+ target-path = "/";
- __overlay__ {
- dacpro_osc: dacpro_osc {
- compatible = "hifiberry,dacpro-clk";
---- a/arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts
-@@ -6,7 +6,7 @@
- compatible = "brcm,bcm2835";
-
- fragment@0 {
-- target-path = "/clocks";
-+ target-path = "/";
- __overlay__ {
- dacpro_osc: dacpro_osc {
- compatible = "hifiberry,dacpro-clk";
---- a/arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts
-@@ -8,7 +8,7 @@
- compatible = "brcm,bcm2835";
-
- fragment@0 {
-- target-path = "/clocks";
-+ target-path = "/";
- __overlay__ {
- dachd_osc: pll_dachd_osc {
- compatible = "hifiberry,dachd-clk";
--- /dev/null
+From 38e906c77467bf83ec130bea6859b46ea1e0d4b8 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 30 Jan 2020 12:35:44 +0000
+Subject: [PATCH] staging: vc04_services: Fix vcsm overflow bug when
+ counting transactions
+
+The response block and local state were using u16 and u32 respectively
+to represent transaction id. When the former would wrap, there is a
+mismatch and subsequent transactions will be marked as failures.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c
++++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c
+@@ -34,7 +34,7 @@ struct sm_cmd_rsp_blk {
+ /* To be signaled when the response is there */
+ struct completion cmplt;
+
+- u16 id;
++ u32 id;
+ u16 length;
+
+ u8 msg[VC_SM_MAX_MSG_LEN];
--- /dev/null
+From 04f569021b0d24ec9f5c3671447b77157c859d16 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 7 Feb 2020 09:51:31 +0000
+Subject: [PATCH] overlays: Add timeout_ms parameter to gpio-poweroff
+
+The timeout_ms parameter specifies in milliseconds how long the kernel
+waits for power-down before issuing a WARN. The default value is 3000 ms.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 2 ++
+ arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts | 1 +
+ 2 files changed, 3 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -821,6 +821,8 @@ Params: gpiopin GPIO for
+ input Set if the gpio pin should be configured as
+ an input.
+ export Set to export the configured pin to sysfs
++ timeout_ms Specify (in ms) how long the kernel waits for
++ power-down before issuing a WARN (default 3000).
+
+
+ Name: gpio-shutdown
+--- a/arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts
++++ b/arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts
+@@ -32,5 +32,6 @@
+ active_low = <&power_ctrl>,"gpios:8";
+ input = <&power_ctrl>,"input?";
+ export = <&power_ctrl>,"export?";
++ timeout_ms = <&power_ctrl>,"timeout-ms:0";
+ };
+ };
+++ /dev/null
-From 38e906c77467bf83ec130bea6859b46ea1e0d4b8 Mon Sep 17 00:00:00 2001
-From: Naushir Patuck <naush@raspberrypi.com>
-Date: Thu, 30 Jan 2020 12:35:44 +0000
-Subject: [PATCH] staging: vc04_services: Fix vcsm overflow bug when
- counting transactions
-
-The response block and local state were using u16 and u32 respectively
-to represent transaction id. When the former would wrap, there is a
-mismatch and subsequent transactions will be marked as failures.
-
-Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
----
- drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c
-+++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c
-@@ -34,7 +34,7 @@ struct sm_cmd_rsp_blk {
- /* To be signaled when the response is there */
- struct completion cmplt;
-
-- u16 id;
-+ u32 id;
- u16 length;
-
- u8 msg[VC_SM_MAX_MSG_LEN];
--- /dev/null
+From 8f22c4228bbb91697ab3510f5a6176e530c0d639 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 6 Feb 2020 12:23:15 +0000
+Subject: [PATCH] of: overlay: Correct symbol path fixups
+
+When symbols from overlays are added to the live tree their paths must
+be rebased. The translated symbol is normally the result of joining
+the fragment-relative path (with a leading "/") to the target path
+(either copied directly from the "target-path" property or resolved
+from the phandle). This translation fails when the target is the root
+node (a common case for Raspberry Pi overlays) because the resulting
+path starts with a double slash. For example, if target-path is "/" and
+the fragment adds a node called "newnode", the label associated with
+that node will be assigned the path "//newnode", which can't be found
+in the tree.
+
+Fix the failure case by explicitly replacing a target path of "/" with
+an empty string.
+
+Fixes: d1651b03c2df ("of: overlay: add overlay symbols to live device tree")
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/of/overlay.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/of/overlay.c
++++ b/drivers/of/overlay.c
+@@ -245,6 +245,8 @@ static struct property *dup_and_fixup_sy
+ if (!target_path)
+ return NULL;
+ target_path_len = strlen(target_path);
++ if (!strcmp(target_path, "/"))
++ target_path_len = 0;
+
+ new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
+ if (!new_prop)
+++ /dev/null
-From 04f569021b0d24ec9f5c3671447b77157c859d16 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Fri, 7 Feb 2020 09:51:31 +0000
-Subject: [PATCH] overlays: Add timeout_ms parameter to gpio-poweroff
-
-The timeout_ms parameter specifies in milliseconds how long the kernel
-waits for power-down before issuing a WARN. The default value is 3000 ms.
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- arch/arm/boot/dts/overlays/README | 2 ++
- arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts | 1 +
- 2 files changed, 3 insertions(+)
-
---- a/arch/arm/boot/dts/overlays/README
-+++ b/arch/arm/boot/dts/overlays/README
-@@ -821,6 +821,8 @@ Params: gpiopin GPIO for
- input Set if the gpio pin should be configured as
- an input.
- export Set to export the configured pin to sysfs
-+ timeout_ms Specify (in ms) how long the kernel waits for
-+ power-down before issuing a WARN (default 3000).
-
-
- Name: gpio-shutdown
---- a/arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts
-@@ -32,5 +32,6 @@
- active_low = <&power_ctrl>,"gpios:8";
- input = <&power_ctrl>,"input?";
- export = <&power_ctrl>,"export?";
-+ timeout_ms = <&power_ctrl>,"timeout-ms:0";
- };
- };
+++ /dev/null
-From 8f22c4228bbb91697ab3510f5a6176e530c0d639 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Thu, 6 Feb 2020 12:23:15 +0000
-Subject: [PATCH] of: overlay: Correct symbol path fixups
-
-When symbols from overlays are added to the live tree their paths must
-be rebased. The translated symbol is normally the result of joining
-the fragment-relative path (with a leading "/") to the target path
-(either copied directly from the "target-path" property or resolved
-from the phandle). This translation fails when the target is the root
-node (a common case for Raspberry Pi overlays) because the resulting
-path starts with a double slash. For example, if target-path is "/" and
-the fragment adds a node called "newnode", the label associated with
-that node will be assigned the path "//newnode", which can't be found
-in the tree.
-
-Fix the failure case by explicitly replacing a target path of "/" with
-an empty string.
-
-Fixes: d1651b03c2df ("of: overlay: add overlay symbols to live device tree")
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- drivers/of/overlay.c | 2 ++
- 1 file changed, 2 insertions(+)
-
---- a/drivers/of/overlay.c
-+++ b/drivers/of/overlay.c
-@@ -245,6 +245,8 @@ static struct property *dup_and_fixup_sy
- if (!target_path)
- return NULL;
- target_path_len = strlen(target_path);
-+ if (!strcmp(target_path, "/"))
-+ target_path_len = 0;
-
- new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
- if (!new_prop)
--- /dev/null
+From 65318cd76f4523acf8ffe8fe7448fb7d913f8c66 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 3 Mar 2020 09:43:41 +0000
+Subject: [PATCH] overlays: sc16ic750-i2c: Fix xtal parameter
+
+The xtal parameter is targetting the wrong node - fix it.
+
+See: https://github.com/raspberrypi/linux/issues/3156
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts
++++ b/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts
+@@ -32,7 +32,7 @@
+ __overrides__ {
+ int_pin = <&sc16is750>,"interrupts:0";
+ addr = <&sc16is750>,"reg:0",<&sc16is750_clk>,"name";
+- xtal = <&sc16is750>,"clock-frequency:0";
++ xtal = <&sc16is750_clk>,"clock-frequency:0";
+ };
+
+ };
--- /dev/null
+From 25ab98ceb9844642c994b5766de1033552d1aef2 Mon Sep 17 00:00:00 2001
+From: Robin Murphy <robin.murphy@arm.com>
+Date: Wed, 3 Jul 2019 18:23:01 +0100
+Subject: [PATCH] of/address: Introduce of_get_next_dma_parent() helper
+
+commit 862ab5578f754117742c8b8c8e5ddf98bdb190ba upstream.
+
+Add of_get_next_dma_parent() helper which is similar to
+__of_get_dma_parent(), but can be used in iterators and decrements the
+ref count on the prior parent.
+
+Signed-off-by: Robin Murphy <robin.murphy@arm.com>
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Rob Herring <robh@kernel.org>
+---
+ drivers/of/address.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/of/address.c
++++ b/drivers/of/address.c
+@@ -695,6 +695,16 @@ static struct device_node *__of_get_dma_
+ return of_node_get(args.np);
+ }
+
++static struct device_node *of_get_next_dma_parent(struct device_node *np)
++{
++ struct device_node *parent;
++
++ parent = __of_get_dma_parent(np);
++ of_node_put(np);
++
++ return parent;
++}
++
+ u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr)
+ {
+ struct device_node *host;
+++ /dev/null
-From 65318cd76f4523acf8ffe8fe7448fb7d913f8c66 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Tue, 3 Mar 2020 09:43:41 +0000
-Subject: [PATCH] overlays: sc16ic750-i2c: Fix xtal parameter
-
-The xtal parameter is targetting the wrong node - fix it.
-
-See: https://github.com/raspberrypi/linux/issues/3156
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts
-@@ -32,7 +32,7 @@
- __overrides__ {
- int_pin = <&sc16is750>,"interrupts:0";
- addr = <&sc16is750>,"reg:0",<&sc16is750_clk>,"name";
-- xtal = <&sc16is750>,"clock-frequency:0";
-+ xtal = <&sc16is750_clk>,"clock-frequency:0";
- };
-
- };
--- /dev/null
+From e4a649779ff6857240fe691cdf147a3b4896e71b Mon Sep 17 00:00:00 2001
+From: Robin Murphy <robin.murphy@arm.com>
+Date: Wed, 3 Jul 2019 14:47:31 +0100
+Subject: [PATCH] of: address: Follow DMA parent for "dma-coherent"
+
+commit c60bf3eb888a362100aa1bdbea351dab681e262a upstream.
+
+Much like for address translation, when checking for DMA coherence we
+should be sure to walk up the DMA hierarchy, rather than the MMIO one,
+now that we can accommodate them being different.
+
+Signed-off-by: Robin Murphy <robin.murphy@arm.com>
+Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Rob Herring <robh@kernel.org>
+---
+ drivers/of/address.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/of/address.c
++++ b/drivers/of/address.c
+@@ -1023,7 +1023,7 @@ bool of_dma_is_coherent(struct device_no
+ of_node_put(node);
+ return true;
+ }
+- node = of_get_next_parent(node);
++ node = of_get_next_dma_parent(node);
+ }
+ of_node_put(node);
+ return false;
+++ /dev/null
-From 25ab98ceb9844642c994b5766de1033552d1aef2 Mon Sep 17 00:00:00 2001
-From: Robin Murphy <robin.murphy@arm.com>
-Date: Wed, 3 Jul 2019 18:23:01 +0100
-Subject: [PATCH] of/address: Introduce of_get_next_dma_parent() helper
-
-commit 862ab5578f754117742c8b8c8e5ddf98bdb190ba upstream.
-
-Add of_get_next_dma_parent() helper which is similar to
-__of_get_dma_parent(), but can be used in iterators and decrements the
-ref count on the prior parent.
-
-Signed-off-by: Robin Murphy <robin.murphy@arm.com>
-Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
-Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Rob Herring <robh@kernel.org>
----
- drivers/of/address.c | 10 ++++++++++
- 1 file changed, 10 insertions(+)
-
---- a/drivers/of/address.c
-+++ b/drivers/of/address.c
-@@ -695,6 +695,16 @@ static struct device_node *__of_get_dma_
- return of_node_get(args.np);
- }
-
-+static struct device_node *of_get_next_dma_parent(struct device_node *np)
-+{
-+ struct device_node *parent;
-+
-+ parent = __of_get_dma_parent(np);
-+ of_node_put(np);
-+
-+ return parent;
-+}
-+
- u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr)
- {
- struct device_node *host;
--- /dev/null
+From 839aeedc908eb729b9014e7d1d38e109778a52d2 Mon Sep 17 00:00:00 2001
+From: Robin Murphy <robin.murphy@arm.com>
+Date: Tue, 2 Jul 2019 18:42:39 +0100
+Subject: [PATCH] of: Factor out #{addr,size}-cells parsing
+
+In some cases such as PCI host controllers, we may have a "parent bus"
+which is an OF leaf node, but still need to correctly parse ranges from
+the point of view of that bus. For that, factor out variants of the
+"#addr-cells" and "#size-cells" parsers which do not assume they have a
+device node and thus immediately traverse upwards before reading the
+relevant property.
+
+Signed-off-by: Robin Murphy <robin.murphy@arm.com>
+[robh: don't make of_bus_n_{addr,size}_cells() public]
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Rob Herring <robh@kernel.org>
+
+(cherry picked from commit b68ac8dc22ebbf003e26e44bf4dd3030c076df5a)
+---
+ drivers/of/address.c | 2 ++
+ drivers/of/base.c | 32 ++++++++++++++++++++++----------
+ drivers/of/of_private.h | 14 ++++++++++++++
+ 3 files changed, 38 insertions(+), 10 deletions(-)
+
+--- a/drivers/of/address.c
++++ b/drivers/of/address.c
+@@ -14,6 +14,8 @@
+ #include <linux/slab.h>
+ #include <linux/string.h>
+
++#include "of_private.h"
++
+ /* Max address size we deal with */
+ #define OF_MAX_ADDR_CELLS 4
+ #define OF_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS)
+--- a/drivers/of/base.c
++++ b/drivers/of/base.c
+@@ -86,34 +86,46 @@ static bool __of_node_is_type(const stru
+ return np && match && type && !strcmp(match, type);
+ }
+
+-int of_n_addr_cells(struct device_node *np)
++int of_bus_n_addr_cells(struct device_node *np)
+ {
+ u32 cells;
+
+- do {
+- if (np->parent)
+- np = np->parent;
++ for (; np; np = np->parent)
+ if (!of_property_read_u32(np, "#address-cells", &cells))
+ return cells;
+- } while (np->parent);
++
+ /* No #address-cells property for the root node */
+ return OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
+ }
++
++int of_n_addr_cells(struct device_node *np)
++{
++ if (np->parent)
++ np = np->parent;
++
++ return of_bus_n_addr_cells(np);
++}
+ EXPORT_SYMBOL(of_n_addr_cells);
+
+-int of_n_size_cells(struct device_node *np)
++int of_bus_n_size_cells(struct device_node *np)
+ {
+ u32 cells;
+
+- do {
+- if (np->parent)
+- np = np->parent;
++ for (; np; np = np->parent)
+ if (!of_property_read_u32(np, "#size-cells", &cells))
+ return cells;
+- } while (np->parent);
++
+ /* No #size-cells property for the root node */
+ return OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
+ }
++
++int of_n_size_cells(struct device_node *np)
++{
++ if (np->parent)
++ np = np->parent;
++
++ return of_bus_n_size_cells(np);
++}
+ EXPORT_SYMBOL(of_n_size_cells);
+
+ #ifdef CONFIG_NUMA
+--- a/drivers/of/of_private.h
++++ b/drivers/of/of_private.h
+@@ -158,4 +158,18 @@ extern void __of_sysfs_remove_bin_file(s
+ #define for_each_transaction_entry_reverse(_oft, _te) \
+ list_for_each_entry_reverse(_te, &(_oft)->te_list, node)
+
++extern int of_bus_n_addr_cells(struct device_node *np);
++extern int of_bus_n_size_cells(struct device_node *np);
++
++#ifdef CONFIG_OF_ADDRESS
++extern int of_dma_get_range(struct device_node *np, u64 *dma_addr,
++ u64 *paddr, u64 *size);
++#else
++static inline int of_dma_get_range(struct device_node *np, u64 *dma_addr,
++ u64 *paddr, u64 *size)
++{
++ return -ENODEV;
++}
++#endif
++
+ #endif /* _LINUX_OF_PRIVATE_H */
+++ /dev/null
-From e4a649779ff6857240fe691cdf147a3b4896e71b Mon Sep 17 00:00:00 2001
-From: Robin Murphy <robin.murphy@arm.com>
-Date: Wed, 3 Jul 2019 14:47:31 +0100
-Subject: [PATCH] of: address: Follow DMA parent for "dma-coherent"
-
-commit c60bf3eb888a362100aa1bdbea351dab681e262a upstream.
-
-Much like for address translation, when checking for DMA coherence we
-should be sure to walk up the DMA hierarchy, rather than the MMIO one,
-now that we can accommodate them being different.
-
-Signed-off-by: Robin Murphy <robin.murphy@arm.com>
-Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Rob Herring <robh@kernel.org>
----
- drivers/of/address.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/of/address.c
-+++ b/drivers/of/address.c
-@@ -1023,7 +1023,7 @@ bool of_dma_is_coherent(struct device_no
- of_node_put(node);
- return true;
- }
-- node = of_get_next_parent(node);
-+ node = of_get_next_dma_parent(node);
- }
- of_node_put(node);
- return false;
+++ /dev/null
-From 839aeedc908eb729b9014e7d1d38e109778a52d2 Mon Sep 17 00:00:00 2001
-From: Robin Murphy <robin.murphy@arm.com>
-Date: Tue, 2 Jul 2019 18:42:39 +0100
-Subject: [PATCH] of: Factor out #{addr,size}-cells parsing
-
-In some cases such as PCI host controllers, we may have a "parent bus"
-which is an OF leaf node, but still need to correctly parse ranges from
-the point of view of that bus. For that, factor out variants of the
-"#addr-cells" and "#size-cells" parsers which do not assume they have a
-device node and thus immediately traverse upwards before reading the
-relevant property.
-
-Signed-off-by: Robin Murphy <robin.murphy@arm.com>
-[robh: don't make of_bus_n_{addr,size}_cells() public]
-Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
-Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Rob Herring <robh@kernel.org>
-
-(cherry picked from commit b68ac8dc22ebbf003e26e44bf4dd3030c076df5a)
----
- drivers/of/address.c | 2 ++
- drivers/of/base.c | 32 ++++++++++++++++++++++----------
- drivers/of/of_private.h | 14 ++++++++++++++
- 3 files changed, 38 insertions(+), 10 deletions(-)
-
---- a/drivers/of/address.c
-+++ b/drivers/of/address.c
-@@ -14,6 +14,8 @@
- #include <linux/slab.h>
- #include <linux/string.h>
-
-+#include "of_private.h"
-+
- /* Max address size we deal with */
- #define OF_MAX_ADDR_CELLS 4
- #define OF_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS)
---- a/drivers/of/base.c
-+++ b/drivers/of/base.c
-@@ -86,34 +86,46 @@ static bool __of_node_is_type(const stru
- return np && match && type && !strcmp(match, type);
- }
-
--int of_n_addr_cells(struct device_node *np)
-+int of_bus_n_addr_cells(struct device_node *np)
- {
- u32 cells;
-
-- do {
-- if (np->parent)
-- np = np->parent;
-+ for (; np; np = np->parent)
- if (!of_property_read_u32(np, "#address-cells", &cells))
- return cells;
-- } while (np->parent);
-+
- /* No #address-cells property for the root node */
- return OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
- }
-+
-+int of_n_addr_cells(struct device_node *np)
-+{
-+ if (np->parent)
-+ np = np->parent;
-+
-+ return of_bus_n_addr_cells(np);
-+}
- EXPORT_SYMBOL(of_n_addr_cells);
-
--int of_n_size_cells(struct device_node *np)
-+int of_bus_n_size_cells(struct device_node *np)
- {
- u32 cells;
-
-- do {
-- if (np->parent)
-- np = np->parent;
-+ for (; np; np = np->parent)
- if (!of_property_read_u32(np, "#size-cells", &cells))
- return cells;
-- } while (np->parent);
-+
- /* No #size-cells property for the root node */
- return OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
- }
-+
-+int of_n_size_cells(struct device_node *np)
-+{
-+ if (np->parent)
-+ np = np->parent;
-+
-+ return of_bus_n_size_cells(np);
-+}
- EXPORT_SYMBOL(of_n_size_cells);
-
- #ifdef CONFIG_NUMA
---- a/drivers/of/of_private.h
-+++ b/drivers/of/of_private.h
-@@ -158,4 +158,18 @@ extern void __of_sysfs_remove_bin_file(s
- #define for_each_transaction_entry_reverse(_oft, _te) \
- list_for_each_entry_reverse(_te, &(_oft)->te_list, node)
-
-+extern int of_bus_n_addr_cells(struct device_node *np);
-+extern int of_bus_n_size_cells(struct device_node *np);
-+
-+#ifdef CONFIG_OF_ADDRESS
-+extern int of_dma_get_range(struct device_node *np, u64 *dma_addr,
-+ u64 *paddr, u64 *size);
-+#else
-+static inline int of_dma_get_range(struct device_node *np, u64 *dma_addr,
-+ u64 *paddr, u64 *size)
-+{
-+ return -ENODEV;
-+}
-+#endif
-+
- #endif /* _LINUX_OF_PRIVATE_H */
--- /dev/null
+From 39f5d9e883393e32938eac45b564f74afde8a942 Mon Sep 17 00:00:00 2001
+From: Rob Herring <robh@kernel.org>
+Date: Wed, 4 Sep 2019 11:43:30 +0100
+Subject: [PATCH] of/address: Translate 'dma-ranges' for parent nodes
+ missing 'dma-ranges'
+
+commit 81db12ee15cb83926e290a8a3654a2dfebc80935 upstream.
+
+'dma-ranges' frequently exists without parent nodes having 'dma-ranges'.
+While this is an error for 'ranges', this is fine because DMA capable
+devices always have a translatable DMA address. Also, with no
+'dma-ranges' at all, the assumption is that DMA addresses are 1:1 with
+no restrictions unless perhaps the device itself has implicit
+restrictions.
+
+Cc: Robin Murphy <robin.murphy@arm.com>
+Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Rob Herring <robh@kernel.org>
+---
+ drivers/of/address.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/of/address.c
++++ b/drivers/of/address.c
+@@ -519,9 +519,13 @@ static int of_translate_one(struct devic
+ *
+ * As far as we know, this damage only exists on Apple machines, so
+ * This code is only enabled on powerpc. --gcl
++ *
++ * This quirk also applies for 'dma-ranges' which frequently exist in
++ * child nodes without 'dma-ranges' in the parent nodes. --RobH
+ */
+ ranges = of_get_property(parent, rprop, &rlen);
+- if (ranges == NULL && !of_empty_ranges_quirk(parent)) {
++ if (ranges == NULL && !of_empty_ranges_quirk(parent) &&
++ strcmp(rprop, "dma-ranges")) {
+ pr_debug("no ranges; cannot translate\n");
+ return 1;
+ }
--- /dev/null
+From 7631cb95056f03136c9e0a35484e8bebe7b52650 Mon Sep 17 00:00:00 2001
+From: Robin Murphy <robin.murphy@arm.com>
+Date: Wed, 3 Jul 2019 18:42:20 +0100
+Subject: [PATCH] of: Make of_dma_get_range() work on bus nodes
+
+commit 951d48855d86e72e0d6de73440fe09d363168064 upstream.
+
+Since the "dma-ranges" property is only valid for a node representing a
+bus, of_dma_get_range() currently assumes the node passed in is a leaf
+representing a device, and starts the walk from its parent. In cases
+like PCI host controllers on typical FDT systems, however, where the PCI
+endpoints are probed dynamically the initial leaf node represents the
+'bus' itself, and this logic means we fail to consider any "dma-ranges"
+describing the host bridge itself. Rework the logic such that
+of_dma_get_range() also works correctly starting from a bus node
+containing "dma-ranges".
+
+While this does mean "dma-ranges" could incorrectly be in a device leaf
+node, there isn't really any way in this function to ensure that a leaf
+node is or isn't a bus node.
+
+Signed-off-by: Robin Murphy <robin.murphy@arm.com>
+[robh: Allow for the bus child node to still be passed in]
+Signed-off-by: Rob Herring <robh@kernel.org>
+Reviewed-by: Robin Murphy <robin.murphy@arm.com>
+Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+---
+ drivers/of/address.c | 44 ++++++++++++++++++--------------------------
+ 1 file changed, 18 insertions(+), 26 deletions(-)
+
+--- a/drivers/of/address.c
++++ b/drivers/of/address.c
+@@ -940,47 +940,39 @@ int of_dma_get_range(struct device_node
+ const __be32 *ranges = NULL;
+ int len, naddr, nsize, pna;
+ int ret = 0;
++ bool found_dma_ranges = false;
+ u64 dmaaddr;
+
+- if (!node)
+- return -EINVAL;
+-
+- while (1) {
+- struct device_node *parent;
+-
+- naddr = of_n_addr_cells(node);
+- nsize = of_n_size_cells(node);
+-
+- parent = __of_get_dma_parent(node);
+- of_node_put(node);
+-
+- node = parent;
+- if (!node)
+- break;
+-
++ while (node) {
+ ranges = of_get_property(node, "dma-ranges", &len);
+
+ /* Ignore empty ranges, they imply no translation required */
+ if (ranges && len > 0)
+ break;
+
+- /*
+- * At least empty ranges has to be defined for parent node if
+- * DMA is supported
+- */
+- if (!ranges)
+- break;
++ /* Once we find 'dma-ranges', then a missing one is an error */
++ if (found_dma_ranges && !ranges) {
++ ret = -ENODEV;
++ goto out;
++ }
++ found_dma_ranges = true;
++
++ node = of_get_next_dma_parent(node);
+ }
+
+- if (!ranges) {
++ if (!node || !ranges) {
+ pr_debug("no dma-ranges found for node(%pOF)\n", np);
+ ret = -ENODEV;
+ goto out;
+ }
+
+- len /= sizeof(u32);
+-
++ naddr = of_bus_n_addr_cells(node);
++ nsize = of_bus_n_size_cells(node);
+ pna = of_n_addr_cells(node);
++ if ((len / sizeof(__be32)) % (pna + naddr + nsize)) {
++ ret = -EINVAL;
++ goto out;
++ }
+
+ /* dma-ranges format:
+ * DMA addr : naddr cells
+@@ -988,7 +980,7 @@ int of_dma_get_range(struct device_node
+ * size : nsize cells
+ */
+ dmaaddr = of_read_number(ranges, naddr);
+- *paddr = of_translate_dma_address(np, ranges);
++ *paddr = of_translate_dma_address(node, ranges + naddr);
+ if (*paddr == OF_BAD_ADDR) {
+ pr_err("translation of DMA address(%pad) to CPU address failed node(%pOF)\n",
+ dma_addr, np);
+++ /dev/null
-From 39f5d9e883393e32938eac45b564f74afde8a942 Mon Sep 17 00:00:00 2001
-From: Rob Herring <robh@kernel.org>
-Date: Wed, 4 Sep 2019 11:43:30 +0100
-Subject: [PATCH] of/address: Translate 'dma-ranges' for parent nodes
- missing 'dma-ranges'
-
-commit 81db12ee15cb83926e290a8a3654a2dfebc80935 upstream.
-
-'dma-ranges' frequently exists without parent nodes having 'dma-ranges'.
-While this is an error for 'ranges', this is fine because DMA capable
-devices always have a translatable DMA address. Also, with no
-'dma-ranges' at all, the assumption is that DMA addresses are 1:1 with
-no restrictions unless perhaps the device itself has implicit
-restrictions.
-
-Cc: Robin Murphy <robin.murphy@arm.com>
-Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Rob Herring <robh@kernel.org>
----
- drivers/of/address.c | 6 +++++-
- 1 file changed, 5 insertions(+), 1 deletion(-)
-
---- a/drivers/of/address.c
-+++ b/drivers/of/address.c
-@@ -519,9 +519,13 @@ static int of_translate_one(struct devic
- *
- * As far as we know, this damage only exists on Apple machines, so
- * This code is only enabled on powerpc. --gcl
-+ *
-+ * This quirk also applies for 'dma-ranges' which frequently exist in
-+ * child nodes without 'dma-ranges' in the parent nodes. --RobH
- */
- ranges = of_get_property(parent, rprop, &rlen);
-- if (ranges == NULL && !of_empty_ranges_quirk(parent)) {
-+ if (ranges == NULL && !of_empty_ranges_quirk(parent) &&
-+ strcmp(rprop, "dma-ranges")) {
- pr_debug("no ranges; cannot translate\n");
- return 1;
- }
--- /dev/null
+From c17f622cbb33332a305ef383506740d3d01aa831 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Wed, 11 Sep 2019 20:25:43 +0200
+Subject: [PATCH] arm64: mm: use arm64_dma_phys_limit instead of
+ calling max_zone_dma_phys()
+
+commit ae970dc096b2d39f65f2e18d142e3978dc9ee1c7 upstream.
+
+By the time we call zones_sizes_init() arm64_dma_phys_limit already
+contains the result of max_zone_dma_phys(). We use the variable instead
+of calling the function directly to save some precious cpu time.
+
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+---
+ arch/arm64/mm/init.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm64/mm/init.c
++++ b/arch/arm64/mm/init.c
+@@ -187,7 +187,7 @@ static void __init zone_sizes_init(unsig
+ unsigned long max_zone_pfns[MAX_NR_ZONES] = {0};
+
+ #ifdef CONFIG_ZONE_DMA32
+- max_zone_pfns[ZONE_DMA32] = PFN_DOWN(max_zone_dma_phys());
++ max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma_phys_limit);
+ #endif
+ max_zone_pfns[ZONE_NORMAL] = max;
+
+++ /dev/null
-From 7631cb95056f03136c9e0a35484e8bebe7b52650 Mon Sep 17 00:00:00 2001
-From: Robin Murphy <robin.murphy@arm.com>
-Date: Wed, 3 Jul 2019 18:42:20 +0100
-Subject: [PATCH] of: Make of_dma_get_range() work on bus nodes
-
-commit 951d48855d86e72e0d6de73440fe09d363168064 upstream.
-
-Since the "dma-ranges" property is only valid for a node representing a
-bus, of_dma_get_range() currently assumes the node passed in is a leaf
-representing a device, and starts the walk from its parent. In cases
-like PCI host controllers on typical FDT systems, however, where the PCI
-endpoints are probed dynamically the initial leaf node represents the
-'bus' itself, and this logic means we fail to consider any "dma-ranges"
-describing the host bridge itself. Rework the logic such that
-of_dma_get_range() also works correctly starting from a bus node
-containing "dma-ranges".
-
-While this does mean "dma-ranges" could incorrectly be in a device leaf
-node, there isn't really any way in this function to ensure that a leaf
-node is or isn't a bus node.
-
-Signed-off-by: Robin Murphy <robin.murphy@arm.com>
-[robh: Allow for the bus child node to still be passed in]
-Signed-off-by: Rob Herring <robh@kernel.org>
-Reviewed-by: Robin Murphy <robin.murphy@arm.com>
-Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
----
- drivers/of/address.c | 44 ++++++++++++++++++--------------------------
- 1 file changed, 18 insertions(+), 26 deletions(-)
-
---- a/drivers/of/address.c
-+++ b/drivers/of/address.c
-@@ -940,47 +940,39 @@ int of_dma_get_range(struct device_node
- const __be32 *ranges = NULL;
- int len, naddr, nsize, pna;
- int ret = 0;
-+ bool found_dma_ranges = false;
- u64 dmaaddr;
-
-- if (!node)
-- return -EINVAL;
--
-- while (1) {
-- struct device_node *parent;
--
-- naddr = of_n_addr_cells(node);
-- nsize = of_n_size_cells(node);
--
-- parent = __of_get_dma_parent(node);
-- of_node_put(node);
--
-- node = parent;
-- if (!node)
-- break;
--
-+ while (node) {
- ranges = of_get_property(node, "dma-ranges", &len);
-
- /* Ignore empty ranges, they imply no translation required */
- if (ranges && len > 0)
- break;
-
-- /*
-- * At least empty ranges has to be defined for parent node if
-- * DMA is supported
-- */
-- if (!ranges)
-- break;
-+ /* Once we find 'dma-ranges', then a missing one is an error */
-+ if (found_dma_ranges && !ranges) {
-+ ret = -ENODEV;
-+ goto out;
-+ }
-+ found_dma_ranges = true;
-+
-+ node = of_get_next_dma_parent(node);
- }
-
-- if (!ranges) {
-+ if (!node || !ranges) {
- pr_debug("no dma-ranges found for node(%pOF)\n", np);
- ret = -ENODEV;
- goto out;
- }
-
-- len /= sizeof(u32);
--
-+ naddr = of_bus_n_addr_cells(node);
-+ nsize = of_bus_n_size_cells(node);
- pna = of_n_addr_cells(node);
-+ if ((len / sizeof(__be32)) % (pna + naddr + nsize)) {
-+ ret = -EINVAL;
-+ goto out;
-+ }
-
- /* dma-ranges format:
- * DMA addr : naddr cells
-@@ -988,7 +980,7 @@ int of_dma_get_range(struct device_node
- * size : nsize cells
- */
- dmaaddr = of_read_number(ranges, naddr);
-- *paddr = of_translate_dma_address(np, ranges);
-+ *paddr = of_translate_dma_address(node, ranges + naddr);
- if (*paddr == OF_BAD_ADDR) {
- pr_err("translation of DMA address(%pad) to CPU address failed node(%pOF)\n",
- dma_addr, np);
+++ /dev/null
-From c17f622cbb33332a305ef383506740d3d01aa831 Mon Sep 17 00:00:00 2001
-From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Date: Wed, 11 Sep 2019 20:25:43 +0200
-Subject: [PATCH] arm64: mm: use arm64_dma_phys_limit instead of
- calling max_zone_dma_phys()
-
-commit ae970dc096b2d39f65f2e18d142e3978dc9ee1c7 upstream.
-
-By the time we call zones_sizes_init() arm64_dma_phys_limit already
-contains the result of max_zone_dma_phys(). We use the variable instead
-of calling the function directly to save some precious cpu time.
-
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
-Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
----
- arch/arm64/mm/init.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/arch/arm64/mm/init.c
-+++ b/arch/arm64/mm/init.c
-@@ -187,7 +187,7 @@ static void __init zone_sizes_init(unsig
- unsigned long max_zone_pfns[MAX_NR_ZONES] = {0};
-
- #ifdef CONFIG_ZONE_DMA32
-- max_zone_pfns[ZONE_DMA32] = PFN_DOWN(max_zone_dma_phys());
-+ max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma_phys_limit);
- #endif
- max_zone_pfns[ZONE_NORMAL] = max;
-
--- /dev/null
+From 4d2bd7f66bac81b042afc2a6e742bd776a5a3938 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Wed, 11 Sep 2019 20:25:44 +0200
+Subject: [PATCH] arm64: rename variables used to calculate
+ ZONE_DMA32's size
+
+commit a573cdd7973dedd87e62196c400332896bb236c8 upstream.
+
+Let the name indicate that they are used to calculate ZONE_DMA32's size
+as opposed to ZONE_DMA.
+
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+---
+ arch/arm64/mm/init.c | 30 +++++++++++++++---------------
+ 1 file changed, 15 insertions(+), 15 deletions(-)
+
+--- a/arch/arm64/mm/init.c
++++ b/arch/arm64/mm/init.c
+@@ -56,7 +56,7 @@ EXPORT_SYMBOL(physvirt_offset);
+ struct page *vmemmap __ro_after_init;
+ EXPORT_SYMBOL(vmemmap);
+
+-phys_addr_t arm64_dma_phys_limit __ro_after_init;
++phys_addr_t arm64_dma32_phys_limit __ro_after_init;
+
+ #ifdef CONFIG_KEXEC_CORE
+ /*
+@@ -174,7 +174,7 @@ static void __init reserve_elfcorehdr(vo
+ * currently assumes that for memory starting above 4G, 32-bit devices will
+ * use a DMA offset.
+ */
+-static phys_addr_t __init max_zone_dma_phys(void)
++static phys_addr_t __init max_zone_dma32_phys(void)
+ {
+ phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, 32);
+ return min(offset + (1ULL << 32), memblock_end_of_DRAM());
+@@ -187,7 +187,7 @@ static void __init zone_sizes_init(unsig
+ unsigned long max_zone_pfns[MAX_NR_ZONES] = {0};
+
+ #ifdef CONFIG_ZONE_DMA32
+- max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma_phys_limit);
++ max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma32_phys_limit);
+ #endif
+ max_zone_pfns[ZONE_NORMAL] = max;
+
+@@ -200,16 +200,16 @@ static void __init zone_sizes_init(unsig
+ {
+ struct memblock_region *reg;
+ unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
+- unsigned long max_dma = min;
++ unsigned long max_dma32 = min;
+
+ memset(zone_size, 0, sizeof(zone_size));
+
+ /* 4GB maximum for 32-bit only capable devices */
+ #ifdef CONFIG_ZONE_DMA32
+- max_dma = PFN_DOWN(arm64_dma_phys_limit);
+- zone_size[ZONE_DMA32] = max_dma - min;
++ max_dma32 = PFN_DOWN(arm64_dma32_phys_limit);
++ zone_size[ZONE_DMA32] = max_dma32 - min;
+ #endif
+- zone_size[ZONE_NORMAL] = max - max_dma;
++ zone_size[ZONE_NORMAL] = max - max_dma32;
+
+ memcpy(zhole_size, zone_size, sizeof(zhole_size));
+
+@@ -221,14 +221,14 @@ static void __init zone_sizes_init(unsig
+ continue;
+
+ #ifdef CONFIG_ZONE_DMA32
+- if (start < max_dma) {
+- unsigned long dma_end = min(end, max_dma);
++ if (start < max_dma32) {
++ unsigned long dma_end = min(end, max_dma32);
+ zhole_size[ZONE_DMA32] -= dma_end - start;
+ }
+ #endif
+- if (end > max_dma) {
++ if (end > max_dma32) {
+ unsigned long normal_end = min(end, max);
+- unsigned long normal_start = max(start, max_dma);
++ unsigned long normal_start = max(start, max_dma32);
+ zhole_size[ZONE_NORMAL] -= normal_end - normal_start;
+ }
+ }
+@@ -420,9 +420,9 @@ void __init arm64_memblock_init(void)
+
+ /* 4GB maximum for 32-bit only capable devices */
+ if (IS_ENABLED(CONFIG_ZONE_DMA32))
+- arm64_dma_phys_limit = max_zone_dma_phys();
++ arm64_dma32_phys_limit = max_zone_dma32_phys();
+ else
+- arm64_dma_phys_limit = PHYS_MASK + 1;
++ arm64_dma32_phys_limit = PHYS_MASK + 1;
+
+ reserve_crashkernel();
+
+@@ -430,7 +430,7 @@ void __init arm64_memblock_init(void)
+
+ high_memory = __va(memblock_end_of_DRAM() - 1) + 1;
+
+- dma_contiguous_reserve(arm64_dma_phys_limit);
++ dma_contiguous_reserve(arm64_dma32_phys_limit);
+ }
+
+ void __init bootmem_init(void)
+@@ -534,7 +534,7 @@ static void __init free_unused_memmap(vo
+ void __init mem_init(void)
+ {
+ if (swiotlb_force == SWIOTLB_FORCE ||
+- max_pfn > (arm64_dma_phys_limit >> PAGE_SHIFT))
++ max_pfn > (arm64_dma32_phys_limit >> PAGE_SHIFT))
+ swiotlb_init(1);
+ else
+ swiotlb_force = SWIOTLB_NO_FORCE;
+++ /dev/null
-From 4d2bd7f66bac81b042afc2a6e742bd776a5a3938 Mon Sep 17 00:00:00 2001
-From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Date: Wed, 11 Sep 2019 20:25:44 +0200
-Subject: [PATCH] arm64: rename variables used to calculate
- ZONE_DMA32's size
-
-commit a573cdd7973dedd87e62196c400332896bb236c8 upstream.
-
-Let the name indicate that they are used to calculate ZONE_DMA32's size
-as opposed to ZONE_DMA.
-
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
-Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
----
- arch/arm64/mm/init.c | 30 +++++++++++++++---------------
- 1 file changed, 15 insertions(+), 15 deletions(-)
-
---- a/arch/arm64/mm/init.c
-+++ b/arch/arm64/mm/init.c
-@@ -56,7 +56,7 @@ EXPORT_SYMBOL(physvirt_offset);
- struct page *vmemmap __ro_after_init;
- EXPORT_SYMBOL(vmemmap);
-
--phys_addr_t arm64_dma_phys_limit __ro_after_init;
-+phys_addr_t arm64_dma32_phys_limit __ro_after_init;
-
- #ifdef CONFIG_KEXEC_CORE
- /*
-@@ -174,7 +174,7 @@ static void __init reserve_elfcorehdr(vo
- * currently assumes that for memory starting above 4G, 32-bit devices will
- * use a DMA offset.
- */
--static phys_addr_t __init max_zone_dma_phys(void)
-+static phys_addr_t __init max_zone_dma32_phys(void)
- {
- phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, 32);
- return min(offset + (1ULL << 32), memblock_end_of_DRAM());
-@@ -187,7 +187,7 @@ static void __init zone_sizes_init(unsig
- unsigned long max_zone_pfns[MAX_NR_ZONES] = {0};
-
- #ifdef CONFIG_ZONE_DMA32
-- max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma_phys_limit);
-+ max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma32_phys_limit);
- #endif
- max_zone_pfns[ZONE_NORMAL] = max;
-
-@@ -200,16 +200,16 @@ static void __init zone_sizes_init(unsig
- {
- struct memblock_region *reg;
- unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
-- unsigned long max_dma = min;
-+ unsigned long max_dma32 = min;
-
- memset(zone_size, 0, sizeof(zone_size));
-
- /* 4GB maximum for 32-bit only capable devices */
- #ifdef CONFIG_ZONE_DMA32
-- max_dma = PFN_DOWN(arm64_dma_phys_limit);
-- zone_size[ZONE_DMA32] = max_dma - min;
-+ max_dma32 = PFN_DOWN(arm64_dma32_phys_limit);
-+ zone_size[ZONE_DMA32] = max_dma32 - min;
- #endif
-- zone_size[ZONE_NORMAL] = max - max_dma;
-+ zone_size[ZONE_NORMAL] = max - max_dma32;
-
- memcpy(zhole_size, zone_size, sizeof(zhole_size));
-
-@@ -221,14 +221,14 @@ static void __init zone_sizes_init(unsig
- continue;
-
- #ifdef CONFIG_ZONE_DMA32
-- if (start < max_dma) {
-- unsigned long dma_end = min(end, max_dma);
-+ if (start < max_dma32) {
-+ unsigned long dma_end = min(end, max_dma32);
- zhole_size[ZONE_DMA32] -= dma_end - start;
- }
- #endif
-- if (end > max_dma) {
-+ if (end > max_dma32) {
- unsigned long normal_end = min(end, max);
-- unsigned long normal_start = max(start, max_dma);
-+ unsigned long normal_start = max(start, max_dma32);
- zhole_size[ZONE_NORMAL] -= normal_end - normal_start;
- }
- }
-@@ -420,9 +420,9 @@ void __init arm64_memblock_init(void)
-
- /* 4GB maximum for 32-bit only capable devices */
- if (IS_ENABLED(CONFIG_ZONE_DMA32))
-- arm64_dma_phys_limit = max_zone_dma_phys();
-+ arm64_dma32_phys_limit = max_zone_dma32_phys();
- else
-- arm64_dma_phys_limit = PHYS_MASK + 1;
-+ arm64_dma32_phys_limit = PHYS_MASK + 1;
-
- reserve_crashkernel();
-
-@@ -430,7 +430,7 @@ void __init arm64_memblock_init(void)
-
- high_memory = __va(memblock_end_of_DRAM() - 1) + 1;
-
-- dma_contiguous_reserve(arm64_dma_phys_limit);
-+ dma_contiguous_reserve(arm64_dma32_phys_limit);
- }
-
- void __init bootmem_init(void)
-@@ -534,7 +534,7 @@ static void __init free_unused_memmap(vo
- void __init mem_init(void)
- {
- if (swiotlb_force == SWIOTLB_FORCE ||
-- max_pfn > (arm64_dma_phys_limit >> PAGE_SHIFT))
-+ max_pfn > (arm64_dma32_phys_limit >> PAGE_SHIFT))
- swiotlb_init(1);
- else
- swiotlb_force = SWIOTLB_NO_FORCE;
--- /dev/null
+From 1fb65f4bc30fbadd0c89521985ff8142693c9631 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Wed, 11 Sep 2019 20:25:45 +0200
+Subject: [PATCH] arm64: use both ZONE_DMA and ZONE_DMA32
+
+commit 1a8e1cef7603e218339ac63cb3178b25554524e5 upstream.
+
+So far all arm64 devices have supported 32 bit DMA masks for their
+peripherals. This is not true anymore for the Raspberry Pi 4 as most of
+it's peripherals can only address the first GB of memory on a total of
+up to 4 GB.
+
+This goes against ZONE_DMA32's intent, as it's expected for ZONE_DMA32
+to be addressable with a 32 bit mask. So it was decided to re-introduce
+ZONE_DMA in arm64.
+
+ZONE_DMA will contain the lower 1G of memory, which is currently the
+memory area addressable by any peripheral on an arm64 device.
+ZONE_DMA32 will contain the rest of the 32 bit addressable memory.
+
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+---
+ arch/arm64/Kconfig | 4 +++
+ arch/arm64/include/asm/page.h | 2 ++
+ arch/arm64/mm/init.c | 54 +++++++++++++++++++++++++----------
+ 3 files changed, 45 insertions(+), 15 deletions(-)
+
+--- a/arch/arm64/Kconfig
++++ b/arch/arm64/Kconfig
+@@ -267,6 +267,10 @@ config GENERIC_CSUM
+ config GENERIC_CALIBRATE_DELAY
+ def_bool y
+
++config ZONE_DMA
++ bool "Support DMA zone" if EXPERT
++ default y
++
+ config ZONE_DMA32
+ bool "Support DMA32 zone" if EXPERT
+ default y
+--- a/arch/arm64/include/asm/page.h
++++ b/arch/arm64/include/asm/page.h
+@@ -38,4 +38,6 @@ extern int pfn_valid(unsigned long);
+
+ #include <asm-generic/getorder.h>
+
++#define ARCH_ZONE_DMA_BITS 30
++
+ #endif
+--- a/arch/arm64/mm/init.c
++++ b/arch/arm64/mm/init.c
+@@ -56,6 +56,13 @@ EXPORT_SYMBOL(physvirt_offset);
+ struct page *vmemmap __ro_after_init;
+ EXPORT_SYMBOL(vmemmap);
+
++/*
++ * We create both ZONE_DMA and ZONE_DMA32. ZONE_DMA covers the first 1G of
++ * memory as some devices, namely the Raspberry Pi 4, have peripherals with
++ * this limited view of the memory. ZONE_DMA32 will cover the rest of the 32
++ * bit addressable memory area.
++ */
++phys_addr_t arm64_dma_phys_limit __ro_after_init;
+ phys_addr_t arm64_dma32_phys_limit __ro_after_init;
+
+ #ifdef CONFIG_KEXEC_CORE
+@@ -169,15 +176,16 @@ static void __init reserve_elfcorehdr(vo
+ {
+ }
+ #endif /* CONFIG_CRASH_DUMP */
++
+ /*
+- * Return the maximum physical address for ZONE_DMA32 (DMA_BIT_MASK(32)). It
+- * currently assumes that for memory starting above 4G, 32-bit devices will
+- * use a DMA offset.
++ * Return the maximum physical address for a zone with a given address size
++ * limit. It currently assumes that for memory starting above 4G, 32-bit
++ * devices will use a DMA offset.
+ */
+-static phys_addr_t __init max_zone_dma32_phys(void)
++static phys_addr_t __init max_zone_phys(unsigned int zone_bits)
+ {
+- phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, 32);
+- return min(offset + (1ULL << 32), memblock_end_of_DRAM());
++ phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, zone_bits);
++ return min(offset + (1ULL << zone_bits), memblock_end_of_DRAM());
+ }
+
+ #ifdef CONFIG_NUMA
+@@ -186,6 +194,9 @@ static void __init zone_sizes_init(unsig
+ {
+ unsigned long max_zone_pfns[MAX_NR_ZONES] = {0};
+
++#ifdef CONFIG_ZONE_DMA
++ max_zone_pfns[ZONE_DMA] = PFN_DOWN(arm64_dma_phys_limit);
++#endif
+ #ifdef CONFIG_ZONE_DMA32
+ max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma32_phys_limit);
+ #endif
+@@ -201,13 +212,18 @@ static void __init zone_sizes_init(unsig
+ struct memblock_region *reg;
+ unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
+ unsigned long max_dma32 = min;
++ unsigned long max_dma = min;
+
+ memset(zone_size, 0, sizeof(zone_size));
+
+- /* 4GB maximum for 32-bit only capable devices */
++#ifdef CONFIG_ZONE_DMA
++ max_dma = PFN_DOWN(arm64_dma_phys_limit);
++ zone_size[ZONE_DMA] = max_dma - min;
++ max_dma32 = max_dma;
++#endif
+ #ifdef CONFIG_ZONE_DMA32
+ max_dma32 = PFN_DOWN(arm64_dma32_phys_limit);
+- zone_size[ZONE_DMA32] = max_dma32 - min;
++ zone_size[ZONE_DMA32] = max_dma32 - max_dma;
+ #endif
+ zone_size[ZONE_NORMAL] = max - max_dma32;
+
+@@ -219,11 +235,17 @@ static void __init zone_sizes_init(unsig
+
+ if (start >= max)
+ continue;
+-
++#ifdef CONFIG_ZONE_DMA
++ if (start < max_dma) {
++ unsigned long dma_end = min_not_zero(end, max_dma);
++ zhole_size[ZONE_DMA] -= dma_end - start;
++ }
++#endif
+ #ifdef CONFIG_ZONE_DMA32
+ if (start < max_dma32) {
+- unsigned long dma_end = min(end, max_dma32);
+- zhole_size[ZONE_DMA32] -= dma_end - start;
++ unsigned long dma32_end = min(end, max_dma32);
++ unsigned long dma32_start = max(start, max_dma);
++ zhole_size[ZONE_DMA32] -= dma32_end - dma32_start;
+ }
+ #endif
+ if (end > max_dma32) {
+@@ -418,9 +440,11 @@ void __init arm64_memblock_init(void)
+
+ early_init_fdt_scan_reserved_mem();
+
+- /* 4GB maximum for 32-bit only capable devices */
++ if (IS_ENABLED(CONFIG_ZONE_DMA))
++ arm64_dma_phys_limit = max_zone_phys(ARCH_ZONE_DMA_BITS);
++
+ if (IS_ENABLED(CONFIG_ZONE_DMA32))
+- arm64_dma32_phys_limit = max_zone_dma32_phys();
++ arm64_dma32_phys_limit = max_zone_phys(32);
+ else
+ arm64_dma32_phys_limit = PHYS_MASK + 1;
+
+@@ -430,7 +454,7 @@ void __init arm64_memblock_init(void)
+
+ high_memory = __va(memblock_end_of_DRAM() - 1) + 1;
+
+- dma_contiguous_reserve(arm64_dma32_phys_limit);
++ dma_contiguous_reserve(arm64_dma_phys_limit ? : arm64_dma32_phys_limit);
+ }
+
+ void __init bootmem_init(void)
+@@ -534,7 +558,7 @@ static void __init free_unused_memmap(vo
+ void __init mem_init(void)
+ {
+ if (swiotlb_force == SWIOTLB_FORCE ||
+- max_pfn > (arm64_dma32_phys_limit >> PAGE_SHIFT))
++ max_pfn > PFN_DOWN(arm64_dma_phys_limit ? : arm64_dma32_phys_limit))
+ swiotlb_init(1);
+ else
+ swiotlb_force = SWIOTLB_NO_FORCE;
+++ /dev/null
-From 1fb65f4bc30fbadd0c89521985ff8142693c9631 Mon Sep 17 00:00:00 2001
-From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Date: Wed, 11 Sep 2019 20:25:45 +0200
-Subject: [PATCH] arm64: use both ZONE_DMA and ZONE_DMA32
-
-commit 1a8e1cef7603e218339ac63cb3178b25554524e5 upstream.
-
-So far all arm64 devices have supported 32 bit DMA masks for their
-peripherals. This is not true anymore for the Raspberry Pi 4 as most of
-it's peripherals can only address the first GB of memory on a total of
-up to 4 GB.
-
-This goes against ZONE_DMA32's intent, as it's expected for ZONE_DMA32
-to be addressable with a 32 bit mask. So it was decided to re-introduce
-ZONE_DMA in arm64.
-
-ZONE_DMA will contain the lower 1G of memory, which is currently the
-memory area addressable by any peripheral on an arm64 device.
-ZONE_DMA32 will contain the rest of the 32 bit addressable memory.
-
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
-Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
----
- arch/arm64/Kconfig | 4 +++
- arch/arm64/include/asm/page.h | 2 ++
- arch/arm64/mm/init.c | 54 +++++++++++++++++++++++++----------
- 3 files changed, 45 insertions(+), 15 deletions(-)
-
---- a/arch/arm64/Kconfig
-+++ b/arch/arm64/Kconfig
-@@ -267,6 +267,10 @@ config GENERIC_CSUM
- config GENERIC_CALIBRATE_DELAY
- def_bool y
-
-+config ZONE_DMA
-+ bool "Support DMA zone" if EXPERT
-+ default y
-+
- config ZONE_DMA32
- bool "Support DMA32 zone" if EXPERT
- default y
---- a/arch/arm64/include/asm/page.h
-+++ b/arch/arm64/include/asm/page.h
-@@ -38,4 +38,6 @@ extern int pfn_valid(unsigned long);
-
- #include <asm-generic/getorder.h>
-
-+#define ARCH_ZONE_DMA_BITS 30
-+
- #endif
---- a/arch/arm64/mm/init.c
-+++ b/arch/arm64/mm/init.c
-@@ -56,6 +56,13 @@ EXPORT_SYMBOL(physvirt_offset);
- struct page *vmemmap __ro_after_init;
- EXPORT_SYMBOL(vmemmap);
-
-+/*
-+ * We create both ZONE_DMA and ZONE_DMA32. ZONE_DMA covers the first 1G of
-+ * memory as some devices, namely the Raspberry Pi 4, have peripherals with
-+ * this limited view of the memory. ZONE_DMA32 will cover the rest of the 32
-+ * bit addressable memory area.
-+ */
-+phys_addr_t arm64_dma_phys_limit __ro_after_init;
- phys_addr_t arm64_dma32_phys_limit __ro_after_init;
-
- #ifdef CONFIG_KEXEC_CORE
-@@ -169,15 +176,16 @@ static void __init reserve_elfcorehdr(vo
- {
- }
- #endif /* CONFIG_CRASH_DUMP */
-+
- /*
-- * Return the maximum physical address for ZONE_DMA32 (DMA_BIT_MASK(32)). It
-- * currently assumes that for memory starting above 4G, 32-bit devices will
-- * use a DMA offset.
-+ * Return the maximum physical address for a zone with a given address size
-+ * limit. It currently assumes that for memory starting above 4G, 32-bit
-+ * devices will use a DMA offset.
- */
--static phys_addr_t __init max_zone_dma32_phys(void)
-+static phys_addr_t __init max_zone_phys(unsigned int zone_bits)
- {
-- phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, 32);
-- return min(offset + (1ULL << 32), memblock_end_of_DRAM());
-+ phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, zone_bits);
-+ return min(offset + (1ULL << zone_bits), memblock_end_of_DRAM());
- }
-
- #ifdef CONFIG_NUMA
-@@ -186,6 +194,9 @@ static void __init zone_sizes_init(unsig
- {
- unsigned long max_zone_pfns[MAX_NR_ZONES] = {0};
-
-+#ifdef CONFIG_ZONE_DMA
-+ max_zone_pfns[ZONE_DMA] = PFN_DOWN(arm64_dma_phys_limit);
-+#endif
- #ifdef CONFIG_ZONE_DMA32
- max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma32_phys_limit);
- #endif
-@@ -201,13 +212,18 @@ static void __init zone_sizes_init(unsig
- struct memblock_region *reg;
- unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
- unsigned long max_dma32 = min;
-+ unsigned long max_dma = min;
-
- memset(zone_size, 0, sizeof(zone_size));
-
-- /* 4GB maximum for 32-bit only capable devices */
-+#ifdef CONFIG_ZONE_DMA
-+ max_dma = PFN_DOWN(arm64_dma_phys_limit);
-+ zone_size[ZONE_DMA] = max_dma - min;
-+ max_dma32 = max_dma;
-+#endif
- #ifdef CONFIG_ZONE_DMA32
- max_dma32 = PFN_DOWN(arm64_dma32_phys_limit);
-- zone_size[ZONE_DMA32] = max_dma32 - min;
-+ zone_size[ZONE_DMA32] = max_dma32 - max_dma;
- #endif
- zone_size[ZONE_NORMAL] = max - max_dma32;
-
-@@ -219,11 +235,17 @@ static void __init zone_sizes_init(unsig
-
- if (start >= max)
- continue;
--
-+#ifdef CONFIG_ZONE_DMA
-+ if (start < max_dma) {
-+ unsigned long dma_end = min_not_zero(end, max_dma);
-+ zhole_size[ZONE_DMA] -= dma_end - start;
-+ }
-+#endif
- #ifdef CONFIG_ZONE_DMA32
- if (start < max_dma32) {
-- unsigned long dma_end = min(end, max_dma32);
-- zhole_size[ZONE_DMA32] -= dma_end - start;
-+ unsigned long dma32_end = min(end, max_dma32);
-+ unsigned long dma32_start = max(start, max_dma);
-+ zhole_size[ZONE_DMA32] -= dma32_end - dma32_start;
- }
- #endif
- if (end > max_dma32) {
-@@ -418,9 +440,11 @@ void __init arm64_memblock_init(void)
-
- early_init_fdt_scan_reserved_mem();
-
-- /* 4GB maximum for 32-bit only capable devices */
-+ if (IS_ENABLED(CONFIG_ZONE_DMA))
-+ arm64_dma_phys_limit = max_zone_phys(ARCH_ZONE_DMA_BITS);
-+
- if (IS_ENABLED(CONFIG_ZONE_DMA32))
-- arm64_dma32_phys_limit = max_zone_dma32_phys();
-+ arm64_dma32_phys_limit = max_zone_phys(32);
- else
- arm64_dma32_phys_limit = PHYS_MASK + 1;
-
-@@ -430,7 +454,7 @@ void __init arm64_memblock_init(void)
-
- high_memory = __va(memblock_end_of_DRAM() - 1) + 1;
-
-- dma_contiguous_reserve(arm64_dma32_phys_limit);
-+ dma_contiguous_reserve(arm64_dma_phys_limit ? : arm64_dma32_phys_limit);
- }
-
- void __init bootmem_init(void)
-@@ -534,7 +558,7 @@ static void __init free_unused_memmap(vo
- void __init mem_init(void)
- {
- if (swiotlb_force == SWIOTLB_FORCE ||
-- max_pfn > (arm64_dma32_phys_limit >> PAGE_SHIFT))
-+ max_pfn > PFN_DOWN(arm64_dma_phys_limit ? : arm64_dma32_phys_limit))
- swiotlb_init(1);
- else
- swiotlb_force = SWIOTLB_NO_FORCE;
--- /dev/null
+From 1c108eaeae73a504ac1b2d882bc1fefb91eecf17 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Wed, 11 Sep 2019 20:25:46 +0200
+Subject: [PATCH] mm: refresh ZONE_DMA and ZONE_DMA32 comments in 'enum
+ zone_type'
+
+commit 734f9246e791d8da278957b2c326d7709b2a97c0 upstream.
+
+These zones usage has evolved with time and the comments were outdated.
+This joins both ZONE_DMA and ZONE_DMA32 explanation and gives up to date
+examples on how they are used on different architectures.
+
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+---
+ include/linux/mmzone.h | 45 ++++++++++++++++++++++++------------------
+ 1 file changed, 26 insertions(+), 19 deletions(-)
+
+--- a/include/linux/mmzone.h
++++ b/include/linux/mmzone.h
+@@ -358,33 +358,40 @@ struct per_cpu_nodestat {
+ #endif /* !__GENERATING_BOUNDS.H */
+
+ enum zone_type {
+-#ifdef CONFIG_ZONE_DMA
+ /*
+- * ZONE_DMA is used when there are devices that are not able
+- * to do DMA to all of addressable memory (ZONE_NORMAL). Then we
+- * carve out the portion of memory that is needed for these devices.
+- * The range is arch specific.
+- *
+- * Some examples
+- *
+- * Architecture Limit
+- * ---------------------------
+- * parisc, ia64, sparc <4G
+- * s390, powerpc <2G
+- * arm Various
+- * alpha Unlimited or 0-16MB.
++ * ZONE_DMA and ZONE_DMA32 are used when there are peripherals not able
++ * to DMA to all of the addressable memory (ZONE_NORMAL).
++ * On architectures where this area covers the whole 32 bit address
++ * space ZONE_DMA32 is used. ZONE_DMA is left for the ones with smaller
++ * DMA addressing constraints. This distinction is important as a 32bit
++ * DMA mask is assumed when ZONE_DMA32 is defined. Some 64-bit
++ * platforms may need both zones as they support peripherals with
++ * different DMA addressing limitations.
++ *
++ * Some examples:
++ *
++ * - i386 and x86_64 have a fixed 16M ZONE_DMA and ZONE_DMA32 for the
++ * rest of the lower 4G.
++ *
++ * - arm only uses ZONE_DMA, the size, up to 4G, may vary depending on
++ * the specific device.
++ *
++ * - arm64 has a fixed 1G ZONE_DMA and ZONE_DMA32 for the rest of the
++ * lower 4G.
++ *
++ * - powerpc only uses ZONE_DMA, the size, up to 2G, may vary
++ * depending on the specific device.
+ *
+- * i386, x86_64 and multiple other arches
+- * <16M.
++ * - s390 uses ZONE_DMA fixed to the lower 2G.
++ *
++ * - ia64 and riscv only use ZONE_DMA32.
++ *
++ * - parisc uses neither.
+ */
++#ifdef CONFIG_ZONE_DMA
+ ZONE_DMA,
+ #endif
+ #ifdef CONFIG_ZONE_DMA32
+- /*
+- * x86_64 needs two ZONE_DMAs because it supports devices that are
+- * only able to do DMA to the lower 16M but also 32 bit devices that
+- * can only do DMA areas below 4G.
+- */
+ ZONE_DMA32,
+ #endif
+ /*
+++ /dev/null
-From 1c108eaeae73a504ac1b2d882bc1fefb91eecf17 Mon Sep 17 00:00:00 2001
-From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Date: Wed, 11 Sep 2019 20:25:46 +0200
-Subject: [PATCH] mm: refresh ZONE_DMA and ZONE_DMA32 comments in 'enum
- zone_type'
-
-commit 734f9246e791d8da278957b2c326d7709b2a97c0 upstream.
-
-These zones usage has evolved with time and the comments were outdated.
-This joins both ZONE_DMA and ZONE_DMA32 explanation and gives up to date
-examples on how they are used on different architectures.
-
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Reviewed-by: Christoph Hellwig <hch@lst.de>
-Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
-Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
----
- include/linux/mmzone.h | 45 ++++++++++++++++++++++++------------------
- 1 file changed, 26 insertions(+), 19 deletions(-)
-
---- a/include/linux/mmzone.h
-+++ b/include/linux/mmzone.h
-@@ -358,33 +358,40 @@ struct per_cpu_nodestat {
- #endif /* !__GENERATING_BOUNDS.H */
-
- enum zone_type {
--#ifdef CONFIG_ZONE_DMA
- /*
-- * ZONE_DMA is used when there are devices that are not able
-- * to do DMA to all of addressable memory (ZONE_NORMAL). Then we
-- * carve out the portion of memory that is needed for these devices.
-- * The range is arch specific.
-- *
-- * Some examples
-- *
-- * Architecture Limit
-- * ---------------------------
-- * parisc, ia64, sparc <4G
-- * s390, powerpc <2G
-- * arm Various
-- * alpha Unlimited or 0-16MB.
-+ * ZONE_DMA and ZONE_DMA32 are used when there are peripherals not able
-+ * to DMA to all of the addressable memory (ZONE_NORMAL).
-+ * On architectures where this area covers the whole 32 bit address
-+ * space ZONE_DMA32 is used. ZONE_DMA is left for the ones with smaller
-+ * DMA addressing constraints. This distinction is important as a 32bit
-+ * DMA mask is assumed when ZONE_DMA32 is defined. Some 64-bit
-+ * platforms may need both zones as they support peripherals with
-+ * different DMA addressing limitations.
-+ *
-+ * Some examples:
-+ *
-+ * - i386 and x86_64 have a fixed 16M ZONE_DMA and ZONE_DMA32 for the
-+ * rest of the lower 4G.
-+ *
-+ * - arm only uses ZONE_DMA, the size, up to 4G, may vary depending on
-+ * the specific device.
-+ *
-+ * - arm64 has a fixed 1G ZONE_DMA and ZONE_DMA32 for the rest of the
-+ * lower 4G.
-+ *
-+ * - powerpc only uses ZONE_DMA, the size, up to 2G, may vary
-+ * depending on the specific device.
- *
-- * i386, x86_64 and multiple other arches
-- * <16M.
-+ * - s390 uses ZONE_DMA fixed to the lower 2G.
-+ *
-+ * - ia64 and riscv only use ZONE_DMA32.
-+ *
-+ * - parisc uses neither.
- */
-+#ifdef CONFIG_ZONE_DMA
- ZONE_DMA,
- #endif
- #ifdef CONFIG_ZONE_DMA32
-- /*
-- * x86_64 needs two ZONE_DMAs because it supports devices that are
-- * only able to do DMA to the lower 16M but also 32 bit devices that
-- * can only do DMA areas below 4G.
-- */
- ZONE_DMA32,
- #endif
- /*
--- /dev/null
+From dacb1a46835914b8c3862db15726bcc0a68af8f5 Mon Sep 17 00:00:00 2001
+From: Rob Herring <robh@kernel.org>
+Date: Mon, 28 Oct 2019 11:32:32 -0500
+Subject: [PATCH] resource: Add a resource_list_first_type helper
+
+commit 494f8b10d832456a96be4ee7317425f6936cabc8 upstream.
+
+A common pattern is looping over a resource_list just to get a matching
+entry with a specific type. Add resource_list_first_type() helper which
+implements this.
+
+Signed-off-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+---
+ include/linux/resource_ext.h | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+--- a/include/linux/resource_ext.h
++++ b/include/linux/resource_ext.h
+@@ -66,4 +66,16 @@ resource_list_destroy_entry(struct resou
+ #define resource_list_for_each_entry_safe(entry, tmp, list) \
+ list_for_each_entry_safe((entry), (tmp), (list), node)
+
++static inline struct resource_entry *
++resource_list_first_type(struct list_head *list, unsigned long type)
++{
++ struct resource_entry *entry;
++
++ resource_list_for_each_entry(entry, list) {
++ if (resource_type(entry->res) == type)
++ return entry;
++ }
++ return NULL;
++}
++
+ #endif /* _LINUX_RESOURCE_EXT_H */
--- /dev/null
+From 78b03f0aef9f67c4db700ba5dc56e2c8f562d181 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Mon, 14 Oct 2019 20:31:03 +0200
+Subject: [PATCH] dma/direct: turn ARCH_ZONE_DMA_BITS into a variable
+
+commit 8b5369ea580964dbc982781bfb9fb93459fc5e8d upstream.
+
+Some architectures, notably ARM, are interested in tweaking this
+depending on their runtime DMA addressing limitations.
+
+Acked-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+---
+ arch/arm64/include/asm/page.h | 2 --
+ arch/arm64/mm/init.c | 9 +++++++--
+ arch/powerpc/include/asm/page.h | 9 ---------
+ arch/powerpc/mm/mem.c | 20 +++++++++++++++-----
+ arch/s390/include/asm/page.h | 2 --
+ arch/s390/mm/init.c | 1 +
+ include/linux/dma-direct.h | 2 ++
+ kernel/dma/direct.c | 13 ++++++-------
+ 8 files changed, 31 insertions(+), 27 deletions(-)
+
+--- a/arch/arm64/include/asm/page.h
++++ b/arch/arm64/include/asm/page.h
+@@ -38,6 +38,4 @@ extern int pfn_valid(unsigned long);
+
+ #include <asm-generic/getorder.h>
+
+-#define ARCH_ZONE_DMA_BITS 30
+-
+ #endif
+--- a/arch/arm64/mm/init.c
++++ b/arch/arm64/mm/init.c
+@@ -20,6 +20,7 @@
+ #include <linux/sort.h>
+ #include <linux/of.h>
+ #include <linux/of_fdt.h>
++#include <linux/dma-direct.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/dma-contiguous.h>
+ #include <linux/efi.h>
+@@ -41,6 +42,8 @@
+ #include <asm/tlb.h>
+ #include <asm/alternative.h>
+
++#define ARM64_ZONE_DMA_BITS 30
++
+ /*
+ * We need to be able to catch inadvertent references to memstart_addr
+ * that occur (potentially in generic code) before arm64_memblock_init()
+@@ -440,8 +443,10 @@ void __init arm64_memblock_init(void)
+
+ early_init_fdt_scan_reserved_mem();
+
+- if (IS_ENABLED(CONFIG_ZONE_DMA))
+- arm64_dma_phys_limit = max_zone_phys(ARCH_ZONE_DMA_BITS);
++ if (IS_ENABLED(CONFIG_ZONE_DMA)) {
++ zone_dma_bits = ARM64_ZONE_DMA_BITS;
++ arm64_dma_phys_limit = max_zone_phys(ARM64_ZONE_DMA_BITS);
++ }
+
+ if (IS_ENABLED(CONFIG_ZONE_DMA32))
+ arm64_dma32_phys_limit = max_zone_phys(32);
+--- a/arch/powerpc/include/asm/page.h
++++ b/arch/powerpc/include/asm/page.h
+@@ -334,13 +334,4 @@ struct vm_area_struct;
+ #endif /* __ASSEMBLY__ */
+ #include <asm/slice.h>
+
+-/*
+- * Allow 30-bit DMA for very limited Broadcom wifi chips on many powerbooks.
+- */
+-#ifdef CONFIG_PPC32
+-#define ARCH_ZONE_DMA_BITS 30
+-#else
+-#define ARCH_ZONE_DMA_BITS 31
+-#endif
+-
+ #endif /* _ASM_POWERPC_PAGE_H */
+--- a/arch/powerpc/mm/mem.c
++++ b/arch/powerpc/mm/mem.c
+@@ -31,6 +31,7 @@
+ #include <linux/slab.h>
+ #include <linux/vmalloc.h>
+ #include <linux/memremap.h>
++#include <linux/dma-direct.h>
+
+ #include <asm/pgalloc.h>
+ #include <asm/prom.h>
+@@ -223,10 +224,10 @@ static int __init mark_nonram_nosave(voi
+ * everything else. GFP_DMA32 page allocations automatically fall back to
+ * ZONE_DMA.
+ *
+- * By using 31-bit unconditionally, we can exploit ARCH_ZONE_DMA_BITS to
+- * inform the generic DMA mapping code. 32-bit only devices (if not handled
+- * by an IOMMU anyway) will take a first dip into ZONE_NORMAL and get
+- * otherwise served by ZONE_DMA.
++ * By using 31-bit unconditionally, we can exploit zone_dma_bits to inform the
++ * generic DMA mapping code. 32-bit only devices (if not handled by an IOMMU
++ * anyway) will take a first dip into ZONE_NORMAL and get otherwise served by
++ * ZONE_DMA.
+ */
+ static unsigned long max_zone_pfns[MAX_NR_ZONES];
+
+@@ -259,9 +260,18 @@ void __init paging_init(void)
+ printk(KERN_DEBUG "Memory hole size: %ldMB\n",
+ (long int)((top_of_ram - total_ram) >> 20));
+
++ /*
++ * Allow 30-bit DMA for very limited Broadcom wifi chips on many
++ * powerbooks.
++ */
++ if (IS_ENABLED(CONFIG_PPC32))
++ zone_dma_bits = 30;
++ else
++ zone_dma_bits = 31;
++
+ #ifdef CONFIG_ZONE_DMA
+ max_zone_pfns[ZONE_DMA] = min(max_low_pfn,
+- 1UL << (ARCH_ZONE_DMA_BITS - PAGE_SHIFT));
++ 1UL << (zone_dma_bits - PAGE_SHIFT));
+ #endif
+ max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
+ #ifdef CONFIG_HIGHMEM
+--- a/arch/s390/include/asm/page.h
++++ b/arch/s390/include/asm/page.h
+@@ -179,8 +179,6 @@ static inline int devmem_is_allowed(unsi
+ #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | \
+ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
+
+-#define ARCH_ZONE_DMA_BITS 31
+-
+ #include <asm-generic/memory_model.h>
+ #include <asm-generic/getorder.h>
+
+--- a/arch/s390/mm/init.c
++++ b/arch/s390/mm/init.c
+@@ -118,6 +118,7 @@ void __init paging_init(void)
+
+ sparse_memory_present_with_active_regions(MAX_NUMNODES);
+ sparse_init();
++ zone_dma_bits = 31;
+ memset(max_zone_pfns, 0, sizeof(max_zone_pfns));
+ max_zone_pfns[ZONE_DMA] = PFN_DOWN(MAX_DMA_ADDRESS);
+ max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
+--- a/include/linux/dma-direct.h
++++ b/include/linux/dma-direct.h
+@@ -8,6 +8,8 @@
+
+ static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr);
+
++extern unsigned int zone_dma_bits;
++
+ #ifdef CONFIG_ARCH_HAS_PHYS_TO_DMA
+ #include <asm/dma-direct.h>
+ #else
+--- a/kernel/dma/direct.c
++++ b/kernel/dma/direct.c
+@@ -16,12 +16,11 @@
+ #include <linux/swiotlb.h>
+
+ /*
+- * Most architectures use ZONE_DMA for the first 16 Megabytes, but
+- * some use it for entirely different regions:
++ * Most architectures use ZONE_DMA for the first 16 Megabytes, but some use it
++ * it for entirely different regions. In that case the arch code needs to
++ * override the variable below for dma-direct to work properly.
+ */
+-#ifndef ARCH_ZONE_DMA_BITS
+-#define ARCH_ZONE_DMA_BITS 24
+-#endif
++unsigned int zone_dma_bits __ro_after_init = 24;
+
+ static void report_addr(struct device *dev, dma_addr_t dma_addr, size_t size)
+ {
+@@ -69,7 +68,7 @@ static gfp_t __dma_direct_optimal_gfp_ma
+ * Note that GFP_DMA32 and GFP_DMA are no ops without the corresponding
+ * zones.
+ */
+- if (*phys_mask <= DMA_BIT_MASK(ARCH_ZONE_DMA_BITS))
++ if (*phys_mask <= DMA_BIT_MASK(zone_dma_bits))
+ return GFP_DMA;
+ if (*phys_mask <= DMA_BIT_MASK(32))
+ return GFP_DMA32;
+@@ -395,7 +394,7 @@ int dma_direct_supported(struct device *
+ u64 min_mask;
+
+ if (IS_ENABLED(CONFIG_ZONE_DMA))
+- min_mask = DMA_BIT_MASK(ARCH_ZONE_DMA_BITS);
++ min_mask = DMA_BIT_MASK(zone_dma_bits);
+ else
+ min_mask = DMA_BIT_MASK(30);
+
+++ /dev/null
-From dacb1a46835914b8c3862db15726bcc0a68af8f5 Mon Sep 17 00:00:00 2001
-From: Rob Herring <robh@kernel.org>
-Date: Mon, 28 Oct 2019 11:32:32 -0500
-Subject: [PATCH] resource: Add a resource_list_first_type helper
-
-commit 494f8b10d832456a96be4ee7317425f6936cabc8 upstream.
-
-A common pattern is looping over a resource_list just to get a matching
-entry with a specific type. Add resource_list_first_type() helper which
-implements this.
-
-Signed-off-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
----
- include/linux/resource_ext.h | 12 ++++++++++++
- 1 file changed, 12 insertions(+)
-
---- a/include/linux/resource_ext.h
-+++ b/include/linux/resource_ext.h
-@@ -66,4 +66,16 @@ resource_list_destroy_entry(struct resou
- #define resource_list_for_each_entry_safe(entry, tmp, list) \
- list_for_each_entry_safe((entry), (tmp), (list), node)
-
-+static inline struct resource_entry *
-+resource_list_first_type(struct list_head *list, unsigned long type)
-+{
-+ struct resource_entry *entry;
-+
-+ resource_list_for_each_entry(entry, list) {
-+ if (resource_type(entry->res) == type)
-+ return entry;
-+ }
-+ return NULL;
-+}
-+
- #endif /* _LINUX_RESOURCE_EXT_H */
+++ /dev/null
-From 78b03f0aef9f67c4db700ba5dc56e2c8f562d181 Mon Sep 17 00:00:00 2001
-From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Date: Mon, 14 Oct 2019 20:31:03 +0200
-Subject: [PATCH] dma/direct: turn ARCH_ZONE_DMA_BITS into a variable
-
-commit 8b5369ea580964dbc982781bfb9fb93459fc5e8d upstream.
-
-Some architectures, notably ARM, are interested in tweaking this
-depending on their runtime DMA addressing limitations.
-
-Acked-by: Christoph Hellwig <hch@lst.de>
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
----
- arch/arm64/include/asm/page.h | 2 --
- arch/arm64/mm/init.c | 9 +++++++--
- arch/powerpc/include/asm/page.h | 9 ---------
- arch/powerpc/mm/mem.c | 20 +++++++++++++++-----
- arch/s390/include/asm/page.h | 2 --
- arch/s390/mm/init.c | 1 +
- include/linux/dma-direct.h | 2 ++
- kernel/dma/direct.c | 13 ++++++-------
- 8 files changed, 31 insertions(+), 27 deletions(-)
-
---- a/arch/arm64/include/asm/page.h
-+++ b/arch/arm64/include/asm/page.h
-@@ -38,6 +38,4 @@ extern int pfn_valid(unsigned long);
-
- #include <asm-generic/getorder.h>
-
--#define ARCH_ZONE_DMA_BITS 30
--
- #endif
---- a/arch/arm64/mm/init.c
-+++ b/arch/arm64/mm/init.c
-@@ -20,6 +20,7 @@
- #include <linux/sort.h>
- #include <linux/of.h>
- #include <linux/of_fdt.h>
-+#include <linux/dma-direct.h>
- #include <linux/dma-mapping.h>
- #include <linux/dma-contiguous.h>
- #include <linux/efi.h>
-@@ -41,6 +42,8 @@
- #include <asm/tlb.h>
- #include <asm/alternative.h>
-
-+#define ARM64_ZONE_DMA_BITS 30
-+
- /*
- * We need to be able to catch inadvertent references to memstart_addr
- * that occur (potentially in generic code) before arm64_memblock_init()
-@@ -440,8 +443,10 @@ void __init arm64_memblock_init(void)
-
- early_init_fdt_scan_reserved_mem();
-
-- if (IS_ENABLED(CONFIG_ZONE_DMA))
-- arm64_dma_phys_limit = max_zone_phys(ARCH_ZONE_DMA_BITS);
-+ if (IS_ENABLED(CONFIG_ZONE_DMA)) {
-+ zone_dma_bits = ARM64_ZONE_DMA_BITS;
-+ arm64_dma_phys_limit = max_zone_phys(ARM64_ZONE_DMA_BITS);
-+ }
-
- if (IS_ENABLED(CONFIG_ZONE_DMA32))
- arm64_dma32_phys_limit = max_zone_phys(32);
---- a/arch/powerpc/include/asm/page.h
-+++ b/arch/powerpc/include/asm/page.h
-@@ -334,13 +334,4 @@ struct vm_area_struct;
- #endif /* __ASSEMBLY__ */
- #include <asm/slice.h>
-
--/*
-- * Allow 30-bit DMA for very limited Broadcom wifi chips on many powerbooks.
-- */
--#ifdef CONFIG_PPC32
--#define ARCH_ZONE_DMA_BITS 30
--#else
--#define ARCH_ZONE_DMA_BITS 31
--#endif
--
- #endif /* _ASM_POWERPC_PAGE_H */
---- a/arch/powerpc/mm/mem.c
-+++ b/arch/powerpc/mm/mem.c
-@@ -31,6 +31,7 @@
- #include <linux/slab.h>
- #include <linux/vmalloc.h>
- #include <linux/memremap.h>
-+#include <linux/dma-direct.h>
-
- #include <asm/pgalloc.h>
- #include <asm/prom.h>
-@@ -223,10 +224,10 @@ static int __init mark_nonram_nosave(voi
- * everything else. GFP_DMA32 page allocations automatically fall back to
- * ZONE_DMA.
- *
-- * By using 31-bit unconditionally, we can exploit ARCH_ZONE_DMA_BITS to
-- * inform the generic DMA mapping code. 32-bit only devices (if not handled
-- * by an IOMMU anyway) will take a first dip into ZONE_NORMAL and get
-- * otherwise served by ZONE_DMA.
-+ * By using 31-bit unconditionally, we can exploit zone_dma_bits to inform the
-+ * generic DMA mapping code. 32-bit only devices (if not handled by an IOMMU
-+ * anyway) will take a first dip into ZONE_NORMAL and get otherwise served by
-+ * ZONE_DMA.
- */
- static unsigned long max_zone_pfns[MAX_NR_ZONES];
-
-@@ -259,9 +260,18 @@ void __init paging_init(void)
- printk(KERN_DEBUG "Memory hole size: %ldMB\n",
- (long int)((top_of_ram - total_ram) >> 20));
-
-+ /*
-+ * Allow 30-bit DMA for very limited Broadcom wifi chips on many
-+ * powerbooks.
-+ */
-+ if (IS_ENABLED(CONFIG_PPC32))
-+ zone_dma_bits = 30;
-+ else
-+ zone_dma_bits = 31;
-+
- #ifdef CONFIG_ZONE_DMA
- max_zone_pfns[ZONE_DMA] = min(max_low_pfn,
-- 1UL << (ARCH_ZONE_DMA_BITS - PAGE_SHIFT));
-+ 1UL << (zone_dma_bits - PAGE_SHIFT));
- #endif
- max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
- #ifdef CONFIG_HIGHMEM
---- a/arch/s390/include/asm/page.h
-+++ b/arch/s390/include/asm/page.h
-@@ -179,8 +179,6 @@ static inline int devmem_is_allowed(unsi
- #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | \
- VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
-
--#define ARCH_ZONE_DMA_BITS 31
--
- #include <asm-generic/memory_model.h>
- #include <asm-generic/getorder.h>
-
---- a/arch/s390/mm/init.c
-+++ b/arch/s390/mm/init.c
-@@ -118,6 +118,7 @@ void __init paging_init(void)
-
- sparse_memory_present_with_active_regions(MAX_NUMNODES);
- sparse_init();
-+ zone_dma_bits = 31;
- memset(max_zone_pfns, 0, sizeof(max_zone_pfns));
- max_zone_pfns[ZONE_DMA] = PFN_DOWN(MAX_DMA_ADDRESS);
- max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
---- a/include/linux/dma-direct.h
-+++ b/include/linux/dma-direct.h
-@@ -8,6 +8,8 @@
-
- static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr);
-
-+extern unsigned int zone_dma_bits;
-+
- #ifdef CONFIG_ARCH_HAS_PHYS_TO_DMA
- #include <asm/dma-direct.h>
- #else
---- a/kernel/dma/direct.c
-+++ b/kernel/dma/direct.c
-@@ -16,12 +16,11 @@
- #include <linux/swiotlb.h>
-
- /*
-- * Most architectures use ZONE_DMA for the first 16 Megabytes, but
-- * some use it for entirely different regions:
-+ * Most architectures use ZONE_DMA for the first 16 Megabytes, but some use it
-+ * it for entirely different regions. In that case the arch code needs to
-+ * override the variable below for dma-direct to work properly.
- */
--#ifndef ARCH_ZONE_DMA_BITS
--#define ARCH_ZONE_DMA_BITS 24
--#endif
-+unsigned int zone_dma_bits __ro_after_init = 24;
-
- static void report_addr(struct device *dev, dma_addr_t dma_addr, size_t size)
- {
-@@ -69,7 +68,7 @@ static gfp_t __dma_direct_optimal_gfp_ma
- * Note that GFP_DMA32 and GFP_DMA are no ops without the corresponding
- * zones.
- */
-- if (*phys_mask <= DMA_BIT_MASK(ARCH_ZONE_DMA_BITS))
-+ if (*phys_mask <= DMA_BIT_MASK(zone_dma_bits))
- return GFP_DMA;
- if (*phys_mask <= DMA_BIT_MASK(32))
- return GFP_DMA32;
-@@ -395,7 +394,7 @@ int dma_direct_supported(struct device *
- u64 min_mask;
-
- if (IS_ENABLED(CONFIG_ZONE_DMA))
-- min_mask = DMA_BIT_MASK(ARCH_ZONE_DMA_BITS);
-+ min_mask = DMA_BIT_MASK(zone_dma_bits);
- else
- min_mask = DMA_BIT_MASK(30);
-
--- /dev/null
+From 97a48106d1698038720495fdd49c491b283bf110 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Thu, 7 Nov 2019 16:06:45 +0100
+Subject: [PATCH] x86/PCI: sta2x11: use default DMA address translation
+
+commit e380a0394c36a3a878c858418d5dd7f5f195b6fc upstream.
+
+The devices found behind this PCIe chip have unusual DMA mapping
+constraints as there is an AMBA interconnect placed in between them and
+the different PCI endpoints. The offset between physical memory
+addresses and AMBA's view is provided by reading a PCI config register,
+which is saved and used whenever DMA mapping is needed.
+
+It turns out that this DMA setup can be represented by properly setting
+'dma_pfn_offset', 'dma_bus_mask' and 'dma_mask' during the PCI device
+enable fixup. And ultimately allows us to get rid of this device's
+custom DMA functions.
+
+Aside from the code deletion and DMA setup, sta2x11_pdev_to_mapping() is
+moved to avoid warnings whenever CONFIG_PM is not enabled.
+
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+---
+ arch/x86/Kconfig | 1 -
+ arch/x86/include/asm/device.h | 3 -
+ arch/x86/include/asm/dma-direct.h | 9 --
+ arch/x86/pci/sta2x11-fixup.c | 135 ++++++------------------------
+ 4 files changed, 26 insertions(+), 122 deletions(-)
+ delete mode 100644 arch/x86/include/asm/dma-direct.h
+
+--- a/arch/x86/Kconfig
++++ b/arch/x86/Kconfig
+@@ -708,7 +708,6 @@ config X86_SUPPORTS_MEMORY_FAILURE
+ config STA2X11
+ bool "STA2X11 Companion Chip Support"
+ depends on X86_32_NON_STANDARD && PCI
+- select ARCH_HAS_PHYS_TO_DMA
+ select SWIOTLB
+ select MFD_STA2X11
+ select GPIOLIB
+--- a/arch/x86/include/asm/device.h
++++ b/arch/x86/include/asm/device.h
+@@ -6,9 +6,6 @@ struct dev_archdata {
+ #if defined(CONFIG_INTEL_IOMMU) || defined(CONFIG_AMD_IOMMU)
+ void *iommu; /* hook for IOMMU specific extension */
+ #endif
+-#ifdef CONFIG_STA2X11
+- bool is_sta2x11;
+-#endif
+ };
+
+ #if defined(CONFIG_X86_DEV_DMA_OPS) && defined(CONFIG_PCI_DOMAINS)
+--- a/arch/x86/include/asm/dma-direct.h
++++ /dev/null
+@@ -1,9 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
+-#ifndef ASM_X86_DMA_DIRECT_H
+-#define ASM_X86_DMA_DIRECT_H 1
+-
+-bool dma_capable(struct device *dev, dma_addr_t addr, size_t size);
+-dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr);
+-phys_addr_t __dma_to_phys(struct device *dev, dma_addr_t daddr);
+-
+-#endif /* ASM_X86_DMA_DIRECT_H */
+--- a/arch/x86/pci/sta2x11-fixup.c
++++ b/arch/x86/pci/sta2x11-fixup.c
+@@ -30,7 +30,6 @@ struct sta2x11_ahb_regs { /* saved durin
+ };
+
+ struct sta2x11_mapping {
+- u32 amba_base;
+ int is_suspended;
+ struct sta2x11_ahb_regs regs[STA2X11_NR_FUNCS];
+ };
+@@ -92,18 +91,6 @@ static int sta2x11_pdev_to_ep(struct pci
+ return pdev->bus->number - instance->bus0;
+ }
+
+-static struct sta2x11_mapping *sta2x11_pdev_to_mapping(struct pci_dev *pdev)
+-{
+- struct sta2x11_instance *instance;
+- int ep;
+-
+- instance = sta2x11_pdev_to_instance(pdev);
+- if (!instance)
+- return NULL;
+- ep = sta2x11_pdev_to_ep(pdev);
+- return instance->map + ep;
+-}
+-
+ /* This is exported, as some devices need to access the MFD registers */
+ struct sta2x11_instance *sta2x11_get_instance(struct pci_dev *pdev)
+ {
+@@ -111,39 +98,6 @@ struct sta2x11_instance *sta2x11_get_ins
+ }
+ EXPORT_SYMBOL(sta2x11_get_instance);
+
+-
+-/**
+- * p2a - Translate physical address to STA2x11 AMBA address,
+- * used for DMA transfers to STA2x11
+- * @p: Physical address
+- * @pdev: PCI device (must be hosted within the connext)
+- */
+-static dma_addr_t p2a(dma_addr_t p, struct pci_dev *pdev)
+-{
+- struct sta2x11_mapping *map;
+- dma_addr_t a;
+-
+- map = sta2x11_pdev_to_mapping(pdev);
+- a = p + map->amba_base;
+- return a;
+-}
+-
+-/**
+- * a2p - Translate STA2x11 AMBA address to physical address
+- * used for DMA transfers from STA2x11
+- * @a: STA2x11 AMBA address
+- * @pdev: PCI device (must be hosted within the connext)
+- */
+-static dma_addr_t a2p(dma_addr_t a, struct pci_dev *pdev)
+-{
+- struct sta2x11_mapping *map;
+- dma_addr_t p;
+-
+- map = sta2x11_pdev_to_mapping(pdev);
+- p = a - map->amba_base;
+- return p;
+-}
+-
+ /* At setup time, we use our own ops if the device is a ConneXt one */
+ static void sta2x11_setup_pdev(struct pci_dev *pdev)
+ {
+@@ -151,9 +105,6 @@ static void sta2x11_setup_pdev(struct pc
+
+ if (!instance) /* either a sta2x11 bridge or another ST device */
+ return;
+- pci_set_consistent_dma_mask(pdev, STA2X11_AMBA_SIZE - 1);
+- pci_set_dma_mask(pdev, STA2X11_AMBA_SIZE - 1);
+- pdev->dev.archdata.is_sta2x11 = true;
+
+ /* We must enable all devices as master, for audio DMA to work */
+ pci_set_master(pdev);
+@@ -161,61 +112,6 @@ static void sta2x11_setup_pdev(struct pc
+ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, sta2x11_setup_pdev);
+
+ /*
+- * The following three functions are exported (used in swiotlb: FIXME)
+- */
+-/**
+- * dma_capable - Check if device can manage DMA transfers (FIXME: kill it)
+- * @dev: device for a PCI device
+- * @addr: DMA address
+- * @size: DMA size
+- */
+-bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
+-{
+- struct sta2x11_mapping *map;
+-
+- if (!dev->archdata.is_sta2x11) {
+- if (!dev->dma_mask)
+- return false;
+- return addr + size - 1 <= *dev->dma_mask;
+- }
+-
+- map = sta2x11_pdev_to_mapping(to_pci_dev(dev));
+-
+- if (!map || (addr < map->amba_base))
+- return false;
+- if (addr + size >= map->amba_base + STA2X11_AMBA_SIZE) {
+- return false;
+- }
+-
+- return true;
+-}
+-
+-/**
+- * __phys_to_dma - Return the DMA AMBA address used for this STA2x11 device
+- * @dev: device for a PCI device
+- * @paddr: Physical address
+- */
+-dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr)
+-{
+- if (!dev->archdata.is_sta2x11)
+- return paddr;
+- return p2a(paddr, to_pci_dev(dev));
+-}
+-
+-/**
+- * dma_to_phys - Return the physical address used for this STA2x11 DMA address
+- * @dev: device for a PCI device
+- * @daddr: STA2x11 AMBA DMA address
+- */
+-phys_addr_t __dma_to_phys(struct device *dev, dma_addr_t daddr)
+-{
+- if (!dev->archdata.is_sta2x11)
+- return daddr;
+- return a2p(daddr, to_pci_dev(dev));
+-}
+-
+-
+-/*
+ * At boot we must set up the mappings for the pcie-to-amba bridge.
+ * It involves device access, and the same happens at suspend/resume time
+ */
+@@ -234,12 +130,22 @@ phys_addr_t __dma_to_phys(struct device
+ /* At probe time, enable mapping for each endpoint, using the pdev */
+ static void sta2x11_map_ep(struct pci_dev *pdev)
+ {
+- struct sta2x11_mapping *map = sta2x11_pdev_to_mapping(pdev);
++ struct sta2x11_instance *instance = sta2x11_pdev_to_instance(pdev);
++ struct device *dev = &pdev->dev;
++ u32 amba_base, max_amba_addr;
+ int i;
+
+- if (!map)
++ if (!instance)
+ return;
+- pci_read_config_dword(pdev, AHB_BASE(0), &map->amba_base);
++
++ pci_read_config_dword(pdev, AHB_BASE(0), &amba_base);
++ max_amba_addr = amba_base + STA2X11_AMBA_SIZE - 1;
++
++ dev->dma_pfn_offset = PFN_DOWN(-amba_base);
++
++ dev->bus_dma_mask = max_amba_addr;
++ pci_set_consistent_dma_mask(pdev, max_amba_addr);
++ pci_set_dma_mask(pdev, max_amba_addr);
+
+ /* Configure AHB mapping */
+ pci_write_config_dword(pdev, AHB_PEXLBASE(0), 0);
+@@ -253,13 +159,24 @@ static void sta2x11_map_ep(struct pci_de
+
+ dev_info(&pdev->dev,
+ "sta2x11: Map EP %i: AMBA address %#8x-%#8x\n",
+- sta2x11_pdev_to_ep(pdev), map->amba_base,
+- map->amba_base + STA2X11_AMBA_SIZE - 1);
++ sta2x11_pdev_to_ep(pdev), amba_base, max_amba_addr);
+ }
+ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, sta2x11_map_ep);
+
+ #ifdef CONFIG_PM /* Some register values must be saved and restored */
+
++static struct sta2x11_mapping *sta2x11_pdev_to_mapping(struct pci_dev *pdev)
++{
++ struct sta2x11_instance *instance;
++ int ep;
++
++ instance = sta2x11_pdev_to_instance(pdev);
++ if (!instance)
++ return NULL;
++ ep = sta2x11_pdev_to_ep(pdev);
++ return instance->map + ep;
++}
++
+ static void suspend_mapping(struct pci_dev *pdev)
+ {
+ struct sta2x11_mapping *map = sta2x11_pdev_to_mapping(pdev);
--- /dev/null
+From 125a18144253e3a3f4bcad24484ee9b590dc47c6 Mon Sep 17 00:00:00 2001
+From: Rob Herring <robh@kernel.org>
+Date: Wed, 30 Oct 2019 17:30:57 -0500
+Subject: [PATCH] PCI: of: Add inbound resource parsing to helpers
+
+Extend devm_of_pci_get_host_bridge_resources() and
+pci_parse_request_of_pci_ranges() helpers to also parse the inbound
+addresses from DT 'dma-ranges' and populate a resource list with the
+translated addresses. This will help ensure 'dma-ranges' is always
+parsed in a consistent way.
+
+Tested-by: Srinath Mannam <srinath.mannam@broadcom.com>
+Tested-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com> # for AArdvark
+Signed-off-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+Reviewed-by: Srinath Mannam <srinath.mannam@broadcom.com>
+Reviewed-by: Andrew Murray <andrew.murray@arm.com>
+Acked-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
+Cc: Jingoo Han <jingoohan1@gmail.com>
+Cc: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
+Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+Cc: Bjorn Helgaas <bhelgaas@google.com>
+Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
+Cc: Will Deacon <will@kernel.org>
+Cc: Linus Walleij <linus.walleij@linaro.org>
+Cc: Toan Le <toan@os.amperecomputing.com>
+Cc: Ley Foon Tan <lftan@altera.com>
+Cc: Tom Joseph <tjoseph@cadence.com>
+Cc: Ray Jui <rjui@broadcom.com>
+Cc: Scott Branden <sbranden@broadcom.com>
+Cc: bcm-kernel-feedback-list@broadcom.com
+Cc: Ryder Lee <ryder.lee@mediatek.com>
+Cc: Karthikeyan Mitran <m.karthikeyan@mobiveil.co.in>
+Cc: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
+Cc: Simon Horman <horms@verge.net.au>
+Cc: Shawn Lin <shawn.lin@rock-chips.com>
+Cc: Heiko Stuebner <heiko@sntech.de>
+Cc: Michal Simek <michal.simek@xilinx.com>
+Cc: rfi@lists.rocketboards.org
+Cc: linux-mediatek@lists.infradead.org
+Cc: linux-renesas-soc@vger.kernel.org
+Cc: linux-rockchip@lists.infradead.org
+(cherry picked from commit 331f63457165a30c708280de2c77f1742c6351dc)
+---
+ .../pci/controller/dwc/pcie-designware-host.c | 8 +--
+ drivers/pci/controller/pci-aardvark.c | 3 +-
+ drivers/pci/controller/pci-ftpci100.c | 4 +-
+ drivers/pci/controller/pci-host-common.c | 2 +-
+ drivers/pci/controller/pci-v3-semi.c | 8 +--
+ drivers/pci/controller/pci-versatile.c | 3 +-
+ drivers/pci/controller/pci-xgene.c | 4 +-
+ drivers/pci/controller/pcie-altera.c | 5 +-
+ drivers/pci/controller/pcie-cadence-host.c | 2 +-
+ drivers/pci/controller/pcie-iproc-platform.c | 4 +-
+ drivers/pci/controller/pcie-mediatek.c | 4 +-
+ drivers/pci/controller/pcie-mobiveil.c | 4 +-
+ drivers/pci/controller/pcie-rcar.c | 3 +-
+ drivers/pci/controller/pcie-rockchip-host.c | 4 +-
+ drivers/pci/controller/pcie-xilinx-nwl.c | 4 +-
+ drivers/pci/controller/pcie-xilinx.c | 4 +-
+ drivers/pci/of.c | 61 ++++++++++++++++---
+ drivers/pci/pci.h | 8 ++-
+ include/linux/pci.h | 9 ++-
+ 19 files changed, 96 insertions(+), 48 deletions(-)
+
+--- a/drivers/pci/controller/dwc/pcie-designware-host.c
++++ b/drivers/pci/controller/dwc/pcie-designware-host.c
+@@ -343,12 +343,8 @@ int dw_pcie_host_init(struct pcie_port *
+ if (!bridge)
+ return -ENOMEM;
+
+- ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
+- &bridge->windows, &pp->io_base);
+- if (ret)
+- return ret;
+-
+- ret = devm_request_pci_bus_resources(dev, &bridge->windows);
++ ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++ &bridge->dma_ranges, NULL);
+ if (ret)
+ return ret;
+
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1023,7 +1023,8 @@ static int advk_pcie_probe(struct platfo
+ return ret;
+ }
+
+- ret = advk_pcie_parse_request_of_pci_ranges(pcie);
++ ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++ &bridge->dma_ranges, &bus);
+ if (ret) {
+ dev_err(dev, "Failed to parse resources\n");
+ return ret;
+--- a/drivers/pci/controller/pci-ftpci100.c
++++ b/drivers/pci/controller/pci-ftpci100.c
+@@ -480,8 +480,8 @@ static int faraday_pci_probe(struct plat
+ if (IS_ERR(p->base))
+ return PTR_ERR(p->base);
+
+- ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
+- &res, &io_base);
++ ret = pci_parse_request_of_pci_ranges(dev, &host->windows,
++ &host->dma_ranges, NULL);
+ if (ret)
+ return ret;
+
+--- a/drivers/pci/controller/pci-host-common.c
++++ b/drivers/pci/controller/pci-host-common.c
+@@ -27,7 +27,7 @@ static struct pci_config_window *gen_pci
+ struct pci_config_window *cfg;
+
+ /* Parse our PCI ranges and request their resources */
+- err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range);
++ err = pci_parse_request_of_pci_ranges(dev, resources, NULL, &bus_range);
+ if (err)
+ return ERR_PTR(err);
+
+--- a/drivers/pci/controller/pci-v3-semi.c
++++ b/drivers/pci/controller/pci-v3-semi.c
+@@ -793,12 +793,8 @@ static int v3_pci_probe(struct platform_
+ if (IS_ERR(v3->config_base))
+ return PTR_ERR(v3->config_base);
+
+- ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
+- &io_base);
+- if (ret)
+- return ret;
+-
+- ret = devm_request_pci_bus_resources(dev, &res);
++ ret = pci_parse_request_of_pci_ranges(dev, &host->windows,
++ &host->dma_ranges, NULL);
+ if (ret)
+ return ret;
+
+--- a/drivers/pci/controller/pci-versatile.c
++++ b/drivers/pci/controller/pci-versatile.c
+@@ -141,7 +141,8 @@ static int versatile_pci_probe(struct pl
+ if (IS_ERR(versatile_cfg_base[1]))
+ return PTR_ERR(versatile_cfg_base[1]);
+
+- ret = versatile_pci_parse_request_of_pci_ranges(dev, &pci_res);
++ ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++ NULL, NULL);
+ if (ret)
+ return ret;
+
+--- a/drivers/pci/controller/pci-xgene.c
++++ b/drivers/pci/controller/pci-xgene.c
+@@ -634,8 +634,8 @@ static int xgene_pcie_probe(struct platf
+ if (ret)
+ return ret;
+
+- ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
+- &iobase);
++ ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++ &bridge->dma_ranges, NULL);
+ if (ret)
+ return ret;
+
+--- a/drivers/pci/controller/pcie-altera.c
++++ b/drivers/pci/controller/pcie-altera.c
+@@ -833,9 +833,8 @@ static int altera_pcie_probe(struct plat
+ return ret;
+ }
+
+- INIT_LIST_HEAD(&pcie->resources);
+-
+- ret = altera_pcie_parse_request_of_pci_ranges(pcie);
++ ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++ &bridge->dma_ranges, NULL);
+ if (ret) {
+ dev_err(dev, "Failed add resources\n");
+ return ret;
+--- a/drivers/pci/controller/pcie-cadence-host.c
++++ b/drivers/pci/controller/pcie-cadence-host.c
+@@ -211,7 +211,7 @@ static int cdns_pcie_host_init(struct de
+ int err;
+
+ /* Parse our PCI ranges and request their resources */
+- err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range);
++ err = pci_parse_request_of_pci_ranges(dev, resources, NULL, &bus_range);
+ if (err)
+ return err;
+
+--- a/drivers/pci/controller/pcie-iproc-platform.c
++++ b/drivers/pci/controller/pcie-iproc-platform.c
+@@ -97,8 +97,8 @@ static int iproc_pcie_pltfm_probe(struct
+ if (IS_ERR(pcie->phy))
+ return PTR_ERR(pcie->phy);
+
+- ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &resources,
+- &iobase);
++ ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++ &bridge->dma_ranges, NULL);
+ if (ret) {
+ dev_err(dev, "unable to get PCI host bridge resources\n");
+ return ret;
+--- a/drivers/pci/controller/pcie-mediatek.c
++++ b/drivers/pci/controller/pcie-mediatek.c
+@@ -1027,8 +1027,8 @@ static int mtk_pcie_setup(struct mtk_pci
+ resource_size_t io_base;
+ int err;
+
+- err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
+- windows, &io_base);
++ err = pci_parse_request_of_pci_ranges(dev, windows,
++ &host->dma_ranges, &bus);
+ if (err)
+ return err;
+
+--- a/drivers/pci/controller/pcie-mobiveil.c
++++ b/drivers/pci/controller/pcie-mobiveil.c
+@@ -883,8 +883,8 @@ static int mobiveil_pcie_probe(struct pl
+ INIT_LIST_HEAD(&pcie->resources);
+
+ /* parse the host bridge base addresses from the device tree file */
+- ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
+- &pcie->resources, &iobase);
++ ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++ &bridge->dma_ranges, NULL);
+ if (ret) {
+ dev_err(dev, "Getting bridge resources failed\n");
+ return ret;
+--- a/drivers/pci/controller/pcie-rcar.c
++++ b/drivers/pci/controller/pcie-rcar.c
+@@ -1143,7 +1143,8 @@ static int rcar_pcie_probe(struct platfo
+ pcie->dev = dev;
+ platform_set_drvdata(pdev, pcie);
+
+- err = pci_parse_request_of_pci_ranges(dev, &pcie->resources, NULL);
++ err = pci_parse_request_of_pci_ranges(dev, &pcie->resources,
++ &bridge->dma_ranges, NULL);
+ if (err)
+ goto err_free_bridge;
+
+--- a/drivers/pci/controller/pcie-rockchip-host.c
++++ b/drivers/pci/controller/pcie-rockchip-host.c
+@@ -995,8 +995,8 @@ static int rockchip_pcie_probe(struct pl
+ if (err < 0)
+ goto err_deinit_port;
+
+- err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
+- &res, &io_base);
++ err = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++ &bridge->dma_ranges, &bus_res);
+ if (err)
+ goto err_remove_irq_domain;
+
+--- a/drivers/pci/controller/pcie-xilinx-nwl.c
++++ b/drivers/pci/controller/pcie-xilinx-nwl.c
+@@ -845,8 +845,8 @@ static int nwl_pcie_probe(struct platfor
+ return err;
+ }
+
+- err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
+- &iobase);
++ err = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++ &bridge->dma_ranges, NULL);
+ if (err) {
+ dev_err(dev, "Getting bridge resources failed\n");
+ return err;
+--- a/drivers/pci/controller/pcie-xilinx.c
++++ b/drivers/pci/controller/pcie-xilinx.c
+@@ -647,8 +647,8 @@ static int xilinx_pcie_probe(struct plat
+ return err;
+ }
+
+- err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
+- &iobase);
++ err = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++ &bridge->dma_ranges, NULL);
+ if (err) {
+ dev_err(dev, "Getting bridge resources failed\n");
+ return err;
+--- a/drivers/pci/of.c
++++ b/drivers/pci/of.c
+@@ -257,14 +257,16 @@ EXPORT_SYMBOL_GPL(of_pci_check_probe_onl
+ */
+ int devm_of_pci_get_host_bridge_resources(struct device *dev,
+ unsigned char busno, unsigned char bus_max,
+- struct list_head *resources, resource_size_t *io_base)
++ struct list_head *resources,
++ struct list_head *ib_resources,
++ resource_size_t *io_base)
+ {
+ struct device_node *dev_node = dev->of_node;
+ struct resource *res, tmp_res;
+ struct resource *bus_range;
+ struct of_pci_range range;
+ struct of_pci_range_parser parser;
+- char range_type[4];
++ const char *range_type;
+ int err;
+
+ if (io_base)
+@@ -298,12 +300,12 @@ int devm_of_pci_get_host_bridge_resource
+ for_each_of_pci_range(&parser, &range) {
+ /* Read next ranges element */
+ if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_IO)
+- snprintf(range_type, 4, " IO");
++ range_type = "IO";
+ else if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_MEM)
+- snprintf(range_type, 4, "MEM");
++ range_type = "MEM";
+ else
+- snprintf(range_type, 4, "err");
+- dev_info(dev, " %s %#010llx..%#010llx -> %#010llx\n",
++ range_type = "err";
++ dev_info(dev, " %6s %#012llx..%#012llx -> %#012llx\n",
+ range_type, range.cpu_addr,
+ range.cpu_addr + range.size - 1, range.pci_addr);
+
+@@ -340,6 +342,48 @@ int devm_of_pci_get_host_bridge_resource
+ pci_add_resource_offset(resources, res, res->start - range.pci_addr);
+ }
+
++ /* Check for dma-ranges property */
++ if (!ib_resources)
++ return 0;
++ err = of_pci_dma_range_parser_init(&parser, dev_node);
++ if (err)
++ return 0;
++
++ dev_dbg(dev, "Parsing dma-ranges property...\n");
++ for_each_of_pci_range(&parser, &range) {
++ struct resource_entry *entry;
++ /*
++ * If we failed translation or got a zero-sized region
++ * then skip this range
++ */
++ if (((range.flags & IORESOURCE_TYPE_BITS) != IORESOURCE_MEM) ||
++ range.cpu_addr == OF_BAD_ADDR || range.size == 0)
++ continue;
++
++ dev_info(dev, " %6s %#012llx..%#012llx -> %#012llx\n",
++ "IB MEM", range.cpu_addr,
++ range.cpu_addr + range.size - 1, range.pci_addr);
++
++
++ err = of_pci_range_to_resource(&range, dev_node, &tmp_res);
++ if (err)
++ continue;
++
++ res = devm_kmemdup(dev, &tmp_res, sizeof(tmp_res), GFP_KERNEL);
++ if (!res) {
++ err = -ENOMEM;
++ goto failed;
++ }
++
++ /* Keep the resource list sorted */
++ resource_list_for_each_entry(entry, ib_resources)
++ if (entry->res->start > res->start)
++ break;
++
++ pci_add_resource_offset(&entry->node, res,
++ res->start - range.pci_addr);
++ }
++
+ return 0;
+
+ failed:
+@@ -482,6 +526,7 @@ EXPORT_SYMBOL_GPL(of_irq_parse_and_map_p
+
+ int pci_parse_request_of_pci_ranges(struct device *dev,
+ struct list_head *resources,
++ struct list_head *ib_resources,
+ struct resource **bus_range)
+ {
+ int err, res_valid = 0;
+@@ -489,8 +534,10 @@ int pci_parse_request_of_pci_ranges(stru
+ struct resource_entry *win, *tmp;
+
+ INIT_LIST_HEAD(resources);
++ if (ib_resources)
++ INIT_LIST_HEAD(ib_resources);
+ err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, resources,
+- &iobase);
++ ib_resources, &iobase);
+ if (err)
+ return err;
+
+--- a/drivers/pci/pci.h
++++ b/drivers/pci/pci.h
+@@ -636,11 +636,15 @@ static inline void pci_release_bus_of_no
+ #if defined(CONFIG_OF_ADDRESS)
+ int devm_of_pci_get_host_bridge_resources(struct device *dev,
+ unsigned char busno, unsigned char bus_max,
+- struct list_head *resources, resource_size_t *io_base);
++ struct list_head *resources,
++ struct list_head *ib_resources,
++ resource_size_t *io_base);
+ #else
+ static inline int devm_of_pci_get_host_bridge_resources(struct device *dev,
+ unsigned char busno, unsigned char bus_max,
+- struct list_head *resources, resource_size_t *io_base)
++ struct list_head *resources,
++ struct list_head *ib_resources,
++ resource_size_t *io_base)
+ {
+ return -EINVAL;
+ }
+--- a/include/linux/pci.h
++++ b/include/linux/pci.h
+@@ -2278,6 +2278,7 @@ struct irq_domain;
+ struct irq_domain *pci_host_bridge_of_msi_domain(struct pci_bus *bus);
+ int pci_parse_request_of_pci_ranges(struct device *dev,
+ struct list_head *resources,
++ struct list_head *ib_resources,
+ struct resource **bus_range);
+
+ /* Arch may override this (weak) */
+@@ -2286,9 +2287,11 @@ struct device_node *pcibios_get_phb_of_n
+ #else /* CONFIG_OF */
+ static inline struct irq_domain *
+ pci_host_bridge_of_msi_domain(struct pci_bus *bus) { return NULL; }
+-static inline int pci_parse_request_of_pci_ranges(struct device *dev,
+- struct list_head *resources,
+- struct resource **bus_range)
++static inline int
++pci_parse_request_of_pci_ranges(struct device *dev,
++ struct list_head *resources,
++ struct list_head *ib_resources,
++ struct resource **bus_range)
+ {
+ return -EINVAL;
+ }
+++ /dev/null
-From 97a48106d1698038720495fdd49c491b283bf110 Mon Sep 17 00:00:00 2001
-From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Date: Thu, 7 Nov 2019 16:06:45 +0100
-Subject: [PATCH] x86/PCI: sta2x11: use default DMA address translation
-
-commit e380a0394c36a3a878c858418d5dd7f5f195b6fc upstream.
-
-The devices found behind this PCIe chip have unusual DMA mapping
-constraints as there is an AMBA interconnect placed in between them and
-the different PCI endpoints. The offset between physical memory
-addresses and AMBA's view is provided by reading a PCI config register,
-which is saved and used whenever DMA mapping is needed.
-
-It turns out that this DMA setup can be represented by properly setting
-'dma_pfn_offset', 'dma_bus_mask' and 'dma_mask' during the PCI device
-enable fixup. And ultimately allows us to get rid of this device's
-custom DMA functions.
-
-Aside from the code deletion and DMA setup, sta2x11_pdev_to_mapping() is
-moved to avoid warnings whenever CONFIG_PM is not enabled.
-
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Christoph Hellwig <hch@lst.de>
----
- arch/x86/Kconfig | 1 -
- arch/x86/include/asm/device.h | 3 -
- arch/x86/include/asm/dma-direct.h | 9 --
- arch/x86/pci/sta2x11-fixup.c | 135 ++++++------------------------
- 4 files changed, 26 insertions(+), 122 deletions(-)
- delete mode 100644 arch/x86/include/asm/dma-direct.h
-
---- a/arch/x86/Kconfig
-+++ b/arch/x86/Kconfig
-@@ -708,7 +708,6 @@ config X86_SUPPORTS_MEMORY_FAILURE
- config STA2X11
- bool "STA2X11 Companion Chip Support"
- depends on X86_32_NON_STANDARD && PCI
-- select ARCH_HAS_PHYS_TO_DMA
- select SWIOTLB
- select MFD_STA2X11
- select GPIOLIB
---- a/arch/x86/include/asm/device.h
-+++ b/arch/x86/include/asm/device.h
-@@ -6,9 +6,6 @@ struct dev_archdata {
- #if defined(CONFIG_INTEL_IOMMU) || defined(CONFIG_AMD_IOMMU)
- void *iommu; /* hook for IOMMU specific extension */
- #endif
--#ifdef CONFIG_STA2X11
-- bool is_sta2x11;
--#endif
- };
-
- #if defined(CONFIG_X86_DEV_DMA_OPS) && defined(CONFIG_PCI_DOMAINS)
---- a/arch/x86/include/asm/dma-direct.h
-+++ /dev/null
-@@ -1,9 +0,0 @@
--/* SPDX-License-Identifier: GPL-2.0 */
--#ifndef ASM_X86_DMA_DIRECT_H
--#define ASM_X86_DMA_DIRECT_H 1
--
--bool dma_capable(struct device *dev, dma_addr_t addr, size_t size);
--dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr);
--phys_addr_t __dma_to_phys(struct device *dev, dma_addr_t daddr);
--
--#endif /* ASM_X86_DMA_DIRECT_H */
---- a/arch/x86/pci/sta2x11-fixup.c
-+++ b/arch/x86/pci/sta2x11-fixup.c
-@@ -30,7 +30,6 @@ struct sta2x11_ahb_regs { /* saved durin
- };
-
- struct sta2x11_mapping {
-- u32 amba_base;
- int is_suspended;
- struct sta2x11_ahb_regs regs[STA2X11_NR_FUNCS];
- };
-@@ -92,18 +91,6 @@ static int sta2x11_pdev_to_ep(struct pci
- return pdev->bus->number - instance->bus0;
- }
-
--static struct sta2x11_mapping *sta2x11_pdev_to_mapping(struct pci_dev *pdev)
--{
-- struct sta2x11_instance *instance;
-- int ep;
--
-- instance = sta2x11_pdev_to_instance(pdev);
-- if (!instance)
-- return NULL;
-- ep = sta2x11_pdev_to_ep(pdev);
-- return instance->map + ep;
--}
--
- /* This is exported, as some devices need to access the MFD registers */
- struct sta2x11_instance *sta2x11_get_instance(struct pci_dev *pdev)
- {
-@@ -111,39 +98,6 @@ struct sta2x11_instance *sta2x11_get_ins
- }
- EXPORT_SYMBOL(sta2x11_get_instance);
-
--
--/**
-- * p2a - Translate physical address to STA2x11 AMBA address,
-- * used for DMA transfers to STA2x11
-- * @p: Physical address
-- * @pdev: PCI device (must be hosted within the connext)
-- */
--static dma_addr_t p2a(dma_addr_t p, struct pci_dev *pdev)
--{
-- struct sta2x11_mapping *map;
-- dma_addr_t a;
--
-- map = sta2x11_pdev_to_mapping(pdev);
-- a = p + map->amba_base;
-- return a;
--}
--
--/**
-- * a2p - Translate STA2x11 AMBA address to physical address
-- * used for DMA transfers from STA2x11
-- * @a: STA2x11 AMBA address
-- * @pdev: PCI device (must be hosted within the connext)
-- */
--static dma_addr_t a2p(dma_addr_t a, struct pci_dev *pdev)
--{
-- struct sta2x11_mapping *map;
-- dma_addr_t p;
--
-- map = sta2x11_pdev_to_mapping(pdev);
-- p = a - map->amba_base;
-- return p;
--}
--
- /* At setup time, we use our own ops if the device is a ConneXt one */
- static void sta2x11_setup_pdev(struct pci_dev *pdev)
- {
-@@ -151,9 +105,6 @@ static void sta2x11_setup_pdev(struct pc
-
- if (!instance) /* either a sta2x11 bridge or another ST device */
- return;
-- pci_set_consistent_dma_mask(pdev, STA2X11_AMBA_SIZE - 1);
-- pci_set_dma_mask(pdev, STA2X11_AMBA_SIZE - 1);
-- pdev->dev.archdata.is_sta2x11 = true;
-
- /* We must enable all devices as master, for audio DMA to work */
- pci_set_master(pdev);
-@@ -161,61 +112,6 @@ static void sta2x11_setup_pdev(struct pc
- DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, sta2x11_setup_pdev);
-
- /*
-- * The following three functions are exported (used in swiotlb: FIXME)
-- */
--/**
-- * dma_capable - Check if device can manage DMA transfers (FIXME: kill it)
-- * @dev: device for a PCI device
-- * @addr: DMA address
-- * @size: DMA size
-- */
--bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
--{
-- struct sta2x11_mapping *map;
--
-- if (!dev->archdata.is_sta2x11) {
-- if (!dev->dma_mask)
-- return false;
-- return addr + size - 1 <= *dev->dma_mask;
-- }
--
-- map = sta2x11_pdev_to_mapping(to_pci_dev(dev));
--
-- if (!map || (addr < map->amba_base))
-- return false;
-- if (addr + size >= map->amba_base + STA2X11_AMBA_SIZE) {
-- return false;
-- }
--
-- return true;
--}
--
--/**
-- * __phys_to_dma - Return the DMA AMBA address used for this STA2x11 device
-- * @dev: device for a PCI device
-- * @paddr: Physical address
-- */
--dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr)
--{
-- if (!dev->archdata.is_sta2x11)
-- return paddr;
-- return p2a(paddr, to_pci_dev(dev));
--}
--
--/**
-- * dma_to_phys - Return the physical address used for this STA2x11 DMA address
-- * @dev: device for a PCI device
-- * @daddr: STA2x11 AMBA DMA address
-- */
--phys_addr_t __dma_to_phys(struct device *dev, dma_addr_t daddr)
--{
-- if (!dev->archdata.is_sta2x11)
-- return daddr;
-- return a2p(daddr, to_pci_dev(dev));
--}
--
--
--/*
- * At boot we must set up the mappings for the pcie-to-amba bridge.
- * It involves device access, and the same happens at suspend/resume time
- */
-@@ -234,12 +130,22 @@ phys_addr_t __dma_to_phys(struct device
- /* At probe time, enable mapping for each endpoint, using the pdev */
- static void sta2x11_map_ep(struct pci_dev *pdev)
- {
-- struct sta2x11_mapping *map = sta2x11_pdev_to_mapping(pdev);
-+ struct sta2x11_instance *instance = sta2x11_pdev_to_instance(pdev);
-+ struct device *dev = &pdev->dev;
-+ u32 amba_base, max_amba_addr;
- int i;
-
-- if (!map)
-+ if (!instance)
- return;
-- pci_read_config_dword(pdev, AHB_BASE(0), &map->amba_base);
-+
-+ pci_read_config_dword(pdev, AHB_BASE(0), &amba_base);
-+ max_amba_addr = amba_base + STA2X11_AMBA_SIZE - 1;
-+
-+ dev->dma_pfn_offset = PFN_DOWN(-amba_base);
-+
-+ dev->bus_dma_mask = max_amba_addr;
-+ pci_set_consistent_dma_mask(pdev, max_amba_addr);
-+ pci_set_dma_mask(pdev, max_amba_addr);
-
- /* Configure AHB mapping */
- pci_write_config_dword(pdev, AHB_PEXLBASE(0), 0);
-@@ -253,13 +159,24 @@ static void sta2x11_map_ep(struct pci_de
-
- dev_info(&pdev->dev,
- "sta2x11: Map EP %i: AMBA address %#8x-%#8x\n",
-- sta2x11_pdev_to_ep(pdev), map->amba_base,
-- map->amba_base + STA2X11_AMBA_SIZE - 1);
-+ sta2x11_pdev_to_ep(pdev), amba_base, max_amba_addr);
- }
- DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, sta2x11_map_ep);
-
- #ifdef CONFIG_PM /* Some register values must be saved and restored */
-
-+static struct sta2x11_mapping *sta2x11_pdev_to_mapping(struct pci_dev *pdev)
-+{
-+ struct sta2x11_instance *instance;
-+ int ep;
-+
-+ instance = sta2x11_pdev_to_instance(pdev);
-+ if (!instance)
-+ return NULL;
-+ ep = sta2x11_pdev_to_ep(pdev);
-+ return instance->map + ep;
-+}
-+
- static void suspend_mapping(struct pci_dev *pdev)
- {
- struct sta2x11_mapping *map = sta2x11_pdev_to_mapping(pdev);
+++ /dev/null
-From 125a18144253e3a3f4bcad24484ee9b590dc47c6 Mon Sep 17 00:00:00 2001
-From: Rob Herring <robh@kernel.org>
-Date: Wed, 30 Oct 2019 17:30:57 -0500
-Subject: [PATCH] PCI: of: Add inbound resource parsing to helpers
-
-Extend devm_of_pci_get_host_bridge_resources() and
-pci_parse_request_of_pci_ranges() helpers to also parse the inbound
-addresses from DT 'dma-ranges' and populate a resource list with the
-translated addresses. This will help ensure 'dma-ranges' is always
-parsed in a consistent way.
-
-Tested-by: Srinath Mannam <srinath.mannam@broadcom.com>
-Tested-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com> # for AArdvark
-Signed-off-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
-Reviewed-by: Srinath Mannam <srinath.mannam@broadcom.com>
-Reviewed-by: Andrew Murray <andrew.murray@arm.com>
-Acked-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
-Cc: Jingoo Han <jingoohan1@gmail.com>
-Cc: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
-Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
-Cc: Bjorn Helgaas <bhelgaas@google.com>
-Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
-Cc: Will Deacon <will@kernel.org>
-Cc: Linus Walleij <linus.walleij@linaro.org>
-Cc: Toan Le <toan@os.amperecomputing.com>
-Cc: Ley Foon Tan <lftan@altera.com>
-Cc: Tom Joseph <tjoseph@cadence.com>
-Cc: Ray Jui <rjui@broadcom.com>
-Cc: Scott Branden <sbranden@broadcom.com>
-Cc: bcm-kernel-feedback-list@broadcom.com
-Cc: Ryder Lee <ryder.lee@mediatek.com>
-Cc: Karthikeyan Mitran <m.karthikeyan@mobiveil.co.in>
-Cc: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
-Cc: Simon Horman <horms@verge.net.au>
-Cc: Shawn Lin <shawn.lin@rock-chips.com>
-Cc: Heiko Stuebner <heiko@sntech.de>
-Cc: Michal Simek <michal.simek@xilinx.com>
-Cc: rfi@lists.rocketboards.org
-Cc: linux-mediatek@lists.infradead.org
-Cc: linux-renesas-soc@vger.kernel.org
-Cc: linux-rockchip@lists.infradead.org
-(cherry picked from commit 331f63457165a30c708280de2c77f1742c6351dc)
----
- .../pci/controller/dwc/pcie-designware-host.c | 8 +--
- drivers/pci/controller/pci-aardvark.c | 3 +-
- drivers/pci/controller/pci-ftpci100.c | 4 +-
- drivers/pci/controller/pci-host-common.c | 2 +-
- drivers/pci/controller/pci-v3-semi.c | 8 +--
- drivers/pci/controller/pci-versatile.c | 3 +-
- drivers/pci/controller/pci-xgene.c | 4 +-
- drivers/pci/controller/pcie-altera.c | 5 +-
- drivers/pci/controller/pcie-cadence-host.c | 2 +-
- drivers/pci/controller/pcie-iproc-platform.c | 4 +-
- drivers/pci/controller/pcie-mediatek.c | 4 +-
- drivers/pci/controller/pcie-mobiveil.c | 4 +-
- drivers/pci/controller/pcie-rcar.c | 3 +-
- drivers/pci/controller/pcie-rockchip-host.c | 4 +-
- drivers/pci/controller/pcie-xilinx-nwl.c | 4 +-
- drivers/pci/controller/pcie-xilinx.c | 4 +-
- drivers/pci/of.c | 61 ++++++++++++++++---
- drivers/pci/pci.h | 8 ++-
- include/linux/pci.h | 9 ++-
- 19 files changed, 96 insertions(+), 48 deletions(-)
-
---- a/drivers/pci/controller/dwc/pcie-designware-host.c
-+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
-@@ -343,12 +343,8 @@ int dw_pcie_host_init(struct pcie_port *
- if (!bridge)
- return -ENOMEM;
-
-- ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
-- &bridge->windows, &pp->io_base);
-- if (ret)
-- return ret;
--
-- ret = devm_request_pci_bus_resources(dev, &bridge->windows);
-+ ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+ &bridge->dma_ranges, NULL);
- if (ret)
- return ret;
-
---- a/drivers/pci/controller/pci-aardvark.c
-+++ b/drivers/pci/controller/pci-aardvark.c
-@@ -1023,7 +1023,8 @@ static int advk_pcie_probe(struct platfo
- return ret;
- }
-
-- ret = advk_pcie_parse_request_of_pci_ranges(pcie);
-+ ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+ &bridge->dma_ranges, &bus);
- if (ret) {
- dev_err(dev, "Failed to parse resources\n");
- return ret;
---- a/drivers/pci/controller/pci-ftpci100.c
-+++ b/drivers/pci/controller/pci-ftpci100.c
-@@ -480,8 +480,8 @@ static int faraday_pci_probe(struct plat
- if (IS_ERR(p->base))
- return PTR_ERR(p->base);
-
-- ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
-- &res, &io_base);
-+ ret = pci_parse_request_of_pci_ranges(dev, &host->windows,
-+ &host->dma_ranges, NULL);
- if (ret)
- return ret;
-
---- a/drivers/pci/controller/pci-host-common.c
-+++ b/drivers/pci/controller/pci-host-common.c
-@@ -27,7 +27,7 @@ static struct pci_config_window *gen_pci
- struct pci_config_window *cfg;
-
- /* Parse our PCI ranges and request their resources */
-- err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range);
-+ err = pci_parse_request_of_pci_ranges(dev, resources, NULL, &bus_range);
- if (err)
- return ERR_PTR(err);
-
---- a/drivers/pci/controller/pci-v3-semi.c
-+++ b/drivers/pci/controller/pci-v3-semi.c
-@@ -793,12 +793,8 @@ static int v3_pci_probe(struct platform_
- if (IS_ERR(v3->config_base))
- return PTR_ERR(v3->config_base);
-
-- ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
-- &io_base);
-- if (ret)
-- return ret;
--
-- ret = devm_request_pci_bus_resources(dev, &res);
-+ ret = pci_parse_request_of_pci_ranges(dev, &host->windows,
-+ &host->dma_ranges, NULL);
- if (ret)
- return ret;
-
---- a/drivers/pci/controller/pci-versatile.c
-+++ b/drivers/pci/controller/pci-versatile.c
-@@ -141,7 +141,8 @@ static int versatile_pci_probe(struct pl
- if (IS_ERR(versatile_cfg_base[1]))
- return PTR_ERR(versatile_cfg_base[1]);
-
-- ret = versatile_pci_parse_request_of_pci_ranges(dev, &pci_res);
-+ ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+ NULL, NULL);
- if (ret)
- return ret;
-
---- a/drivers/pci/controller/pci-xgene.c
-+++ b/drivers/pci/controller/pci-xgene.c
-@@ -634,8 +634,8 @@ static int xgene_pcie_probe(struct platf
- if (ret)
- return ret;
-
-- ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
-- &iobase);
-+ ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+ &bridge->dma_ranges, NULL);
- if (ret)
- return ret;
-
---- a/drivers/pci/controller/pcie-altera.c
-+++ b/drivers/pci/controller/pcie-altera.c
-@@ -833,9 +833,8 @@ static int altera_pcie_probe(struct plat
- return ret;
- }
-
-- INIT_LIST_HEAD(&pcie->resources);
--
-- ret = altera_pcie_parse_request_of_pci_ranges(pcie);
-+ ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+ &bridge->dma_ranges, NULL);
- if (ret) {
- dev_err(dev, "Failed add resources\n");
- return ret;
---- a/drivers/pci/controller/pcie-cadence-host.c
-+++ b/drivers/pci/controller/pcie-cadence-host.c
-@@ -211,7 +211,7 @@ static int cdns_pcie_host_init(struct de
- int err;
-
- /* Parse our PCI ranges and request their resources */
-- err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range);
-+ err = pci_parse_request_of_pci_ranges(dev, resources, NULL, &bus_range);
- if (err)
- return err;
-
---- a/drivers/pci/controller/pcie-iproc-platform.c
-+++ b/drivers/pci/controller/pcie-iproc-platform.c
-@@ -97,8 +97,8 @@ static int iproc_pcie_pltfm_probe(struct
- if (IS_ERR(pcie->phy))
- return PTR_ERR(pcie->phy);
-
-- ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &resources,
-- &iobase);
-+ ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+ &bridge->dma_ranges, NULL);
- if (ret) {
- dev_err(dev, "unable to get PCI host bridge resources\n");
- return ret;
---- a/drivers/pci/controller/pcie-mediatek.c
-+++ b/drivers/pci/controller/pcie-mediatek.c
-@@ -1027,8 +1027,8 @@ static int mtk_pcie_setup(struct mtk_pci
- resource_size_t io_base;
- int err;
-
-- err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
-- windows, &io_base);
-+ err = pci_parse_request_of_pci_ranges(dev, windows,
-+ &host->dma_ranges, &bus);
- if (err)
- return err;
-
---- a/drivers/pci/controller/pcie-mobiveil.c
-+++ b/drivers/pci/controller/pcie-mobiveil.c
-@@ -883,8 +883,8 @@ static int mobiveil_pcie_probe(struct pl
- INIT_LIST_HEAD(&pcie->resources);
-
- /* parse the host bridge base addresses from the device tree file */
-- ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
-- &pcie->resources, &iobase);
-+ ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+ &bridge->dma_ranges, NULL);
- if (ret) {
- dev_err(dev, "Getting bridge resources failed\n");
- return ret;
---- a/drivers/pci/controller/pcie-rcar.c
-+++ b/drivers/pci/controller/pcie-rcar.c
-@@ -1143,7 +1143,8 @@ static int rcar_pcie_probe(struct platfo
- pcie->dev = dev;
- platform_set_drvdata(pdev, pcie);
-
-- err = pci_parse_request_of_pci_ranges(dev, &pcie->resources, NULL);
-+ err = pci_parse_request_of_pci_ranges(dev, &pcie->resources,
-+ &bridge->dma_ranges, NULL);
- if (err)
- goto err_free_bridge;
-
---- a/drivers/pci/controller/pcie-rockchip-host.c
-+++ b/drivers/pci/controller/pcie-rockchip-host.c
-@@ -995,8 +995,8 @@ static int rockchip_pcie_probe(struct pl
- if (err < 0)
- goto err_deinit_port;
-
-- err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
-- &res, &io_base);
-+ err = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+ &bridge->dma_ranges, &bus_res);
- if (err)
- goto err_remove_irq_domain;
-
---- a/drivers/pci/controller/pcie-xilinx-nwl.c
-+++ b/drivers/pci/controller/pcie-xilinx-nwl.c
-@@ -845,8 +845,8 @@ static int nwl_pcie_probe(struct platfor
- return err;
- }
-
-- err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
-- &iobase);
-+ err = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+ &bridge->dma_ranges, NULL);
- if (err) {
- dev_err(dev, "Getting bridge resources failed\n");
- return err;
---- a/drivers/pci/controller/pcie-xilinx.c
-+++ b/drivers/pci/controller/pcie-xilinx.c
-@@ -647,8 +647,8 @@ static int xilinx_pcie_probe(struct plat
- return err;
- }
-
-- err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
-- &iobase);
-+ err = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+ &bridge->dma_ranges, NULL);
- if (err) {
- dev_err(dev, "Getting bridge resources failed\n");
- return err;
---- a/drivers/pci/of.c
-+++ b/drivers/pci/of.c
-@@ -257,14 +257,16 @@ EXPORT_SYMBOL_GPL(of_pci_check_probe_onl
- */
- int devm_of_pci_get_host_bridge_resources(struct device *dev,
- unsigned char busno, unsigned char bus_max,
-- struct list_head *resources, resource_size_t *io_base)
-+ struct list_head *resources,
-+ struct list_head *ib_resources,
-+ resource_size_t *io_base)
- {
- struct device_node *dev_node = dev->of_node;
- struct resource *res, tmp_res;
- struct resource *bus_range;
- struct of_pci_range range;
- struct of_pci_range_parser parser;
-- char range_type[4];
-+ const char *range_type;
- int err;
-
- if (io_base)
-@@ -298,12 +300,12 @@ int devm_of_pci_get_host_bridge_resource
- for_each_of_pci_range(&parser, &range) {
- /* Read next ranges element */
- if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_IO)
-- snprintf(range_type, 4, " IO");
-+ range_type = "IO";
- else if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_MEM)
-- snprintf(range_type, 4, "MEM");
-+ range_type = "MEM";
- else
-- snprintf(range_type, 4, "err");
-- dev_info(dev, " %s %#010llx..%#010llx -> %#010llx\n",
-+ range_type = "err";
-+ dev_info(dev, " %6s %#012llx..%#012llx -> %#012llx\n",
- range_type, range.cpu_addr,
- range.cpu_addr + range.size - 1, range.pci_addr);
-
-@@ -340,6 +342,48 @@ int devm_of_pci_get_host_bridge_resource
- pci_add_resource_offset(resources, res, res->start - range.pci_addr);
- }
-
-+ /* Check for dma-ranges property */
-+ if (!ib_resources)
-+ return 0;
-+ err = of_pci_dma_range_parser_init(&parser, dev_node);
-+ if (err)
-+ return 0;
-+
-+ dev_dbg(dev, "Parsing dma-ranges property...\n");
-+ for_each_of_pci_range(&parser, &range) {
-+ struct resource_entry *entry;
-+ /*
-+ * If we failed translation or got a zero-sized region
-+ * then skip this range
-+ */
-+ if (((range.flags & IORESOURCE_TYPE_BITS) != IORESOURCE_MEM) ||
-+ range.cpu_addr == OF_BAD_ADDR || range.size == 0)
-+ continue;
-+
-+ dev_info(dev, " %6s %#012llx..%#012llx -> %#012llx\n",
-+ "IB MEM", range.cpu_addr,
-+ range.cpu_addr + range.size - 1, range.pci_addr);
-+
-+
-+ err = of_pci_range_to_resource(&range, dev_node, &tmp_res);
-+ if (err)
-+ continue;
-+
-+ res = devm_kmemdup(dev, &tmp_res, sizeof(tmp_res), GFP_KERNEL);
-+ if (!res) {
-+ err = -ENOMEM;
-+ goto failed;
-+ }
-+
-+ /* Keep the resource list sorted */
-+ resource_list_for_each_entry(entry, ib_resources)
-+ if (entry->res->start > res->start)
-+ break;
-+
-+ pci_add_resource_offset(&entry->node, res,
-+ res->start - range.pci_addr);
-+ }
-+
- return 0;
-
- failed:
-@@ -482,6 +526,7 @@ EXPORT_SYMBOL_GPL(of_irq_parse_and_map_p
-
- int pci_parse_request_of_pci_ranges(struct device *dev,
- struct list_head *resources,
-+ struct list_head *ib_resources,
- struct resource **bus_range)
- {
- int err, res_valid = 0;
-@@ -489,8 +534,10 @@ int pci_parse_request_of_pci_ranges(stru
- struct resource_entry *win, *tmp;
-
- INIT_LIST_HEAD(resources);
-+ if (ib_resources)
-+ INIT_LIST_HEAD(ib_resources);
- err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, resources,
-- &iobase);
-+ ib_resources, &iobase);
- if (err)
- return err;
-
---- a/drivers/pci/pci.h
-+++ b/drivers/pci/pci.h
-@@ -636,11 +636,15 @@ static inline void pci_release_bus_of_no
- #if defined(CONFIG_OF_ADDRESS)
- int devm_of_pci_get_host_bridge_resources(struct device *dev,
- unsigned char busno, unsigned char bus_max,
-- struct list_head *resources, resource_size_t *io_base);
-+ struct list_head *resources,
-+ struct list_head *ib_resources,
-+ resource_size_t *io_base);
- #else
- static inline int devm_of_pci_get_host_bridge_resources(struct device *dev,
- unsigned char busno, unsigned char bus_max,
-- struct list_head *resources, resource_size_t *io_base)
-+ struct list_head *resources,
-+ struct list_head *ib_resources,
-+ resource_size_t *io_base)
- {
- return -EINVAL;
- }
---- a/include/linux/pci.h
-+++ b/include/linux/pci.h
-@@ -2278,6 +2278,7 @@ struct irq_domain;
- struct irq_domain *pci_host_bridge_of_msi_domain(struct pci_bus *bus);
- int pci_parse_request_of_pci_ranges(struct device *dev,
- struct list_head *resources,
-+ struct list_head *ib_resources,
- struct resource **bus_range);
-
- /* Arch may override this (weak) */
-@@ -2286,9 +2287,11 @@ struct device_node *pcibios_get_phb_of_n
- #else /* CONFIG_OF */
- static inline struct irq_domain *
- pci_host_bridge_of_msi_domain(struct pci_bus *bus) { return NULL; }
--static inline int pci_parse_request_of_pci_ranges(struct device *dev,
-- struct list_head *resources,
-- struct resource **bus_range)
-+static inline int
-+pci_parse_request_of_pci_ranges(struct device *dev,
-+ struct list_head *resources,
-+ struct list_head *ib_resources,
-+ struct resource **bus_range)
- {
- return -EINVAL;
- }
--- /dev/null
+From 203e0c39b262fc1da6f976495c32ec38ea93a137 Mon Sep 17 00:00:00 2001
+From: Christoph Hellwig <hch@lst.de>
+Date: Tue, 12 Nov 2019 17:06:04 +0100
+Subject: [PATCH] dma-direct: unify the dma_capable definitions
+
+commit 130c1ccbf55330b55e82612a6e54eebb82c9d746 upstream.
+
+Currently each architectures that wants to override dma_to_phys and
+phys_to_dma also has to provide dma_capable. But there isn't really
+any good reason for that. powerpc and mips just have copies of the
+generic one minus the latests fix, and the arm one was the inspiration
+for said fix, but misses the bus_dma_mask handling.
+Make all architectures use the generic version instead.
+
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Acked-by: Michael Ellerman <mpe@ellerman.id.au> (powerpc)
+Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+---
+ arch/arm/include/asm/dma-direct.h | 19 -------------------
+ arch/mips/include/asm/dma-direct.h | 8 --------
+ arch/powerpc/include/asm/dma-direct.h | 9 ---------
+ include/linux/dma-direct.h | 2 +-
+ 4 files changed, 1 insertion(+), 37 deletions(-)
+
+--- a/arch/arm/include/asm/dma-direct.h
++++ b/arch/arm/include/asm/dma-direct.h
+@@ -14,23 +14,4 @@ static inline phys_addr_t __dma_to_phys(
+ return __pfn_to_phys(dma_to_pfn(dev, dev_addr)) + offset;
+ }
+
+-static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
+-{
+- u64 limit, mask;
+-
+- if (!dev->dma_mask)
+- return 0;
+-
+- mask = *dev->dma_mask;
+-
+- limit = (mask + 1) & ~mask;
+- if (limit && size > limit)
+- return 0;
+-
+- if ((addr | (addr + size - 1)) & ~mask)
+- return 0;
+-
+- return 1;
+-}
+-
+ #endif /* ASM_ARM_DMA_DIRECT_H */
+--- a/arch/mips/include/asm/dma-direct.h
++++ b/arch/mips/include/asm/dma-direct.h
+@@ -2,14 +2,6 @@
+ #ifndef _MIPS_DMA_DIRECT_H
+ #define _MIPS_DMA_DIRECT_H 1
+
+-static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
+-{
+- if (!dev->dma_mask)
+- return false;
+-
+- return addr + size - 1 <= *dev->dma_mask;
+-}
+-
+ dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr);
+ phys_addr_t __dma_to_phys(struct device *dev, dma_addr_t daddr);
+
+--- a/arch/powerpc/include/asm/dma-direct.h
++++ b/arch/powerpc/include/asm/dma-direct.h
+@@ -2,15 +2,6 @@
+ #ifndef ASM_POWERPC_DMA_DIRECT_H
+ #define ASM_POWERPC_DMA_DIRECT_H 1
+
+-static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
+-{
+- if (!dev->dma_mask)
+- return false;
+-
+- return addr + size - 1 <=
+- min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
+-}
+-
+ static inline dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr)
+ {
+ if (!dev)
+--- a/include/linux/dma-direct.h
++++ b/include/linux/dma-direct.h
+@@ -26,6 +26,7 @@ static inline phys_addr_t __dma_to_phys(
+
+ return paddr + ((phys_addr_t)dev->dma_pfn_offset << PAGE_SHIFT);
+ }
++#endif /* !CONFIG_ARCH_HAS_PHYS_TO_DMA */
+
+ static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
+ {
+@@ -40,7 +41,6 @@ static inline bool dma_capable(struct de
+
+ return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
+ }
+-#endif /* !CONFIG_ARCH_HAS_PHYS_TO_DMA */
+
+ #ifdef CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED
+ bool force_dma_unencrypted(struct device *dev);
--- /dev/null
+From a3794022e928547de664abd03b61280163c7f13a Mon Sep 17 00:00:00 2001
+From: Christoph Hellwig <hch@lst.de>
+Date: Tue, 12 Nov 2019 17:07:43 +0100
+Subject: [PATCH] dma-direct: avoid a forward declaration for
+ phys_to_dma
+
+Move dma_capable down a bit so that we don't need a forward declaration
+for phys_to_dma.
+
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+(cherry picked from commit c7345159f7db6fb69ec1c3b3f8f28cd05c731be2)
+---
+ include/linux/dma-direct.h | 30 ++++++++++++++----------------
+ 1 file changed, 14 insertions(+), 16 deletions(-)
+
+--- a/include/linux/dma-direct.h
++++ b/include/linux/dma-direct.h
+@@ -6,8 +6,6 @@
+ #include <linux/memblock.h> /* for min_low_pfn */
+ #include <linux/mem_encrypt.h>
+
+-static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr);
+-
+ extern unsigned int zone_dma_bits;
+
+ #ifdef CONFIG_ARCH_HAS_PHYS_TO_DMA
+@@ -28,20 +26,6 @@ static inline phys_addr_t __dma_to_phys(
+ }
+ #endif /* !CONFIG_ARCH_HAS_PHYS_TO_DMA */
+
+-static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
+-{
+- dma_addr_t end = addr + size - 1;
+-
+- if (!dev->dma_mask)
+- return false;
+-
+- if (!IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
+- min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn)))
+- return false;
+-
+- return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
+-}
+-
+ #ifdef CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED
+ bool force_dma_unencrypted(struct device *dev);
+ #else
+@@ -67,6 +51,20 @@ static inline phys_addr_t dma_to_phys(st
+ return __sme_clr(__dma_to_phys(dev, daddr));
+ }
+
++static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
++{
++ dma_addr_t end = addr + size - 1;
++
++ if (!dev->dma_mask)
++ return false;
++
++ if (!IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
++ min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn)))
++ return false;
++
++ return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
++}
++
+ u64 dma_direct_get_required_mask(struct device *dev);
+ void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
+ gfp_t gfp, unsigned long attrs);
+++ /dev/null
-From 203e0c39b262fc1da6f976495c32ec38ea93a137 Mon Sep 17 00:00:00 2001
-From: Christoph Hellwig <hch@lst.de>
-Date: Tue, 12 Nov 2019 17:06:04 +0100
-Subject: [PATCH] dma-direct: unify the dma_capable definitions
-
-commit 130c1ccbf55330b55e82612a6e54eebb82c9d746 upstream.
-
-Currently each architectures that wants to override dma_to_phys and
-phys_to_dma also has to provide dma_capable. But there isn't really
-any good reason for that. powerpc and mips just have copies of the
-generic one minus the latests fix, and the arm one was the inspiration
-for said fix, but misses the bus_dma_mask handling.
-Make all architectures use the generic version instead.
-
-Signed-off-by: Christoph Hellwig <hch@lst.de>
-Acked-by: Michael Ellerman <mpe@ellerman.id.au> (powerpc)
-Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
----
- arch/arm/include/asm/dma-direct.h | 19 -------------------
- arch/mips/include/asm/dma-direct.h | 8 --------
- arch/powerpc/include/asm/dma-direct.h | 9 ---------
- include/linux/dma-direct.h | 2 +-
- 4 files changed, 1 insertion(+), 37 deletions(-)
-
---- a/arch/arm/include/asm/dma-direct.h
-+++ b/arch/arm/include/asm/dma-direct.h
-@@ -14,23 +14,4 @@ static inline phys_addr_t __dma_to_phys(
- return __pfn_to_phys(dma_to_pfn(dev, dev_addr)) + offset;
- }
-
--static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
--{
-- u64 limit, mask;
--
-- if (!dev->dma_mask)
-- return 0;
--
-- mask = *dev->dma_mask;
--
-- limit = (mask + 1) & ~mask;
-- if (limit && size > limit)
-- return 0;
--
-- if ((addr | (addr + size - 1)) & ~mask)
-- return 0;
--
-- return 1;
--}
--
- #endif /* ASM_ARM_DMA_DIRECT_H */
---- a/arch/mips/include/asm/dma-direct.h
-+++ b/arch/mips/include/asm/dma-direct.h
-@@ -2,14 +2,6 @@
- #ifndef _MIPS_DMA_DIRECT_H
- #define _MIPS_DMA_DIRECT_H 1
-
--static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
--{
-- if (!dev->dma_mask)
-- return false;
--
-- return addr + size - 1 <= *dev->dma_mask;
--}
--
- dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr);
- phys_addr_t __dma_to_phys(struct device *dev, dma_addr_t daddr);
-
---- a/arch/powerpc/include/asm/dma-direct.h
-+++ b/arch/powerpc/include/asm/dma-direct.h
-@@ -2,15 +2,6 @@
- #ifndef ASM_POWERPC_DMA_DIRECT_H
- #define ASM_POWERPC_DMA_DIRECT_H 1
-
--static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
--{
-- if (!dev->dma_mask)
-- return false;
--
-- return addr + size - 1 <=
-- min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
--}
--
- static inline dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr)
- {
- if (!dev)
---- a/include/linux/dma-direct.h
-+++ b/include/linux/dma-direct.h
-@@ -26,6 +26,7 @@ static inline phys_addr_t __dma_to_phys(
-
- return paddr + ((phys_addr_t)dev->dma_pfn_offset << PAGE_SHIFT);
- }
-+#endif /* !CONFIG_ARCH_HAS_PHYS_TO_DMA */
-
- static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
- {
-@@ -40,7 +41,6 @@ static inline bool dma_capable(struct de
-
- return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
- }
--#endif /* !CONFIG_ARCH_HAS_PHYS_TO_DMA */
-
- #ifdef CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED
- bool force_dma_unencrypted(struct device *dev);
+++ /dev/null
-From a3794022e928547de664abd03b61280163c7f13a Mon Sep 17 00:00:00 2001
-From: Christoph Hellwig <hch@lst.de>
-Date: Tue, 12 Nov 2019 17:07:43 +0100
-Subject: [PATCH] dma-direct: avoid a forward declaration for
- phys_to_dma
-
-Move dma_capable down a bit so that we don't need a forward declaration
-for phys_to_dma.
-
-Signed-off-by: Christoph Hellwig <hch@lst.de>
-Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-(cherry picked from commit c7345159f7db6fb69ec1c3b3f8f28cd05c731be2)
----
- include/linux/dma-direct.h | 30 ++++++++++++++----------------
- 1 file changed, 14 insertions(+), 16 deletions(-)
-
---- a/include/linux/dma-direct.h
-+++ b/include/linux/dma-direct.h
-@@ -6,8 +6,6 @@
- #include <linux/memblock.h> /* for min_low_pfn */
- #include <linux/mem_encrypt.h>
-
--static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr);
--
- extern unsigned int zone_dma_bits;
-
- #ifdef CONFIG_ARCH_HAS_PHYS_TO_DMA
-@@ -28,20 +26,6 @@ static inline phys_addr_t __dma_to_phys(
- }
- #endif /* !CONFIG_ARCH_HAS_PHYS_TO_DMA */
-
--static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
--{
-- dma_addr_t end = addr + size - 1;
--
-- if (!dev->dma_mask)
-- return false;
--
-- if (!IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
-- min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn)))
-- return false;
--
-- return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
--}
--
- #ifdef CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED
- bool force_dma_unencrypted(struct device *dev);
- #else
-@@ -67,6 +51,20 @@ static inline phys_addr_t dma_to_phys(st
- return __sme_clr(__dma_to_phys(dev, daddr));
- }
-
-+static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
-+{
-+ dma_addr_t end = addr + size - 1;
-+
-+ if (!dev->dma_mask)
-+ return false;
-+
-+ if (!IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
-+ min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn)))
-+ return false;
-+
-+ return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
-+}
-+
- u64 dma_direct_get_required_mask(struct device *dev);
- void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
- gfp_t gfp, unsigned long attrs);
--- /dev/null
+From b763f24aed409296eb76d085c279b2c40462f8a1 Mon Sep 17 00:00:00 2001
+From: Christoph Hellwig <hch@lst.de>
+Date: Tue, 19 Nov 2019 17:38:58 +0100
+Subject: [PATCH] dma-direct: exclude dma_direct_map_resource from the
+ min_low_pfn check
+
+commit 68a33b1794665ba8a1d1ef1d3bfcc7c587d380a6 upstream.
+
+The valid memory address check in dma_capable only makes sense when mapping
+normal memory, not when using dma_map_resource to map a device resource.
+Add a new boolean argument to dma_capable to exclude that check for the
+dma_map_resource case.
+
+Fixes: b12d66278dd6 ("dma-direct: check for overflows on 32 bit DMA addresses")
+Reported-by: Marek Szyprowski <m.szyprowski@samsung.com>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Acked-by: Marek Szyprowski <m.szyprowski@samsung.com>
+Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
+---
+ arch/x86/kernel/amd_gart_64.c | 4 ++--
+ drivers/xen/swiotlb-xen.c | 4 ++--
+ include/linux/dma-direct.h | 5 +++--
+ kernel/dma/direct.c | 4 ++--
+ kernel/dma/swiotlb.c | 2 +-
+ 5 files changed, 10 insertions(+), 9 deletions(-)
+
+--- a/arch/x86/kernel/amd_gart_64.c
++++ b/arch/x86/kernel/amd_gart_64.c
+@@ -185,13 +185,13 @@ static void iommu_full(struct device *de
+ static inline int
+ need_iommu(struct device *dev, unsigned long addr, size_t size)
+ {
+- return force_iommu || !dma_capable(dev, addr, size);
++ return force_iommu || !dma_capable(dev, addr, size, true);
+ }
+
+ static inline int
+ nonforced_iommu(struct device *dev, unsigned long addr, size_t size)
+ {
+- return !dma_capable(dev, addr, size);
++ return !dma_capable(dev, addr, size, true);
+ }
+
+ /* Map a single continuous physical area into the IOMMU.
+--- a/drivers/xen/swiotlb-xen.c
++++ b/drivers/xen/swiotlb-xen.c
+@@ -375,7 +375,7 @@ static dma_addr_t xen_swiotlb_map_page(s
+ * we can safely return the device addr and not worry about bounce
+ * buffering it.
+ */
+- if (dma_capable(dev, dev_addr, size) &&
++ if (dma_capable(dev, dev_addr, size, true) &&
+ !range_straddles_page_boundary(phys, size) &&
+ !xen_arch_need_swiotlb(dev, phys, dev_addr) &&
+ swiotlb_force != SWIOTLB_FORCE)
+@@ -397,7 +397,7 @@ static dma_addr_t xen_swiotlb_map_page(s
+ /*
+ * Ensure that the address returned is DMA'ble
+ */
+- if (unlikely(!dma_capable(dev, dev_addr, size))) {
++ if (unlikely(!dma_capable(dev, dev_addr, size, true))) {
+ swiotlb_tbl_unmap_single(dev, map, size, size, dir,
+ attrs | DMA_ATTR_SKIP_CPU_SYNC);
+ return DMA_MAPPING_ERROR;
+--- a/include/linux/dma-direct.h
++++ b/include/linux/dma-direct.h
+@@ -51,14 +51,15 @@ static inline phys_addr_t dma_to_phys(st
+ return __sme_clr(__dma_to_phys(dev, daddr));
+ }
+
+-static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
++static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size,
++ bool is_ram)
+ {
+ dma_addr_t end = addr + size - 1;
+
+ if (!dev->dma_mask)
+ return false;
+
+- if (!IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
++ if (is_ram && !IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
+ min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn)))
+ return false;
+
+--- a/kernel/dma/direct.c
++++ b/kernel/dma/direct.c
+@@ -325,7 +325,7 @@ static inline bool dma_direct_possible(s
+ size_t size)
+ {
+ return swiotlb_force != SWIOTLB_FORCE &&
+- dma_capable(dev, dma_addr, size);
++ dma_capable(dev, dma_addr, size, true);
+ }
+
+ dma_addr_t dma_direct_map_page(struct device *dev, struct page *page,
+@@ -374,7 +374,7 @@ dma_addr_t dma_direct_map_resource(struc
+ {
+ dma_addr_t dma_addr = paddr;
+
+- if (unlikely(!dma_capable(dev, dma_addr, size))) {
++ if (unlikely(!dma_capable(dev, dma_addr, size, false))) {
+ report_addr(dev, dma_addr, size);
+ return DMA_MAPPING_ERROR;
+ }
+--- a/kernel/dma/swiotlb.c
++++ b/kernel/dma/swiotlb.c
+@@ -678,7 +678,7 @@ bool swiotlb_map(struct device *dev, phy
+
+ /* Ensure that the address returned is DMA'ble */
+ *dma_addr = __phys_to_dma(dev, *phys);
+- if (unlikely(!dma_capable(dev, *dma_addr, size))) {
++ if (unlikely(!dma_capable(dev, *dma_addr, size, true))) {
+ swiotlb_tbl_unmap_single(dev, *phys, size, size, dir,
+ attrs | DMA_ATTR_SKIP_CPU_SYNC);
+ return false;
+++ /dev/null
-From b763f24aed409296eb76d085c279b2c40462f8a1 Mon Sep 17 00:00:00 2001
-From: Christoph Hellwig <hch@lst.de>
-Date: Tue, 19 Nov 2019 17:38:58 +0100
-Subject: [PATCH] dma-direct: exclude dma_direct_map_resource from the
- min_low_pfn check
-
-commit 68a33b1794665ba8a1d1ef1d3bfcc7c587d380a6 upstream.
-
-The valid memory address check in dma_capable only makes sense when mapping
-normal memory, not when using dma_map_resource to map a device resource.
-Add a new boolean argument to dma_capable to exclude that check for the
-dma_map_resource case.
-
-Fixes: b12d66278dd6 ("dma-direct: check for overflows on 32 bit DMA addresses")
-Reported-by: Marek Szyprowski <m.szyprowski@samsung.com>
-Signed-off-by: Christoph Hellwig <hch@lst.de>
-Acked-by: Marek Szyprowski <m.szyprowski@samsung.com>
-Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
----
- arch/x86/kernel/amd_gart_64.c | 4 ++--
- drivers/xen/swiotlb-xen.c | 4 ++--
- include/linux/dma-direct.h | 5 +++--
- kernel/dma/direct.c | 4 ++--
- kernel/dma/swiotlb.c | 2 +-
- 5 files changed, 10 insertions(+), 9 deletions(-)
-
---- a/arch/x86/kernel/amd_gart_64.c
-+++ b/arch/x86/kernel/amd_gart_64.c
-@@ -185,13 +185,13 @@ static void iommu_full(struct device *de
- static inline int
- need_iommu(struct device *dev, unsigned long addr, size_t size)
- {
-- return force_iommu || !dma_capable(dev, addr, size);
-+ return force_iommu || !dma_capable(dev, addr, size, true);
- }
-
- static inline int
- nonforced_iommu(struct device *dev, unsigned long addr, size_t size)
- {
-- return !dma_capable(dev, addr, size);
-+ return !dma_capable(dev, addr, size, true);
- }
-
- /* Map a single continuous physical area into the IOMMU.
---- a/drivers/xen/swiotlb-xen.c
-+++ b/drivers/xen/swiotlb-xen.c
-@@ -375,7 +375,7 @@ static dma_addr_t xen_swiotlb_map_page(s
- * we can safely return the device addr and not worry about bounce
- * buffering it.
- */
-- if (dma_capable(dev, dev_addr, size) &&
-+ if (dma_capable(dev, dev_addr, size, true) &&
- !range_straddles_page_boundary(phys, size) &&
- !xen_arch_need_swiotlb(dev, phys, dev_addr) &&
- swiotlb_force != SWIOTLB_FORCE)
-@@ -397,7 +397,7 @@ static dma_addr_t xen_swiotlb_map_page(s
- /*
- * Ensure that the address returned is DMA'ble
- */
-- if (unlikely(!dma_capable(dev, dev_addr, size))) {
-+ if (unlikely(!dma_capable(dev, dev_addr, size, true))) {
- swiotlb_tbl_unmap_single(dev, map, size, size, dir,
- attrs | DMA_ATTR_SKIP_CPU_SYNC);
- return DMA_MAPPING_ERROR;
---- a/include/linux/dma-direct.h
-+++ b/include/linux/dma-direct.h
-@@ -51,14 +51,15 @@ static inline phys_addr_t dma_to_phys(st
- return __sme_clr(__dma_to_phys(dev, daddr));
- }
-
--static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
-+static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size,
-+ bool is_ram)
- {
- dma_addr_t end = addr + size - 1;
-
- if (!dev->dma_mask)
- return false;
-
-- if (!IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
-+ if (is_ram && !IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
- min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn)))
- return false;
-
---- a/kernel/dma/direct.c
-+++ b/kernel/dma/direct.c
-@@ -325,7 +325,7 @@ static inline bool dma_direct_possible(s
- size_t size)
- {
- return swiotlb_force != SWIOTLB_FORCE &&
-- dma_capable(dev, dma_addr, size);
-+ dma_capable(dev, dma_addr, size, true);
- }
-
- dma_addr_t dma_direct_map_page(struct device *dev, struct page *page,
-@@ -374,7 +374,7 @@ dma_addr_t dma_direct_map_resource(struc
- {
- dma_addr_t dma_addr = paddr;
-
-- if (unlikely(!dma_capable(dev, dma_addr, size))) {
-+ if (unlikely(!dma_capable(dev, dma_addr, size, false))) {
- report_addr(dev, dma_addr, size);
- return DMA_MAPPING_ERROR;
- }
---- a/kernel/dma/swiotlb.c
-+++ b/kernel/dma/swiotlb.c
-@@ -678,7 +678,7 @@ bool swiotlb_map(struct device *dev, phy
-
- /* Ensure that the address returned is DMA'ble */
- *dma_addr = __phys_to_dma(dev, *phys);
-- if (unlikely(!dma_capable(dev, *dma_addr, size))) {
-+ if (unlikely(!dma_capable(dev, *dma_addr, size, true))) {
- swiotlb_tbl_unmap_single(dev, *phys, size, size, dir,
- attrs | DMA_ATTR_SKIP_CPU_SYNC);
- return false;
--- /dev/null
+From d5430c466b3c3b5f631ee37be333a40924575b72 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Thu, 21 Nov 2019 10:26:44 +0100
+Subject: [PATCH] dma-mapping: treat dev->bus_dma_mask as a DMA limit
+
+commit a7ba70f1787f977f970cd116076c6fce4b9e01cc upstream.
+
+Using a mask to represent bus DMA constraints has a set of limitations.
+The biggest one being it can only hold a power of two (minus one). The
+DMA mapping code is already aware of this and treats dev->bus_dma_mask
+as a limit. This quirk is already used by some architectures although
+still rare.
+
+With the introduction of the Raspberry Pi 4 we've found a new contender
+for the use of bus DMA limits, as its PCIe bus can only address the
+lower 3GB of memory (of a total of 4GB). This is impossible to represent
+with a mask. To make things worse the device-tree code rounds non power
+of two bus DMA limits to the next power of two, which is unacceptable in
+this case.
+
+In the light of this, rename dev->bus_dma_mask to dev->bus_dma_limit all
+over the tree and treat it as such. Note that dev->bus_dma_limit should
+contain the higher accessible DMA address.
+
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Robin Murphy <robin.murphy@arm.com>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+---
+ arch/mips/pci/fixup-sb1250.c | 16 ++++++++--------
+ arch/powerpc/sysdev/fsl_pci.c | 6 +++---
+ arch/x86/kernel/pci-dma.c | 2 +-
+ arch/x86/mm/mem_encrypt.c | 2 +-
+ arch/x86/pci/sta2x11-fixup.c | 2 +-
+ drivers/acpi/arm64/iort.c | 20 +++++++-------------
+ drivers/ata/ahci.c | 2 +-
+ drivers/iommu/dma-iommu.c | 3 +--
+ drivers/of/device.c | 9 +++++----
+ include/linux/device.h | 6 +++---
+ include/linux/dma-direct.h | 2 +-
+ include/linux/dma-mapping.h | 2 +-
+ kernel/dma/direct.c | 27 +++++++++++++--------------
+ 13 files changed, 46 insertions(+), 53 deletions(-)
+
+--- a/arch/mips/pci/fixup-sb1250.c
++++ b/arch/mips/pci/fixup-sb1250.c
+@@ -21,22 +21,22 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SI
+
+ /*
+ * The BCM1250, etc. PCI host bridge does not support DAC on its 32-bit
+- * bus, so we set the bus's DMA mask accordingly. However the HT link
++ * bus, so we set the bus's DMA limit accordingly. However the HT link
+ * down the artificial PCI-HT bridge supports 40-bit addressing and the
+ * SP1011 HT-PCI bridge downstream supports both DAC and a 64-bit bus
+ * width, so we record the PCI-HT bridge's secondary and subordinate bus
+- * numbers and do not set the mask for devices present in the inclusive
++ * numbers and do not set the limit for devices present in the inclusive
+ * range of those.
+ */
+-struct sb1250_bus_dma_mask_exclude {
++struct sb1250_bus_dma_limit_exclude {
+ bool set;
+ unsigned char start;
+ unsigned char end;
+ };
+
+-static int sb1250_bus_dma_mask(struct pci_dev *dev, void *data)
++static int sb1250_bus_dma_limit(struct pci_dev *dev, void *data)
+ {
+- struct sb1250_bus_dma_mask_exclude *exclude = data;
++ struct sb1250_bus_dma_limit_exclude *exclude = data;
+ bool exclude_this;
+ bool ht_bridge;
+
+@@ -55,7 +55,7 @@ static int sb1250_bus_dma_mask(struct pc
+ exclude->start, exclude->end);
+ } else {
+ dev_dbg(&dev->dev, "disabling DAC for device");
+- dev->dev.bus_dma_mask = DMA_BIT_MASK(32);
++ dev->dev.bus_dma_limit = DMA_BIT_MASK(32);
+ }
+
+ return 0;
+@@ -63,9 +63,9 @@ static int sb1250_bus_dma_mask(struct pc
+
+ static void quirk_sb1250_pci_dac(struct pci_dev *dev)
+ {
+- struct sb1250_bus_dma_mask_exclude exclude = { .set = false };
++ struct sb1250_bus_dma_limit_exclude exclude = { .set = false };
+
+- pci_walk_bus(dev->bus, sb1250_bus_dma_mask, &exclude);
++ pci_walk_bus(dev->bus, sb1250_bus_dma_limit, &exclude);
+ }
+ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SIBYTE, PCI_DEVICE_ID_BCM1250_PCI,
+ quirk_sb1250_pci_dac);
+--- a/arch/powerpc/sysdev/fsl_pci.c
++++ b/arch/powerpc/sysdev/fsl_pci.c
+@@ -115,8 +115,8 @@ static void pci_dma_dev_setup_swiotlb(st
+ {
+ struct pci_controller *hose = pci_bus_to_host(pdev->bus);
+
+- pdev->dev.bus_dma_mask =
+- hose->dma_window_base_cur + hose->dma_window_size;
++ pdev->dev.bus_dma_limit =
++ hose->dma_window_base_cur + hose->dma_window_size - 1;
+ }
+
+ static void setup_swiotlb_ops(struct pci_controller *hose)
+@@ -135,7 +135,7 @@ static void fsl_pci_dma_set_mask(struct
+ * mapping that allows addressing any RAM address from across PCI.
+ */
+ if (dev_is_pci(dev) && dma_mask >= pci64_dma_offset * 2 - 1) {
+- dev->bus_dma_mask = 0;
++ dev->bus_dma_limit = 0;
+ dev->archdata.dma_offset = pci64_dma_offset;
+ }
+ }
+--- a/arch/x86/kernel/pci-dma.c
++++ b/arch/x86/kernel/pci-dma.c
+@@ -146,7 +146,7 @@ rootfs_initcall(pci_iommu_init);
+
+ static int via_no_dac_cb(struct pci_dev *pdev, void *data)
+ {
+- pdev->dev.bus_dma_mask = DMA_BIT_MASK(32);
++ pdev->dev.bus_dma_limit = DMA_BIT_MASK(32);
+ return 0;
+ }
+
+--- a/arch/x86/mm/mem_encrypt.c
++++ b/arch/x86/mm/mem_encrypt.c
+@@ -367,7 +367,7 @@ bool force_dma_unencrypted(struct device
+ if (sme_active()) {
+ u64 dma_enc_mask = DMA_BIT_MASK(__ffs64(sme_me_mask));
+ u64 dma_dev_mask = min_not_zero(dev->coherent_dma_mask,
+- dev->bus_dma_mask);
++ dev->bus_dma_limit);
+
+ if (dma_dev_mask <= dma_enc_mask)
+ return true;
+--- a/arch/x86/pci/sta2x11-fixup.c
++++ b/arch/x86/pci/sta2x11-fixup.c
+@@ -143,7 +143,7 @@ static void sta2x11_map_ep(struct pci_de
+
+ dev->dma_pfn_offset = PFN_DOWN(-amba_base);
+
+- dev->bus_dma_mask = max_amba_addr;
++ dev->bus_dma_limit = max_amba_addr;
+ pci_set_consistent_dma_mask(pdev, max_amba_addr);
+ pci_set_dma_mask(pdev, max_amba_addr);
+
+--- a/drivers/acpi/arm64/iort.c
++++ b/drivers/acpi/arm64/iort.c
+@@ -1057,8 +1057,8 @@ static int rc_dma_get_range(struct devic
+ */
+ void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *dma_size)
+ {
+- u64 mask, dmaaddr = 0, size = 0, offset = 0;
+- int ret, msb;
++ u64 end, mask, dmaaddr = 0, size = 0, offset = 0;
++ int ret;
+
+ /*
+ * If @dev is expected to be DMA-capable then the bus code that created
+@@ -1085,19 +1085,13 @@ void iort_dma_setup(struct device *dev,
+ }
+
+ if (!ret) {
+- msb = fls64(dmaaddr + size - 1);
+ /*
+- * Round-up to the power-of-two mask or set
+- * the mask to the whole 64-bit address space
+- * in case the DMA region covers the full
+- * memory window.
++ * Limit coherent and dma mask based on size retrieved from
++ * firmware.
+ */
+- mask = msb == 64 ? U64_MAX : (1ULL << msb) - 1;
+- /*
+- * Limit coherent and dma mask based on size
+- * retrieved from firmware.
+- */
+- dev->bus_dma_mask = mask;
++ end = dmaaddr + size - 1;
++ mask = DMA_BIT_MASK(ilog2(end) + 1);
++ dev->bus_dma_limit = end;
+ dev->coherent_dma_mask = mask;
+ *dev->dma_mask = mask;
+ }
+--- a/drivers/ata/ahci.c
++++ b/drivers/ata/ahci.c
+@@ -899,7 +899,7 @@ static int ahci_configure_dma_masks(stru
+ * value, don't extend it here. This happens on STA2X11, for example.
+ *
+ * XXX: manipulating the DMA mask from platform code is completely
+- * bogus, platform code should use dev->bus_dma_mask instead..
++ * bogus, platform code should use dev->bus_dma_limit instead..
+ */
+ if (pdev->dma_mask && pdev->dma_mask < DMA_BIT_MASK(32))
+ return 0;
+--- a/drivers/iommu/dma-iommu.c
++++ b/drivers/iommu/dma-iommu.c
+@@ -404,8 +404,7 @@ static dma_addr_t iommu_dma_alloc_iova(s
+ if (iova_len < (1 << (IOVA_RANGE_CACHE_MAX_SIZE - 1)))
+ iova_len = roundup_pow_of_two(iova_len);
+
+- if (dev->bus_dma_mask)
+- dma_limit &= dev->bus_dma_mask;
++ dma_limit = min_not_zero(dma_limit, dev->bus_dma_limit);
+
+ if (domain->geometry.force_aperture)
+ dma_limit = min(dma_limit, domain->geometry.aperture_end);
+--- a/drivers/of/device.c
++++ b/drivers/of/device.c
+@@ -93,7 +93,7 @@ int of_dma_configure(struct device *dev,
+ bool coherent;
+ unsigned long offset;
+ const struct iommu_ops *iommu;
+- u64 mask;
++ u64 mask, end;
+
+ ret = of_dma_get_range(np, &dma_addr, &paddr, &size);
+ if (ret < 0) {
+@@ -148,12 +148,13 @@ int of_dma_configure(struct device *dev,
+ * Limit coherent and dma mask based on size and default mask
+ * set by the driver.
+ */
+- mask = DMA_BIT_MASK(ilog2(dma_addr + size - 1) + 1);
++ end = dma_addr + size - 1;
++ mask = DMA_BIT_MASK(ilog2(end) + 1);
+ dev->coherent_dma_mask &= mask;
+ *dev->dma_mask &= mask;
+- /* ...but only set bus mask if we found valid dma-ranges earlier */
++ /* ...but only set bus limit if we found valid dma-ranges earlier */
+ if (!ret)
+- dev->bus_dma_mask = mask;
++ dev->bus_dma_limit = end;
+
+ coherent = of_dma_is_coherent(np);
+ dev_dbg(dev, "device is%sdma coherent\n",
+--- a/include/linux/device.h
++++ b/include/linux/device.h
+@@ -1186,8 +1186,8 @@ struct dev_links_info {
+ * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all
+ * hardware supports 64-bit addresses for consistent allocations
+ * such descriptors.
+- * @bus_dma_mask: Mask of an upstream bridge or bus which imposes a smaller DMA
+- * limit than the device itself supports.
++ * @bus_dma_limit: Limit of an upstream bridge or bus which imposes a smaller
++ * DMA limit than the device itself supports.
+ * @dma_pfn_offset: offset of DMA memory range relatively of RAM
+ * @dma_parms: A low level driver may set these to teach IOMMU code about
+ * segment limitations.
+@@ -1270,7 +1270,7 @@ struct device {
+ not all hardware supports
+ 64 bit addresses for consistent
+ allocations such descriptors. */
+- u64 bus_dma_mask; /* upstream dma_mask constraint */
++ u64 bus_dma_limit; /* upstream dma constraint */
+ unsigned long dma_pfn_offset;
+
+ struct device_dma_parameters *dma_parms;
+--- a/include/linux/dma-direct.h
++++ b/include/linux/dma-direct.h
+@@ -63,7 +63,7 @@ static inline bool dma_capable(struct de
+ min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn)))
+ return false;
+
+- return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
++ return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_limit);
+ }
+
+ u64 dma_direct_get_required_mask(struct device *dev);
+--- a/include/linux/dma-mapping.h
++++ b/include/linux/dma-mapping.h
+@@ -697,7 +697,7 @@ static inline int dma_coerce_mask_and_co
+ */
+ static inline bool dma_addressing_limited(struct device *dev)
+ {
+- return min_not_zero(dma_get_mask(dev), dev->bus_dma_mask) <
++ return min_not_zero(dma_get_mask(dev), dev->bus_dma_limit) <
+ dma_get_required_mask(dev);
+ }
+
+--- a/kernel/dma/direct.c
++++ b/kernel/dma/direct.c
+@@ -26,10 +26,10 @@ static void report_addr(struct device *d
+ {
+ if (!dev->dma_mask) {
+ dev_err_once(dev, "DMA map on device without dma_mask\n");
+- } else if (*dev->dma_mask >= DMA_BIT_MASK(32) || dev->bus_dma_mask) {
++ } else if (*dev->dma_mask >= DMA_BIT_MASK(32) || dev->bus_dma_limit) {
+ dev_err_once(dev,
+- "overflow %pad+%zu of DMA mask %llx bus mask %llx\n",
+- &dma_addr, size, *dev->dma_mask, dev->bus_dma_mask);
++ "overflow %pad+%zu of DMA mask %llx bus limit %llx\n",
++ &dma_addr, size, *dev->dma_mask, dev->bus_dma_limit);
+ }
+ WARN_ON_ONCE(1);
+ }
+@@ -50,15 +50,14 @@ u64 dma_direct_get_required_mask(struct
+ }
+
+ static gfp_t __dma_direct_optimal_gfp_mask(struct device *dev, u64 dma_mask,
+- u64 *phys_mask)
++ u64 *phys_limit)
+ {
+- if (dev->bus_dma_mask && dev->bus_dma_mask < dma_mask)
+- dma_mask = dev->bus_dma_mask;
++ u64 dma_limit = min_not_zero(dma_mask, dev->bus_dma_limit);
+
+ if (force_dma_unencrypted(dev))
+- *phys_mask = __dma_to_phys(dev, dma_mask);
++ *phys_limit = __dma_to_phys(dev, dma_limit);
+ else
+- *phys_mask = dma_to_phys(dev, dma_mask);
++ *phys_limit = dma_to_phys(dev, dma_limit);
+
+ /*
+ * Optimistically try the zone that the physical address mask falls
+@@ -68,9 +67,9 @@ static gfp_t __dma_direct_optimal_gfp_ma
+ * Note that GFP_DMA32 and GFP_DMA are no ops without the corresponding
+ * zones.
+ */
+- if (*phys_mask <= DMA_BIT_MASK(zone_dma_bits))
++ if (*phys_limit <= DMA_BIT_MASK(zone_dma_bits))
+ return GFP_DMA;
+- if (*phys_mask <= DMA_BIT_MASK(32))
++ if (*phys_limit <= DMA_BIT_MASK(32))
+ return GFP_DMA32;
+ return 0;
+ }
+@@ -78,7 +77,7 @@ static gfp_t __dma_direct_optimal_gfp_ma
+ static bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
+ {
+ return phys_to_dma_direct(dev, phys) + size - 1 <=
+- min_not_zero(dev->coherent_dma_mask, dev->bus_dma_mask);
++ min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit);
+ }
+
+ struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
+@@ -87,7 +86,7 @@ struct page *__dma_direct_alloc_pages(st
+ size_t alloc_size = PAGE_ALIGN(size);
+ int node = dev_to_node(dev);
+ struct page *page = NULL;
+- u64 phys_mask;
++ u64 phys_limit;
+
+ if (attrs & DMA_ATTR_NO_WARN)
+ gfp |= __GFP_NOWARN;
+@@ -95,7 +94,7 @@ struct page *__dma_direct_alloc_pages(st
+ /* we always manually zero the memory once we are done: */
+ gfp &= ~__GFP_ZERO;
+ gfp |= __dma_direct_optimal_gfp_mask(dev, dev->coherent_dma_mask,
+- &phys_mask);
++ &phys_limit);
+ page = dma_alloc_contiguous(dev, alloc_size, gfp);
+ if (page && !dma_coherent_ok(dev, page_to_phys(page), size)) {
+ dma_free_contiguous(dev, page, alloc_size);
+@@ -109,7 +108,7 @@ again:
+ page = NULL;
+
+ if (IS_ENABLED(CONFIG_ZONE_DMA32) &&
+- phys_mask < DMA_BIT_MASK(64) &&
++ phys_limit < DMA_BIT_MASK(64) &&
+ !(gfp & (GFP_DMA32 | GFP_DMA))) {
+ gfp |= GFP_DMA32;
+ goto again;
--- /dev/null
+From 0ec0bc884f6cf1ec9775c750f78ce28be7da4340 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Mon, 16 Dec 2019 12:01:08 +0100
+Subject: [PATCH] ARM: dts: bcm2711: Enable PCIe controller
+
+commit d5c8dc0d4c880fbde5293cc186b1ab23466254c4 upstream.
+
+This enables bcm2711's PCIe bus, which is hardwired to a VIA
+Technologies XHCI USB 3.0 controller.
+
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+---
+ arch/arm/boot/dts/bcm2711.dtsi | 31 ++++++++++++++++++++++++++++++-
+ 1 file changed, 30 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/bcm2711.dtsi
++++ b/arch/arm/boot/dts/bcm2711.dtsi
+@@ -331,7 +331,36 @@
+ #address-cells = <2>;
+ #size-cells = <1>;
+
+- ranges = <0x0 0x7c000000 0x0 0xfc000000 0x03800000>;
++ ranges = <0x0 0x7c000000 0x0 0xfc000000 0x03800000>,
++ <0x6 0x00000000 0x6 0x00000000 0x40000000>;
++
++ pcie0: pcie@7d500000 {
++ compatible = "brcm,bcm2711-pcie";
++ reg = <0x0 0x7d500000 0x9310>;
++ device_type = "pci";
++ #address-cells = <3>;
++ #interrupt-cells = <1>;
++ #size-cells = <2>;
++ interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>,
++ <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "pcie", "msi";
++ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
++ interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 143
++ IRQ_TYPE_LEVEL_HIGH>;
++ msi-controller;
++ msi-parent = <&pcie0>;
++
++ ranges = <0x02000000 0x0 0xf8000000 0x6 0x00000000
++ 0x0 0x04000000>;
++ /*
++ * The wrapper around the PCIe block has a bug
++ * preventing it from accessing beyond the first 3GB of
++ * memory.
++ */
++ dma-ranges = <0x02000000 0x0 0x00000000 0x0 0x00000000
++ 0x0 0xc0000000>;
++ brcm,enable-ssc;
++ };
+
+ genet: ethernet@7d580000 {
+ compatible = "brcm,bcm2711-genet-v5";
+++ /dev/null
-From d5430c466b3c3b5f631ee37be333a40924575b72 Mon Sep 17 00:00:00 2001
-From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Date: Thu, 21 Nov 2019 10:26:44 +0100
-Subject: [PATCH] dma-mapping: treat dev->bus_dma_mask as a DMA limit
-
-commit a7ba70f1787f977f970cd116076c6fce4b9e01cc upstream.
-
-Using a mask to represent bus DMA constraints has a set of limitations.
-The biggest one being it can only hold a power of two (minus one). The
-DMA mapping code is already aware of this and treats dev->bus_dma_mask
-as a limit. This quirk is already used by some architectures although
-still rare.
-
-With the introduction of the Raspberry Pi 4 we've found a new contender
-for the use of bus DMA limits, as its PCIe bus can only address the
-lower 3GB of memory (of a total of 4GB). This is impossible to represent
-with a mask. To make things worse the device-tree code rounds non power
-of two bus DMA limits to the next power of two, which is unacceptable in
-this case.
-
-In the light of this, rename dev->bus_dma_mask to dev->bus_dma_limit all
-over the tree and treat it as such. Note that dev->bus_dma_limit should
-contain the higher accessible DMA address.
-
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Reviewed-by: Robin Murphy <robin.murphy@arm.com>
-Signed-off-by: Christoph Hellwig <hch@lst.de>
----
- arch/mips/pci/fixup-sb1250.c | 16 ++++++++--------
- arch/powerpc/sysdev/fsl_pci.c | 6 +++---
- arch/x86/kernel/pci-dma.c | 2 +-
- arch/x86/mm/mem_encrypt.c | 2 +-
- arch/x86/pci/sta2x11-fixup.c | 2 +-
- drivers/acpi/arm64/iort.c | 20 +++++++-------------
- drivers/ata/ahci.c | 2 +-
- drivers/iommu/dma-iommu.c | 3 +--
- drivers/of/device.c | 9 +++++----
- include/linux/device.h | 6 +++---
- include/linux/dma-direct.h | 2 +-
- include/linux/dma-mapping.h | 2 +-
- kernel/dma/direct.c | 27 +++++++++++++--------------
- 13 files changed, 46 insertions(+), 53 deletions(-)
-
---- a/arch/mips/pci/fixup-sb1250.c
-+++ b/arch/mips/pci/fixup-sb1250.c
-@@ -21,22 +21,22 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SI
-
- /*
- * The BCM1250, etc. PCI host bridge does not support DAC on its 32-bit
-- * bus, so we set the bus's DMA mask accordingly. However the HT link
-+ * bus, so we set the bus's DMA limit accordingly. However the HT link
- * down the artificial PCI-HT bridge supports 40-bit addressing and the
- * SP1011 HT-PCI bridge downstream supports both DAC and a 64-bit bus
- * width, so we record the PCI-HT bridge's secondary and subordinate bus
-- * numbers and do not set the mask for devices present in the inclusive
-+ * numbers and do not set the limit for devices present in the inclusive
- * range of those.
- */
--struct sb1250_bus_dma_mask_exclude {
-+struct sb1250_bus_dma_limit_exclude {
- bool set;
- unsigned char start;
- unsigned char end;
- };
-
--static int sb1250_bus_dma_mask(struct pci_dev *dev, void *data)
-+static int sb1250_bus_dma_limit(struct pci_dev *dev, void *data)
- {
-- struct sb1250_bus_dma_mask_exclude *exclude = data;
-+ struct sb1250_bus_dma_limit_exclude *exclude = data;
- bool exclude_this;
- bool ht_bridge;
-
-@@ -55,7 +55,7 @@ static int sb1250_bus_dma_mask(struct pc
- exclude->start, exclude->end);
- } else {
- dev_dbg(&dev->dev, "disabling DAC for device");
-- dev->dev.bus_dma_mask = DMA_BIT_MASK(32);
-+ dev->dev.bus_dma_limit = DMA_BIT_MASK(32);
- }
-
- return 0;
-@@ -63,9 +63,9 @@ static int sb1250_bus_dma_mask(struct pc
-
- static void quirk_sb1250_pci_dac(struct pci_dev *dev)
- {
-- struct sb1250_bus_dma_mask_exclude exclude = { .set = false };
-+ struct sb1250_bus_dma_limit_exclude exclude = { .set = false };
-
-- pci_walk_bus(dev->bus, sb1250_bus_dma_mask, &exclude);
-+ pci_walk_bus(dev->bus, sb1250_bus_dma_limit, &exclude);
- }
- DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SIBYTE, PCI_DEVICE_ID_BCM1250_PCI,
- quirk_sb1250_pci_dac);
---- a/arch/powerpc/sysdev/fsl_pci.c
-+++ b/arch/powerpc/sysdev/fsl_pci.c
-@@ -115,8 +115,8 @@ static void pci_dma_dev_setup_swiotlb(st
- {
- struct pci_controller *hose = pci_bus_to_host(pdev->bus);
-
-- pdev->dev.bus_dma_mask =
-- hose->dma_window_base_cur + hose->dma_window_size;
-+ pdev->dev.bus_dma_limit =
-+ hose->dma_window_base_cur + hose->dma_window_size - 1;
- }
-
- static void setup_swiotlb_ops(struct pci_controller *hose)
-@@ -135,7 +135,7 @@ static void fsl_pci_dma_set_mask(struct
- * mapping that allows addressing any RAM address from across PCI.
- */
- if (dev_is_pci(dev) && dma_mask >= pci64_dma_offset * 2 - 1) {
-- dev->bus_dma_mask = 0;
-+ dev->bus_dma_limit = 0;
- dev->archdata.dma_offset = pci64_dma_offset;
- }
- }
---- a/arch/x86/kernel/pci-dma.c
-+++ b/arch/x86/kernel/pci-dma.c
-@@ -146,7 +146,7 @@ rootfs_initcall(pci_iommu_init);
-
- static int via_no_dac_cb(struct pci_dev *pdev, void *data)
- {
-- pdev->dev.bus_dma_mask = DMA_BIT_MASK(32);
-+ pdev->dev.bus_dma_limit = DMA_BIT_MASK(32);
- return 0;
- }
-
---- a/arch/x86/mm/mem_encrypt.c
-+++ b/arch/x86/mm/mem_encrypt.c
-@@ -367,7 +367,7 @@ bool force_dma_unencrypted(struct device
- if (sme_active()) {
- u64 dma_enc_mask = DMA_BIT_MASK(__ffs64(sme_me_mask));
- u64 dma_dev_mask = min_not_zero(dev->coherent_dma_mask,
-- dev->bus_dma_mask);
-+ dev->bus_dma_limit);
-
- if (dma_dev_mask <= dma_enc_mask)
- return true;
---- a/arch/x86/pci/sta2x11-fixup.c
-+++ b/arch/x86/pci/sta2x11-fixup.c
-@@ -143,7 +143,7 @@ static void sta2x11_map_ep(struct pci_de
-
- dev->dma_pfn_offset = PFN_DOWN(-amba_base);
-
-- dev->bus_dma_mask = max_amba_addr;
-+ dev->bus_dma_limit = max_amba_addr;
- pci_set_consistent_dma_mask(pdev, max_amba_addr);
- pci_set_dma_mask(pdev, max_amba_addr);
-
---- a/drivers/acpi/arm64/iort.c
-+++ b/drivers/acpi/arm64/iort.c
-@@ -1057,8 +1057,8 @@ static int rc_dma_get_range(struct devic
- */
- void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *dma_size)
- {
-- u64 mask, dmaaddr = 0, size = 0, offset = 0;
-- int ret, msb;
-+ u64 end, mask, dmaaddr = 0, size = 0, offset = 0;
-+ int ret;
-
- /*
- * If @dev is expected to be DMA-capable then the bus code that created
-@@ -1085,19 +1085,13 @@ void iort_dma_setup(struct device *dev,
- }
-
- if (!ret) {
-- msb = fls64(dmaaddr + size - 1);
- /*
-- * Round-up to the power-of-two mask or set
-- * the mask to the whole 64-bit address space
-- * in case the DMA region covers the full
-- * memory window.
-+ * Limit coherent and dma mask based on size retrieved from
-+ * firmware.
- */
-- mask = msb == 64 ? U64_MAX : (1ULL << msb) - 1;
-- /*
-- * Limit coherent and dma mask based on size
-- * retrieved from firmware.
-- */
-- dev->bus_dma_mask = mask;
-+ end = dmaaddr + size - 1;
-+ mask = DMA_BIT_MASK(ilog2(end) + 1);
-+ dev->bus_dma_limit = end;
- dev->coherent_dma_mask = mask;
- *dev->dma_mask = mask;
- }
---- a/drivers/ata/ahci.c
-+++ b/drivers/ata/ahci.c
-@@ -899,7 +899,7 @@ static int ahci_configure_dma_masks(stru
- * value, don't extend it here. This happens on STA2X11, for example.
- *
- * XXX: manipulating the DMA mask from platform code is completely
-- * bogus, platform code should use dev->bus_dma_mask instead..
-+ * bogus, platform code should use dev->bus_dma_limit instead..
- */
- if (pdev->dma_mask && pdev->dma_mask < DMA_BIT_MASK(32))
- return 0;
---- a/drivers/iommu/dma-iommu.c
-+++ b/drivers/iommu/dma-iommu.c
-@@ -404,8 +404,7 @@ static dma_addr_t iommu_dma_alloc_iova(s
- if (iova_len < (1 << (IOVA_RANGE_CACHE_MAX_SIZE - 1)))
- iova_len = roundup_pow_of_two(iova_len);
-
-- if (dev->bus_dma_mask)
-- dma_limit &= dev->bus_dma_mask;
-+ dma_limit = min_not_zero(dma_limit, dev->bus_dma_limit);
-
- if (domain->geometry.force_aperture)
- dma_limit = min(dma_limit, domain->geometry.aperture_end);
---- a/drivers/of/device.c
-+++ b/drivers/of/device.c
-@@ -93,7 +93,7 @@ int of_dma_configure(struct device *dev,
- bool coherent;
- unsigned long offset;
- const struct iommu_ops *iommu;
-- u64 mask;
-+ u64 mask, end;
-
- ret = of_dma_get_range(np, &dma_addr, &paddr, &size);
- if (ret < 0) {
-@@ -148,12 +148,13 @@ int of_dma_configure(struct device *dev,
- * Limit coherent and dma mask based on size and default mask
- * set by the driver.
- */
-- mask = DMA_BIT_MASK(ilog2(dma_addr + size - 1) + 1);
-+ end = dma_addr + size - 1;
-+ mask = DMA_BIT_MASK(ilog2(end) + 1);
- dev->coherent_dma_mask &= mask;
- *dev->dma_mask &= mask;
-- /* ...but only set bus mask if we found valid dma-ranges earlier */
-+ /* ...but only set bus limit if we found valid dma-ranges earlier */
- if (!ret)
-- dev->bus_dma_mask = mask;
-+ dev->bus_dma_limit = end;
-
- coherent = of_dma_is_coherent(np);
- dev_dbg(dev, "device is%sdma coherent\n",
---- a/include/linux/device.h
-+++ b/include/linux/device.h
-@@ -1186,8 +1186,8 @@ struct dev_links_info {
- * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all
- * hardware supports 64-bit addresses for consistent allocations
- * such descriptors.
-- * @bus_dma_mask: Mask of an upstream bridge or bus which imposes a smaller DMA
-- * limit than the device itself supports.
-+ * @bus_dma_limit: Limit of an upstream bridge or bus which imposes a smaller
-+ * DMA limit than the device itself supports.
- * @dma_pfn_offset: offset of DMA memory range relatively of RAM
- * @dma_parms: A low level driver may set these to teach IOMMU code about
- * segment limitations.
-@@ -1270,7 +1270,7 @@ struct device {
- not all hardware supports
- 64 bit addresses for consistent
- allocations such descriptors. */
-- u64 bus_dma_mask; /* upstream dma_mask constraint */
-+ u64 bus_dma_limit; /* upstream dma constraint */
- unsigned long dma_pfn_offset;
-
- struct device_dma_parameters *dma_parms;
---- a/include/linux/dma-direct.h
-+++ b/include/linux/dma-direct.h
-@@ -63,7 +63,7 @@ static inline bool dma_capable(struct de
- min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn)))
- return false;
-
-- return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
-+ return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_limit);
- }
-
- u64 dma_direct_get_required_mask(struct device *dev);
---- a/include/linux/dma-mapping.h
-+++ b/include/linux/dma-mapping.h
-@@ -697,7 +697,7 @@ static inline int dma_coerce_mask_and_co
- */
- static inline bool dma_addressing_limited(struct device *dev)
- {
-- return min_not_zero(dma_get_mask(dev), dev->bus_dma_mask) <
-+ return min_not_zero(dma_get_mask(dev), dev->bus_dma_limit) <
- dma_get_required_mask(dev);
- }
-
---- a/kernel/dma/direct.c
-+++ b/kernel/dma/direct.c
-@@ -26,10 +26,10 @@ static void report_addr(struct device *d
- {
- if (!dev->dma_mask) {
- dev_err_once(dev, "DMA map on device without dma_mask\n");
-- } else if (*dev->dma_mask >= DMA_BIT_MASK(32) || dev->bus_dma_mask) {
-+ } else if (*dev->dma_mask >= DMA_BIT_MASK(32) || dev->bus_dma_limit) {
- dev_err_once(dev,
-- "overflow %pad+%zu of DMA mask %llx bus mask %llx\n",
-- &dma_addr, size, *dev->dma_mask, dev->bus_dma_mask);
-+ "overflow %pad+%zu of DMA mask %llx bus limit %llx\n",
-+ &dma_addr, size, *dev->dma_mask, dev->bus_dma_limit);
- }
- WARN_ON_ONCE(1);
- }
-@@ -50,15 +50,14 @@ u64 dma_direct_get_required_mask(struct
- }
-
- static gfp_t __dma_direct_optimal_gfp_mask(struct device *dev, u64 dma_mask,
-- u64 *phys_mask)
-+ u64 *phys_limit)
- {
-- if (dev->bus_dma_mask && dev->bus_dma_mask < dma_mask)
-- dma_mask = dev->bus_dma_mask;
-+ u64 dma_limit = min_not_zero(dma_mask, dev->bus_dma_limit);
-
- if (force_dma_unencrypted(dev))
-- *phys_mask = __dma_to_phys(dev, dma_mask);
-+ *phys_limit = __dma_to_phys(dev, dma_limit);
- else
-- *phys_mask = dma_to_phys(dev, dma_mask);
-+ *phys_limit = dma_to_phys(dev, dma_limit);
-
- /*
- * Optimistically try the zone that the physical address mask falls
-@@ -68,9 +67,9 @@ static gfp_t __dma_direct_optimal_gfp_ma
- * Note that GFP_DMA32 and GFP_DMA are no ops without the corresponding
- * zones.
- */
-- if (*phys_mask <= DMA_BIT_MASK(zone_dma_bits))
-+ if (*phys_limit <= DMA_BIT_MASK(zone_dma_bits))
- return GFP_DMA;
-- if (*phys_mask <= DMA_BIT_MASK(32))
-+ if (*phys_limit <= DMA_BIT_MASK(32))
- return GFP_DMA32;
- return 0;
- }
-@@ -78,7 +77,7 @@ static gfp_t __dma_direct_optimal_gfp_ma
- static bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
- {
- return phys_to_dma_direct(dev, phys) + size - 1 <=
-- min_not_zero(dev->coherent_dma_mask, dev->bus_dma_mask);
-+ min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit);
- }
-
- struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
-@@ -87,7 +86,7 @@ struct page *__dma_direct_alloc_pages(st
- size_t alloc_size = PAGE_ALIGN(size);
- int node = dev_to_node(dev);
- struct page *page = NULL;
-- u64 phys_mask;
-+ u64 phys_limit;
-
- if (attrs & DMA_ATTR_NO_WARN)
- gfp |= __GFP_NOWARN;
-@@ -95,7 +94,7 @@ struct page *__dma_direct_alloc_pages(st
- /* we always manually zero the memory once we are done: */
- gfp &= ~__GFP_ZERO;
- gfp |= __dma_direct_optimal_gfp_mask(dev, dev->coherent_dma_mask,
-- &phys_mask);
-+ &phys_limit);
- page = dma_alloc_contiguous(dev, alloc_size, gfp);
- if (page && !dma_coherent_ok(dev, page_to_phys(page), size)) {
- dma_free_contiguous(dev, page, alloc_size);
-@@ -109,7 +108,7 @@ again:
- page = NULL;
-
- if (IS_ENABLED(CONFIG_ZONE_DMA32) &&
-- phys_mask < DMA_BIT_MASK(64) &&
-+ phys_limit < DMA_BIT_MASK(64) &&
- !(gfp & (GFP_DMA32 | GFP_DMA))) {
- gfp |= GFP_DMA32;
- goto again;
+++ /dev/null
-From 0ec0bc884f6cf1ec9775c750f78ce28be7da4340 Mon Sep 17 00:00:00 2001
-From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Date: Mon, 16 Dec 2019 12:01:08 +0100
-Subject: [PATCH] ARM: dts: bcm2711: Enable PCIe controller
-
-commit d5c8dc0d4c880fbde5293cc186b1ab23466254c4 upstream.
-
-This enables bcm2711's PCIe bus, which is hardwired to a VIA
-Technologies XHCI USB 3.0 controller.
-
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
----
- arch/arm/boot/dts/bcm2711.dtsi | 31 ++++++++++++++++++++++++++++++-
- 1 file changed, 30 insertions(+), 1 deletion(-)
-
---- a/arch/arm/boot/dts/bcm2711.dtsi
-+++ b/arch/arm/boot/dts/bcm2711.dtsi
-@@ -331,7 +331,36 @@
- #address-cells = <2>;
- #size-cells = <1>;
-
-- ranges = <0x0 0x7c000000 0x0 0xfc000000 0x03800000>;
-+ ranges = <0x0 0x7c000000 0x0 0xfc000000 0x03800000>,
-+ <0x6 0x00000000 0x6 0x00000000 0x40000000>;
-+
-+ pcie0: pcie@7d500000 {
-+ compatible = "brcm,bcm2711-pcie";
-+ reg = <0x0 0x7d500000 0x9310>;
-+ device_type = "pci";
-+ #address-cells = <3>;
-+ #interrupt-cells = <1>;
-+ #size-cells = <2>;
-+ interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>,
-+ <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
-+ interrupt-names = "pcie", "msi";
-+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
-+ interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 143
-+ IRQ_TYPE_LEVEL_HIGH>;
-+ msi-controller;
-+ msi-parent = <&pcie0>;
-+
-+ ranges = <0x02000000 0x0 0xf8000000 0x6 0x00000000
-+ 0x0 0x04000000>;
-+ /*
-+ * The wrapper around the PCIe block has a bug
-+ * preventing it from accessing beyond the first 3GB of
-+ * memory.
-+ */
-+ dma-ranges = <0x02000000 0x0 0x00000000 0x0 0x00000000
-+ 0x0 0xc0000000>;
-+ brcm,enable-ssc;
-+ };
-
- genet: ethernet@7d580000 {
- compatible = "brcm,bcm2711-genet-v5";
--- /dev/null
+From 4d9470c29736bf81bdb0d21da24cf350b1e99402 Mon Sep 17 00:00:00 2001
+From: Jim Quinlan <james.quinlan@broadcom.com>
+Date: Mon, 16 Dec 2019 12:01:09 +0100
+Subject: [PATCH] PCI: brcmstb: Add Broadcom STB PCIe host controller
+ driver
+
+commit c0452137034bda8f686dd9a2e167949bfffd6776 upstream.
+
+This adds a basic driver for Broadcom's STB PCIe controller, for now
+aimed at Raspberry Pi 4's SoC, bcm2711.
+
+Signed-off-by: Jim Quinlan <james.quinlan@broadcom.com>
+Co-developed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+[lorenzo.pieralisi@arm.com: updated brcm_pcie_get_rc_bar2_size_and_offset()according to https://lore.kernel.org/linux-pci/be8ddb33a7360af1815cf686f77f3f0913d02be3.camel@suse.de]
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+Reviewed-by: Andrew Murray <andrew.murray@arm.com>
+Reviewed-by: Jeremy Linton <jeremy.linton@arm.com>
+---
+ drivers/pci/controller/Kconfig | 8 +
+ drivers/pci/controller/Makefile | 1 +
+ drivers/pci/controller/pcie-brcmstb.c | 755 ++++++++++++++++++++++++++
+ 3 files changed, 764 insertions(+)
+ create mode 100644 drivers/pci/controller/pcie-brcmstb.c
+
+--- a/drivers/pci/controller/Kconfig
++++ b/drivers/pci/controller/Kconfig
+@@ -281,6 +281,14 @@ config VMD
+ To compile this driver as a module, choose M here: the
+ module will be called vmd.
+
++config PCIE_BRCMSTB
++ tristate "Broadcom Brcmstb PCIe host controller"
++ depends on ARCH_BCM2835 || COMPILE_TEST
++ depends on OF
++ help
++ Say Y here to enable PCIe host controller support for
++ Broadcom STB based SoCs, like the Raspberry Pi 4.
++
+ config PCI_HYPERV_INTERFACE
+ tristate "Hyper-V PCI Interface"
+ depends on X86 && HYPERV && PCI_MSI && PCI_MSI_IRQ_DOMAIN && X86_64
+--- a/drivers/pci/controller/Makefile
++++ b/drivers/pci/controller/Makefile
+@@ -30,6 +30,7 @@ obj-$(CONFIG_PCIE_MEDIATEK) += pcie-medi
+ obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o
+ obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o
+ obj-$(CONFIG_VMD) += vmd.o
++obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o
+ # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW
+ obj-y += dwc/
+
+--- /dev/null
++++ b/drivers/pci/controller/pcie-brcmstb.c
+@@ -0,0 +1,755 @@
++// SPDX-License-Identifier: GPL-2.0+
++/* Copyright (C) 2009 - 2019 Broadcom */
++
++#include <linux/bitfield.h>
++#include <linux/clk.h>
++#include <linux/compiler.h>
++#include <linux/delay.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/ioport.h>
++#include <linux/irqdomain.h>
++#include <linux/kernel.h>
++#include <linux/list.h>
++#include <linux/log2.h>
++#include <linux/module.h>
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++#include <linux/of_pci.h>
++#include <linux/of_platform.h>
++#include <linux/pci.h>
++#include <linux/printk.h>
++#include <linux/sizes.h>
++#include <linux/slab.h>
++#include <linux/string.h>
++#include <linux/types.h>
++
++#include "../pci.h"
++
++/* BRCM_PCIE_CAP_REGS - Offset for the mandatory capability config regs */
++#define BRCM_PCIE_CAP_REGS 0x00ac
++
++/* Broadcom STB PCIe Register Offsets */
++#define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1 0x0188
++#define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK 0xc
++#define PCIE_RC_CFG_VENDOR_SPCIFIC_REG1_LITTLE_ENDIAN 0x0
++
++#define PCIE_RC_CFG_PRIV1_ID_VAL3 0x043c
++#define PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK 0xffffff
++
++#define PCIE_RC_DL_MDIO_ADDR 0x1100
++#define PCIE_RC_DL_MDIO_WR_DATA 0x1104
++#define PCIE_RC_DL_MDIO_RD_DATA 0x1108
++
++#define PCIE_MISC_MISC_CTRL 0x4008
++#define PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK 0x1000
++#define PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK 0x2000
++#define PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK 0x300000
++#define PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_128 0x0
++#define PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK 0xf8000000
++
++#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO 0x400c
++#define PCIE_MEM_WIN0_LO(win) \
++ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO + ((win) * 4)
++
++#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI 0x4010
++#define PCIE_MEM_WIN0_HI(win) \
++ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI + ((win) * 4)
++
++#define PCIE_MISC_RC_BAR1_CONFIG_LO 0x402c
++#define PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK 0x1f
++
++#define PCIE_MISC_RC_BAR2_CONFIG_LO 0x4034
++#define PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK 0x1f
++#define PCIE_MISC_RC_BAR2_CONFIG_HI 0x4038
++
++#define PCIE_MISC_RC_BAR3_CONFIG_LO 0x403c
++#define PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK 0x1f
++
++#define PCIE_MISC_PCIE_CTRL 0x4064
++#define PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK 0x1
++
++#define PCIE_MISC_PCIE_STATUS 0x4068
++#define PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK 0x80
++#define PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK 0x20
++#define PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK 0x10
++#define PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK 0x40
++
++#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT 0x4070
++#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK 0xfff00000
++#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK 0xfff0
++#define PCIE_MEM_WIN0_BASE_LIMIT(win) \
++ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT + ((win) * 4)
++
++#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI 0x4080
++#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK 0xff
++#define PCIE_MEM_WIN0_BASE_HI(win) \
++ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI + ((win) * 8)
++
++#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI 0x4084
++#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK 0xff
++#define PCIE_MEM_WIN0_LIMIT_HI(win) \
++ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI + ((win) * 8)
++
++#define PCIE_MISC_HARD_PCIE_HARD_DEBUG 0x4204
++#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK 0x2
++#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x08000000
++
++#define PCIE_MSI_INTR2_STATUS 0x4500
++#define PCIE_MSI_INTR2_CLR 0x4508
++#define PCIE_MSI_INTR2_MASK_SET 0x4510
++#define PCIE_MSI_INTR2_MASK_CLR 0x4514
++
++#define PCIE_EXT_CFG_DATA 0x8000
++
++#define PCIE_EXT_CFG_INDEX 0x9000
++#define PCIE_EXT_BUSNUM_SHIFT 20
++#define PCIE_EXT_SLOT_SHIFT 15
++#define PCIE_EXT_FUNC_SHIFT 12
++
++#define PCIE_RGR1_SW_INIT_1 0x9210
++#define PCIE_RGR1_SW_INIT_1_PERST_MASK 0x1
++#define PCIE_RGR1_SW_INIT_1_INIT_MASK 0x2
++
++/* PCIe parameters */
++#define BRCM_NUM_PCIE_OUT_WINS 0x4
++
++/* MDIO registers */
++#define MDIO_PORT0 0x0
++#define MDIO_DATA_MASK 0x7fffffff
++#define MDIO_PORT_MASK 0xf0000
++#define MDIO_REGAD_MASK 0xffff
++#define MDIO_CMD_MASK 0xfff00000
++#define MDIO_CMD_READ 0x1
++#define MDIO_CMD_WRITE 0x0
++#define MDIO_DATA_DONE_MASK 0x80000000
++#define MDIO_RD_DONE(x) (((x) & MDIO_DATA_DONE_MASK) ? 1 : 0)
++#define MDIO_WT_DONE(x) (((x) & MDIO_DATA_DONE_MASK) ? 0 : 1)
++#define SSC_REGS_ADDR 0x1100
++#define SET_ADDR_OFFSET 0x1f
++#define SSC_CNTL_OFFSET 0x2
++#define SSC_CNTL_OVRD_EN_MASK 0x8000
++#define SSC_CNTL_OVRD_VAL_MASK 0x4000
++#define SSC_STATUS_OFFSET 0x1
++#define SSC_STATUS_SSC_MASK 0x400
++#define SSC_STATUS_PLL_LOCK_MASK 0x800
++
++/* Internal PCIe Host Controller Information.*/
++struct brcm_pcie {
++ struct device *dev;
++ void __iomem *base;
++ struct clk *clk;
++ struct pci_bus *root_bus;
++ struct device_node *np;
++ bool ssc;
++ int gen;
++};
++
++/*
++ * This is to convert the size of the inbound "BAR" region to the
++ * non-linear values of PCIE_X_MISC_RC_BAR[123]_CONFIG_LO.SIZE
++ */
++static int brcm_pcie_encode_ibar_size(u64 size)
++{
++ int log2_in = ilog2(size);
++
++ if (log2_in >= 12 && log2_in <= 15)
++ /* Covers 4KB to 32KB (inclusive) */
++ return (log2_in - 12) + 0x1c;
++ else if (log2_in >= 16 && log2_in <= 35)
++ /* Covers 64KB to 32GB, (inclusive) */
++ return log2_in - 15;
++ /* Something is awry so disable */
++ return 0;
++}
++
++static u32 brcm_pcie_mdio_form_pkt(int port, int regad, int cmd)
++{
++ u32 pkt = 0;
++
++ pkt |= FIELD_PREP(MDIO_PORT_MASK, port);
++ pkt |= FIELD_PREP(MDIO_REGAD_MASK, regad);
++ pkt |= FIELD_PREP(MDIO_CMD_MASK, cmd);
++
++ return pkt;
++}
++
++/* negative return value indicates error */
++static int brcm_pcie_mdio_read(void __iomem *base, u8 port, u8 regad, u32 *val)
++{
++ int tries;
++ u32 data;
++
++ writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_READ),
++ base + PCIE_RC_DL_MDIO_ADDR);
++ readl(base + PCIE_RC_DL_MDIO_ADDR);
++
++ data = readl(base + PCIE_RC_DL_MDIO_RD_DATA);
++ for (tries = 0; !MDIO_RD_DONE(data) && tries < 10; tries++) {
++ udelay(10);
++ data = readl(base + PCIE_RC_DL_MDIO_RD_DATA);
++ }
++
++ *val = FIELD_GET(MDIO_DATA_MASK, data);
++ return MDIO_RD_DONE(data) ? 0 : -EIO;
++}
++
++/* negative return value indicates error */
++static int brcm_pcie_mdio_write(void __iomem *base, u8 port,
++ u8 regad, u16 wrdata)
++{
++ int tries;
++ u32 data;
++
++ writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_WRITE),
++ base + PCIE_RC_DL_MDIO_ADDR);
++ readl(base + PCIE_RC_DL_MDIO_ADDR);
++ writel(MDIO_DATA_DONE_MASK | wrdata, base + PCIE_RC_DL_MDIO_WR_DATA);
++
++ data = readl(base + PCIE_RC_DL_MDIO_WR_DATA);
++ for (tries = 0; !MDIO_WT_DONE(data) && tries < 10; tries++) {
++ udelay(10);
++ data = readl(base + PCIE_RC_DL_MDIO_WR_DATA);
++ }
++
++ return MDIO_WT_DONE(data) ? 0 : -EIO;
++}
++
++/*
++ * Configures device for Spread Spectrum Clocking (SSC) mode; a negative
++ * return value indicates error.
++ */
++static int brcm_pcie_set_ssc(struct brcm_pcie *pcie)
++{
++ int pll, ssc;
++ int ret;
++ u32 tmp;
++
++ ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET,
++ SSC_REGS_ADDR);
++ if (ret < 0)
++ return ret;
++
++ ret = brcm_pcie_mdio_read(pcie->base, MDIO_PORT0,
++ SSC_CNTL_OFFSET, &tmp);
++ if (ret < 0)
++ return ret;
++
++ u32p_replace_bits(&tmp, 1, SSC_CNTL_OVRD_EN_MASK);
++ u32p_replace_bits(&tmp, 1, SSC_CNTL_OVRD_VAL_MASK);
++ ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0,
++ SSC_CNTL_OFFSET, tmp);
++ if (ret < 0)
++ return ret;
++
++ usleep_range(1000, 2000);
++ ret = brcm_pcie_mdio_read(pcie->base, MDIO_PORT0,
++ SSC_STATUS_OFFSET, &tmp);
++ if (ret < 0)
++ return ret;
++
++ ssc = FIELD_GET(SSC_STATUS_SSC_MASK, tmp);
++ pll = FIELD_GET(SSC_STATUS_PLL_LOCK_MASK, tmp);
++
++ return ssc && pll ? 0 : -EIO;
++}
++
++/* Limits operation to a specific generation (1, 2, or 3) */
++static void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen)
++{
++ u16 lnkctl2 = readw(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2);
++ u32 lnkcap = readl(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP);
++
++ lnkcap = (lnkcap & ~PCI_EXP_LNKCAP_SLS) | gen;
++ writel(lnkcap, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP);
++
++ lnkctl2 = (lnkctl2 & ~0xf) | gen;
++ writew(lnkctl2, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2);
++}
++
++static void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie,
++ unsigned int win, u64 cpu_addr,
++ u64 pcie_addr, u64 size)
++{
++ u32 cpu_addr_mb_high, limit_addr_mb_high;
++ phys_addr_t cpu_addr_mb, limit_addr_mb;
++ int high_addr_shift;
++ u32 tmp;
++
++ /* Set the base of the pcie_addr window */
++ writel(lower_32_bits(pcie_addr), pcie->base + PCIE_MEM_WIN0_LO(win));
++ writel(upper_32_bits(pcie_addr), pcie->base + PCIE_MEM_WIN0_HI(win));
++
++ /* Write the addr base & limit lower bits (in MBs) */
++ cpu_addr_mb = cpu_addr / SZ_1M;
++ limit_addr_mb = (cpu_addr + size - 1) / SZ_1M;
++
++ tmp = readl(pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win));
++ u32p_replace_bits(&tmp, cpu_addr_mb,
++ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK);
++ u32p_replace_bits(&tmp, limit_addr_mb,
++ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK);
++ writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win));
++
++ /* Write the cpu & limit addr upper bits */
++ high_addr_shift =
++ HWEIGHT32(PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK);
++
++ cpu_addr_mb_high = cpu_addr_mb >> high_addr_shift;
++ tmp = readl(pcie->base + PCIE_MEM_WIN0_BASE_HI(win));
++ u32p_replace_bits(&tmp, cpu_addr_mb_high,
++ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK);
++ writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_HI(win));
++
++ limit_addr_mb_high = limit_addr_mb >> high_addr_shift;
++ tmp = readl(pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
++ u32p_replace_bits(&tmp, limit_addr_mb_high,
++ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK);
++ writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
++}
++
++/* The controller is capable of serving in both RC and EP roles */
++static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie)
++{
++ void __iomem *base = pcie->base;
++ u32 val = readl(base + PCIE_MISC_PCIE_STATUS);
++
++ return !!FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK, val);
++}
++
++static bool brcm_pcie_link_up(struct brcm_pcie *pcie)
++{
++ u32 val = readl(pcie->base + PCIE_MISC_PCIE_STATUS);
++ u32 dla = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK, val);
++ u32 plu = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK, val);
++
++ return dla && plu;
++}
++
++/* Configuration space read/write support */
++static inline int brcm_pcie_cfg_index(int busnr, int devfn, int reg)
++{
++ return ((PCI_SLOT(devfn) & 0x1f) << PCIE_EXT_SLOT_SHIFT)
++ | ((PCI_FUNC(devfn) & 0x07) << PCIE_EXT_FUNC_SHIFT)
++ | (busnr << PCIE_EXT_BUSNUM_SHIFT)
++ | (reg & ~3);
++}
++
++static void __iomem *brcm_pcie_map_conf(struct pci_bus *bus, unsigned int devfn,
++ int where)
++{
++ struct brcm_pcie *pcie = bus->sysdata;
++ void __iomem *base = pcie->base;
++ int idx;
++
++ /* Accesses to the RC go right to the RC registers if slot==0 */
++ if (pci_is_root_bus(bus))
++ return PCI_SLOT(devfn) ? NULL : base + where;
++
++ /* For devices, write to the config space index register */
++ idx = brcm_pcie_cfg_index(bus->number, devfn, 0);
++ writel(idx, pcie->base + PCIE_EXT_CFG_INDEX);
++ return base + PCIE_EXT_CFG_DATA + where;
++}
++
++static struct pci_ops brcm_pcie_ops = {
++ .map_bus = brcm_pcie_map_conf,
++ .read = pci_generic_config_read,
++ .write = pci_generic_config_write,
++};
++
++static inline void brcm_pcie_bridge_sw_init_set(struct brcm_pcie *pcie, u32 val)
++{
++ u32 tmp;
++
++ tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1);
++ u32p_replace_bits(&tmp, val, PCIE_RGR1_SW_INIT_1_INIT_MASK);
++ writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1);
++}
++
++static inline void brcm_pcie_perst_set(struct brcm_pcie *pcie, u32 val)
++{
++ u32 tmp;
++
++ tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1);
++ u32p_replace_bits(&tmp, val, PCIE_RGR1_SW_INIT_1_PERST_MASK);
++ writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1);
++}
++
++static inline int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie,
++ u64 *rc_bar2_size,
++ u64 *rc_bar2_offset)
++{
++ struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
++ struct device *dev = pcie->dev;
++ struct resource_entry *entry;
++
++ entry = resource_list_first_type(&bridge->dma_ranges, IORESOURCE_MEM);
++ if (!entry)
++ return -ENODEV;
++
++
++ /*
++ * The controller expects the inbound window offset to be calculated as
++ * the difference between PCIe's address space and CPU's. The offset
++ * provided by the firmware is calculated the opposite way, so we
++ * negate it.
++ */
++ *rc_bar2_offset = -entry->offset;
++ *rc_bar2_size = 1ULL << fls64(entry->res->end - entry->res->start);
++
++ /*
++ * We validate the inbound memory view even though we should trust
++ * whatever the device-tree provides. This is because of an HW issue on
++ * early Raspberry Pi 4's revisions (bcm2711). It turns out its
++ * firmware has to dynamically edit dma-ranges due to a bug on the
++ * PCIe controller integration, which prohibits any access above the
++ * lower 3GB of memory. Given this, we decided to keep the dma-ranges
++ * in check, avoiding hard to debug device-tree related issues in the
++ * future:
++ *
++ * The PCIe host controller by design must set the inbound viewport to
++ * be a contiguous arrangement of all of the system's memory. In
++ * addition, its size mut be a power of two. To further complicate
++ * matters, the viewport must start on a pcie-address that is aligned
++ * on a multiple of its size. If a portion of the viewport does not
++ * represent system memory -- e.g. 3GB of memory requires a 4GB
++ * viewport -- we can map the outbound memory in or after 3GB and even
++ * though the viewport will overlap the outbound memory the controller
++ * will know to send outbound memory downstream and everything else
++ * upstream.
++ *
++ * For example:
++ *
++ * - The best-case scenario, memory up to 3GB, is to place the inbound
++ * region in the first 4GB of pcie-space, as some legacy devices can
++ * only address 32bits. We would also like to put the MSI under 4GB
++ * as well, since some devices require a 32bit MSI target address.
++ *
++ * - If the system memory is 4GB or larger we cannot start the inbound
++ * region at location 0 (since we have to allow some space for
++ * outbound memory @ 3GB). So instead it will start at the 1x
++ * multiple of its size
++ */
++ if (!*rc_bar2_size || *rc_bar2_offset % *rc_bar2_size ||
++ (*rc_bar2_offset < SZ_4G && *rc_bar2_offset > SZ_2G)) {
++ dev_err(dev, "Invalid rc_bar2_offset/size: size 0x%llx, off 0x%llx\n",
++ *rc_bar2_size, *rc_bar2_offset);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int brcm_pcie_setup(struct brcm_pcie *pcie)
++{
++ struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
++ u64 rc_bar2_offset, rc_bar2_size;
++ void __iomem *base = pcie->base;
++ struct device *dev = pcie->dev;
++ struct resource_entry *entry;
++ unsigned int scb_size_val;
++ bool ssc_good = false;
++ struct resource *res;
++ int num_out_wins = 0;
++ u16 nlw, cls, lnksta;
++ int i, ret;
++ u32 tmp;
++
++ /* Reset the bridge */
++ brcm_pcie_bridge_sw_init_set(pcie, 1);
++
++ usleep_range(100, 200);
++
++ /* Take the bridge out of reset */
++ brcm_pcie_bridge_sw_init_set(pcie, 0);
++
++ tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++ tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK;
++ writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++ /* Wait for SerDes to be stable */
++ usleep_range(100, 200);
++
++ /* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN */
++ u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK);
++ u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK);
++ u32p_replace_bits(&tmp, PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_128,
++ PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK);
++ writel(tmp, base + PCIE_MISC_MISC_CTRL);
++
++ ret = brcm_pcie_get_rc_bar2_size_and_offset(pcie, &rc_bar2_size,
++ &rc_bar2_offset);
++ if (ret)
++ return ret;
++
++ tmp = lower_32_bits(rc_bar2_offset);
++ u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(rc_bar2_size),
++ PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK);
++ writel(tmp, base + PCIE_MISC_RC_BAR2_CONFIG_LO);
++ writel(upper_32_bits(rc_bar2_offset),
++ base + PCIE_MISC_RC_BAR2_CONFIG_HI);
++
++ scb_size_val = rc_bar2_size ?
++ ilog2(rc_bar2_size) - 15 : 0xf; /* 0xf is 1GB */
++ tmp = readl(base + PCIE_MISC_MISC_CTRL);
++ u32p_replace_bits(&tmp, scb_size_val,
++ PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK);
++ writel(tmp, base + PCIE_MISC_MISC_CTRL);
++
++ /* disable the PCIe->GISB memory window (RC_BAR1) */
++ tmp = readl(base + PCIE_MISC_RC_BAR1_CONFIG_LO);
++ tmp &= ~PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK;
++ writel(tmp, base + PCIE_MISC_RC_BAR1_CONFIG_LO);
++
++ /* disable the PCIe->SCB memory window (RC_BAR3) */
++ tmp = readl(base + PCIE_MISC_RC_BAR3_CONFIG_LO);
++ tmp &= ~PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK;
++ writel(tmp, base + PCIE_MISC_RC_BAR3_CONFIG_LO);
++
++ /* Mask all interrupts since we are not handling any yet */
++ writel(0xffffffff, pcie->base + PCIE_MSI_INTR2_MASK_SET);
++
++ /* clear any interrupts we find on boot */
++ writel(0xffffffff, pcie->base + PCIE_MSI_INTR2_CLR);
++
++ if (pcie->gen)
++ brcm_pcie_set_gen(pcie, pcie->gen);
++
++ /* Unassert the fundamental reset */
++ brcm_pcie_perst_set(pcie, 0);
++
++ /*
++ * Give the RC/EP time to wake up, before trying to configure RC.
++ * Intermittently check status for link-up, up to a total of 100ms.
++ */
++ for (i = 0; i < 100 && !brcm_pcie_link_up(pcie); i += 5)
++ msleep(5);
++
++ if (!brcm_pcie_link_up(pcie)) {
++ dev_err(dev, "link down\n");
++ return -ENODEV;
++ }
++
++ if (!brcm_pcie_rc_mode(pcie)) {
++ dev_err(dev, "PCIe misconfigured; is in EP mode\n");
++ return -EINVAL;
++ }
++
++ resource_list_for_each_entry(entry, &bridge->windows) {
++ res = entry->res;
++
++ if (resource_type(res) != IORESOURCE_MEM)
++ continue;
++
++ if (num_out_wins >= BRCM_NUM_PCIE_OUT_WINS) {
++ dev_err(pcie->dev, "too many outbound wins\n");
++ return -EINVAL;
++ }
++
++ brcm_pcie_set_outbound_win(pcie, num_out_wins, res->start,
++ res->start - entry->offset,
++ resource_size(res));
++ num_out_wins++;
++ }
++
++ /*
++ * For config space accesses on the RC, show the right class for
++ * a PCIe-PCIe bridge (the default setting is to be EP mode).
++ */
++ tmp = readl(base + PCIE_RC_CFG_PRIV1_ID_VAL3);
++ u32p_replace_bits(&tmp, 0x060400,
++ PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK);
++ writel(tmp, base + PCIE_RC_CFG_PRIV1_ID_VAL3);
++
++ if (pcie->ssc) {
++ ret = brcm_pcie_set_ssc(pcie);
++ if (ret == 0)
++ ssc_good = true;
++ else
++ dev_err(dev, "failed attempt to enter ssc mode\n");
++ }
++
++ lnksta = readw(base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKSTA);
++ cls = FIELD_GET(PCI_EXP_LNKSTA_CLS, lnksta);
++ nlw = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta);
++ dev_info(dev, "link up, %s x%u %s\n",
++ PCIE_SPEED2STR(cls + PCI_SPEED_133MHz_PCIX_533),
++ nlw, ssc_good ? "(SSC)" : "(!SSC)");
++
++ /* PCIe->SCB endian mode for BAR */
++ tmp = readl(base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1);
++ u32p_replace_bits(&tmp, PCIE_RC_CFG_VENDOR_SPCIFIC_REG1_LITTLE_ENDIAN,
++ PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK);
++ writel(tmp, base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1);
++
++ /*
++ * Refclk from RC should be gated with CLKREQ# input when ASPM L0s,L1
++ * is enabled => setting the CLKREQ_DEBUG_ENABLE field to 1.
++ */
++ tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++ tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK;
++ writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++
++ return 0;
++}
++
++/* L23 is a low-power PCIe link state */
++static void brcm_pcie_enter_l23(struct brcm_pcie *pcie)
++{
++ void __iomem *base = pcie->base;
++ int l23, i;
++ u32 tmp;
++
++ /* Assert request for L23 */
++ tmp = readl(base + PCIE_MISC_PCIE_CTRL);
++ u32p_replace_bits(&tmp, 1, PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK);
++ writel(tmp, base + PCIE_MISC_PCIE_CTRL);
++
++ /* Wait up to 36 msec for L23 */
++ tmp = readl(base + PCIE_MISC_PCIE_STATUS);
++ l23 = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK, tmp);
++ for (i = 0; i < 15 && !l23; i++) {
++ usleep_range(2000, 2400);
++ tmp = readl(base + PCIE_MISC_PCIE_STATUS);
++ l23 = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK,
++ tmp);
++ }
++
++ if (!l23)
++ dev_err(pcie->dev, "failed to enter low-power link state\n");
++}
++
++static void brcm_pcie_turn_off(struct brcm_pcie *pcie)
++{
++ void __iomem *base = pcie->base;
++ int tmp;
++
++ if (brcm_pcie_link_up(pcie))
++ brcm_pcie_enter_l23(pcie);
++ /* Assert fundamental reset */
++ brcm_pcie_perst_set(pcie, 1);
++
++ /* Deassert request for L23 in case it was asserted */
++ tmp = readl(base + PCIE_MISC_PCIE_CTRL);
++ u32p_replace_bits(&tmp, 0, PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK);
++ writel(tmp, base + PCIE_MISC_PCIE_CTRL);
++
++ /* Turn off SerDes */
++ tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++ u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK);
++ writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++
++ /* Shutdown PCIe bridge */
++ brcm_pcie_bridge_sw_init_set(pcie, 1);
++}
++
++static void __brcm_pcie_remove(struct brcm_pcie *pcie)
++{
++ brcm_pcie_turn_off(pcie);
++ clk_disable_unprepare(pcie->clk);
++ clk_put(pcie->clk);
++}
++
++static int brcm_pcie_remove(struct platform_device *pdev)
++{
++ struct brcm_pcie *pcie = platform_get_drvdata(pdev);
++
++ pci_stop_root_bus(pcie->root_bus);
++ pci_remove_root_bus(pcie->root_bus);
++ __brcm_pcie_remove(pcie);
++
++ return 0;
++}
++
++static int brcm_pcie_probe(struct platform_device *pdev)
++{
++ struct device_node *np = pdev->dev.of_node;
++ struct pci_host_bridge *bridge;
++ struct brcm_pcie *pcie;
++ struct pci_bus *child;
++ struct resource *res;
++ int ret;
++
++ bridge = devm_pci_alloc_host_bridge(&pdev->dev, sizeof(*pcie));
++ if (!bridge)
++ return -ENOMEM;
++
++ pcie = pci_host_bridge_priv(bridge);
++ pcie->dev = &pdev->dev;
++ pcie->np = np;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ pcie->base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(pcie->base))
++ return PTR_ERR(pcie->base);
++
++ pcie->clk = devm_clk_get_optional(&pdev->dev, "sw_pcie");
++ if (IS_ERR(pcie->clk))
++ return PTR_ERR(pcie->clk);
++
++ ret = of_pci_get_max_link_speed(np);
++ pcie->gen = (ret < 0) ? 0 : ret;
++
++ pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc");
++
++ ret = pci_parse_request_of_pci_ranges(pcie->dev, &bridge->windows,
++ &bridge->dma_ranges, NULL);
++ if (ret)
++ return ret;
++
++ ret = clk_prepare_enable(pcie->clk);
++ if (ret) {
++ dev_err(&pdev->dev, "could not enable clock\n");
++ return ret;
++ }
++
++ ret = brcm_pcie_setup(pcie);
++ if (ret)
++ goto fail;
++
++ bridge->dev.parent = &pdev->dev;
++ bridge->busnr = 0;
++ bridge->ops = &brcm_pcie_ops;
++ bridge->sysdata = pcie;
++ bridge->map_irq = of_irq_parse_and_map_pci;
++ bridge->swizzle_irq = pci_common_swizzle;
++
++ ret = pci_scan_root_bus_bridge(bridge);
++ if (ret < 0) {
++ dev_err(pcie->dev, "Scanning root bridge failed\n");
++ goto fail;
++ }
++
++ pci_assign_unassigned_bus_resources(bridge->bus);
++ list_for_each_entry(child, &bridge->bus->children, node)
++ pcie_bus_configure_settings(child);
++ pci_bus_add_devices(bridge->bus);
++ platform_set_drvdata(pdev, pcie);
++ pcie->root_bus = bridge->bus;
++
++ return 0;
++fail:
++ __brcm_pcie_remove(pcie);
++ return ret;
++}
++
++static const struct of_device_id brcm_pcie_match[] = {
++ { .compatible = "brcm,bcm2711-pcie" },
++ {},
++};
++MODULE_DEVICE_TABLE(of, brcm_pcie_match);
++
++static struct platform_driver brcm_pcie_driver = {
++ .probe = brcm_pcie_probe,
++ .remove = brcm_pcie_remove,
++ .driver = {
++ .name = "brcm-pcie",
++ .of_match_table = brcm_pcie_match,
++ },
++};
++module_platform_driver(brcm_pcie_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Broadcom STB PCIe RC driver");
++MODULE_AUTHOR("Broadcom");
+++ /dev/null
-From 4d9470c29736bf81bdb0d21da24cf350b1e99402 Mon Sep 17 00:00:00 2001
-From: Jim Quinlan <james.quinlan@broadcom.com>
-Date: Mon, 16 Dec 2019 12:01:09 +0100
-Subject: [PATCH] PCI: brcmstb: Add Broadcom STB PCIe host controller
- driver
-
-commit c0452137034bda8f686dd9a2e167949bfffd6776 upstream.
-
-This adds a basic driver for Broadcom's STB PCIe controller, for now
-aimed at Raspberry Pi 4's SoC, bcm2711.
-
-Signed-off-by: Jim Quinlan <james.quinlan@broadcom.com>
-Co-developed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-[lorenzo.pieralisi@arm.com: updated brcm_pcie_get_rc_bar2_size_and_offset()according to https://lore.kernel.org/linux-pci/be8ddb33a7360af1815cf686f77f3f0913d02be3.camel@suse.de]
-Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
-Reviewed-by: Andrew Murray <andrew.murray@arm.com>
-Reviewed-by: Jeremy Linton <jeremy.linton@arm.com>
----
- drivers/pci/controller/Kconfig | 8 +
- drivers/pci/controller/Makefile | 1 +
- drivers/pci/controller/pcie-brcmstb.c | 755 ++++++++++++++++++++++++++
- 3 files changed, 764 insertions(+)
- create mode 100644 drivers/pci/controller/pcie-brcmstb.c
-
---- a/drivers/pci/controller/Kconfig
-+++ b/drivers/pci/controller/Kconfig
-@@ -281,6 +281,14 @@ config VMD
- To compile this driver as a module, choose M here: the
- module will be called vmd.
-
-+config PCIE_BRCMSTB
-+ tristate "Broadcom Brcmstb PCIe host controller"
-+ depends on ARCH_BCM2835 || COMPILE_TEST
-+ depends on OF
-+ help
-+ Say Y here to enable PCIe host controller support for
-+ Broadcom STB based SoCs, like the Raspberry Pi 4.
-+
- config PCI_HYPERV_INTERFACE
- tristate "Hyper-V PCI Interface"
- depends on X86 && HYPERV && PCI_MSI && PCI_MSI_IRQ_DOMAIN && X86_64
---- a/drivers/pci/controller/Makefile
-+++ b/drivers/pci/controller/Makefile
-@@ -30,6 +30,7 @@ obj-$(CONFIG_PCIE_MEDIATEK) += pcie-medi
- obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o
- obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o
- obj-$(CONFIG_VMD) += vmd.o
-+obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o
- # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW
- obj-y += dwc/
-
---- /dev/null
-+++ b/drivers/pci/controller/pcie-brcmstb.c
-@@ -0,0 +1,755 @@
-+// SPDX-License-Identifier: GPL-2.0+
-+/* Copyright (C) 2009 - 2019 Broadcom */
-+
-+#include <linux/bitfield.h>
-+#include <linux/clk.h>
-+#include <linux/compiler.h>
-+#include <linux/delay.h>
-+#include <linux/init.h>
-+#include <linux/interrupt.h>
-+#include <linux/io.h>
-+#include <linux/ioport.h>
-+#include <linux/irqdomain.h>
-+#include <linux/kernel.h>
-+#include <linux/list.h>
-+#include <linux/log2.h>
-+#include <linux/module.h>
-+#include <linux/of_address.h>
-+#include <linux/of_irq.h>
-+#include <linux/of_pci.h>
-+#include <linux/of_platform.h>
-+#include <linux/pci.h>
-+#include <linux/printk.h>
-+#include <linux/sizes.h>
-+#include <linux/slab.h>
-+#include <linux/string.h>
-+#include <linux/types.h>
-+
-+#include "../pci.h"
-+
-+/* BRCM_PCIE_CAP_REGS - Offset for the mandatory capability config regs */
-+#define BRCM_PCIE_CAP_REGS 0x00ac
-+
-+/* Broadcom STB PCIe Register Offsets */
-+#define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1 0x0188
-+#define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK 0xc
-+#define PCIE_RC_CFG_VENDOR_SPCIFIC_REG1_LITTLE_ENDIAN 0x0
-+
-+#define PCIE_RC_CFG_PRIV1_ID_VAL3 0x043c
-+#define PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK 0xffffff
-+
-+#define PCIE_RC_DL_MDIO_ADDR 0x1100
-+#define PCIE_RC_DL_MDIO_WR_DATA 0x1104
-+#define PCIE_RC_DL_MDIO_RD_DATA 0x1108
-+
-+#define PCIE_MISC_MISC_CTRL 0x4008
-+#define PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK 0x1000
-+#define PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK 0x2000
-+#define PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK 0x300000
-+#define PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_128 0x0
-+#define PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK 0xf8000000
-+
-+#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO 0x400c
-+#define PCIE_MEM_WIN0_LO(win) \
-+ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO + ((win) * 4)
-+
-+#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI 0x4010
-+#define PCIE_MEM_WIN0_HI(win) \
-+ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI + ((win) * 4)
-+
-+#define PCIE_MISC_RC_BAR1_CONFIG_LO 0x402c
-+#define PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK 0x1f
-+
-+#define PCIE_MISC_RC_BAR2_CONFIG_LO 0x4034
-+#define PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK 0x1f
-+#define PCIE_MISC_RC_BAR2_CONFIG_HI 0x4038
-+
-+#define PCIE_MISC_RC_BAR3_CONFIG_LO 0x403c
-+#define PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK 0x1f
-+
-+#define PCIE_MISC_PCIE_CTRL 0x4064
-+#define PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK 0x1
-+
-+#define PCIE_MISC_PCIE_STATUS 0x4068
-+#define PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK 0x80
-+#define PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK 0x20
-+#define PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK 0x10
-+#define PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK 0x40
-+
-+#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT 0x4070
-+#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK 0xfff00000
-+#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK 0xfff0
-+#define PCIE_MEM_WIN0_BASE_LIMIT(win) \
-+ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT + ((win) * 4)
-+
-+#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI 0x4080
-+#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK 0xff
-+#define PCIE_MEM_WIN0_BASE_HI(win) \
-+ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI + ((win) * 8)
-+
-+#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI 0x4084
-+#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK 0xff
-+#define PCIE_MEM_WIN0_LIMIT_HI(win) \
-+ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI + ((win) * 8)
-+
-+#define PCIE_MISC_HARD_PCIE_HARD_DEBUG 0x4204
-+#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK 0x2
-+#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x08000000
-+
-+#define PCIE_MSI_INTR2_STATUS 0x4500
-+#define PCIE_MSI_INTR2_CLR 0x4508
-+#define PCIE_MSI_INTR2_MASK_SET 0x4510
-+#define PCIE_MSI_INTR2_MASK_CLR 0x4514
-+
-+#define PCIE_EXT_CFG_DATA 0x8000
-+
-+#define PCIE_EXT_CFG_INDEX 0x9000
-+#define PCIE_EXT_BUSNUM_SHIFT 20
-+#define PCIE_EXT_SLOT_SHIFT 15
-+#define PCIE_EXT_FUNC_SHIFT 12
-+
-+#define PCIE_RGR1_SW_INIT_1 0x9210
-+#define PCIE_RGR1_SW_INIT_1_PERST_MASK 0x1
-+#define PCIE_RGR1_SW_INIT_1_INIT_MASK 0x2
-+
-+/* PCIe parameters */
-+#define BRCM_NUM_PCIE_OUT_WINS 0x4
-+
-+/* MDIO registers */
-+#define MDIO_PORT0 0x0
-+#define MDIO_DATA_MASK 0x7fffffff
-+#define MDIO_PORT_MASK 0xf0000
-+#define MDIO_REGAD_MASK 0xffff
-+#define MDIO_CMD_MASK 0xfff00000
-+#define MDIO_CMD_READ 0x1
-+#define MDIO_CMD_WRITE 0x0
-+#define MDIO_DATA_DONE_MASK 0x80000000
-+#define MDIO_RD_DONE(x) (((x) & MDIO_DATA_DONE_MASK) ? 1 : 0)
-+#define MDIO_WT_DONE(x) (((x) & MDIO_DATA_DONE_MASK) ? 0 : 1)
-+#define SSC_REGS_ADDR 0x1100
-+#define SET_ADDR_OFFSET 0x1f
-+#define SSC_CNTL_OFFSET 0x2
-+#define SSC_CNTL_OVRD_EN_MASK 0x8000
-+#define SSC_CNTL_OVRD_VAL_MASK 0x4000
-+#define SSC_STATUS_OFFSET 0x1
-+#define SSC_STATUS_SSC_MASK 0x400
-+#define SSC_STATUS_PLL_LOCK_MASK 0x800
-+
-+/* Internal PCIe Host Controller Information.*/
-+struct brcm_pcie {
-+ struct device *dev;
-+ void __iomem *base;
-+ struct clk *clk;
-+ struct pci_bus *root_bus;
-+ struct device_node *np;
-+ bool ssc;
-+ int gen;
-+};
-+
-+/*
-+ * This is to convert the size of the inbound "BAR" region to the
-+ * non-linear values of PCIE_X_MISC_RC_BAR[123]_CONFIG_LO.SIZE
-+ */
-+static int brcm_pcie_encode_ibar_size(u64 size)
-+{
-+ int log2_in = ilog2(size);
-+
-+ if (log2_in >= 12 && log2_in <= 15)
-+ /* Covers 4KB to 32KB (inclusive) */
-+ return (log2_in - 12) + 0x1c;
-+ else if (log2_in >= 16 && log2_in <= 35)
-+ /* Covers 64KB to 32GB, (inclusive) */
-+ return log2_in - 15;
-+ /* Something is awry so disable */
-+ return 0;
-+}
-+
-+static u32 brcm_pcie_mdio_form_pkt(int port, int regad, int cmd)
-+{
-+ u32 pkt = 0;
-+
-+ pkt |= FIELD_PREP(MDIO_PORT_MASK, port);
-+ pkt |= FIELD_PREP(MDIO_REGAD_MASK, regad);
-+ pkt |= FIELD_PREP(MDIO_CMD_MASK, cmd);
-+
-+ return pkt;
-+}
-+
-+/* negative return value indicates error */
-+static int brcm_pcie_mdio_read(void __iomem *base, u8 port, u8 regad, u32 *val)
-+{
-+ int tries;
-+ u32 data;
-+
-+ writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_READ),
-+ base + PCIE_RC_DL_MDIO_ADDR);
-+ readl(base + PCIE_RC_DL_MDIO_ADDR);
-+
-+ data = readl(base + PCIE_RC_DL_MDIO_RD_DATA);
-+ for (tries = 0; !MDIO_RD_DONE(data) && tries < 10; tries++) {
-+ udelay(10);
-+ data = readl(base + PCIE_RC_DL_MDIO_RD_DATA);
-+ }
-+
-+ *val = FIELD_GET(MDIO_DATA_MASK, data);
-+ return MDIO_RD_DONE(data) ? 0 : -EIO;
-+}
-+
-+/* negative return value indicates error */
-+static int brcm_pcie_mdio_write(void __iomem *base, u8 port,
-+ u8 regad, u16 wrdata)
-+{
-+ int tries;
-+ u32 data;
-+
-+ writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_WRITE),
-+ base + PCIE_RC_DL_MDIO_ADDR);
-+ readl(base + PCIE_RC_DL_MDIO_ADDR);
-+ writel(MDIO_DATA_DONE_MASK | wrdata, base + PCIE_RC_DL_MDIO_WR_DATA);
-+
-+ data = readl(base + PCIE_RC_DL_MDIO_WR_DATA);
-+ for (tries = 0; !MDIO_WT_DONE(data) && tries < 10; tries++) {
-+ udelay(10);
-+ data = readl(base + PCIE_RC_DL_MDIO_WR_DATA);
-+ }
-+
-+ return MDIO_WT_DONE(data) ? 0 : -EIO;
-+}
-+
-+/*
-+ * Configures device for Spread Spectrum Clocking (SSC) mode; a negative
-+ * return value indicates error.
-+ */
-+static int brcm_pcie_set_ssc(struct brcm_pcie *pcie)
-+{
-+ int pll, ssc;
-+ int ret;
-+ u32 tmp;
-+
-+ ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET,
-+ SSC_REGS_ADDR);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = brcm_pcie_mdio_read(pcie->base, MDIO_PORT0,
-+ SSC_CNTL_OFFSET, &tmp);
-+ if (ret < 0)
-+ return ret;
-+
-+ u32p_replace_bits(&tmp, 1, SSC_CNTL_OVRD_EN_MASK);
-+ u32p_replace_bits(&tmp, 1, SSC_CNTL_OVRD_VAL_MASK);
-+ ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0,
-+ SSC_CNTL_OFFSET, tmp);
-+ if (ret < 0)
-+ return ret;
-+
-+ usleep_range(1000, 2000);
-+ ret = brcm_pcie_mdio_read(pcie->base, MDIO_PORT0,
-+ SSC_STATUS_OFFSET, &tmp);
-+ if (ret < 0)
-+ return ret;
-+
-+ ssc = FIELD_GET(SSC_STATUS_SSC_MASK, tmp);
-+ pll = FIELD_GET(SSC_STATUS_PLL_LOCK_MASK, tmp);
-+
-+ return ssc && pll ? 0 : -EIO;
-+}
-+
-+/* Limits operation to a specific generation (1, 2, or 3) */
-+static void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen)
-+{
-+ u16 lnkctl2 = readw(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2);
-+ u32 lnkcap = readl(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP);
-+
-+ lnkcap = (lnkcap & ~PCI_EXP_LNKCAP_SLS) | gen;
-+ writel(lnkcap, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP);
-+
-+ lnkctl2 = (lnkctl2 & ~0xf) | gen;
-+ writew(lnkctl2, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2);
-+}
-+
-+static void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie,
-+ unsigned int win, u64 cpu_addr,
-+ u64 pcie_addr, u64 size)
-+{
-+ u32 cpu_addr_mb_high, limit_addr_mb_high;
-+ phys_addr_t cpu_addr_mb, limit_addr_mb;
-+ int high_addr_shift;
-+ u32 tmp;
-+
-+ /* Set the base of the pcie_addr window */
-+ writel(lower_32_bits(pcie_addr), pcie->base + PCIE_MEM_WIN0_LO(win));
-+ writel(upper_32_bits(pcie_addr), pcie->base + PCIE_MEM_WIN0_HI(win));
-+
-+ /* Write the addr base & limit lower bits (in MBs) */
-+ cpu_addr_mb = cpu_addr / SZ_1M;
-+ limit_addr_mb = (cpu_addr + size - 1) / SZ_1M;
-+
-+ tmp = readl(pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win));
-+ u32p_replace_bits(&tmp, cpu_addr_mb,
-+ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK);
-+ u32p_replace_bits(&tmp, limit_addr_mb,
-+ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK);
-+ writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win));
-+
-+ /* Write the cpu & limit addr upper bits */
-+ high_addr_shift =
-+ HWEIGHT32(PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK);
-+
-+ cpu_addr_mb_high = cpu_addr_mb >> high_addr_shift;
-+ tmp = readl(pcie->base + PCIE_MEM_WIN0_BASE_HI(win));
-+ u32p_replace_bits(&tmp, cpu_addr_mb_high,
-+ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK);
-+ writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_HI(win));
-+
-+ limit_addr_mb_high = limit_addr_mb >> high_addr_shift;
-+ tmp = readl(pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
-+ u32p_replace_bits(&tmp, limit_addr_mb_high,
-+ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK);
-+ writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
-+}
-+
-+/* The controller is capable of serving in both RC and EP roles */
-+static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie)
-+{
-+ void __iomem *base = pcie->base;
-+ u32 val = readl(base + PCIE_MISC_PCIE_STATUS);
-+
-+ return !!FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK, val);
-+}
-+
-+static bool brcm_pcie_link_up(struct brcm_pcie *pcie)
-+{
-+ u32 val = readl(pcie->base + PCIE_MISC_PCIE_STATUS);
-+ u32 dla = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK, val);
-+ u32 plu = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK, val);
-+
-+ return dla && plu;
-+}
-+
-+/* Configuration space read/write support */
-+static inline int brcm_pcie_cfg_index(int busnr, int devfn, int reg)
-+{
-+ return ((PCI_SLOT(devfn) & 0x1f) << PCIE_EXT_SLOT_SHIFT)
-+ | ((PCI_FUNC(devfn) & 0x07) << PCIE_EXT_FUNC_SHIFT)
-+ | (busnr << PCIE_EXT_BUSNUM_SHIFT)
-+ | (reg & ~3);
-+}
-+
-+static void __iomem *brcm_pcie_map_conf(struct pci_bus *bus, unsigned int devfn,
-+ int where)
-+{
-+ struct brcm_pcie *pcie = bus->sysdata;
-+ void __iomem *base = pcie->base;
-+ int idx;
-+
-+ /* Accesses to the RC go right to the RC registers if slot==0 */
-+ if (pci_is_root_bus(bus))
-+ return PCI_SLOT(devfn) ? NULL : base + where;
-+
-+ /* For devices, write to the config space index register */
-+ idx = brcm_pcie_cfg_index(bus->number, devfn, 0);
-+ writel(idx, pcie->base + PCIE_EXT_CFG_INDEX);
-+ return base + PCIE_EXT_CFG_DATA + where;
-+}
-+
-+static struct pci_ops brcm_pcie_ops = {
-+ .map_bus = brcm_pcie_map_conf,
-+ .read = pci_generic_config_read,
-+ .write = pci_generic_config_write,
-+};
-+
-+static inline void brcm_pcie_bridge_sw_init_set(struct brcm_pcie *pcie, u32 val)
-+{
-+ u32 tmp;
-+
-+ tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1);
-+ u32p_replace_bits(&tmp, val, PCIE_RGR1_SW_INIT_1_INIT_MASK);
-+ writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1);
-+}
-+
-+static inline void brcm_pcie_perst_set(struct brcm_pcie *pcie, u32 val)
-+{
-+ u32 tmp;
-+
-+ tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1);
-+ u32p_replace_bits(&tmp, val, PCIE_RGR1_SW_INIT_1_PERST_MASK);
-+ writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1);
-+}
-+
-+static inline int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie,
-+ u64 *rc_bar2_size,
-+ u64 *rc_bar2_offset)
-+{
-+ struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
-+ struct device *dev = pcie->dev;
-+ struct resource_entry *entry;
-+
-+ entry = resource_list_first_type(&bridge->dma_ranges, IORESOURCE_MEM);
-+ if (!entry)
-+ return -ENODEV;
-+
-+
-+ /*
-+ * The controller expects the inbound window offset to be calculated as
-+ * the difference between PCIe's address space and CPU's. The offset
-+ * provided by the firmware is calculated the opposite way, so we
-+ * negate it.
-+ */
-+ *rc_bar2_offset = -entry->offset;
-+ *rc_bar2_size = 1ULL << fls64(entry->res->end - entry->res->start);
-+
-+ /*
-+ * We validate the inbound memory view even though we should trust
-+ * whatever the device-tree provides. This is because of an HW issue on
-+ * early Raspberry Pi 4's revisions (bcm2711). It turns out its
-+ * firmware has to dynamically edit dma-ranges due to a bug on the
-+ * PCIe controller integration, which prohibits any access above the
-+ * lower 3GB of memory. Given this, we decided to keep the dma-ranges
-+ * in check, avoiding hard to debug device-tree related issues in the
-+ * future:
-+ *
-+ * The PCIe host controller by design must set the inbound viewport to
-+ * be a contiguous arrangement of all of the system's memory. In
-+ * addition, its size mut be a power of two. To further complicate
-+ * matters, the viewport must start on a pcie-address that is aligned
-+ * on a multiple of its size. If a portion of the viewport does not
-+ * represent system memory -- e.g. 3GB of memory requires a 4GB
-+ * viewport -- we can map the outbound memory in or after 3GB and even
-+ * though the viewport will overlap the outbound memory the controller
-+ * will know to send outbound memory downstream and everything else
-+ * upstream.
-+ *
-+ * For example:
-+ *
-+ * - The best-case scenario, memory up to 3GB, is to place the inbound
-+ * region in the first 4GB of pcie-space, as some legacy devices can
-+ * only address 32bits. We would also like to put the MSI under 4GB
-+ * as well, since some devices require a 32bit MSI target address.
-+ *
-+ * - If the system memory is 4GB or larger we cannot start the inbound
-+ * region at location 0 (since we have to allow some space for
-+ * outbound memory @ 3GB). So instead it will start at the 1x
-+ * multiple of its size
-+ */
-+ if (!*rc_bar2_size || *rc_bar2_offset % *rc_bar2_size ||
-+ (*rc_bar2_offset < SZ_4G && *rc_bar2_offset > SZ_2G)) {
-+ dev_err(dev, "Invalid rc_bar2_offset/size: size 0x%llx, off 0x%llx\n",
-+ *rc_bar2_size, *rc_bar2_offset);
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+static int brcm_pcie_setup(struct brcm_pcie *pcie)
-+{
-+ struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
-+ u64 rc_bar2_offset, rc_bar2_size;
-+ void __iomem *base = pcie->base;
-+ struct device *dev = pcie->dev;
-+ struct resource_entry *entry;
-+ unsigned int scb_size_val;
-+ bool ssc_good = false;
-+ struct resource *res;
-+ int num_out_wins = 0;
-+ u16 nlw, cls, lnksta;
-+ int i, ret;
-+ u32 tmp;
-+
-+ /* Reset the bridge */
-+ brcm_pcie_bridge_sw_init_set(pcie, 1);
-+
-+ usleep_range(100, 200);
-+
-+ /* Take the bridge out of reset */
-+ brcm_pcie_bridge_sw_init_set(pcie, 0);
-+
-+ tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
-+ tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK;
-+ writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
-+ /* Wait for SerDes to be stable */
-+ usleep_range(100, 200);
-+
-+ /* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN */
-+ u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK);
-+ u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK);
-+ u32p_replace_bits(&tmp, PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_128,
-+ PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK);
-+ writel(tmp, base + PCIE_MISC_MISC_CTRL);
-+
-+ ret = brcm_pcie_get_rc_bar2_size_and_offset(pcie, &rc_bar2_size,
-+ &rc_bar2_offset);
-+ if (ret)
-+ return ret;
-+
-+ tmp = lower_32_bits(rc_bar2_offset);
-+ u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(rc_bar2_size),
-+ PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK);
-+ writel(tmp, base + PCIE_MISC_RC_BAR2_CONFIG_LO);
-+ writel(upper_32_bits(rc_bar2_offset),
-+ base + PCIE_MISC_RC_BAR2_CONFIG_HI);
-+
-+ scb_size_val = rc_bar2_size ?
-+ ilog2(rc_bar2_size) - 15 : 0xf; /* 0xf is 1GB */
-+ tmp = readl(base + PCIE_MISC_MISC_CTRL);
-+ u32p_replace_bits(&tmp, scb_size_val,
-+ PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK);
-+ writel(tmp, base + PCIE_MISC_MISC_CTRL);
-+
-+ /* disable the PCIe->GISB memory window (RC_BAR1) */
-+ tmp = readl(base + PCIE_MISC_RC_BAR1_CONFIG_LO);
-+ tmp &= ~PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK;
-+ writel(tmp, base + PCIE_MISC_RC_BAR1_CONFIG_LO);
-+
-+ /* disable the PCIe->SCB memory window (RC_BAR3) */
-+ tmp = readl(base + PCIE_MISC_RC_BAR3_CONFIG_LO);
-+ tmp &= ~PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK;
-+ writel(tmp, base + PCIE_MISC_RC_BAR3_CONFIG_LO);
-+
-+ /* Mask all interrupts since we are not handling any yet */
-+ writel(0xffffffff, pcie->base + PCIE_MSI_INTR2_MASK_SET);
-+
-+ /* clear any interrupts we find on boot */
-+ writel(0xffffffff, pcie->base + PCIE_MSI_INTR2_CLR);
-+
-+ if (pcie->gen)
-+ brcm_pcie_set_gen(pcie, pcie->gen);
-+
-+ /* Unassert the fundamental reset */
-+ brcm_pcie_perst_set(pcie, 0);
-+
-+ /*
-+ * Give the RC/EP time to wake up, before trying to configure RC.
-+ * Intermittently check status for link-up, up to a total of 100ms.
-+ */
-+ for (i = 0; i < 100 && !brcm_pcie_link_up(pcie); i += 5)
-+ msleep(5);
-+
-+ if (!brcm_pcie_link_up(pcie)) {
-+ dev_err(dev, "link down\n");
-+ return -ENODEV;
-+ }
-+
-+ if (!brcm_pcie_rc_mode(pcie)) {
-+ dev_err(dev, "PCIe misconfigured; is in EP mode\n");
-+ return -EINVAL;
-+ }
-+
-+ resource_list_for_each_entry(entry, &bridge->windows) {
-+ res = entry->res;
-+
-+ if (resource_type(res) != IORESOURCE_MEM)
-+ continue;
-+
-+ if (num_out_wins >= BRCM_NUM_PCIE_OUT_WINS) {
-+ dev_err(pcie->dev, "too many outbound wins\n");
-+ return -EINVAL;
-+ }
-+
-+ brcm_pcie_set_outbound_win(pcie, num_out_wins, res->start,
-+ res->start - entry->offset,
-+ resource_size(res));
-+ num_out_wins++;
-+ }
-+
-+ /*
-+ * For config space accesses on the RC, show the right class for
-+ * a PCIe-PCIe bridge (the default setting is to be EP mode).
-+ */
-+ tmp = readl(base + PCIE_RC_CFG_PRIV1_ID_VAL3);
-+ u32p_replace_bits(&tmp, 0x060400,
-+ PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK);
-+ writel(tmp, base + PCIE_RC_CFG_PRIV1_ID_VAL3);
-+
-+ if (pcie->ssc) {
-+ ret = brcm_pcie_set_ssc(pcie);
-+ if (ret == 0)
-+ ssc_good = true;
-+ else
-+ dev_err(dev, "failed attempt to enter ssc mode\n");
-+ }
-+
-+ lnksta = readw(base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKSTA);
-+ cls = FIELD_GET(PCI_EXP_LNKSTA_CLS, lnksta);
-+ nlw = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta);
-+ dev_info(dev, "link up, %s x%u %s\n",
-+ PCIE_SPEED2STR(cls + PCI_SPEED_133MHz_PCIX_533),
-+ nlw, ssc_good ? "(SSC)" : "(!SSC)");
-+
-+ /* PCIe->SCB endian mode for BAR */
-+ tmp = readl(base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1);
-+ u32p_replace_bits(&tmp, PCIE_RC_CFG_VENDOR_SPCIFIC_REG1_LITTLE_ENDIAN,
-+ PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK);
-+ writel(tmp, base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1);
-+
-+ /*
-+ * Refclk from RC should be gated with CLKREQ# input when ASPM L0s,L1
-+ * is enabled => setting the CLKREQ_DEBUG_ENABLE field to 1.
-+ */
-+ tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
-+ tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK;
-+ writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
-+
-+ return 0;
-+}
-+
-+/* L23 is a low-power PCIe link state */
-+static void brcm_pcie_enter_l23(struct brcm_pcie *pcie)
-+{
-+ void __iomem *base = pcie->base;
-+ int l23, i;
-+ u32 tmp;
-+
-+ /* Assert request for L23 */
-+ tmp = readl(base + PCIE_MISC_PCIE_CTRL);
-+ u32p_replace_bits(&tmp, 1, PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK);
-+ writel(tmp, base + PCIE_MISC_PCIE_CTRL);
-+
-+ /* Wait up to 36 msec for L23 */
-+ tmp = readl(base + PCIE_MISC_PCIE_STATUS);
-+ l23 = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK, tmp);
-+ for (i = 0; i < 15 && !l23; i++) {
-+ usleep_range(2000, 2400);
-+ tmp = readl(base + PCIE_MISC_PCIE_STATUS);
-+ l23 = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK,
-+ tmp);
-+ }
-+
-+ if (!l23)
-+ dev_err(pcie->dev, "failed to enter low-power link state\n");
-+}
-+
-+static void brcm_pcie_turn_off(struct brcm_pcie *pcie)
-+{
-+ void __iomem *base = pcie->base;
-+ int tmp;
-+
-+ if (brcm_pcie_link_up(pcie))
-+ brcm_pcie_enter_l23(pcie);
-+ /* Assert fundamental reset */
-+ brcm_pcie_perst_set(pcie, 1);
-+
-+ /* Deassert request for L23 in case it was asserted */
-+ tmp = readl(base + PCIE_MISC_PCIE_CTRL);
-+ u32p_replace_bits(&tmp, 0, PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK);
-+ writel(tmp, base + PCIE_MISC_PCIE_CTRL);
-+
-+ /* Turn off SerDes */
-+ tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
-+ u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK);
-+ writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
-+
-+ /* Shutdown PCIe bridge */
-+ brcm_pcie_bridge_sw_init_set(pcie, 1);
-+}
-+
-+static void __brcm_pcie_remove(struct brcm_pcie *pcie)
-+{
-+ brcm_pcie_turn_off(pcie);
-+ clk_disable_unprepare(pcie->clk);
-+ clk_put(pcie->clk);
-+}
-+
-+static int brcm_pcie_remove(struct platform_device *pdev)
-+{
-+ struct brcm_pcie *pcie = platform_get_drvdata(pdev);
-+
-+ pci_stop_root_bus(pcie->root_bus);
-+ pci_remove_root_bus(pcie->root_bus);
-+ __brcm_pcie_remove(pcie);
-+
-+ return 0;
-+}
-+
-+static int brcm_pcie_probe(struct platform_device *pdev)
-+{
-+ struct device_node *np = pdev->dev.of_node;
-+ struct pci_host_bridge *bridge;
-+ struct brcm_pcie *pcie;
-+ struct pci_bus *child;
-+ struct resource *res;
-+ int ret;
-+
-+ bridge = devm_pci_alloc_host_bridge(&pdev->dev, sizeof(*pcie));
-+ if (!bridge)
-+ return -ENOMEM;
-+
-+ pcie = pci_host_bridge_priv(bridge);
-+ pcie->dev = &pdev->dev;
-+ pcie->np = np;
-+
-+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+ pcie->base = devm_ioremap_resource(&pdev->dev, res);
-+ if (IS_ERR(pcie->base))
-+ return PTR_ERR(pcie->base);
-+
-+ pcie->clk = devm_clk_get_optional(&pdev->dev, "sw_pcie");
-+ if (IS_ERR(pcie->clk))
-+ return PTR_ERR(pcie->clk);
-+
-+ ret = of_pci_get_max_link_speed(np);
-+ pcie->gen = (ret < 0) ? 0 : ret;
-+
-+ pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc");
-+
-+ ret = pci_parse_request_of_pci_ranges(pcie->dev, &bridge->windows,
-+ &bridge->dma_ranges, NULL);
-+ if (ret)
-+ return ret;
-+
-+ ret = clk_prepare_enable(pcie->clk);
-+ if (ret) {
-+ dev_err(&pdev->dev, "could not enable clock\n");
-+ return ret;
-+ }
-+
-+ ret = brcm_pcie_setup(pcie);
-+ if (ret)
-+ goto fail;
-+
-+ bridge->dev.parent = &pdev->dev;
-+ bridge->busnr = 0;
-+ bridge->ops = &brcm_pcie_ops;
-+ bridge->sysdata = pcie;
-+ bridge->map_irq = of_irq_parse_and_map_pci;
-+ bridge->swizzle_irq = pci_common_swizzle;
-+
-+ ret = pci_scan_root_bus_bridge(bridge);
-+ if (ret < 0) {
-+ dev_err(pcie->dev, "Scanning root bridge failed\n");
-+ goto fail;
-+ }
-+
-+ pci_assign_unassigned_bus_resources(bridge->bus);
-+ list_for_each_entry(child, &bridge->bus->children, node)
-+ pcie_bus_configure_settings(child);
-+ pci_bus_add_devices(bridge->bus);
-+ platform_set_drvdata(pdev, pcie);
-+ pcie->root_bus = bridge->bus;
-+
-+ return 0;
-+fail:
-+ __brcm_pcie_remove(pcie);
-+ return ret;
-+}
-+
-+static const struct of_device_id brcm_pcie_match[] = {
-+ { .compatible = "brcm,bcm2711-pcie" },
-+ {},
-+};
-+MODULE_DEVICE_TABLE(of, brcm_pcie_match);
-+
-+static struct platform_driver brcm_pcie_driver = {
-+ .probe = brcm_pcie_probe,
-+ .remove = brcm_pcie_remove,
-+ .driver = {
-+ .name = "brcm-pcie",
-+ .of_match_table = brcm_pcie_match,
-+ },
-+};
-+module_platform_driver(brcm_pcie_driver);
-+
-+MODULE_LICENSE("GPL");
-+MODULE_DESCRIPTION("Broadcom STB PCIe RC driver");
-+MODULE_AUTHOR("Broadcom");
--- /dev/null
+From 1a90ecdfae1c0cf1b242276f6f0e3d98b5877f14 Mon Sep 17 00:00:00 2001
+From: Jim Quinlan <james.quinlan@broadcom.com>
+Date: Mon, 16 Dec 2019 12:01:10 +0100
+Subject: [PATCH] PCI: brcmstb: Add MSI support
+
+commit 40ca1bf580ef24df30702032ba5e40dfdcaa200b upstream.
+
+This adds MSI support to the Broadcom STB PCIe host controller. The MSI
+controller is physically located within the PCIe block, however, there
+is no reason why the MSI controller could not be moved elsewhere in the
+future. MSIX is not supported by the HW.
+
+Since the internal Brcmstb MSI controller is intertwined with the PCIe
+controller, it is not its own platform device but rather part of the
+PCIe platform device.
+
+Signed-off-by: Jim Quinlan <james.quinlan@broadcom.com>
+Co-developed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+Reviewed-by: Marc Zyngier <maz@kernel.org>
+Reviewed-by: Andrew Murray <andrew.murray@arm.com>
+---
+ drivers/pci/controller/Kconfig | 1 +
+ drivers/pci/controller/pcie-brcmstb.c | 262 +++++++++++++++++++++++++-
+ 2 files changed, 262 insertions(+), 1 deletion(-)
+
+--- a/drivers/pci/controller/Kconfig
++++ b/drivers/pci/controller/Kconfig
+@@ -285,6 +285,7 @@ config PCIE_BRCMSTB
+ tristate "Broadcom Brcmstb PCIe host controller"
+ depends on ARCH_BCM2835 || COMPILE_TEST
+ depends on OF
++ depends on PCI_MSI_IRQ_DOMAIN
+ help
+ Say Y here to enable PCIe host controller support for
+ Broadcom STB based SoCs, like the Raspberry Pi 4.
+--- a/drivers/pci/controller/pcie-brcmstb.c
++++ b/drivers/pci/controller/pcie-brcmstb.c
+@@ -2,6 +2,7 @@
+ /* Copyright (C) 2009 - 2019 Broadcom */
+
+ #include <linux/bitfield.h>
++#include <linux/bitops.h>
+ #include <linux/clk.h>
+ #include <linux/compiler.h>
+ #include <linux/delay.h>
+@@ -9,11 +10,13 @@
+ #include <linux/interrupt.h>
+ #include <linux/io.h>
+ #include <linux/ioport.h>
++#include <linux/irqchip/chained_irq.h>
+ #include <linux/irqdomain.h>
+ #include <linux/kernel.h>
+ #include <linux/list.h>
+ #include <linux/log2.h>
+ #include <linux/module.h>
++#include <linux/msi.h>
+ #include <linux/of_address.h>
+ #include <linux/of_irq.h>
+ #include <linux/of_pci.h>
+@@ -67,6 +70,12 @@
+ #define PCIE_MISC_RC_BAR3_CONFIG_LO 0x403c
+ #define PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK 0x1f
+
++#define PCIE_MISC_MSI_BAR_CONFIG_LO 0x4044
++#define PCIE_MISC_MSI_BAR_CONFIG_HI 0x4048
++
++#define PCIE_MISC_MSI_DATA_CONFIG 0x404c
++#define PCIE_MISC_MSI_DATA_CONFIG_VAL 0xffe06540
++
+ #define PCIE_MISC_PCIE_CTRL 0x4064
+ #define PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK 0x1
+
+@@ -114,6 +123,11 @@
+
+ /* PCIe parameters */
+ #define BRCM_NUM_PCIE_OUT_WINS 0x4
++#define BRCM_INT_PCI_MSI_NR 32
++
++/* MSI target adresses */
++#define BRCM_MSI_TARGET_ADDR_LT_4GB 0x0fffffffcULL
++#define BRCM_MSI_TARGET_ADDR_GT_4GB 0xffffffffcULL
+
+ /* MDIO registers */
+ #define MDIO_PORT0 0x0
+@@ -135,6 +149,19 @@
+ #define SSC_STATUS_SSC_MASK 0x400
+ #define SSC_STATUS_PLL_LOCK_MASK 0x800
+
++struct brcm_msi {
++ struct device *dev;
++ void __iomem *base;
++ struct device_node *np;
++ struct irq_domain *msi_domain;
++ struct irq_domain *inner_domain;
++ struct mutex lock; /* guards the alloc/free operations */
++ u64 target_addr;
++ int irq;
++ /* used indicates which MSI interrupts have been alloc'd */
++ unsigned long used;
++};
++
+ /* Internal PCIe Host Controller Information.*/
+ struct brcm_pcie {
+ struct device *dev;
+@@ -144,6 +171,8 @@ struct brcm_pcie {
+ struct device_node *np;
+ bool ssc;
+ int gen;
++ u64 msi_target_addr;
++ struct brcm_msi *msi;
+ };
+
+ /*
+@@ -309,6 +338,215 @@ static void brcm_pcie_set_outbound_win(s
+ writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
+ }
+
++static struct irq_chip brcm_msi_irq_chip = {
++ .name = "BRCM STB PCIe MSI",
++ .irq_ack = irq_chip_ack_parent,
++ .irq_mask = pci_msi_mask_irq,
++ .irq_unmask = pci_msi_unmask_irq,
++};
++
++static struct msi_domain_info brcm_msi_domain_info = {
++ /* Multi MSI is supported by the controller, but not by this driver */
++ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
++ .chip = &brcm_msi_irq_chip,
++};
++
++static void brcm_pcie_msi_isr(struct irq_desc *desc)
++{
++ struct irq_chip *chip = irq_desc_get_chip(desc);
++ unsigned long status, virq;
++ struct brcm_msi *msi;
++ struct device *dev;
++ u32 bit;
++
++ chained_irq_enter(chip, desc);
++ msi = irq_desc_get_handler_data(desc);
++ dev = msi->dev;
++
++ status = readl(msi->base + PCIE_MSI_INTR2_STATUS);
++ for_each_set_bit(bit, &status, BRCM_INT_PCI_MSI_NR) {
++ virq = irq_find_mapping(msi->inner_domain, bit);
++ if (virq)
++ generic_handle_irq(virq);
++ else
++ dev_dbg(dev, "unexpected MSI\n");
++ }
++
++ chained_irq_exit(chip, desc);
++}
++
++static void brcm_msi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
++{
++ struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
++
++ msg->address_lo = lower_32_bits(msi->target_addr);
++ msg->address_hi = upper_32_bits(msi->target_addr);
++ msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL) | data->hwirq;
++}
++
++static int brcm_msi_set_affinity(struct irq_data *irq_data,
++ const struct cpumask *mask, bool force)
++{
++ return -EINVAL;
++}
++
++static void brcm_msi_ack_irq(struct irq_data *data)
++{
++ struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
++
++ writel(1 << data->hwirq, msi->base + PCIE_MSI_INTR2_CLR);
++}
++
++
++static struct irq_chip brcm_msi_bottom_irq_chip = {
++ .name = "BRCM STB MSI",
++ .irq_compose_msi_msg = brcm_msi_compose_msi_msg,
++ .irq_set_affinity = brcm_msi_set_affinity,
++ .irq_ack = brcm_msi_ack_irq,
++};
++
++static int brcm_msi_alloc(struct brcm_msi *msi)
++{
++ int hwirq;
++
++ mutex_lock(&msi->lock);
++ hwirq = bitmap_find_free_region(&msi->used, BRCM_INT_PCI_MSI_NR, 0);
++ mutex_unlock(&msi->lock);
++
++ return hwirq;
++}
++
++static void brcm_msi_free(struct brcm_msi *msi, unsigned long hwirq)
++{
++ mutex_lock(&msi->lock);
++ bitmap_release_region(&msi->used, hwirq, 0);
++ mutex_unlock(&msi->lock);
++}
++
++static int brcm_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
++ unsigned int nr_irqs, void *args)
++{
++ struct brcm_msi *msi = domain->host_data;
++ int hwirq;
++
++ hwirq = brcm_msi_alloc(msi);
++
++ if (hwirq < 0)
++ return hwirq;
++
++ irq_domain_set_info(domain, virq, (irq_hw_number_t)hwirq,
++ &brcm_msi_bottom_irq_chip, domain->host_data,
++ handle_edge_irq, NULL, NULL);
++ return 0;
++}
++
++static void brcm_irq_domain_free(struct irq_domain *domain,
++ unsigned int virq, unsigned int nr_irqs)
++{
++ struct irq_data *d = irq_domain_get_irq_data(domain, virq);
++ struct brcm_msi *msi = irq_data_get_irq_chip_data(d);
++
++ brcm_msi_free(msi, d->hwirq);
++}
++
++static const struct irq_domain_ops msi_domain_ops = {
++ .alloc = brcm_irq_domain_alloc,
++ .free = brcm_irq_domain_free,
++};
++
++static int brcm_allocate_domains(struct brcm_msi *msi)
++{
++ struct fwnode_handle *fwnode = of_node_to_fwnode(msi->np);
++ struct device *dev = msi->dev;
++
++ msi->inner_domain = irq_domain_add_linear(NULL, BRCM_INT_PCI_MSI_NR,
++ &msi_domain_ops, msi);
++ if (!msi->inner_domain) {
++ dev_err(dev, "failed to create IRQ domain\n");
++ return -ENOMEM;
++ }
++
++ msi->msi_domain = pci_msi_create_irq_domain(fwnode,
++ &brcm_msi_domain_info,
++ msi->inner_domain);
++ if (!msi->msi_domain) {
++ dev_err(dev, "failed to create MSI domain\n");
++ irq_domain_remove(msi->inner_domain);
++ return -ENOMEM;
++ }
++
++ return 0;
++}
++
++static void brcm_free_domains(struct brcm_msi *msi)
++{
++ irq_domain_remove(msi->msi_domain);
++ irq_domain_remove(msi->inner_domain);
++}
++
++static void brcm_msi_remove(struct brcm_pcie *pcie)
++{
++ struct brcm_msi *msi = pcie->msi;
++
++ if (!msi)
++ return;
++ irq_set_chained_handler(msi->irq, NULL);
++ irq_set_handler_data(msi->irq, NULL);
++ brcm_free_domains(msi);
++}
++
++static void brcm_msi_set_regs(struct brcm_msi *msi)
++{
++ writel(0xffffffff, msi->base + PCIE_MSI_INTR2_MASK_CLR);
++
++ /*
++ * The 0 bit of PCIE_MISC_MSI_BAR_CONFIG_LO is repurposed to MSI
++ * enable, which we set to 1.
++ */
++ writel(lower_32_bits(msi->target_addr) | 0x1,
++ msi->base + PCIE_MISC_MSI_BAR_CONFIG_LO);
++ writel(upper_32_bits(msi->target_addr),
++ msi->base + PCIE_MISC_MSI_BAR_CONFIG_HI);
++
++ writel(PCIE_MISC_MSI_DATA_CONFIG_VAL,
++ msi->base + PCIE_MISC_MSI_DATA_CONFIG);
++}
++
++static int brcm_pcie_enable_msi(struct brcm_pcie *pcie)
++{
++ struct brcm_msi *msi;
++ int irq, ret;
++ struct device *dev = pcie->dev;
++
++ irq = irq_of_parse_and_map(dev->of_node, 1);
++ if (irq <= 0) {
++ dev_err(dev, "cannot map MSI interrupt\n");
++ return -ENODEV;
++ }
++
++ msi = devm_kzalloc(dev, sizeof(struct brcm_msi), GFP_KERNEL);
++ if (!msi)
++ return -ENOMEM;
++
++ mutex_init(&msi->lock);
++ msi->dev = dev;
++ msi->base = pcie->base;
++ msi->np = pcie->np;
++ msi->target_addr = pcie->msi_target_addr;
++ msi->irq = irq;
++
++ ret = brcm_allocate_domains(msi);
++ if (ret)
++ return ret;
++
++ irq_set_chained_handler_and_data(msi->irq, brcm_pcie_msi_isr, msi);
++
++ brcm_msi_set_regs(msi);
++ pcie->msi = msi;
++
++ return 0;
++}
++
+ /* The controller is capable of serving in both RC and EP roles */
+ static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie)
+ {
+@@ -497,6 +735,18 @@ static int brcm_pcie_setup(struct brcm_p
+ PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK);
+ writel(tmp, base + PCIE_MISC_MISC_CTRL);
+
++ /*
++ * We ideally want the MSI target address to be located in the 32bit
++ * addressable memory area. Some devices might depend on it. This is
++ * possible either when the inbound window is located above the lower
++ * 4GB or when the inbound area is smaller than 4GB (taking into
++ * account the rounding-up we're forced to perform).
++ */
++ if (rc_bar2_offset >= SZ_4G || (rc_bar2_size + rc_bar2_offset) < SZ_4G)
++ pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_LT_4GB;
++ else
++ pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_GT_4GB;
++
+ /* disable the PCIe->GISB memory window (RC_BAR1) */
+ tmp = readl(base + PCIE_MISC_RC_BAR1_CONFIG_LO);
+ tmp &= ~PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK;
+@@ -646,6 +896,7 @@ static void brcm_pcie_turn_off(struct br
+
+ static void __brcm_pcie_remove(struct brcm_pcie *pcie)
+ {
++ brcm_msi_remove(pcie);
+ brcm_pcie_turn_off(pcie);
+ clk_disable_unprepare(pcie->clk);
+ clk_put(pcie->clk);
+@@ -664,7 +915,7 @@ static int brcm_pcie_remove(struct platf
+
+ static int brcm_pcie_probe(struct platform_device *pdev)
+ {
+- struct device_node *np = pdev->dev.of_node;
++ struct device_node *np = pdev->dev.of_node, *msi_np;
+ struct pci_host_bridge *bridge;
+ struct brcm_pcie *pcie;
+ struct pci_bus *child;
+@@ -708,6 +959,15 @@ static int brcm_pcie_probe(struct platfo
+ if (ret)
+ goto fail;
+
++ msi_np = of_parse_phandle(pcie->np, "msi-parent", 0);
++ if (pci_msi_enabled() && msi_np == pcie->np) {
++ ret = brcm_pcie_enable_msi(pcie);
++ if (ret) {
++ dev_err(pcie->dev, "probe of internal MSI failed");
++ goto fail;
++ }
++ }
++
+ bridge->dev.parent = &pdev->dev;
+ bridge->busnr = 0;
+ bridge->ops = &brcm_pcie_ops;
+++ /dev/null
-From 1a90ecdfae1c0cf1b242276f6f0e3d98b5877f14 Mon Sep 17 00:00:00 2001
-From: Jim Quinlan <james.quinlan@broadcom.com>
-Date: Mon, 16 Dec 2019 12:01:10 +0100
-Subject: [PATCH] PCI: brcmstb: Add MSI support
-
-commit 40ca1bf580ef24df30702032ba5e40dfdcaa200b upstream.
-
-This adds MSI support to the Broadcom STB PCIe host controller. The MSI
-controller is physically located within the PCIe block, however, there
-is no reason why the MSI controller could not be moved elsewhere in the
-future. MSIX is not supported by the HW.
-
-Since the internal Brcmstb MSI controller is intertwined with the PCIe
-controller, it is not its own platform device but rather part of the
-PCIe platform device.
-
-Signed-off-by: Jim Quinlan <james.quinlan@broadcom.com>
-Co-developed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
-Reviewed-by: Marc Zyngier <maz@kernel.org>
-Reviewed-by: Andrew Murray <andrew.murray@arm.com>
----
- drivers/pci/controller/Kconfig | 1 +
- drivers/pci/controller/pcie-brcmstb.c | 262 +++++++++++++++++++++++++-
- 2 files changed, 262 insertions(+), 1 deletion(-)
-
---- a/drivers/pci/controller/Kconfig
-+++ b/drivers/pci/controller/Kconfig
-@@ -285,6 +285,7 @@ config PCIE_BRCMSTB
- tristate "Broadcom Brcmstb PCIe host controller"
- depends on ARCH_BCM2835 || COMPILE_TEST
- depends on OF
-+ depends on PCI_MSI_IRQ_DOMAIN
- help
- Say Y here to enable PCIe host controller support for
- Broadcom STB based SoCs, like the Raspberry Pi 4.
---- a/drivers/pci/controller/pcie-brcmstb.c
-+++ b/drivers/pci/controller/pcie-brcmstb.c
-@@ -2,6 +2,7 @@
- /* Copyright (C) 2009 - 2019 Broadcom */
-
- #include <linux/bitfield.h>
-+#include <linux/bitops.h>
- #include <linux/clk.h>
- #include <linux/compiler.h>
- #include <linux/delay.h>
-@@ -9,11 +10,13 @@
- #include <linux/interrupt.h>
- #include <linux/io.h>
- #include <linux/ioport.h>
-+#include <linux/irqchip/chained_irq.h>
- #include <linux/irqdomain.h>
- #include <linux/kernel.h>
- #include <linux/list.h>
- #include <linux/log2.h>
- #include <linux/module.h>
-+#include <linux/msi.h>
- #include <linux/of_address.h>
- #include <linux/of_irq.h>
- #include <linux/of_pci.h>
-@@ -67,6 +70,12 @@
- #define PCIE_MISC_RC_BAR3_CONFIG_LO 0x403c
- #define PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK 0x1f
-
-+#define PCIE_MISC_MSI_BAR_CONFIG_LO 0x4044
-+#define PCIE_MISC_MSI_BAR_CONFIG_HI 0x4048
-+
-+#define PCIE_MISC_MSI_DATA_CONFIG 0x404c
-+#define PCIE_MISC_MSI_DATA_CONFIG_VAL 0xffe06540
-+
- #define PCIE_MISC_PCIE_CTRL 0x4064
- #define PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK 0x1
-
-@@ -114,6 +123,11 @@
-
- /* PCIe parameters */
- #define BRCM_NUM_PCIE_OUT_WINS 0x4
-+#define BRCM_INT_PCI_MSI_NR 32
-+
-+/* MSI target adresses */
-+#define BRCM_MSI_TARGET_ADDR_LT_4GB 0x0fffffffcULL
-+#define BRCM_MSI_TARGET_ADDR_GT_4GB 0xffffffffcULL
-
- /* MDIO registers */
- #define MDIO_PORT0 0x0
-@@ -135,6 +149,19 @@
- #define SSC_STATUS_SSC_MASK 0x400
- #define SSC_STATUS_PLL_LOCK_MASK 0x800
-
-+struct brcm_msi {
-+ struct device *dev;
-+ void __iomem *base;
-+ struct device_node *np;
-+ struct irq_domain *msi_domain;
-+ struct irq_domain *inner_domain;
-+ struct mutex lock; /* guards the alloc/free operations */
-+ u64 target_addr;
-+ int irq;
-+ /* used indicates which MSI interrupts have been alloc'd */
-+ unsigned long used;
-+};
-+
- /* Internal PCIe Host Controller Information.*/
- struct brcm_pcie {
- struct device *dev;
-@@ -144,6 +171,8 @@ struct brcm_pcie {
- struct device_node *np;
- bool ssc;
- int gen;
-+ u64 msi_target_addr;
-+ struct brcm_msi *msi;
- };
-
- /*
-@@ -309,6 +338,215 @@ static void brcm_pcie_set_outbound_win(s
- writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
- }
-
-+static struct irq_chip brcm_msi_irq_chip = {
-+ .name = "BRCM STB PCIe MSI",
-+ .irq_ack = irq_chip_ack_parent,
-+ .irq_mask = pci_msi_mask_irq,
-+ .irq_unmask = pci_msi_unmask_irq,
-+};
-+
-+static struct msi_domain_info brcm_msi_domain_info = {
-+ /* Multi MSI is supported by the controller, but not by this driver */
-+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
-+ .chip = &brcm_msi_irq_chip,
-+};
-+
-+static void brcm_pcie_msi_isr(struct irq_desc *desc)
-+{
-+ struct irq_chip *chip = irq_desc_get_chip(desc);
-+ unsigned long status, virq;
-+ struct brcm_msi *msi;
-+ struct device *dev;
-+ u32 bit;
-+
-+ chained_irq_enter(chip, desc);
-+ msi = irq_desc_get_handler_data(desc);
-+ dev = msi->dev;
-+
-+ status = readl(msi->base + PCIE_MSI_INTR2_STATUS);
-+ for_each_set_bit(bit, &status, BRCM_INT_PCI_MSI_NR) {
-+ virq = irq_find_mapping(msi->inner_domain, bit);
-+ if (virq)
-+ generic_handle_irq(virq);
-+ else
-+ dev_dbg(dev, "unexpected MSI\n");
-+ }
-+
-+ chained_irq_exit(chip, desc);
-+}
-+
-+static void brcm_msi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
-+{
-+ struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
-+
-+ msg->address_lo = lower_32_bits(msi->target_addr);
-+ msg->address_hi = upper_32_bits(msi->target_addr);
-+ msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL) | data->hwirq;
-+}
-+
-+static int brcm_msi_set_affinity(struct irq_data *irq_data,
-+ const struct cpumask *mask, bool force)
-+{
-+ return -EINVAL;
-+}
-+
-+static void brcm_msi_ack_irq(struct irq_data *data)
-+{
-+ struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
-+
-+ writel(1 << data->hwirq, msi->base + PCIE_MSI_INTR2_CLR);
-+}
-+
-+
-+static struct irq_chip brcm_msi_bottom_irq_chip = {
-+ .name = "BRCM STB MSI",
-+ .irq_compose_msi_msg = brcm_msi_compose_msi_msg,
-+ .irq_set_affinity = brcm_msi_set_affinity,
-+ .irq_ack = brcm_msi_ack_irq,
-+};
-+
-+static int brcm_msi_alloc(struct brcm_msi *msi)
-+{
-+ int hwirq;
-+
-+ mutex_lock(&msi->lock);
-+ hwirq = bitmap_find_free_region(&msi->used, BRCM_INT_PCI_MSI_NR, 0);
-+ mutex_unlock(&msi->lock);
-+
-+ return hwirq;
-+}
-+
-+static void brcm_msi_free(struct brcm_msi *msi, unsigned long hwirq)
-+{
-+ mutex_lock(&msi->lock);
-+ bitmap_release_region(&msi->used, hwirq, 0);
-+ mutex_unlock(&msi->lock);
-+}
-+
-+static int brcm_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
-+ unsigned int nr_irqs, void *args)
-+{
-+ struct brcm_msi *msi = domain->host_data;
-+ int hwirq;
-+
-+ hwirq = brcm_msi_alloc(msi);
-+
-+ if (hwirq < 0)
-+ return hwirq;
-+
-+ irq_domain_set_info(domain, virq, (irq_hw_number_t)hwirq,
-+ &brcm_msi_bottom_irq_chip, domain->host_data,
-+ handle_edge_irq, NULL, NULL);
-+ return 0;
-+}
-+
-+static void brcm_irq_domain_free(struct irq_domain *domain,
-+ unsigned int virq, unsigned int nr_irqs)
-+{
-+ struct irq_data *d = irq_domain_get_irq_data(domain, virq);
-+ struct brcm_msi *msi = irq_data_get_irq_chip_data(d);
-+
-+ brcm_msi_free(msi, d->hwirq);
-+}
-+
-+static const struct irq_domain_ops msi_domain_ops = {
-+ .alloc = brcm_irq_domain_alloc,
-+ .free = brcm_irq_domain_free,
-+};
-+
-+static int brcm_allocate_domains(struct brcm_msi *msi)
-+{
-+ struct fwnode_handle *fwnode = of_node_to_fwnode(msi->np);
-+ struct device *dev = msi->dev;
-+
-+ msi->inner_domain = irq_domain_add_linear(NULL, BRCM_INT_PCI_MSI_NR,
-+ &msi_domain_ops, msi);
-+ if (!msi->inner_domain) {
-+ dev_err(dev, "failed to create IRQ domain\n");
-+ return -ENOMEM;
-+ }
-+
-+ msi->msi_domain = pci_msi_create_irq_domain(fwnode,
-+ &brcm_msi_domain_info,
-+ msi->inner_domain);
-+ if (!msi->msi_domain) {
-+ dev_err(dev, "failed to create MSI domain\n");
-+ irq_domain_remove(msi->inner_domain);
-+ return -ENOMEM;
-+ }
-+
-+ return 0;
-+}
-+
-+static void brcm_free_domains(struct brcm_msi *msi)
-+{
-+ irq_domain_remove(msi->msi_domain);
-+ irq_domain_remove(msi->inner_domain);
-+}
-+
-+static void brcm_msi_remove(struct brcm_pcie *pcie)
-+{
-+ struct brcm_msi *msi = pcie->msi;
-+
-+ if (!msi)
-+ return;
-+ irq_set_chained_handler(msi->irq, NULL);
-+ irq_set_handler_data(msi->irq, NULL);
-+ brcm_free_domains(msi);
-+}
-+
-+static void brcm_msi_set_regs(struct brcm_msi *msi)
-+{
-+ writel(0xffffffff, msi->base + PCIE_MSI_INTR2_MASK_CLR);
-+
-+ /*
-+ * The 0 bit of PCIE_MISC_MSI_BAR_CONFIG_LO is repurposed to MSI
-+ * enable, which we set to 1.
-+ */
-+ writel(lower_32_bits(msi->target_addr) | 0x1,
-+ msi->base + PCIE_MISC_MSI_BAR_CONFIG_LO);
-+ writel(upper_32_bits(msi->target_addr),
-+ msi->base + PCIE_MISC_MSI_BAR_CONFIG_HI);
-+
-+ writel(PCIE_MISC_MSI_DATA_CONFIG_VAL,
-+ msi->base + PCIE_MISC_MSI_DATA_CONFIG);
-+}
-+
-+static int brcm_pcie_enable_msi(struct brcm_pcie *pcie)
-+{
-+ struct brcm_msi *msi;
-+ int irq, ret;
-+ struct device *dev = pcie->dev;
-+
-+ irq = irq_of_parse_and_map(dev->of_node, 1);
-+ if (irq <= 0) {
-+ dev_err(dev, "cannot map MSI interrupt\n");
-+ return -ENODEV;
-+ }
-+
-+ msi = devm_kzalloc(dev, sizeof(struct brcm_msi), GFP_KERNEL);
-+ if (!msi)
-+ return -ENOMEM;
-+
-+ mutex_init(&msi->lock);
-+ msi->dev = dev;
-+ msi->base = pcie->base;
-+ msi->np = pcie->np;
-+ msi->target_addr = pcie->msi_target_addr;
-+ msi->irq = irq;
-+
-+ ret = brcm_allocate_domains(msi);
-+ if (ret)
-+ return ret;
-+
-+ irq_set_chained_handler_and_data(msi->irq, brcm_pcie_msi_isr, msi);
-+
-+ brcm_msi_set_regs(msi);
-+ pcie->msi = msi;
-+
-+ return 0;
-+}
-+
- /* The controller is capable of serving in both RC and EP roles */
- static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie)
- {
-@@ -497,6 +735,18 @@ static int brcm_pcie_setup(struct brcm_p
- PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK);
- writel(tmp, base + PCIE_MISC_MISC_CTRL);
-
-+ /*
-+ * We ideally want the MSI target address to be located in the 32bit
-+ * addressable memory area. Some devices might depend on it. This is
-+ * possible either when the inbound window is located above the lower
-+ * 4GB or when the inbound area is smaller than 4GB (taking into
-+ * account the rounding-up we're forced to perform).
-+ */
-+ if (rc_bar2_offset >= SZ_4G || (rc_bar2_size + rc_bar2_offset) < SZ_4G)
-+ pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_LT_4GB;
-+ else
-+ pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_GT_4GB;
-+
- /* disable the PCIe->GISB memory window (RC_BAR1) */
- tmp = readl(base + PCIE_MISC_RC_BAR1_CONFIG_LO);
- tmp &= ~PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK;
-@@ -646,6 +896,7 @@ static void brcm_pcie_turn_off(struct br
-
- static void __brcm_pcie_remove(struct brcm_pcie *pcie)
- {
-+ brcm_msi_remove(pcie);
- brcm_pcie_turn_off(pcie);
- clk_disable_unprepare(pcie->clk);
- clk_put(pcie->clk);
-@@ -664,7 +915,7 @@ static int brcm_pcie_remove(struct platf
-
- static int brcm_pcie_probe(struct platform_device *pdev)
- {
-- struct device_node *np = pdev->dev.of_node;
-+ struct device_node *np = pdev->dev.of_node, *msi_np;
- struct pci_host_bridge *bridge;
- struct brcm_pcie *pcie;
- struct pci_bus *child;
-@@ -708,6 +959,15 @@ static int brcm_pcie_probe(struct platfo
- if (ret)
- goto fail;
-
-+ msi_np = of_parse_phandle(pcie->np, "msi-parent", 0);
-+ if (pci_msi_enabled() && msi_np == pcie->np) {
-+ ret = brcm_pcie_enable_msi(pcie);
-+ if (ret) {
-+ dev_err(pcie->dev, "probe of internal MSI failed");
-+ goto fail;
-+ }
-+ }
-+
- bridge->dev.parent = &pdev->dev;
- bridge->busnr = 0;
- bridge->ops = &brcm_pcie_ops;
--- /dev/null
+From 39192141aa16809323c24d8910e3a63488f7f55d Mon Sep 17 00:00:00 2001
+From: Marek Szyprowski <m.szyprowski@samsung.com>
+Date: Thu, 27 Feb 2020 12:51:46 +0100
+Subject: [PATCH] PCI: brcmstb: Fix build on 32bit ARM platforms with
+ older compilers
+
+commit 73a7a271b3eee7b83f29b13866163776f1cbef89 upstream.
+
+Some older compilers have no implementation for the helper for 64-bit
+unsigned division/modulo, so linking pcie-brcmstb driver causes the
+"undefined reference to `__aeabi_uldivmod'" error.
+
+*rc_bar2_size is always a power of two, because it is calculated as:
+"1ULL << fls64(entry->res->end - entry->res->start)", so the modulo
+operation in the subsequent check can be replaced by a simple logical
+AND with a proper mask.
+
+Link: https://lore.kernel.org/r/20200227115146.24515-1-m.szyprowski@samsung.com
+Fixes: c0452137034b ("PCI: brcmstb: Add Broadcom STB PCIe host controller driver")
+Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Acked-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+---
+ drivers/pci/controller/pcie-brcmstb.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/pci/controller/pcie-brcmstb.c
++++ b/drivers/pci/controller/pcie-brcmstb.c
+@@ -670,7 +670,7 @@ static inline int brcm_pcie_get_rc_bar2_
+ * outbound memory @ 3GB). So instead it will start at the 1x
+ * multiple of its size
+ */
+- if (!*rc_bar2_size || *rc_bar2_offset % *rc_bar2_size ||
++ if (!*rc_bar2_size || (*rc_bar2_offset & (*rc_bar2_size - 1)) ||
+ (*rc_bar2_offset < SZ_4G && *rc_bar2_offset > SZ_2G)) {
+ dev_err(dev, "Invalid rc_bar2_offset/size: size 0x%llx, off 0x%llx\n",
+ *rc_bar2_size, *rc_bar2_offset);
+++ /dev/null
-From 39192141aa16809323c24d8910e3a63488f7f55d Mon Sep 17 00:00:00 2001
-From: Marek Szyprowski <m.szyprowski@samsung.com>
-Date: Thu, 27 Feb 2020 12:51:46 +0100
-Subject: [PATCH] PCI: brcmstb: Fix build on 32bit ARM platforms with
- older compilers
-
-commit 73a7a271b3eee7b83f29b13866163776f1cbef89 upstream.
-
-Some older compilers have no implementation for the helper for 64-bit
-unsigned division/modulo, so linking pcie-brcmstb driver causes the
-"undefined reference to `__aeabi_uldivmod'" error.
-
-*rc_bar2_size is always a power of two, because it is calculated as:
-"1ULL << fls64(entry->res->end - entry->res->start)", so the modulo
-operation in the subsequent check can be replaced by a simple logical
-AND with a proper mask.
-
-Link: https://lore.kernel.org/r/20200227115146.24515-1-m.szyprowski@samsung.com
-Fixes: c0452137034b ("PCI: brcmstb: Add Broadcom STB PCIe host controller driver")
-Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
-Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
-Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Acked-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
----
- drivers/pci/controller/pcie-brcmstb.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/pci/controller/pcie-brcmstb.c
-+++ b/drivers/pci/controller/pcie-brcmstb.c
-@@ -670,7 +670,7 @@ static inline int brcm_pcie_get_rc_bar2_
- * outbound memory @ 3GB). So instead it will start at the 1x
- * multiple of its size
- */
-- if (!*rc_bar2_size || *rc_bar2_offset % *rc_bar2_size ||
-+ if (!*rc_bar2_size || (*rc_bar2_offset & (*rc_bar2_size - 1)) ||
- (*rc_bar2_offset < SZ_4G && *rc_bar2_offset > SZ_2G)) {
- dev_err(dev, "Invalid rc_bar2_offset/size: size 0x%llx, off 0x%llx\n",
- *rc_bar2_size, *rc_bar2_offset);
--- /dev/null
+From 5e9b9f246802f492e7740ab2589aa8c81df5ef20 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 2 Mar 2020 15:05:25 +0000
+Subject: [PATCH] bcm2711-rpi.dtsi: Use upstream pcie node
+
+Now that the upstream bcm2711 DT has a pcie DT node there's no need to
+define one downstream.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 2 +-
+ arch/arm/boot/dts/bcm2711-rpi.dtsi | 41 ---------------------------
+ 2 files changed, 1 insertion(+), 42 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
+@@ -163,7 +163,7 @@
+ i2c6 = &i2c6;
+ /delete-property/ ethernet;
+ /delete-property/ intc;
+- pcie0 = &pcie_0;
++ pcie0 = &pcie0;
+ };
+
+ /delete-node/ wifi-pwrseq;
+--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi
+@@ -66,47 +66,6 @@
+ <0x0 0x00000000 0x0 0x00000000 0xfc000000>;
+ dma-ranges = <0x0 0x00000000 0x0 0x00000000 0xfc000000>;
+
+- pcie_0: pcie@7d500000 {
+- reg = <0x0 0x7d500000 0x9310>,
+- <0x0 0x7e00f300 0x20>;
+- msi-controller;
+- msi-parent = <&pcie_0>;
+- #address-cells = <3>;
+- #interrupt-cells = <1>;
+- #size-cells = <2>;
+- bus-range = <0x0 0x01>;
+- compatible = "brcm,bcm2711b0-pcie", // Safe value
+- "brcm,bcm2711-pcie",
+- "brcm,pci-plat-dev";
+- max-link-speed = <2>;
+- tot-num-pcie = <1>;
+- linux,pci-domain = <0>;
+- interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>,
+- <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+- interrupt-names = "pcie", "msi";
+- interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+- interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 143
+- IRQ_TYPE_LEVEL_HIGH
+- 0 0 0 2 &gicv2 GIC_SPI 144
+- IRQ_TYPE_LEVEL_HIGH
+- 0 0 0 3 &gicv2 GIC_SPI 145
+- IRQ_TYPE_LEVEL_HIGH
+- 0 0 0 4 &gicv2 GIC_SPI 146
+- IRQ_TYPE_LEVEL_HIGH>;
+-
+- /* Map outbound accesses from scb:0x6_00000000-03ffffff
+- * to pci:0x0_f8000000-fbffffff
+- */
+- ranges = <0x02000000 0x0 0xf8000000 0x6 0x00000000
+- 0x0 0x04000000>;
+- /* Map inbound accesses from pci:0x0_00000000..ffffffff
+- * to scb:0x0_00000000-ffffffff
+- */
+- dma-ranges = <0x02000000 0x0 0x00000000 0x0 0x00000000
+- 0x1 0x00000000>;
+- status = "okay";
+- };
+-
+ dma40: dma@7e007b00 {
+ compatible = "brcm,bcm2711-dma";
+ reg = <0x0 0x7e007b00 0x400>;
+++ /dev/null
-From 5e9b9f246802f492e7740ab2589aa8c81df5ef20 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Mon, 2 Mar 2020 15:05:25 +0000
-Subject: [PATCH] bcm2711-rpi.dtsi: Use upstream pcie node
-
-Now that the upstream bcm2711 DT has a pcie DT node there's no need to
-define one downstream.
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 2 +-
- arch/arm/boot/dts/bcm2711-rpi.dtsi | 41 ---------------------------
- 2 files changed, 1 insertion(+), 42 deletions(-)
-
---- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
-+++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
-@@ -163,7 +163,7 @@
- i2c6 = &i2c6;
- /delete-property/ ethernet;
- /delete-property/ intc;
-- pcie0 = &pcie_0;
-+ pcie0 = &pcie0;
- };
-
- /delete-node/ wifi-pwrseq;
---- a/arch/arm/boot/dts/bcm2711-rpi.dtsi
-+++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi
-@@ -66,47 +66,6 @@
- <0x0 0x00000000 0x0 0x00000000 0xfc000000>;
- dma-ranges = <0x0 0x00000000 0x0 0x00000000 0xfc000000>;
-
-- pcie_0: pcie@7d500000 {
-- reg = <0x0 0x7d500000 0x9310>,
-- <0x0 0x7e00f300 0x20>;
-- msi-controller;
-- msi-parent = <&pcie_0>;
-- #address-cells = <3>;
-- #interrupt-cells = <1>;
-- #size-cells = <2>;
-- bus-range = <0x0 0x01>;
-- compatible = "brcm,bcm2711b0-pcie", // Safe value
-- "brcm,bcm2711-pcie",
-- "brcm,pci-plat-dev";
-- max-link-speed = <2>;
-- tot-num-pcie = <1>;
-- linux,pci-domain = <0>;
-- interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>,
-- <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
-- interrupt-names = "pcie", "msi";
-- interrupt-map-mask = <0x0 0x0 0x0 0x7>;
-- interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 143
-- IRQ_TYPE_LEVEL_HIGH
-- 0 0 0 2 &gicv2 GIC_SPI 144
-- IRQ_TYPE_LEVEL_HIGH
-- 0 0 0 3 &gicv2 GIC_SPI 145
-- IRQ_TYPE_LEVEL_HIGH
-- 0 0 0 4 &gicv2 GIC_SPI 146
-- IRQ_TYPE_LEVEL_HIGH>;
--
-- /* Map outbound accesses from scb:0x6_00000000-03ffffff
-- * to pci:0x0_f8000000-fbffffff
-- */
-- ranges = <0x02000000 0x0 0xf8000000 0x6 0x00000000
-- 0x0 0x04000000>;
-- /* Map inbound accesses from pci:0x0_00000000..ffffffff
-- * to scb:0x0_00000000-ffffffff
-- */
-- dma-ranges = <0x02000000 0x0 0x00000000 0x0 0x00000000
-- 0x1 0x00000000>;
-- status = "okay";
-- };
--
- dma40: dma@7e007b00 {
- compatible = "brcm,bcm2711-dma";
- reg = <0x0 0x7e007b00 0x400>;
--- /dev/null
+From a3ceeebaaa66e6786490e850b5019808da3785c0 Mon Sep 17 00:00:00 2001
+From: Andrey Konovalov <andrey.konovalov@linaro.org>
+Date: Mon, 20 Jan 2020 05:15:57 -0300
+Subject: [PATCH] media: dt-bindings: media: i2c: Add IMX219 CMOS
+ sensor binding
+
+Commit 9d730f2cf4c0391785855dd231577d2de2594df9 upstream.
+(Currently on linux-media/master, queued for 5.7)
+
+Add YAML device tree binding for IMX219 CMOS image sensor, and
+the relevant MAINTAINERS entries.
+
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ .../devicetree/bindings/media/i2c/imx219.yaml | 114 ++++++++++++++++++
+ MAINTAINERS | 8 ++
+ 2 files changed, 122 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/media/i2c/imx219.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/media/i2c/imx219.yaml
+@@ -0,0 +1,114 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/media/i2c/imx219.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Sony 1/4.0-Inch 8Mpixel CMOS Digital Image Sensor
++
++maintainers:
++ - Dave Stevenson <dave.stevenson@raspberrypi.com>
++
++description: |-
++ The Sony imx219 is a 1/4.0-inch CMOS active pixel digital image sensor
++ with an active array size of 3280H x 2464V. It is programmable through
++ I2C interface. The I2C address is fixed to 0x10 as per sensor data sheet.
++ Image data is sent through MIPI CSI-2, which is configured as either 2 or
++ 4 data lanes.
++
++properties:
++ compatible:
++ const: sony,imx219
++
++ reg:
++ description: I2C device address
++ maxItems: 1
++
++ clocks:
++ maxItems: 1
++
++ VDIG-supply:
++ description:
++ Digital I/O voltage supply, 1.8 volts
++
++ VANA-supply:
++ description:
++ Analog voltage supply, 2.8 volts
++
++ VDDL-supply:
++ description:
++ Digital core voltage supply, 1.2 volts
++
++ reset-gpios:
++ description: |-
++ Reference to the GPIO connected to the xclr pin, if any.
++ Must be released (set high) after all supplies are applied.
++
++ # See ../video-interfaces.txt for more details
++ port:
++ type: object
++ properties:
++ endpoint:
++ type: object
++ properties:
++ data-lanes:
++ description: |-
++ The sensor supports either two-lane, or four-lane operation.
++ If this property is omitted four-lane operation is assumed.
++ For two-lane operation the property must be set to <1 2>.
++ items:
++ - const: 1
++ - const: 2
++
++ clock-noncontinuous:
++ type: boolean
++ description: |-
++ MIPI CSI-2 clock is non-continuous if this property is present,
++ otherwise it's continuous.
++
++ link-frequencies:
++ allOf:
++ - $ref: /schemas/types.yaml#/definitions/uint64-array
++ description:
++ Allowed data bus frequencies.
++
++ required:
++ - link-frequencies
++
++required:
++ - compatible
++ - reg
++ - clocks
++ - VANA-supply
++ - VDIG-supply
++ - VDDL-supply
++ - port
++
++additionalProperties: false
++
++examples:
++ - |
++ i2c0 {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ imx219: sensor@10 {
++ compatible = "sony,imx219";
++ reg = <0x10>;
++ clocks = <&imx219_clk>;
++ VANA-supply = <&imx219_vana>; /* 2.8v */
++ VDIG-supply = <&imx219_vdig>; /* 1.8v */
++ VDDL-supply = <&imx219_vddl>; /* 1.2v */
++
++ port {
++ imx219_0: endpoint {
++ remote-endpoint = <&csi1_ep>;
++ data-lanes = <1 2>;
++ clock-noncontinuous;
++ link-frequencies = /bits/ 64 <456000000>;
++ };
++ };
++ };
++ };
++
++...
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -15142,6 +15142,14 @@ S: Maintained
+ F: drivers/media/i2c/imx214.c
+ F: Documentation/devicetree/bindings/media/i2c/sony,imx214.txt
+
++SONY IMX219 SENSOR DRIVER
++M: Dave Stevenson <dave.stevenson@raspberrypi.com>
++L: linux-media@vger.kernel.org
++T: git git://linuxtv.org/media_tree.git
++S: Maintained
++F: drivers/media/i2c/imx219.c
++F: Documentation/devicetree/bindings/media/i2c/imx219.yaml
++
+ SONY IMX258 SENSOR DRIVER
+ M: Sakari Ailus <sakari.ailus@linux.intel.com>
+ L: linux-media@vger.kernel.org
+++ /dev/null
-From a3ceeebaaa66e6786490e850b5019808da3785c0 Mon Sep 17 00:00:00 2001
-From: Andrey Konovalov <andrey.konovalov@linaro.org>
-Date: Mon, 20 Jan 2020 05:15:57 -0300
-Subject: [PATCH] media: dt-bindings: media: i2c: Add IMX219 CMOS
- sensor binding
-
-Commit 9d730f2cf4c0391785855dd231577d2de2594df9 upstream.
-(Currently on linux-media/master, queued for 5.7)
-
-Add YAML device tree binding for IMX219 CMOS image sensor, and
-the relevant MAINTAINERS entries.
-
-Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
-Reviewed-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
-Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
----
- .../devicetree/bindings/media/i2c/imx219.yaml | 114 ++++++++++++++++++
- MAINTAINERS | 8 ++
- 2 files changed, 122 insertions(+)
- create mode 100644 Documentation/devicetree/bindings/media/i2c/imx219.yaml
-
---- /dev/null
-+++ b/Documentation/devicetree/bindings/media/i2c/imx219.yaml
-@@ -0,0 +1,114 @@
-+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-+%YAML 1.2
-+---
-+$id: http://devicetree.org/schemas/media/i2c/imx219.yaml#
-+$schema: http://devicetree.org/meta-schemas/core.yaml#
-+
-+title: Sony 1/4.0-Inch 8Mpixel CMOS Digital Image Sensor
-+
-+maintainers:
-+ - Dave Stevenson <dave.stevenson@raspberrypi.com>
-+
-+description: |-
-+ The Sony imx219 is a 1/4.0-inch CMOS active pixel digital image sensor
-+ with an active array size of 3280H x 2464V. It is programmable through
-+ I2C interface. The I2C address is fixed to 0x10 as per sensor data sheet.
-+ Image data is sent through MIPI CSI-2, which is configured as either 2 or
-+ 4 data lanes.
-+
-+properties:
-+ compatible:
-+ const: sony,imx219
-+
-+ reg:
-+ description: I2C device address
-+ maxItems: 1
-+
-+ clocks:
-+ maxItems: 1
-+
-+ VDIG-supply:
-+ description:
-+ Digital I/O voltage supply, 1.8 volts
-+
-+ VANA-supply:
-+ description:
-+ Analog voltage supply, 2.8 volts
-+
-+ VDDL-supply:
-+ description:
-+ Digital core voltage supply, 1.2 volts
-+
-+ reset-gpios:
-+ description: |-
-+ Reference to the GPIO connected to the xclr pin, if any.
-+ Must be released (set high) after all supplies are applied.
-+
-+ # See ../video-interfaces.txt for more details
-+ port:
-+ type: object
-+ properties:
-+ endpoint:
-+ type: object
-+ properties:
-+ data-lanes:
-+ description: |-
-+ The sensor supports either two-lane, or four-lane operation.
-+ If this property is omitted four-lane operation is assumed.
-+ For two-lane operation the property must be set to <1 2>.
-+ items:
-+ - const: 1
-+ - const: 2
-+
-+ clock-noncontinuous:
-+ type: boolean
-+ description: |-
-+ MIPI CSI-2 clock is non-continuous if this property is present,
-+ otherwise it's continuous.
-+
-+ link-frequencies:
-+ allOf:
-+ - $ref: /schemas/types.yaml#/definitions/uint64-array
-+ description:
-+ Allowed data bus frequencies.
-+
-+ required:
-+ - link-frequencies
-+
-+required:
-+ - compatible
-+ - reg
-+ - clocks
-+ - VANA-supply
-+ - VDIG-supply
-+ - VDDL-supply
-+ - port
-+
-+additionalProperties: false
-+
-+examples:
-+ - |
-+ i2c0 {
-+ #address-cells = <1>;
-+ #size-cells = <0>;
-+
-+ imx219: sensor@10 {
-+ compatible = "sony,imx219";
-+ reg = <0x10>;
-+ clocks = <&imx219_clk>;
-+ VANA-supply = <&imx219_vana>; /* 2.8v */
-+ VDIG-supply = <&imx219_vdig>; /* 1.8v */
-+ VDDL-supply = <&imx219_vddl>; /* 1.2v */
-+
-+ port {
-+ imx219_0: endpoint {
-+ remote-endpoint = <&csi1_ep>;
-+ data-lanes = <1 2>;
-+ clock-noncontinuous;
-+ link-frequencies = /bits/ 64 <456000000>;
-+ };
-+ };
-+ };
-+ };
-+
-+...
---- a/MAINTAINERS
-+++ b/MAINTAINERS
-@@ -15142,6 +15142,14 @@ S: Maintained
- F: drivers/media/i2c/imx214.c
- F: Documentation/devicetree/bindings/media/i2c/sony,imx214.txt
-
-+SONY IMX219 SENSOR DRIVER
-+M: Dave Stevenson <dave.stevenson@raspberrypi.com>
-+L: linux-media@vger.kernel.org
-+T: git git://linuxtv.org/media_tree.git
-+S: Maintained
-+F: drivers/media/i2c/imx219.c
-+F: Documentation/devicetree/bindings/media/i2c/imx219.yaml
-+
- SONY IMX258 SENSOR DRIVER
- M: Sakari Ailus <sakari.ailus@linux.intel.com>
- L: linux-media@vger.kernel.org
--- /dev/null
+From 5cd8c4efeb46ce1ef370dd3012a7951ba430b58f Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 20 Jan 2020 05:15:58 -0300
+Subject: [PATCH] media: i2c: Add driver for Sony IMX219 sensor
+
+Commit 1283b3b8f82b9004fbb94398cade5c8e797a2c8d upstream.
+(Currently on linux-media/master, queued for 5.7)
+
+Adds a driver for the 8MPix Sony IMX219 CSI2 sensor.
+Whilst the sensor supports 2 or 4 CSI2 data lanes, this driver
+currently only supports 2 lanes.
+8MPix @ 15fps, 1080P @ 30fps (cropped FOV), and 1640x1232 (2x2 binned)
+@ 30fps are currently supported.
+
+[Sakari Ailus: make imx219_check_hwcfg static]
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ drivers/media/i2c/Kconfig | 11 +
+ drivers/media/i2c/Makefile | 1 +
+ drivers/media/i2c/imx219.c | 1312 ++++++++++++++++++++++++++++++++++++
+ 3 files changed, 1324 insertions(+)
+ create mode 100644 drivers/media/i2c/imx219.c
+
+--- a/drivers/media/i2c/Kconfig
++++ b/drivers/media/i2c/Kconfig
+@@ -578,6 +578,17 @@ config VIDEO_IMX214
+ To compile this driver as a module, choose M here: the
+ module will be called imx214.
+
++config VIDEO_IMX219
++ tristate "Sony IMX219 sensor support"
++ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
++ select V4L2_FWNODE
++ help
++ This is a Video4Linux2 sensor driver for the Sony
++ IMX219 camera.
++
++ To compile this driver as a module, choose M here: the
++ module will be called imx219.
++
+ config VIDEO_IMX258
+ tristate "Sony IMX258 sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+--- a/drivers/media/i2c/Makefile
++++ b/drivers/media/i2c/Makefile
+@@ -111,6 +111,7 @@ obj-$(CONFIG_VIDEO_ML86V7667) += ml86v76
+ obj-$(CONFIG_VIDEO_OV2659) += ov2659.o
+ obj-$(CONFIG_VIDEO_TC358743) += tc358743.o
+ obj-$(CONFIG_VIDEO_IMX214) += imx214.o
++obj-$(CONFIG_VIDEO_IMX219) += imx219.o
+ obj-$(CONFIG_VIDEO_IMX258) += imx258.o
+ obj-$(CONFIG_VIDEO_IMX274) += imx274.o
+ obj-$(CONFIG_VIDEO_IMX319) += imx319.o
+--- /dev/null
++++ b/drivers/media/i2c/imx219.c
+@@ -0,0 +1,1312 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * A V4L2 driver for Sony IMX219 cameras.
++ * Copyright (C) 2019, Raspberry Pi (Trading) Ltd
++ *
++ * Based on Sony imx258 camera driver
++ * Copyright (C) 2018 Intel Corporation
++ *
++ * DT / fwnode changes, and regulator / GPIO control taken from imx214 driver
++ * Copyright 2018 Qtechnology A/S
++ *
++ * Flip handling taken from the Sony IMX319 driver.
++ * Copyright (C) 2018 Intel Corporation
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/clkdev.h>
++#include <linux/delay.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/regulator/consumer.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-mediabus.h>
++#include <asm/unaligned.h>
++
++#define IMX219_REG_VALUE_08BIT 1
++#define IMX219_REG_VALUE_16BIT 2
++
++#define IMX219_REG_MODE_SELECT 0x0100
++#define IMX219_MODE_STANDBY 0x00
++#define IMX219_MODE_STREAMING 0x01
++
++/* Chip ID */
++#define IMX219_REG_CHIP_ID 0x0000
++#define IMX219_CHIP_ID 0x0219
++
++/* External clock frequency is 24.0M */
++#define IMX219_XCLK_FREQ 24000000
++
++/* Pixel rate is fixed at 182.4M for all the modes */
++#define IMX219_PIXEL_RATE 182400000
++
++#define IMX219_DEFAULT_LINK_FREQ 456000000
++
++/* V_TIMING internal */
++#define IMX219_REG_VTS 0x0160
++#define IMX219_VTS_15FPS 0x0dc6
++#define IMX219_VTS_30FPS_1080P 0x06e3
++#define IMX219_VTS_30FPS_BINNED 0x06e3
++#define IMX219_VTS_MAX 0xffff
++
++#define IMX219_VBLANK_MIN 4
++
++/*Frame Length Line*/
++#define IMX219_FLL_MIN 0x08a6
++#define IMX219_FLL_MAX 0xffff
++#define IMX219_FLL_STEP 1
++#define IMX219_FLL_DEFAULT 0x0c98
++
++/* HBLANK control - read only */
++#define IMX219_PPL_DEFAULT 3448
++
++/* Exposure control */
++#define IMX219_REG_EXPOSURE 0x015a
++#define IMX219_EXPOSURE_MIN 4
++#define IMX219_EXPOSURE_STEP 1
++#define IMX219_EXPOSURE_DEFAULT 0x640
++#define IMX219_EXPOSURE_MAX 65535
++
++/* Analog gain control */
++#define IMX219_REG_ANALOG_GAIN 0x0157
++#define IMX219_ANA_GAIN_MIN 0
++#define IMX219_ANA_GAIN_MAX 232
++#define IMX219_ANA_GAIN_STEP 1
++#define IMX219_ANA_GAIN_DEFAULT 0x0
++
++/* Digital gain control */
++#define IMX219_REG_DIGITAL_GAIN 0x0158
++#define IMX219_DGTL_GAIN_MIN 0x0100
++#define IMX219_DGTL_GAIN_MAX 0x0fff
++#define IMX219_DGTL_GAIN_DEFAULT 0x0100
++#define IMX219_DGTL_GAIN_STEP 1
++
++#define IMX219_REG_ORIENTATION 0x0172
++
++/* Test Pattern Control */
++#define IMX219_REG_TEST_PATTERN 0x0600
++#define IMX219_TEST_PATTERN_DISABLE 0
++#define IMX219_TEST_PATTERN_SOLID_COLOR 1
++#define IMX219_TEST_PATTERN_COLOR_BARS 2
++#define IMX219_TEST_PATTERN_GREY_COLOR 3
++#define IMX219_TEST_PATTERN_PN9 4
++
++/* Test pattern colour components */
++#define IMX219_REG_TESTP_RED 0x0602
++#define IMX219_REG_TESTP_GREENR 0x0604
++#define IMX219_REG_TESTP_BLUE 0x0606
++#define IMX219_REG_TESTP_GREENB 0x0608
++#define IMX219_TESTP_COLOUR_MIN 0
++#define IMX219_TESTP_COLOUR_MAX 0x03ff
++#define IMX219_TESTP_COLOUR_STEP 1
++#define IMX219_TESTP_RED_DEFAULT IMX219_TESTP_COLOUR_MAX
++#define IMX219_TESTP_GREENR_DEFAULT 0
++#define IMX219_TESTP_BLUE_DEFAULT 0
++#define IMX219_TESTP_GREENB_DEFAULT 0
++
++struct imx219_reg {
++ u16 address;
++ u8 val;
++};
++
++struct imx219_reg_list {
++ unsigned int num_of_regs;
++ const struct imx219_reg *regs;
++};
++
++/* Mode : resolution and related config&values */
++struct imx219_mode {
++ /* Frame width */
++ unsigned int width;
++ /* Frame height */
++ unsigned int height;
++
++ /* V-timing */
++ unsigned int vts_def;
++
++ /* Default register values */
++ struct imx219_reg_list reg_list;
++};
++
++/*
++ * Register sets lifted off the i2C interface from the Raspberry Pi firmware
++ * driver.
++ * 3280x2464 = mode 2, 1920x1080 = mode 1, and 1640x1232 = mode 4.
++ */
++static const struct imx219_reg mode_3280x2464_regs[] = {
++ {0x0100, 0x00},
++ {0x30eb, 0x0c},
++ {0x30eb, 0x05},
++ {0x300a, 0xff},
++ {0x300b, 0xff},
++ {0x30eb, 0x05},
++ {0x30eb, 0x09},
++ {0x0114, 0x01},
++ {0x0128, 0x00},
++ {0x012a, 0x18},
++ {0x012b, 0x00},
++ {0x0164, 0x00},
++ {0x0165, 0x00},
++ {0x0166, 0x0c},
++ {0x0167, 0xcf},
++ {0x0168, 0x00},
++ {0x0169, 0x00},
++ {0x016a, 0x09},
++ {0x016b, 0x9f},
++ {0x016c, 0x0c},
++ {0x016d, 0xd0},
++ {0x016e, 0x09},
++ {0x016f, 0xa0},
++ {0x0170, 0x01},
++ {0x0171, 0x01},
++ {0x0174, 0x00},
++ {0x0175, 0x00},
++ {0x018c, 0x0a},
++ {0x018d, 0x0a},
++ {0x0301, 0x05},
++ {0x0303, 0x01},
++ {0x0304, 0x03},
++ {0x0305, 0x03},
++ {0x0306, 0x00},
++ {0x0307, 0x39},
++ {0x0309, 0x0a},
++ {0x030b, 0x01},
++ {0x030c, 0x00},
++ {0x030d, 0x72},
++ {0x0624, 0x0c},
++ {0x0625, 0xd0},
++ {0x0626, 0x09},
++ {0x0627, 0xa0},
++ {0x455e, 0x00},
++ {0x471e, 0x4b},
++ {0x4767, 0x0f},
++ {0x4750, 0x14},
++ {0x4540, 0x00},
++ {0x47b4, 0x14},
++ {0x4713, 0x30},
++ {0x478b, 0x10},
++ {0x478f, 0x10},
++ {0x4793, 0x10},
++ {0x4797, 0x0e},
++ {0x479b, 0x0e},
++ {0x0162, 0x0d},
++ {0x0163, 0x78},
++};
++
++static const struct imx219_reg mode_1920_1080_regs[] = {
++ {0x0100, 0x00},
++ {0x30eb, 0x05},
++ {0x30eb, 0x0c},
++ {0x300a, 0xff},
++ {0x300b, 0xff},
++ {0x30eb, 0x05},
++ {0x30eb, 0x09},
++ {0x0114, 0x01},
++ {0x0128, 0x00},
++ {0x012a, 0x18},
++ {0x012b, 0x00},
++ {0x0162, 0x0d},
++ {0x0163, 0x78},
++ {0x0164, 0x02},
++ {0x0165, 0xa8},
++ {0x0166, 0x0a},
++ {0x0167, 0x27},
++ {0x0168, 0x02},
++ {0x0169, 0xb4},
++ {0x016a, 0x06},
++ {0x016b, 0xeb},
++ {0x016c, 0x07},
++ {0x016d, 0x80},
++ {0x016e, 0x04},
++ {0x016f, 0x38},
++ {0x0170, 0x01},
++ {0x0171, 0x01},
++ {0x0174, 0x00},
++ {0x0175, 0x00},
++ {0x018c, 0x0a},
++ {0x018d, 0x0a},
++ {0x0301, 0x05},
++ {0x0303, 0x01},
++ {0x0304, 0x03},
++ {0x0305, 0x03},
++ {0x0306, 0x00},
++ {0x0307, 0x39},
++ {0x0309, 0x0a},
++ {0x030b, 0x01},
++ {0x030c, 0x00},
++ {0x030d, 0x72},
++ {0x0624, 0x07},
++ {0x0625, 0x80},
++ {0x0626, 0x04},
++ {0x0627, 0x38},
++ {0x455e, 0x00},
++ {0x471e, 0x4b},
++ {0x4767, 0x0f},
++ {0x4750, 0x14},
++ {0x4540, 0x00},
++ {0x47b4, 0x14},
++ {0x4713, 0x30},
++ {0x478b, 0x10},
++ {0x478f, 0x10},
++ {0x4793, 0x10},
++ {0x4797, 0x0e},
++ {0x479b, 0x0e},
++ {0x0162, 0x0d},
++ {0x0163, 0x78},
++};
++
++static const struct imx219_reg mode_1640_1232_regs[] = {
++ {0x0100, 0x00},
++ {0x30eb, 0x0c},
++ {0x30eb, 0x05},
++ {0x300a, 0xff},
++ {0x300b, 0xff},
++ {0x30eb, 0x05},
++ {0x30eb, 0x09},
++ {0x0114, 0x01},
++ {0x0128, 0x00},
++ {0x012a, 0x18},
++ {0x012b, 0x00},
++ {0x0164, 0x00},
++ {0x0165, 0x00},
++ {0x0166, 0x0c},
++ {0x0167, 0xcf},
++ {0x0168, 0x00},
++ {0x0169, 0x00},
++ {0x016a, 0x09},
++ {0x016b, 0x9f},
++ {0x016c, 0x06},
++ {0x016d, 0x68},
++ {0x016e, 0x04},
++ {0x016f, 0xd0},
++ {0x0170, 0x01},
++ {0x0171, 0x01},
++ {0x0174, 0x01},
++ {0x0175, 0x01},
++ {0x018c, 0x0a},
++ {0x018d, 0x0a},
++ {0x0301, 0x05},
++ {0x0303, 0x01},
++ {0x0304, 0x03},
++ {0x0305, 0x03},
++ {0x0306, 0x00},
++ {0x0307, 0x39},
++ {0x0309, 0x0a},
++ {0x030b, 0x01},
++ {0x030c, 0x00},
++ {0x030d, 0x72},
++ {0x0624, 0x06},
++ {0x0625, 0x68},
++ {0x0626, 0x04},
++ {0x0627, 0xd0},
++ {0x455e, 0x00},
++ {0x471e, 0x4b},
++ {0x4767, 0x0f},
++ {0x4750, 0x14},
++ {0x4540, 0x00},
++ {0x47b4, 0x14},
++ {0x4713, 0x30},
++ {0x478b, 0x10},
++ {0x478f, 0x10},
++ {0x4793, 0x10},
++ {0x4797, 0x0e},
++ {0x479b, 0x0e},
++ {0x0162, 0x0d},
++ {0x0163, 0x78},
++};
++
++static const char * const imx219_test_pattern_menu[] = {
++ "Disabled",
++ "Color Bars",
++ "Solid Color",
++ "Grey Color Bars",
++ "PN9"
++};
++
++static const int imx219_test_pattern_val[] = {
++ IMX219_TEST_PATTERN_DISABLE,
++ IMX219_TEST_PATTERN_COLOR_BARS,
++ IMX219_TEST_PATTERN_SOLID_COLOR,
++ IMX219_TEST_PATTERN_GREY_COLOR,
++ IMX219_TEST_PATTERN_PN9,
++};
++
++/* regulator supplies */
++static const char * const imx219_supply_name[] = {
++ /* Supplies can be enabled in any order */
++ "VANA", /* Analog (2.8V) supply */
++ "VDIG", /* Digital Core (1.8V) supply */
++ "VDDL", /* IF (1.2V) supply */
++};
++
++#define IMX219_NUM_SUPPLIES ARRAY_SIZE(imx219_supply_name)
++
++/*
++ * Initialisation delay between XCLR low->high and the moment when the sensor
++ * can start capture (i.e. can leave software stanby) must be not less than:
++ * t4 + max(t5, t6 + <time to initialize the sensor register over I2C>)
++ * where
++ * t4 is fixed, and is max 200uS,
++ * t5 is fixed, and is 6000uS,
++ * t6 depends on the sensor external clock, and is max 32000 clock periods.
++ * As per sensor datasheet, the external clock must be from 6MHz to 27MHz.
++ * So for any acceptable external clock t6 is always within the range of
++ * 1185 to 5333 uS, and is always less than t5.
++ * For this reason this is always safe to wait (t4 + t5) = 6200 uS, then
++ * initialize the sensor over I2C, and then exit the software standby.
++ *
++ * This start-up time can be optimized a bit more, if we start the writes
++ * over I2C after (t4+t6), but before (t4+t5) expires. But then sensor
++ * initialization over I2C may complete before (t4+t5) expires, and we must
++ * ensure that capture is not started before (t4+t5).
++ *
++ * This delay doesn't account for the power supply startup time. If needed,
++ * this should be taken care of via the regulator framework. E.g. in the
++ * case of DT for regulator-fixed one should define the startup-delay-us
++ * property.
++ */
++#define IMX219_XCLR_MIN_DELAY_US 6200
++#define IMX219_XCLR_DELAY_RANGE_US 1000
++
++/* Mode configs */
++static const struct imx219_mode supported_modes[] = {
++ {
++ /* 8MPix 15fps mode */
++ .width = 3280,
++ .height = 2464,
++ .vts_def = IMX219_VTS_15FPS,
++ .reg_list = {
++ .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs),
++ .regs = mode_3280x2464_regs,
++ },
++ },
++ {
++ /* 1080P 30fps cropped */
++ .width = 1920,
++ .height = 1080,
++ .vts_def = IMX219_VTS_30FPS_1080P,
++ .reg_list = {
++ .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs),
++ .regs = mode_1920_1080_regs,
++ },
++ },
++ {
++ /* 2x2 binned 30fps mode */
++ .width = 1640,
++ .height = 1232,
++ .vts_def = IMX219_VTS_30FPS_BINNED,
++ .reg_list = {
++ .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs),
++ .regs = mode_1640_1232_regs,
++ },
++ },
++};
++
++struct imx219 {
++ struct v4l2_subdev sd;
++ struct media_pad pad;
++
++ struct clk *xclk; /* system clock to IMX219 */
++ u32 xclk_freq;
++
++ struct gpio_desc *reset_gpio;
++ struct regulator_bulk_data supplies[IMX219_NUM_SUPPLIES];
++
++ struct v4l2_ctrl_handler ctrl_handler;
++ /* V4L2 Controls */
++ struct v4l2_ctrl *pixel_rate;
++ struct v4l2_ctrl *exposure;
++ struct v4l2_ctrl *vflip;
++ struct v4l2_ctrl *hflip;
++ struct v4l2_ctrl *vblank;
++ struct v4l2_ctrl *hblank;
++
++ /* Current mode */
++ const struct imx219_mode *mode;
++
++ /*
++ * Mutex for serialized access:
++ * Protect sensor module set pad format and start/stop streaming safely.
++ */
++ struct mutex mutex;
++
++ /* Streaming on/off */
++ bool streaming;
++};
++
++static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd)
++{
++ return container_of(_sd, struct imx219, sd);
++}
++
++/* Read registers up to 2 at a time */
++static int imx219_read_reg(struct imx219 *imx219, u16 reg, u32 len, u32 *val)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++ struct i2c_msg msgs[2];
++ u8 addr_buf[2] = { reg >> 8, reg & 0xff };
++ u8 data_buf[4] = { 0, };
++ int ret;
++
++ if (len > 4)
++ return -EINVAL;
++
++ /* Write register address */
++ msgs[0].addr = client->addr;
++ msgs[0].flags = 0;
++ msgs[0].len = ARRAY_SIZE(addr_buf);
++ msgs[0].buf = addr_buf;
++
++ /* Read data from register */
++ msgs[1].addr = client->addr;
++ msgs[1].flags = I2C_M_RD;
++ msgs[1].len = len;
++ msgs[1].buf = &data_buf[4 - len];
++
++ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
++ if (ret != ARRAY_SIZE(msgs))
++ return -EIO;
++
++ *val = get_unaligned_be32(data_buf);
++
++ return 0;
++}
++
++/* Write registers up to 2 at a time */
++static int imx219_write_reg(struct imx219 *imx219, u16 reg, u32 len, u32 val)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++ u8 buf[6];
++
++ if (len > 4)
++ return -EINVAL;
++
++ put_unaligned_be16(reg, buf);
++ put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
++ if (i2c_master_send(client, buf, len + 2) != len + 2)
++ return -EIO;
++
++ return 0;
++}
++
++/* Write a list of registers */
++static int imx219_write_regs(struct imx219 *imx219,
++ const struct imx219_reg *regs, u32 len)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++ unsigned int i;
++ int ret;
++
++ for (i = 0; i < len; i++) {
++ ret = imx219_write_reg(imx219, regs[i].address, 1, regs[i].val);
++ if (ret) {
++ dev_err_ratelimited(&client->dev,
++ "Failed to write reg 0x%4.4x. error = %d\n",
++ regs[i].address, ret);
++
++ return ret;
++ }
++ }
++
++ return 0;
++}
++
++/* Get bayer order based on flip setting. */
++static u32 imx219_get_format_code(struct imx219 *imx219)
++{
++ /*
++ * Only one bayer order is supported.
++ * It depends on the flip settings.
++ */
++ static const u32 codes[2][2] = {
++ { MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, },
++ { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10, },
++ };
++
++ lockdep_assert_held(&imx219->mutex);
++ return codes[imx219->vflip->val][imx219->hflip->val];
++}
++
++static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
++{
++ struct imx219 *imx219 = to_imx219(sd);
++ struct v4l2_mbus_framefmt *try_fmt =
++ v4l2_subdev_get_try_format(sd, fh->pad, 0);
++
++ mutex_lock(&imx219->mutex);
++
++ /* Initialize try_fmt */
++ try_fmt->width = supported_modes[0].width;
++ try_fmt->height = supported_modes[0].height;
++ try_fmt->code = imx219_get_format_code(imx219);
++ try_fmt->field = V4L2_FIELD_NONE;
++
++ mutex_unlock(&imx219->mutex);
++
++ return 0;
++}
++
++static int imx219_set_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct imx219 *imx219 =
++ container_of(ctrl->handler, struct imx219, ctrl_handler);
++ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++ int ret;
++
++ if (ctrl->id == V4L2_CID_VBLANK) {
++ int exposure_max, exposure_def;
++
++ /* Update max exposure while meeting expected vblanking */
++ exposure_max = imx219->mode->height + ctrl->val - 4;
++ exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
++ exposure_max : IMX219_EXPOSURE_DEFAULT;
++ __v4l2_ctrl_modify_range(imx219->exposure,
++ imx219->exposure->minimum,
++ exposure_max, imx219->exposure->step,
++ exposure_def);
++ }
++
++ /*
++ * Applying V4L2 control value only happens
++ * when power is up for streaming
++ */
++ if (pm_runtime_get_if_in_use(&client->dev) == 0)
++ return 0;
++
++ switch (ctrl->id) {
++ case V4L2_CID_ANALOGUE_GAIN:
++ ret = imx219_write_reg(imx219, IMX219_REG_ANALOG_GAIN,
++ IMX219_REG_VALUE_08BIT, ctrl->val);
++ break;
++ case V4L2_CID_EXPOSURE:
++ ret = imx219_write_reg(imx219, IMX219_REG_EXPOSURE,
++ IMX219_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_DIGITAL_GAIN:
++ ret = imx219_write_reg(imx219, IMX219_REG_DIGITAL_GAIN,
++ IMX219_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN:
++ ret = imx219_write_reg(imx219, IMX219_REG_TEST_PATTERN,
++ IMX219_REG_VALUE_16BIT,
++ imx219_test_pattern_val[ctrl->val]);
++ break;
++ case V4L2_CID_HFLIP:
++ case V4L2_CID_VFLIP:
++ ret = imx219_write_reg(imx219, IMX219_REG_ORIENTATION, 1,
++ imx219->hflip->val |
++ imx219->vflip->val << 1);
++ break;
++ case V4L2_CID_VBLANK:
++ ret = imx219_write_reg(imx219, IMX219_REG_VTS,
++ IMX219_REG_VALUE_16BIT,
++ imx219->mode->height + ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN_RED:
++ ret = imx219_write_reg(imx219, IMX219_REG_TESTP_RED,
++ IMX219_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN_GREENR:
++ ret = imx219_write_reg(imx219, IMX219_REG_TESTP_GREENR,
++ IMX219_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN_BLUE:
++ ret = imx219_write_reg(imx219, IMX219_REG_TESTP_BLUE,
++ IMX219_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN_GREENB:
++ ret = imx219_write_reg(imx219, IMX219_REG_TESTP_GREENB,
++ IMX219_REG_VALUE_16BIT, ctrl->val);
++ break;
++ default:
++ dev_info(&client->dev,
++ "ctrl(id:0x%x,val:0x%x) is not handled\n",
++ ctrl->id, ctrl->val);
++ ret = -EINVAL;
++ break;
++ }
++
++ pm_runtime_put(&client->dev);
++
++ return ret;
++}
++
++static const struct v4l2_ctrl_ops imx219_ctrl_ops = {
++ .s_ctrl = imx219_set_ctrl,
++};
++
++static int imx219_enum_mbus_code(struct v4l2_subdev *sd,
++ struct v4l2_subdev_pad_config *cfg,
++ struct v4l2_subdev_mbus_code_enum *code)
++{
++ struct imx219 *imx219 = to_imx219(sd);
++
++ /*
++ * Only one bayer order is supported (though it depends on the flip
++ * settings)
++ */
++ if (code->index > 0)
++ return -EINVAL;
++
++ code->code = imx219_get_format_code(imx219);
++
++ return 0;
++}
++
++static int imx219_enum_frame_size(struct v4l2_subdev *sd,
++ struct v4l2_subdev_pad_config *cfg,
++ struct v4l2_subdev_frame_size_enum *fse)
++{
++ struct imx219 *imx219 = to_imx219(sd);
++
++ if (fse->index >= ARRAY_SIZE(supported_modes))
++ return -EINVAL;
++
++ if (fse->code != imx219_get_format_code(imx219))
++ return -EINVAL;
++
++ fse->min_width = supported_modes[fse->index].width;
++ fse->max_width = fse->min_width;
++ fse->min_height = supported_modes[fse->index].height;
++ fse->max_height = fse->min_height;
++
++ return 0;
++}
++
++static void imx219_reset_colorspace(struct v4l2_mbus_framefmt *fmt)
++{
++ fmt->colorspace = V4L2_COLORSPACE_SRGB;
++ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++ fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
++ fmt->colorspace,
++ fmt->ycbcr_enc);
++ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++}
++
++static void imx219_update_pad_format(struct imx219 *imx219,
++ const struct imx219_mode *mode,
++ struct v4l2_subdev_format *fmt)
++{
++ fmt->format.width = mode->width;
++ fmt->format.height = mode->height;
++ fmt->format.code = imx219_get_format_code(imx219);
++ fmt->format.field = V4L2_FIELD_NONE;
++
++ imx219_reset_colorspace(&fmt->format);
++}
++
++static int __imx219_get_pad_format(struct imx219 *imx219,
++ struct v4l2_subdev_pad_config *cfg,
++ struct v4l2_subdev_format *fmt)
++{
++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++ struct v4l2_mbus_framefmt *try_fmt =
++ v4l2_subdev_get_try_format(&imx219->sd, cfg, fmt->pad);
++ /* update the code which could change due to vflip or hflip: */
++ try_fmt->code = imx219_get_format_code(imx219);
++ fmt->format = *try_fmt;
++ } else {
++ imx219_update_pad_format(imx219, imx219->mode, fmt);
++ }
++
++ return 0;
++}
++
++static int imx219_get_pad_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_pad_config *cfg,
++ struct v4l2_subdev_format *fmt)
++{
++ struct imx219 *imx219 = to_imx219(sd);
++ int ret;
++
++ mutex_lock(&imx219->mutex);
++ ret = __imx219_get_pad_format(imx219, cfg, fmt);
++ mutex_unlock(&imx219->mutex);
++
++ return ret;
++}
++
++static int imx219_set_pad_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_pad_config *cfg,
++ struct v4l2_subdev_format *fmt)
++{
++ struct imx219 *imx219 = to_imx219(sd);
++ const struct imx219_mode *mode;
++ struct v4l2_mbus_framefmt *framefmt;
++ int exposure_max, exposure_def, hblank;
++
++ mutex_lock(&imx219->mutex);
++
++ /* Bayer order varies with flips */
++ fmt->format.code = imx219_get_format_code(imx219);
++
++ mode = v4l2_find_nearest_size(supported_modes,
++ ARRAY_SIZE(supported_modes),
++ width, height,
++ fmt->format.width, fmt->format.height);
++ imx219_update_pad_format(imx219, mode, fmt);
++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++ framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
++ *framefmt = fmt->format;
++ } else if (imx219->mode != mode) {
++ imx219->mode = mode;
++ /* Update limits and set FPS to default */
++ __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN,
++ IMX219_VTS_MAX - mode->height, 1,
++ mode->vts_def - mode->height);
++ __v4l2_ctrl_s_ctrl(imx219->vblank,
++ mode->vts_def - mode->height);
++ /* Update max exposure while meeting expected vblanking */
++ exposure_max = mode->vts_def - 4;
++ exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
++ exposure_max : IMX219_EXPOSURE_DEFAULT;
++ __v4l2_ctrl_modify_range(imx219->exposure,
++ imx219->exposure->minimum,
++ exposure_max, imx219->exposure->step,
++ exposure_def);
++ /*
++ * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank
++ * depends on mode->width only, and is not changeble in any
++ * way other than changing the mode.
++ */
++ hblank = IMX219_PPL_DEFAULT - mode->width;
++ __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1,
++ hblank);
++ }
++
++ mutex_unlock(&imx219->mutex);
++
++ return 0;
++}
++
++static int imx219_start_streaming(struct imx219 *imx219)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++ const struct imx219_reg_list *reg_list;
++ int ret;
++
++ /* Apply default values of current mode */
++ reg_list = &imx219->mode->reg_list;
++ ret = imx219_write_regs(imx219, reg_list->regs, reg_list->num_of_regs);
++ if (ret) {
++ dev_err(&client->dev, "%s failed to set mode\n", __func__);
++ return ret;
++ }
++
++ /* Apply customized values from user */
++ ret = __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler);
++ if (ret)
++ return ret;
++
++ /* set stream on register */
++ return imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
++ IMX219_REG_VALUE_08BIT, IMX219_MODE_STREAMING);
++}
++
++static void imx219_stop_streaming(struct imx219 *imx219)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++ int ret;
++
++ /* set stream off register */
++ ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
++ IMX219_REG_VALUE_08BIT, IMX219_MODE_STANDBY);
++ if (ret)
++ dev_err(&client->dev, "%s failed to set stream\n", __func__);
++}
++
++static int imx219_set_stream(struct v4l2_subdev *sd, int enable)
++{
++ struct imx219 *imx219 = to_imx219(sd);
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ int ret = 0;
++
++ mutex_lock(&imx219->mutex);
++ if (imx219->streaming == enable) {
++ mutex_unlock(&imx219->mutex);
++ return 0;
++ }
++
++ if (enable) {
++ ret = pm_runtime_get_sync(&client->dev);
++ if (ret < 0) {
++ pm_runtime_put_noidle(&client->dev);
++ goto err_unlock;
++ }
++
++ /*
++ * Apply default & customized values
++ * and then start streaming.
++ */
++ ret = imx219_start_streaming(imx219);
++ if (ret)
++ goto err_rpm_put;
++ } else {
++ imx219_stop_streaming(imx219);
++ pm_runtime_put(&client->dev);
++ }
++
++ imx219->streaming = enable;
++
++ /* vflip and hflip cannot change during streaming */
++ __v4l2_ctrl_grab(imx219->vflip, enable);
++ __v4l2_ctrl_grab(imx219->hflip, enable);
++
++ mutex_unlock(&imx219->mutex);
++
++ return ret;
++
++err_rpm_put:
++ pm_runtime_put(&client->dev);
++err_unlock:
++ mutex_unlock(&imx219->mutex);
++
++ return ret;
++}
++
++/* Power/clock management functions */
++static int imx219_power_on(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx219 *imx219 = to_imx219(sd);
++ int ret;
++
++ ret = regulator_bulk_enable(IMX219_NUM_SUPPLIES,
++ imx219->supplies);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to enable regulators\n",
++ __func__);
++ return ret;
++ }
++
++ ret = clk_prepare_enable(imx219->xclk);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to enable clock\n",
++ __func__);
++ goto reg_off;
++ }
++
++ gpiod_set_value_cansleep(imx219->reset_gpio, 1);
++ usleep_range(IMX219_XCLR_MIN_DELAY_US,
++ IMX219_XCLR_MIN_DELAY_US + IMX219_XCLR_DELAY_RANGE_US);
++
++ return 0;
++
++reg_off:
++ regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies);
++
++ return ret;
++}
++
++static int imx219_power_off(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx219 *imx219 = to_imx219(sd);
++
++ gpiod_set_value_cansleep(imx219->reset_gpio, 0);
++ regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies);
++ clk_disable_unprepare(imx219->xclk);
++
++ return 0;
++}
++
++static int __maybe_unused imx219_suspend(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx219 *imx219 = to_imx219(sd);
++
++ if (imx219->streaming)
++ imx219_stop_streaming(imx219);
++
++ return 0;
++}
++
++static int __maybe_unused imx219_resume(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx219 *imx219 = to_imx219(sd);
++ int ret;
++
++ if (imx219->streaming) {
++ ret = imx219_start_streaming(imx219);
++ if (ret)
++ goto error;
++ }
++
++ return 0;
++
++error:
++ imx219_stop_streaming(imx219);
++ imx219->streaming = 0;
++
++ return ret;
++}
++
++static int imx219_get_regulators(struct imx219 *imx219)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++ unsigned int i;
++
++ for (i = 0; i < IMX219_NUM_SUPPLIES; i++)
++ imx219->supplies[i].supply = imx219_supply_name[i];
++
++ return devm_regulator_bulk_get(&client->dev,
++ IMX219_NUM_SUPPLIES,
++ imx219->supplies);
++}
++
++/* Verify chip ID */
++static int imx219_identify_module(struct imx219 *imx219)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++ int ret;
++ u32 val;
++
++ ret = imx219_read_reg(imx219, IMX219_REG_CHIP_ID,
++ IMX219_REG_VALUE_16BIT, &val);
++ if (ret) {
++ dev_err(&client->dev, "failed to read chip id %x\n",
++ IMX219_CHIP_ID);
++ return ret;
++ }
++
++ if (val != IMX219_CHIP_ID) {
++ dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
++ IMX219_CHIP_ID, val);
++ return -EIO;
++ }
++
++ return 0;
++}
++
++static const struct v4l2_subdev_core_ops imx219_core_ops = {
++ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops imx219_video_ops = {
++ .s_stream = imx219_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops imx219_pad_ops = {
++ .enum_mbus_code = imx219_enum_mbus_code,
++ .get_fmt = imx219_get_pad_format,
++ .set_fmt = imx219_set_pad_format,
++ .enum_frame_size = imx219_enum_frame_size,
++};
++
++static const struct v4l2_subdev_ops imx219_subdev_ops = {
++ .core = &imx219_core_ops,
++ .video = &imx219_video_ops,
++ .pad = &imx219_pad_ops,
++};
++
++static const struct v4l2_subdev_internal_ops imx219_internal_ops = {
++ .open = imx219_open,
++};
++
++/* Initialize control handlers */
++static int imx219_init_controls(struct imx219 *imx219)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++ struct v4l2_ctrl_handler *ctrl_hdlr;
++ unsigned int height = imx219->mode->height;
++ int exposure_max, exposure_def, hblank;
++ int i, ret;
++
++ ctrl_hdlr = &imx219->ctrl_handler;
++ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9);
++ if (ret)
++ return ret;
++
++ mutex_init(&imx219->mutex);
++ ctrl_hdlr->lock = &imx219->mutex;
++
++ /* By default, PIXEL_RATE is read only */
++ imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++ V4L2_CID_PIXEL_RATE,
++ IMX219_PIXEL_RATE,
++ IMX219_PIXEL_RATE, 1,
++ IMX219_PIXEL_RATE);
++
++ /* Initial vblank/hblank/exposure parameters based on current mode */
++ imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++ V4L2_CID_VBLANK, IMX219_VBLANK_MIN,
++ IMX219_VTS_MAX - height, 1,
++ imx219->mode->vts_def - height);
++ hblank = IMX219_PPL_DEFAULT - imx219->mode->width;
++ imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++ V4L2_CID_HBLANK, hblank, hblank,
++ 1, hblank);
++ if (imx219->hblank)
++ imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++ exposure_max = imx219->mode->vts_def - 4;
++ exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
++ exposure_max : IMX219_EXPOSURE_DEFAULT;
++ imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++ V4L2_CID_EXPOSURE,
++ IMX219_EXPOSURE_MIN, exposure_max,
++ IMX219_EXPOSURE_STEP,
++ exposure_def);
++
++ v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
++ IMX219_ANA_GAIN_MIN, IMX219_ANA_GAIN_MAX,
++ IMX219_ANA_GAIN_STEP, IMX219_ANA_GAIN_DEFAULT);
++
++ v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
++ IMX219_DGTL_GAIN_MIN, IMX219_DGTL_GAIN_MAX,
++ IMX219_DGTL_GAIN_STEP, IMX219_DGTL_GAIN_DEFAULT);
++
++ imx219->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++ V4L2_CID_HFLIP, 0, 1, 1, 0);
++ if (imx219->hflip)
++ imx219->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++
++ imx219->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++ V4L2_CID_VFLIP, 0, 1, 1, 0);
++ if (imx219->vflip)
++ imx219->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++
++ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx219_ctrl_ops,
++ V4L2_CID_TEST_PATTERN,
++ ARRAY_SIZE(imx219_test_pattern_menu) - 1,
++ 0, 0, imx219_test_pattern_menu);
++ for (i = 0; i < 4; i++) {
++ /*
++ * The assumption is that
++ * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1
++ * V4L2_CID_TEST_PATTERN_BLUE == V4L2_CID_TEST_PATTERN_RED + 2
++ * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3
++ */
++ v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++ V4L2_CID_TEST_PATTERN_RED + i,
++ IMX219_TESTP_COLOUR_MIN,
++ IMX219_TESTP_COLOUR_MAX,
++ IMX219_TESTP_COLOUR_STEP,
++ IMX219_TESTP_COLOUR_MAX);
++ /* The "Solid color" pattern is white by default */
++ }
++
++ if (ctrl_hdlr->error) {
++ ret = ctrl_hdlr->error;
++ dev_err(&client->dev, "%s control init failed (%d)\n",
++ __func__, ret);
++ goto error;
++ }
++
++ imx219->sd.ctrl_handler = ctrl_hdlr;
++
++ return 0;
++
++error:
++ v4l2_ctrl_handler_free(ctrl_hdlr);
++ mutex_destroy(&imx219->mutex);
++
++ return ret;
++}
++
++static void imx219_free_controls(struct imx219 *imx219)
++{
++ v4l2_ctrl_handler_free(imx219->sd.ctrl_handler);
++ mutex_destroy(&imx219->mutex);
++}
++
++static int imx219_check_hwcfg(struct device *dev)
++{
++ struct fwnode_handle *endpoint;
++ struct v4l2_fwnode_endpoint ep_cfg = {
++ .bus_type = V4L2_MBUS_CSI2_DPHY
++ };
++ int ret = -EINVAL;
++
++ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
++ if (!endpoint) {
++ dev_err(dev, "endpoint node not found\n");
++ return -EINVAL;
++ }
++
++ if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
++ dev_err(dev, "could not parse endpoint\n");
++ goto error_out;
++ }
++
++ /* Check the number of MIPI CSI2 data lanes */
++ if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
++ dev_err(dev, "only 2 data lanes are currently supported\n");
++ goto error_out;
++ }
++
++ /* Check the link frequency set in device tree */
++ if (!ep_cfg.nr_of_link_frequencies) {
++ dev_err(dev, "link-frequency property not found in DT\n");
++ goto error_out;
++ }
++
++ if (ep_cfg.nr_of_link_frequencies != 1 ||
++ ep_cfg.link_frequencies[0] != IMX219_DEFAULT_LINK_FREQ) {
++ dev_err(dev, "Link frequency not supported: %lld\n",
++ ep_cfg.link_frequencies[0]);
++ goto error_out;
++ }
++
++ ret = 0;
++
++error_out:
++ v4l2_fwnode_endpoint_free(&ep_cfg);
++ fwnode_handle_put(endpoint);
++
++ return ret;
++}
++
++static int imx219_probe(struct i2c_client *client)
++{
++ struct device *dev = &client->dev;
++ struct imx219 *imx219;
++ int ret;
++
++ imx219 = devm_kzalloc(&client->dev, sizeof(*imx219), GFP_KERNEL);
++ if (!imx219)
++ return -ENOMEM;
++
++ v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops);
++
++ /* Check the hardware configuration in device tree */
++ if (imx219_check_hwcfg(dev))
++ return -EINVAL;
++
++ /* Get system clock (xclk) */
++ imx219->xclk = devm_clk_get(dev, NULL);
++ if (IS_ERR(imx219->xclk)) {
++ dev_err(dev, "failed to get xclk\n");
++ return PTR_ERR(imx219->xclk);
++ }
++
++ imx219->xclk_freq = clk_get_rate(imx219->xclk);
++ if (imx219->xclk_freq != IMX219_XCLK_FREQ) {
++ dev_err(dev, "xclk frequency not supported: %d Hz\n",
++ imx219->xclk_freq);
++ return -EINVAL;
++ }
++
++ ret = imx219_get_regulators(imx219);
++ if (ret) {
++ dev_err(dev, "failed to get regulators\n");
++ return ret;
++ }
++
++ /* Request optional enable pin */
++ imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset",
++ GPIOD_OUT_HIGH);
++
++ /*
++ * The sensor must be powered for imx219_identify_module()
++ * to be able to read the CHIP_ID register
++ */
++ ret = imx219_power_on(dev);
++ if (ret)
++ return ret;
++
++ ret = imx219_identify_module(imx219);
++ if (ret)
++ goto error_power_off;
++
++ /* Set default mode to max resolution */
++ imx219->mode = &supported_modes[0];
++
++ ret = imx219_init_controls(imx219);
++ if (ret)
++ goto error_power_off;
++
++ /* Initialize subdev */
++ imx219->sd.internal_ops = &imx219_internal_ops;
++ imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
++ imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++
++ /* Initialize source pad */
++ imx219->pad.flags = MEDIA_PAD_FL_SOURCE;
++
++ ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad);
++ if (ret) {
++ dev_err(dev, "failed to init entity pads: %d\n", ret);
++ goto error_handler_free;
++ }
++
++ ret = v4l2_async_register_subdev_sensor_common(&imx219->sd);
++ if (ret < 0) {
++ dev_err(dev, "failed to register sensor sub-device: %d\n", ret);
++ goto error_media_entity;
++ }
++
++ /* Enable runtime PM and turn off the device */
++ pm_runtime_set_active(dev);
++ pm_runtime_enable(dev);
++ pm_runtime_idle(dev);
++
++ return 0;
++
++error_media_entity:
++ media_entity_cleanup(&imx219->sd.entity);
++
++error_handler_free:
++ imx219_free_controls(imx219);
++
++error_power_off:
++ imx219_power_off(dev);
++
++ return ret;
++}
++
++static int imx219_remove(struct i2c_client *client)
++{
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx219 *imx219 = to_imx219(sd);
++
++ v4l2_async_unregister_subdev(sd);
++ media_entity_cleanup(&sd->entity);
++ imx219_free_controls(imx219);
++
++ pm_runtime_disable(&client->dev);
++ if (!pm_runtime_status_suspended(&client->dev))
++ imx219_power_off(&client->dev);
++ pm_runtime_set_suspended(&client->dev);
++
++ return 0;
++}
++
++static const struct of_device_id imx219_dt_ids[] = {
++ { .compatible = "sony,imx219" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, imx219_dt_ids);
++
++static const struct dev_pm_ops imx219_pm_ops = {
++ SET_SYSTEM_SLEEP_PM_OPS(imx219_suspend, imx219_resume)
++ SET_RUNTIME_PM_OPS(imx219_power_off, imx219_power_on, NULL)
++};
++
++static struct i2c_driver imx219_i2c_driver = {
++ .driver = {
++ .name = "imx219",
++ .of_match_table = imx219_dt_ids,
++ .pm = &imx219_pm_ops,
++ },
++ .probe_new = imx219_probe,
++ .remove = imx219_remove,
++};
++
++module_i2c_driver(imx219_i2c_driver);
++
++MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com");
++MODULE_DESCRIPTION("Sony IMX219 sensor driver");
++MODULE_LICENSE("GPL v2");
+++ /dev/null
-From 5cd8c4efeb46ce1ef370dd3012a7951ba430b58f Mon Sep 17 00:00:00 2001
-From: Dave Stevenson <dave.stevenson@raspberrypi.com>
-Date: Mon, 20 Jan 2020 05:15:58 -0300
-Subject: [PATCH] media: i2c: Add driver for Sony IMX219 sensor
-
-Commit 1283b3b8f82b9004fbb94398cade5c8e797a2c8d upstream.
-(Currently on linux-media/master, queued for 5.7)
-
-Adds a driver for the 8MPix Sony IMX219 CSI2 sensor.
-Whilst the sensor supports 2 or 4 CSI2 data lanes, this driver
-currently only supports 2 lanes.
-8MPix @ 15fps, 1080P @ 30fps (cropped FOV), and 1640x1232 (2x2 binned)
-@ 30fps are currently supported.
-
-[Sakari Ailus: make imx219_check_hwcfg static]
-
-Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
-Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
-Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
-Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
----
- drivers/media/i2c/Kconfig | 11 +
- drivers/media/i2c/Makefile | 1 +
- drivers/media/i2c/imx219.c | 1312 ++++++++++++++++++++++++++++++++++++
- 3 files changed, 1324 insertions(+)
- create mode 100644 drivers/media/i2c/imx219.c
-
---- a/drivers/media/i2c/Kconfig
-+++ b/drivers/media/i2c/Kconfig
-@@ -578,6 +578,17 @@ config VIDEO_IMX214
- To compile this driver as a module, choose M here: the
- module will be called imx214.
-
-+config VIDEO_IMX219
-+ tristate "Sony IMX219 sensor support"
-+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
-+ select V4L2_FWNODE
-+ help
-+ This is a Video4Linux2 sensor driver for the Sony
-+ IMX219 camera.
-+
-+ To compile this driver as a module, choose M here: the
-+ module will be called imx219.
-+
- config VIDEO_IMX258
- tristate "Sony IMX258 sensor support"
- depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
---- a/drivers/media/i2c/Makefile
-+++ b/drivers/media/i2c/Makefile
-@@ -111,6 +111,7 @@ obj-$(CONFIG_VIDEO_ML86V7667) += ml86v76
- obj-$(CONFIG_VIDEO_OV2659) += ov2659.o
- obj-$(CONFIG_VIDEO_TC358743) += tc358743.o
- obj-$(CONFIG_VIDEO_IMX214) += imx214.o
-+obj-$(CONFIG_VIDEO_IMX219) += imx219.o
- obj-$(CONFIG_VIDEO_IMX258) += imx258.o
- obj-$(CONFIG_VIDEO_IMX274) += imx274.o
- obj-$(CONFIG_VIDEO_IMX319) += imx319.o
---- /dev/null
-+++ b/drivers/media/i2c/imx219.c
-@@ -0,0 +1,1312 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/*
-+ * A V4L2 driver for Sony IMX219 cameras.
-+ * Copyright (C) 2019, Raspberry Pi (Trading) Ltd
-+ *
-+ * Based on Sony imx258 camera driver
-+ * Copyright (C) 2018 Intel Corporation
-+ *
-+ * DT / fwnode changes, and regulator / GPIO control taken from imx214 driver
-+ * Copyright 2018 Qtechnology A/S
-+ *
-+ * Flip handling taken from the Sony IMX319 driver.
-+ * Copyright (C) 2018 Intel Corporation
-+ *
-+ */
-+
-+#include <linux/clk.h>
-+#include <linux/clk-provider.h>
-+#include <linux/clkdev.h>
-+#include <linux/delay.h>
-+#include <linux/gpio/consumer.h>
-+#include <linux/i2c.h>
-+#include <linux/module.h>
-+#include <linux/pm_runtime.h>
-+#include <linux/regulator/consumer.h>
-+#include <media/v4l2-ctrls.h>
-+#include <media/v4l2-device.h>
-+#include <media/v4l2-event.h>
-+#include <media/v4l2-fwnode.h>
-+#include <media/v4l2-mediabus.h>
-+#include <asm/unaligned.h>
-+
-+#define IMX219_REG_VALUE_08BIT 1
-+#define IMX219_REG_VALUE_16BIT 2
-+
-+#define IMX219_REG_MODE_SELECT 0x0100
-+#define IMX219_MODE_STANDBY 0x00
-+#define IMX219_MODE_STREAMING 0x01
-+
-+/* Chip ID */
-+#define IMX219_REG_CHIP_ID 0x0000
-+#define IMX219_CHIP_ID 0x0219
-+
-+/* External clock frequency is 24.0M */
-+#define IMX219_XCLK_FREQ 24000000
-+
-+/* Pixel rate is fixed at 182.4M for all the modes */
-+#define IMX219_PIXEL_RATE 182400000
-+
-+#define IMX219_DEFAULT_LINK_FREQ 456000000
-+
-+/* V_TIMING internal */
-+#define IMX219_REG_VTS 0x0160
-+#define IMX219_VTS_15FPS 0x0dc6
-+#define IMX219_VTS_30FPS_1080P 0x06e3
-+#define IMX219_VTS_30FPS_BINNED 0x06e3
-+#define IMX219_VTS_MAX 0xffff
-+
-+#define IMX219_VBLANK_MIN 4
-+
-+/*Frame Length Line*/
-+#define IMX219_FLL_MIN 0x08a6
-+#define IMX219_FLL_MAX 0xffff
-+#define IMX219_FLL_STEP 1
-+#define IMX219_FLL_DEFAULT 0x0c98
-+
-+/* HBLANK control - read only */
-+#define IMX219_PPL_DEFAULT 3448
-+
-+/* Exposure control */
-+#define IMX219_REG_EXPOSURE 0x015a
-+#define IMX219_EXPOSURE_MIN 4
-+#define IMX219_EXPOSURE_STEP 1
-+#define IMX219_EXPOSURE_DEFAULT 0x640
-+#define IMX219_EXPOSURE_MAX 65535
-+
-+/* Analog gain control */
-+#define IMX219_REG_ANALOG_GAIN 0x0157
-+#define IMX219_ANA_GAIN_MIN 0
-+#define IMX219_ANA_GAIN_MAX 232
-+#define IMX219_ANA_GAIN_STEP 1
-+#define IMX219_ANA_GAIN_DEFAULT 0x0
-+
-+/* Digital gain control */
-+#define IMX219_REG_DIGITAL_GAIN 0x0158
-+#define IMX219_DGTL_GAIN_MIN 0x0100
-+#define IMX219_DGTL_GAIN_MAX 0x0fff
-+#define IMX219_DGTL_GAIN_DEFAULT 0x0100
-+#define IMX219_DGTL_GAIN_STEP 1
-+
-+#define IMX219_REG_ORIENTATION 0x0172
-+
-+/* Test Pattern Control */
-+#define IMX219_REG_TEST_PATTERN 0x0600
-+#define IMX219_TEST_PATTERN_DISABLE 0
-+#define IMX219_TEST_PATTERN_SOLID_COLOR 1
-+#define IMX219_TEST_PATTERN_COLOR_BARS 2
-+#define IMX219_TEST_PATTERN_GREY_COLOR 3
-+#define IMX219_TEST_PATTERN_PN9 4
-+
-+/* Test pattern colour components */
-+#define IMX219_REG_TESTP_RED 0x0602
-+#define IMX219_REG_TESTP_GREENR 0x0604
-+#define IMX219_REG_TESTP_BLUE 0x0606
-+#define IMX219_REG_TESTP_GREENB 0x0608
-+#define IMX219_TESTP_COLOUR_MIN 0
-+#define IMX219_TESTP_COLOUR_MAX 0x03ff
-+#define IMX219_TESTP_COLOUR_STEP 1
-+#define IMX219_TESTP_RED_DEFAULT IMX219_TESTP_COLOUR_MAX
-+#define IMX219_TESTP_GREENR_DEFAULT 0
-+#define IMX219_TESTP_BLUE_DEFAULT 0
-+#define IMX219_TESTP_GREENB_DEFAULT 0
-+
-+struct imx219_reg {
-+ u16 address;
-+ u8 val;
-+};
-+
-+struct imx219_reg_list {
-+ unsigned int num_of_regs;
-+ const struct imx219_reg *regs;
-+};
-+
-+/* Mode : resolution and related config&values */
-+struct imx219_mode {
-+ /* Frame width */
-+ unsigned int width;
-+ /* Frame height */
-+ unsigned int height;
-+
-+ /* V-timing */
-+ unsigned int vts_def;
-+
-+ /* Default register values */
-+ struct imx219_reg_list reg_list;
-+};
-+
-+/*
-+ * Register sets lifted off the i2C interface from the Raspberry Pi firmware
-+ * driver.
-+ * 3280x2464 = mode 2, 1920x1080 = mode 1, and 1640x1232 = mode 4.
-+ */
-+static const struct imx219_reg mode_3280x2464_regs[] = {
-+ {0x0100, 0x00},
-+ {0x30eb, 0x0c},
-+ {0x30eb, 0x05},
-+ {0x300a, 0xff},
-+ {0x300b, 0xff},
-+ {0x30eb, 0x05},
-+ {0x30eb, 0x09},
-+ {0x0114, 0x01},
-+ {0x0128, 0x00},
-+ {0x012a, 0x18},
-+ {0x012b, 0x00},
-+ {0x0164, 0x00},
-+ {0x0165, 0x00},
-+ {0x0166, 0x0c},
-+ {0x0167, 0xcf},
-+ {0x0168, 0x00},
-+ {0x0169, 0x00},
-+ {0x016a, 0x09},
-+ {0x016b, 0x9f},
-+ {0x016c, 0x0c},
-+ {0x016d, 0xd0},
-+ {0x016e, 0x09},
-+ {0x016f, 0xa0},
-+ {0x0170, 0x01},
-+ {0x0171, 0x01},
-+ {0x0174, 0x00},
-+ {0x0175, 0x00},
-+ {0x018c, 0x0a},
-+ {0x018d, 0x0a},
-+ {0x0301, 0x05},
-+ {0x0303, 0x01},
-+ {0x0304, 0x03},
-+ {0x0305, 0x03},
-+ {0x0306, 0x00},
-+ {0x0307, 0x39},
-+ {0x0309, 0x0a},
-+ {0x030b, 0x01},
-+ {0x030c, 0x00},
-+ {0x030d, 0x72},
-+ {0x0624, 0x0c},
-+ {0x0625, 0xd0},
-+ {0x0626, 0x09},
-+ {0x0627, 0xa0},
-+ {0x455e, 0x00},
-+ {0x471e, 0x4b},
-+ {0x4767, 0x0f},
-+ {0x4750, 0x14},
-+ {0x4540, 0x00},
-+ {0x47b4, 0x14},
-+ {0x4713, 0x30},
-+ {0x478b, 0x10},
-+ {0x478f, 0x10},
-+ {0x4793, 0x10},
-+ {0x4797, 0x0e},
-+ {0x479b, 0x0e},
-+ {0x0162, 0x0d},
-+ {0x0163, 0x78},
-+};
-+
-+static const struct imx219_reg mode_1920_1080_regs[] = {
-+ {0x0100, 0x00},
-+ {0x30eb, 0x05},
-+ {0x30eb, 0x0c},
-+ {0x300a, 0xff},
-+ {0x300b, 0xff},
-+ {0x30eb, 0x05},
-+ {0x30eb, 0x09},
-+ {0x0114, 0x01},
-+ {0x0128, 0x00},
-+ {0x012a, 0x18},
-+ {0x012b, 0x00},
-+ {0x0162, 0x0d},
-+ {0x0163, 0x78},
-+ {0x0164, 0x02},
-+ {0x0165, 0xa8},
-+ {0x0166, 0x0a},
-+ {0x0167, 0x27},
-+ {0x0168, 0x02},
-+ {0x0169, 0xb4},
-+ {0x016a, 0x06},
-+ {0x016b, 0xeb},
-+ {0x016c, 0x07},
-+ {0x016d, 0x80},
-+ {0x016e, 0x04},
-+ {0x016f, 0x38},
-+ {0x0170, 0x01},
-+ {0x0171, 0x01},
-+ {0x0174, 0x00},
-+ {0x0175, 0x00},
-+ {0x018c, 0x0a},
-+ {0x018d, 0x0a},
-+ {0x0301, 0x05},
-+ {0x0303, 0x01},
-+ {0x0304, 0x03},
-+ {0x0305, 0x03},
-+ {0x0306, 0x00},
-+ {0x0307, 0x39},
-+ {0x0309, 0x0a},
-+ {0x030b, 0x01},
-+ {0x030c, 0x00},
-+ {0x030d, 0x72},
-+ {0x0624, 0x07},
-+ {0x0625, 0x80},
-+ {0x0626, 0x04},
-+ {0x0627, 0x38},
-+ {0x455e, 0x00},
-+ {0x471e, 0x4b},
-+ {0x4767, 0x0f},
-+ {0x4750, 0x14},
-+ {0x4540, 0x00},
-+ {0x47b4, 0x14},
-+ {0x4713, 0x30},
-+ {0x478b, 0x10},
-+ {0x478f, 0x10},
-+ {0x4793, 0x10},
-+ {0x4797, 0x0e},
-+ {0x479b, 0x0e},
-+ {0x0162, 0x0d},
-+ {0x0163, 0x78},
-+};
-+
-+static const struct imx219_reg mode_1640_1232_regs[] = {
-+ {0x0100, 0x00},
-+ {0x30eb, 0x0c},
-+ {0x30eb, 0x05},
-+ {0x300a, 0xff},
-+ {0x300b, 0xff},
-+ {0x30eb, 0x05},
-+ {0x30eb, 0x09},
-+ {0x0114, 0x01},
-+ {0x0128, 0x00},
-+ {0x012a, 0x18},
-+ {0x012b, 0x00},
-+ {0x0164, 0x00},
-+ {0x0165, 0x00},
-+ {0x0166, 0x0c},
-+ {0x0167, 0xcf},
-+ {0x0168, 0x00},
-+ {0x0169, 0x00},
-+ {0x016a, 0x09},
-+ {0x016b, 0x9f},
-+ {0x016c, 0x06},
-+ {0x016d, 0x68},
-+ {0x016e, 0x04},
-+ {0x016f, 0xd0},
-+ {0x0170, 0x01},
-+ {0x0171, 0x01},
-+ {0x0174, 0x01},
-+ {0x0175, 0x01},
-+ {0x018c, 0x0a},
-+ {0x018d, 0x0a},
-+ {0x0301, 0x05},
-+ {0x0303, 0x01},
-+ {0x0304, 0x03},
-+ {0x0305, 0x03},
-+ {0x0306, 0x00},
-+ {0x0307, 0x39},
-+ {0x0309, 0x0a},
-+ {0x030b, 0x01},
-+ {0x030c, 0x00},
-+ {0x030d, 0x72},
-+ {0x0624, 0x06},
-+ {0x0625, 0x68},
-+ {0x0626, 0x04},
-+ {0x0627, 0xd0},
-+ {0x455e, 0x00},
-+ {0x471e, 0x4b},
-+ {0x4767, 0x0f},
-+ {0x4750, 0x14},
-+ {0x4540, 0x00},
-+ {0x47b4, 0x14},
-+ {0x4713, 0x30},
-+ {0x478b, 0x10},
-+ {0x478f, 0x10},
-+ {0x4793, 0x10},
-+ {0x4797, 0x0e},
-+ {0x479b, 0x0e},
-+ {0x0162, 0x0d},
-+ {0x0163, 0x78},
-+};
-+
-+static const char * const imx219_test_pattern_menu[] = {
-+ "Disabled",
-+ "Color Bars",
-+ "Solid Color",
-+ "Grey Color Bars",
-+ "PN9"
-+};
-+
-+static const int imx219_test_pattern_val[] = {
-+ IMX219_TEST_PATTERN_DISABLE,
-+ IMX219_TEST_PATTERN_COLOR_BARS,
-+ IMX219_TEST_PATTERN_SOLID_COLOR,
-+ IMX219_TEST_PATTERN_GREY_COLOR,
-+ IMX219_TEST_PATTERN_PN9,
-+};
-+
-+/* regulator supplies */
-+static const char * const imx219_supply_name[] = {
-+ /* Supplies can be enabled in any order */
-+ "VANA", /* Analog (2.8V) supply */
-+ "VDIG", /* Digital Core (1.8V) supply */
-+ "VDDL", /* IF (1.2V) supply */
-+};
-+
-+#define IMX219_NUM_SUPPLIES ARRAY_SIZE(imx219_supply_name)
-+
-+/*
-+ * Initialisation delay between XCLR low->high and the moment when the sensor
-+ * can start capture (i.e. can leave software stanby) must be not less than:
-+ * t4 + max(t5, t6 + <time to initialize the sensor register over I2C>)
-+ * where
-+ * t4 is fixed, and is max 200uS,
-+ * t5 is fixed, and is 6000uS,
-+ * t6 depends on the sensor external clock, and is max 32000 clock periods.
-+ * As per sensor datasheet, the external clock must be from 6MHz to 27MHz.
-+ * So for any acceptable external clock t6 is always within the range of
-+ * 1185 to 5333 uS, and is always less than t5.
-+ * For this reason this is always safe to wait (t4 + t5) = 6200 uS, then
-+ * initialize the sensor over I2C, and then exit the software standby.
-+ *
-+ * This start-up time can be optimized a bit more, if we start the writes
-+ * over I2C after (t4+t6), but before (t4+t5) expires. But then sensor
-+ * initialization over I2C may complete before (t4+t5) expires, and we must
-+ * ensure that capture is not started before (t4+t5).
-+ *
-+ * This delay doesn't account for the power supply startup time. If needed,
-+ * this should be taken care of via the regulator framework. E.g. in the
-+ * case of DT for regulator-fixed one should define the startup-delay-us
-+ * property.
-+ */
-+#define IMX219_XCLR_MIN_DELAY_US 6200
-+#define IMX219_XCLR_DELAY_RANGE_US 1000
-+
-+/* Mode configs */
-+static const struct imx219_mode supported_modes[] = {
-+ {
-+ /* 8MPix 15fps mode */
-+ .width = 3280,
-+ .height = 2464,
-+ .vts_def = IMX219_VTS_15FPS,
-+ .reg_list = {
-+ .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs),
-+ .regs = mode_3280x2464_regs,
-+ },
-+ },
-+ {
-+ /* 1080P 30fps cropped */
-+ .width = 1920,
-+ .height = 1080,
-+ .vts_def = IMX219_VTS_30FPS_1080P,
-+ .reg_list = {
-+ .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs),
-+ .regs = mode_1920_1080_regs,
-+ },
-+ },
-+ {
-+ /* 2x2 binned 30fps mode */
-+ .width = 1640,
-+ .height = 1232,
-+ .vts_def = IMX219_VTS_30FPS_BINNED,
-+ .reg_list = {
-+ .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs),
-+ .regs = mode_1640_1232_regs,
-+ },
-+ },
-+};
-+
-+struct imx219 {
-+ struct v4l2_subdev sd;
-+ struct media_pad pad;
-+
-+ struct clk *xclk; /* system clock to IMX219 */
-+ u32 xclk_freq;
-+
-+ struct gpio_desc *reset_gpio;
-+ struct regulator_bulk_data supplies[IMX219_NUM_SUPPLIES];
-+
-+ struct v4l2_ctrl_handler ctrl_handler;
-+ /* V4L2 Controls */
-+ struct v4l2_ctrl *pixel_rate;
-+ struct v4l2_ctrl *exposure;
-+ struct v4l2_ctrl *vflip;
-+ struct v4l2_ctrl *hflip;
-+ struct v4l2_ctrl *vblank;
-+ struct v4l2_ctrl *hblank;
-+
-+ /* Current mode */
-+ const struct imx219_mode *mode;
-+
-+ /*
-+ * Mutex for serialized access:
-+ * Protect sensor module set pad format and start/stop streaming safely.
-+ */
-+ struct mutex mutex;
-+
-+ /* Streaming on/off */
-+ bool streaming;
-+};
-+
-+static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd)
-+{
-+ return container_of(_sd, struct imx219, sd);
-+}
-+
-+/* Read registers up to 2 at a time */
-+static int imx219_read_reg(struct imx219 *imx219, u16 reg, u32 len, u32 *val)
-+{
-+ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
-+ struct i2c_msg msgs[2];
-+ u8 addr_buf[2] = { reg >> 8, reg & 0xff };
-+ u8 data_buf[4] = { 0, };
-+ int ret;
-+
-+ if (len > 4)
-+ return -EINVAL;
-+
-+ /* Write register address */
-+ msgs[0].addr = client->addr;
-+ msgs[0].flags = 0;
-+ msgs[0].len = ARRAY_SIZE(addr_buf);
-+ msgs[0].buf = addr_buf;
-+
-+ /* Read data from register */
-+ msgs[1].addr = client->addr;
-+ msgs[1].flags = I2C_M_RD;
-+ msgs[1].len = len;
-+ msgs[1].buf = &data_buf[4 - len];
-+
-+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
-+ if (ret != ARRAY_SIZE(msgs))
-+ return -EIO;
-+
-+ *val = get_unaligned_be32(data_buf);
-+
-+ return 0;
-+}
-+
-+/* Write registers up to 2 at a time */
-+static int imx219_write_reg(struct imx219 *imx219, u16 reg, u32 len, u32 val)
-+{
-+ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
-+ u8 buf[6];
-+
-+ if (len > 4)
-+ return -EINVAL;
-+
-+ put_unaligned_be16(reg, buf);
-+ put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
-+ if (i2c_master_send(client, buf, len + 2) != len + 2)
-+ return -EIO;
-+
-+ return 0;
-+}
-+
-+/* Write a list of registers */
-+static int imx219_write_regs(struct imx219 *imx219,
-+ const struct imx219_reg *regs, u32 len)
-+{
-+ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
-+ unsigned int i;
-+ int ret;
-+
-+ for (i = 0; i < len; i++) {
-+ ret = imx219_write_reg(imx219, regs[i].address, 1, regs[i].val);
-+ if (ret) {
-+ dev_err_ratelimited(&client->dev,
-+ "Failed to write reg 0x%4.4x. error = %d\n",
-+ regs[i].address, ret);
-+
-+ return ret;
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+/* Get bayer order based on flip setting. */
-+static u32 imx219_get_format_code(struct imx219 *imx219)
-+{
-+ /*
-+ * Only one bayer order is supported.
-+ * It depends on the flip settings.
-+ */
-+ static const u32 codes[2][2] = {
-+ { MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, },
-+ { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10, },
-+ };
-+
-+ lockdep_assert_held(&imx219->mutex);
-+ return codes[imx219->vflip->val][imx219->hflip->val];
-+}
-+
-+static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-+{
-+ struct imx219 *imx219 = to_imx219(sd);
-+ struct v4l2_mbus_framefmt *try_fmt =
-+ v4l2_subdev_get_try_format(sd, fh->pad, 0);
-+
-+ mutex_lock(&imx219->mutex);
-+
-+ /* Initialize try_fmt */
-+ try_fmt->width = supported_modes[0].width;
-+ try_fmt->height = supported_modes[0].height;
-+ try_fmt->code = imx219_get_format_code(imx219);
-+ try_fmt->field = V4L2_FIELD_NONE;
-+
-+ mutex_unlock(&imx219->mutex);
-+
-+ return 0;
-+}
-+
-+static int imx219_set_ctrl(struct v4l2_ctrl *ctrl)
-+{
-+ struct imx219 *imx219 =
-+ container_of(ctrl->handler, struct imx219, ctrl_handler);
-+ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
-+ int ret;
-+
-+ if (ctrl->id == V4L2_CID_VBLANK) {
-+ int exposure_max, exposure_def;
-+
-+ /* Update max exposure while meeting expected vblanking */
-+ exposure_max = imx219->mode->height + ctrl->val - 4;
-+ exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
-+ exposure_max : IMX219_EXPOSURE_DEFAULT;
-+ __v4l2_ctrl_modify_range(imx219->exposure,
-+ imx219->exposure->minimum,
-+ exposure_max, imx219->exposure->step,
-+ exposure_def);
-+ }
-+
-+ /*
-+ * Applying V4L2 control value only happens
-+ * when power is up for streaming
-+ */
-+ if (pm_runtime_get_if_in_use(&client->dev) == 0)
-+ return 0;
-+
-+ switch (ctrl->id) {
-+ case V4L2_CID_ANALOGUE_GAIN:
-+ ret = imx219_write_reg(imx219, IMX219_REG_ANALOG_GAIN,
-+ IMX219_REG_VALUE_08BIT, ctrl->val);
-+ break;
-+ case V4L2_CID_EXPOSURE:
-+ ret = imx219_write_reg(imx219, IMX219_REG_EXPOSURE,
-+ IMX219_REG_VALUE_16BIT, ctrl->val);
-+ break;
-+ case V4L2_CID_DIGITAL_GAIN:
-+ ret = imx219_write_reg(imx219, IMX219_REG_DIGITAL_GAIN,
-+ IMX219_REG_VALUE_16BIT, ctrl->val);
-+ break;
-+ case V4L2_CID_TEST_PATTERN:
-+ ret = imx219_write_reg(imx219, IMX219_REG_TEST_PATTERN,
-+ IMX219_REG_VALUE_16BIT,
-+ imx219_test_pattern_val[ctrl->val]);
-+ break;
-+ case V4L2_CID_HFLIP:
-+ case V4L2_CID_VFLIP:
-+ ret = imx219_write_reg(imx219, IMX219_REG_ORIENTATION, 1,
-+ imx219->hflip->val |
-+ imx219->vflip->val << 1);
-+ break;
-+ case V4L2_CID_VBLANK:
-+ ret = imx219_write_reg(imx219, IMX219_REG_VTS,
-+ IMX219_REG_VALUE_16BIT,
-+ imx219->mode->height + ctrl->val);
-+ break;
-+ case V4L2_CID_TEST_PATTERN_RED:
-+ ret = imx219_write_reg(imx219, IMX219_REG_TESTP_RED,
-+ IMX219_REG_VALUE_16BIT, ctrl->val);
-+ break;
-+ case V4L2_CID_TEST_PATTERN_GREENR:
-+ ret = imx219_write_reg(imx219, IMX219_REG_TESTP_GREENR,
-+ IMX219_REG_VALUE_16BIT, ctrl->val);
-+ break;
-+ case V4L2_CID_TEST_PATTERN_BLUE:
-+ ret = imx219_write_reg(imx219, IMX219_REG_TESTP_BLUE,
-+ IMX219_REG_VALUE_16BIT, ctrl->val);
-+ break;
-+ case V4L2_CID_TEST_PATTERN_GREENB:
-+ ret = imx219_write_reg(imx219, IMX219_REG_TESTP_GREENB,
-+ IMX219_REG_VALUE_16BIT, ctrl->val);
-+ break;
-+ default:
-+ dev_info(&client->dev,
-+ "ctrl(id:0x%x,val:0x%x) is not handled\n",
-+ ctrl->id, ctrl->val);
-+ ret = -EINVAL;
-+ break;
-+ }
-+
-+ pm_runtime_put(&client->dev);
-+
-+ return ret;
-+}
-+
-+static const struct v4l2_ctrl_ops imx219_ctrl_ops = {
-+ .s_ctrl = imx219_set_ctrl,
-+};
-+
-+static int imx219_enum_mbus_code(struct v4l2_subdev *sd,
-+ struct v4l2_subdev_pad_config *cfg,
-+ struct v4l2_subdev_mbus_code_enum *code)
-+{
-+ struct imx219 *imx219 = to_imx219(sd);
-+
-+ /*
-+ * Only one bayer order is supported (though it depends on the flip
-+ * settings)
-+ */
-+ if (code->index > 0)
-+ return -EINVAL;
-+
-+ code->code = imx219_get_format_code(imx219);
-+
-+ return 0;
-+}
-+
-+static int imx219_enum_frame_size(struct v4l2_subdev *sd,
-+ struct v4l2_subdev_pad_config *cfg,
-+ struct v4l2_subdev_frame_size_enum *fse)
-+{
-+ struct imx219 *imx219 = to_imx219(sd);
-+
-+ if (fse->index >= ARRAY_SIZE(supported_modes))
-+ return -EINVAL;
-+
-+ if (fse->code != imx219_get_format_code(imx219))
-+ return -EINVAL;
-+
-+ fse->min_width = supported_modes[fse->index].width;
-+ fse->max_width = fse->min_width;
-+ fse->min_height = supported_modes[fse->index].height;
-+ fse->max_height = fse->min_height;
-+
-+ return 0;
-+}
-+
-+static void imx219_reset_colorspace(struct v4l2_mbus_framefmt *fmt)
-+{
-+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
-+ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
-+ fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
-+ fmt->colorspace,
-+ fmt->ycbcr_enc);
-+ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
-+}
-+
-+static void imx219_update_pad_format(struct imx219 *imx219,
-+ const struct imx219_mode *mode,
-+ struct v4l2_subdev_format *fmt)
-+{
-+ fmt->format.width = mode->width;
-+ fmt->format.height = mode->height;
-+ fmt->format.code = imx219_get_format_code(imx219);
-+ fmt->format.field = V4L2_FIELD_NONE;
-+
-+ imx219_reset_colorspace(&fmt->format);
-+}
-+
-+static int __imx219_get_pad_format(struct imx219 *imx219,
-+ struct v4l2_subdev_pad_config *cfg,
-+ struct v4l2_subdev_format *fmt)
-+{
-+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-+ struct v4l2_mbus_framefmt *try_fmt =
-+ v4l2_subdev_get_try_format(&imx219->sd, cfg, fmt->pad);
-+ /* update the code which could change due to vflip or hflip: */
-+ try_fmt->code = imx219_get_format_code(imx219);
-+ fmt->format = *try_fmt;
-+ } else {
-+ imx219_update_pad_format(imx219, imx219->mode, fmt);
-+ }
-+
-+ return 0;
-+}
-+
-+static int imx219_get_pad_format(struct v4l2_subdev *sd,
-+ struct v4l2_subdev_pad_config *cfg,
-+ struct v4l2_subdev_format *fmt)
-+{
-+ struct imx219 *imx219 = to_imx219(sd);
-+ int ret;
-+
-+ mutex_lock(&imx219->mutex);
-+ ret = __imx219_get_pad_format(imx219, cfg, fmt);
-+ mutex_unlock(&imx219->mutex);
-+
-+ return ret;
-+}
-+
-+static int imx219_set_pad_format(struct v4l2_subdev *sd,
-+ struct v4l2_subdev_pad_config *cfg,
-+ struct v4l2_subdev_format *fmt)
-+{
-+ struct imx219 *imx219 = to_imx219(sd);
-+ const struct imx219_mode *mode;
-+ struct v4l2_mbus_framefmt *framefmt;
-+ int exposure_max, exposure_def, hblank;
-+
-+ mutex_lock(&imx219->mutex);
-+
-+ /* Bayer order varies with flips */
-+ fmt->format.code = imx219_get_format_code(imx219);
-+
-+ mode = v4l2_find_nearest_size(supported_modes,
-+ ARRAY_SIZE(supported_modes),
-+ width, height,
-+ fmt->format.width, fmt->format.height);
-+ imx219_update_pad_format(imx219, mode, fmt);
-+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-+ framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
-+ *framefmt = fmt->format;
-+ } else if (imx219->mode != mode) {
-+ imx219->mode = mode;
-+ /* Update limits and set FPS to default */
-+ __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN,
-+ IMX219_VTS_MAX - mode->height, 1,
-+ mode->vts_def - mode->height);
-+ __v4l2_ctrl_s_ctrl(imx219->vblank,
-+ mode->vts_def - mode->height);
-+ /* Update max exposure while meeting expected vblanking */
-+ exposure_max = mode->vts_def - 4;
-+ exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
-+ exposure_max : IMX219_EXPOSURE_DEFAULT;
-+ __v4l2_ctrl_modify_range(imx219->exposure,
-+ imx219->exposure->minimum,
-+ exposure_max, imx219->exposure->step,
-+ exposure_def);
-+ /*
-+ * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank
-+ * depends on mode->width only, and is not changeble in any
-+ * way other than changing the mode.
-+ */
-+ hblank = IMX219_PPL_DEFAULT - mode->width;
-+ __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1,
-+ hblank);
-+ }
-+
-+ mutex_unlock(&imx219->mutex);
-+
-+ return 0;
-+}
-+
-+static int imx219_start_streaming(struct imx219 *imx219)
-+{
-+ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
-+ const struct imx219_reg_list *reg_list;
-+ int ret;
-+
-+ /* Apply default values of current mode */
-+ reg_list = &imx219->mode->reg_list;
-+ ret = imx219_write_regs(imx219, reg_list->regs, reg_list->num_of_regs);
-+ if (ret) {
-+ dev_err(&client->dev, "%s failed to set mode\n", __func__);
-+ return ret;
-+ }
-+
-+ /* Apply customized values from user */
-+ ret = __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler);
-+ if (ret)
-+ return ret;
-+
-+ /* set stream on register */
-+ return imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
-+ IMX219_REG_VALUE_08BIT, IMX219_MODE_STREAMING);
-+}
-+
-+static void imx219_stop_streaming(struct imx219 *imx219)
-+{
-+ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
-+ int ret;
-+
-+ /* set stream off register */
-+ ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
-+ IMX219_REG_VALUE_08BIT, IMX219_MODE_STANDBY);
-+ if (ret)
-+ dev_err(&client->dev, "%s failed to set stream\n", __func__);
-+}
-+
-+static int imx219_set_stream(struct v4l2_subdev *sd, int enable)
-+{
-+ struct imx219 *imx219 = to_imx219(sd);
-+ struct i2c_client *client = v4l2_get_subdevdata(sd);
-+ int ret = 0;
-+
-+ mutex_lock(&imx219->mutex);
-+ if (imx219->streaming == enable) {
-+ mutex_unlock(&imx219->mutex);
-+ return 0;
-+ }
-+
-+ if (enable) {
-+ ret = pm_runtime_get_sync(&client->dev);
-+ if (ret < 0) {
-+ pm_runtime_put_noidle(&client->dev);
-+ goto err_unlock;
-+ }
-+
-+ /*
-+ * Apply default & customized values
-+ * and then start streaming.
-+ */
-+ ret = imx219_start_streaming(imx219);
-+ if (ret)
-+ goto err_rpm_put;
-+ } else {
-+ imx219_stop_streaming(imx219);
-+ pm_runtime_put(&client->dev);
-+ }
-+
-+ imx219->streaming = enable;
-+
-+ /* vflip and hflip cannot change during streaming */
-+ __v4l2_ctrl_grab(imx219->vflip, enable);
-+ __v4l2_ctrl_grab(imx219->hflip, enable);
-+
-+ mutex_unlock(&imx219->mutex);
-+
-+ return ret;
-+
-+err_rpm_put:
-+ pm_runtime_put(&client->dev);
-+err_unlock:
-+ mutex_unlock(&imx219->mutex);
-+
-+ return ret;
-+}
-+
-+/* Power/clock management functions */
-+static int imx219_power_on(struct device *dev)
-+{
-+ struct i2c_client *client = to_i2c_client(dev);
-+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
-+ struct imx219 *imx219 = to_imx219(sd);
-+ int ret;
-+
-+ ret = regulator_bulk_enable(IMX219_NUM_SUPPLIES,
-+ imx219->supplies);
-+ if (ret) {
-+ dev_err(&client->dev, "%s: failed to enable regulators\n",
-+ __func__);
-+ return ret;
-+ }
-+
-+ ret = clk_prepare_enable(imx219->xclk);
-+ if (ret) {
-+ dev_err(&client->dev, "%s: failed to enable clock\n",
-+ __func__);
-+ goto reg_off;
-+ }
-+
-+ gpiod_set_value_cansleep(imx219->reset_gpio, 1);
-+ usleep_range(IMX219_XCLR_MIN_DELAY_US,
-+ IMX219_XCLR_MIN_DELAY_US + IMX219_XCLR_DELAY_RANGE_US);
-+
-+ return 0;
-+
-+reg_off:
-+ regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies);
-+
-+ return ret;
-+}
-+
-+static int imx219_power_off(struct device *dev)
-+{
-+ struct i2c_client *client = to_i2c_client(dev);
-+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
-+ struct imx219 *imx219 = to_imx219(sd);
-+
-+ gpiod_set_value_cansleep(imx219->reset_gpio, 0);
-+ regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies);
-+ clk_disable_unprepare(imx219->xclk);
-+
-+ return 0;
-+}
-+
-+static int __maybe_unused imx219_suspend(struct device *dev)
-+{
-+ struct i2c_client *client = to_i2c_client(dev);
-+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
-+ struct imx219 *imx219 = to_imx219(sd);
-+
-+ if (imx219->streaming)
-+ imx219_stop_streaming(imx219);
-+
-+ return 0;
-+}
-+
-+static int __maybe_unused imx219_resume(struct device *dev)
-+{
-+ struct i2c_client *client = to_i2c_client(dev);
-+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
-+ struct imx219 *imx219 = to_imx219(sd);
-+ int ret;
-+
-+ if (imx219->streaming) {
-+ ret = imx219_start_streaming(imx219);
-+ if (ret)
-+ goto error;
-+ }
-+
-+ return 0;
-+
-+error:
-+ imx219_stop_streaming(imx219);
-+ imx219->streaming = 0;
-+
-+ return ret;
-+}
-+
-+static int imx219_get_regulators(struct imx219 *imx219)
-+{
-+ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
-+ unsigned int i;
-+
-+ for (i = 0; i < IMX219_NUM_SUPPLIES; i++)
-+ imx219->supplies[i].supply = imx219_supply_name[i];
-+
-+ return devm_regulator_bulk_get(&client->dev,
-+ IMX219_NUM_SUPPLIES,
-+ imx219->supplies);
-+}
-+
-+/* Verify chip ID */
-+static int imx219_identify_module(struct imx219 *imx219)
-+{
-+ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
-+ int ret;
-+ u32 val;
-+
-+ ret = imx219_read_reg(imx219, IMX219_REG_CHIP_ID,
-+ IMX219_REG_VALUE_16BIT, &val);
-+ if (ret) {
-+ dev_err(&client->dev, "failed to read chip id %x\n",
-+ IMX219_CHIP_ID);
-+ return ret;
-+ }
-+
-+ if (val != IMX219_CHIP_ID) {
-+ dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
-+ IMX219_CHIP_ID, val);
-+ return -EIO;
-+ }
-+
-+ return 0;
-+}
-+
-+static const struct v4l2_subdev_core_ops imx219_core_ops = {
-+ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
-+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
-+};
-+
-+static const struct v4l2_subdev_video_ops imx219_video_ops = {
-+ .s_stream = imx219_set_stream,
-+};
-+
-+static const struct v4l2_subdev_pad_ops imx219_pad_ops = {
-+ .enum_mbus_code = imx219_enum_mbus_code,
-+ .get_fmt = imx219_get_pad_format,
-+ .set_fmt = imx219_set_pad_format,
-+ .enum_frame_size = imx219_enum_frame_size,
-+};
-+
-+static const struct v4l2_subdev_ops imx219_subdev_ops = {
-+ .core = &imx219_core_ops,
-+ .video = &imx219_video_ops,
-+ .pad = &imx219_pad_ops,
-+};
-+
-+static const struct v4l2_subdev_internal_ops imx219_internal_ops = {
-+ .open = imx219_open,
-+};
-+
-+/* Initialize control handlers */
-+static int imx219_init_controls(struct imx219 *imx219)
-+{
-+ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
-+ struct v4l2_ctrl_handler *ctrl_hdlr;
-+ unsigned int height = imx219->mode->height;
-+ int exposure_max, exposure_def, hblank;
-+ int i, ret;
-+
-+ ctrl_hdlr = &imx219->ctrl_handler;
-+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9);
-+ if (ret)
-+ return ret;
-+
-+ mutex_init(&imx219->mutex);
-+ ctrl_hdlr->lock = &imx219->mutex;
-+
-+ /* By default, PIXEL_RATE is read only */
-+ imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
-+ V4L2_CID_PIXEL_RATE,
-+ IMX219_PIXEL_RATE,
-+ IMX219_PIXEL_RATE, 1,
-+ IMX219_PIXEL_RATE);
-+
-+ /* Initial vblank/hblank/exposure parameters based on current mode */
-+ imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
-+ V4L2_CID_VBLANK, IMX219_VBLANK_MIN,
-+ IMX219_VTS_MAX - height, 1,
-+ imx219->mode->vts_def - height);
-+ hblank = IMX219_PPL_DEFAULT - imx219->mode->width;
-+ imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
-+ V4L2_CID_HBLANK, hblank, hblank,
-+ 1, hblank);
-+ if (imx219->hblank)
-+ imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
-+ exposure_max = imx219->mode->vts_def - 4;
-+ exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
-+ exposure_max : IMX219_EXPOSURE_DEFAULT;
-+ imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
-+ V4L2_CID_EXPOSURE,
-+ IMX219_EXPOSURE_MIN, exposure_max,
-+ IMX219_EXPOSURE_STEP,
-+ exposure_def);
-+
-+ v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
-+ IMX219_ANA_GAIN_MIN, IMX219_ANA_GAIN_MAX,
-+ IMX219_ANA_GAIN_STEP, IMX219_ANA_GAIN_DEFAULT);
-+
-+ v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
-+ IMX219_DGTL_GAIN_MIN, IMX219_DGTL_GAIN_MAX,
-+ IMX219_DGTL_GAIN_STEP, IMX219_DGTL_GAIN_DEFAULT);
-+
-+ imx219->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
-+ V4L2_CID_HFLIP, 0, 1, 1, 0);
-+ if (imx219->hflip)
-+ imx219->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
-+
-+ imx219->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
-+ V4L2_CID_VFLIP, 0, 1, 1, 0);
-+ if (imx219->vflip)
-+ imx219->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
-+
-+ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx219_ctrl_ops,
-+ V4L2_CID_TEST_PATTERN,
-+ ARRAY_SIZE(imx219_test_pattern_menu) - 1,
-+ 0, 0, imx219_test_pattern_menu);
-+ for (i = 0; i < 4; i++) {
-+ /*
-+ * The assumption is that
-+ * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1
-+ * V4L2_CID_TEST_PATTERN_BLUE == V4L2_CID_TEST_PATTERN_RED + 2
-+ * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3
-+ */
-+ v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
-+ V4L2_CID_TEST_PATTERN_RED + i,
-+ IMX219_TESTP_COLOUR_MIN,
-+ IMX219_TESTP_COLOUR_MAX,
-+ IMX219_TESTP_COLOUR_STEP,
-+ IMX219_TESTP_COLOUR_MAX);
-+ /* The "Solid color" pattern is white by default */
-+ }
-+
-+ if (ctrl_hdlr->error) {
-+ ret = ctrl_hdlr->error;
-+ dev_err(&client->dev, "%s control init failed (%d)\n",
-+ __func__, ret);
-+ goto error;
-+ }
-+
-+ imx219->sd.ctrl_handler = ctrl_hdlr;
-+
-+ return 0;
-+
-+error:
-+ v4l2_ctrl_handler_free(ctrl_hdlr);
-+ mutex_destroy(&imx219->mutex);
-+
-+ return ret;
-+}
-+
-+static void imx219_free_controls(struct imx219 *imx219)
-+{
-+ v4l2_ctrl_handler_free(imx219->sd.ctrl_handler);
-+ mutex_destroy(&imx219->mutex);
-+}
-+
-+static int imx219_check_hwcfg(struct device *dev)
-+{
-+ struct fwnode_handle *endpoint;
-+ struct v4l2_fwnode_endpoint ep_cfg = {
-+ .bus_type = V4L2_MBUS_CSI2_DPHY
-+ };
-+ int ret = -EINVAL;
-+
-+ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
-+ if (!endpoint) {
-+ dev_err(dev, "endpoint node not found\n");
-+ return -EINVAL;
-+ }
-+
-+ if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
-+ dev_err(dev, "could not parse endpoint\n");
-+ goto error_out;
-+ }
-+
-+ /* Check the number of MIPI CSI2 data lanes */
-+ if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
-+ dev_err(dev, "only 2 data lanes are currently supported\n");
-+ goto error_out;
-+ }
-+
-+ /* Check the link frequency set in device tree */
-+ if (!ep_cfg.nr_of_link_frequencies) {
-+ dev_err(dev, "link-frequency property not found in DT\n");
-+ goto error_out;
-+ }
-+
-+ if (ep_cfg.nr_of_link_frequencies != 1 ||
-+ ep_cfg.link_frequencies[0] != IMX219_DEFAULT_LINK_FREQ) {
-+ dev_err(dev, "Link frequency not supported: %lld\n",
-+ ep_cfg.link_frequencies[0]);
-+ goto error_out;
-+ }
-+
-+ ret = 0;
-+
-+error_out:
-+ v4l2_fwnode_endpoint_free(&ep_cfg);
-+ fwnode_handle_put(endpoint);
-+
-+ return ret;
-+}
-+
-+static int imx219_probe(struct i2c_client *client)
-+{
-+ struct device *dev = &client->dev;
-+ struct imx219 *imx219;
-+ int ret;
-+
-+ imx219 = devm_kzalloc(&client->dev, sizeof(*imx219), GFP_KERNEL);
-+ if (!imx219)
-+ return -ENOMEM;
-+
-+ v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops);
-+
-+ /* Check the hardware configuration in device tree */
-+ if (imx219_check_hwcfg(dev))
-+ return -EINVAL;
-+
-+ /* Get system clock (xclk) */
-+ imx219->xclk = devm_clk_get(dev, NULL);
-+ if (IS_ERR(imx219->xclk)) {
-+ dev_err(dev, "failed to get xclk\n");
-+ return PTR_ERR(imx219->xclk);
-+ }
-+
-+ imx219->xclk_freq = clk_get_rate(imx219->xclk);
-+ if (imx219->xclk_freq != IMX219_XCLK_FREQ) {
-+ dev_err(dev, "xclk frequency not supported: %d Hz\n",
-+ imx219->xclk_freq);
-+ return -EINVAL;
-+ }
-+
-+ ret = imx219_get_regulators(imx219);
-+ if (ret) {
-+ dev_err(dev, "failed to get regulators\n");
-+ return ret;
-+ }
-+
-+ /* Request optional enable pin */
-+ imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset",
-+ GPIOD_OUT_HIGH);
-+
-+ /*
-+ * The sensor must be powered for imx219_identify_module()
-+ * to be able to read the CHIP_ID register
-+ */
-+ ret = imx219_power_on(dev);
-+ if (ret)
-+ return ret;
-+
-+ ret = imx219_identify_module(imx219);
-+ if (ret)
-+ goto error_power_off;
-+
-+ /* Set default mode to max resolution */
-+ imx219->mode = &supported_modes[0];
-+
-+ ret = imx219_init_controls(imx219);
-+ if (ret)
-+ goto error_power_off;
-+
-+ /* Initialize subdev */
-+ imx219->sd.internal_ops = &imx219_internal_ops;
-+ imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-+ imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
-+
-+ /* Initialize source pad */
-+ imx219->pad.flags = MEDIA_PAD_FL_SOURCE;
-+
-+ ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad);
-+ if (ret) {
-+ dev_err(dev, "failed to init entity pads: %d\n", ret);
-+ goto error_handler_free;
-+ }
-+
-+ ret = v4l2_async_register_subdev_sensor_common(&imx219->sd);
-+ if (ret < 0) {
-+ dev_err(dev, "failed to register sensor sub-device: %d\n", ret);
-+ goto error_media_entity;
-+ }
-+
-+ /* Enable runtime PM and turn off the device */
-+ pm_runtime_set_active(dev);
-+ pm_runtime_enable(dev);
-+ pm_runtime_idle(dev);
-+
-+ return 0;
-+
-+error_media_entity:
-+ media_entity_cleanup(&imx219->sd.entity);
-+
-+error_handler_free:
-+ imx219_free_controls(imx219);
-+
-+error_power_off:
-+ imx219_power_off(dev);
-+
-+ return ret;
-+}
-+
-+static int imx219_remove(struct i2c_client *client)
-+{
-+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
-+ struct imx219 *imx219 = to_imx219(sd);
-+
-+ v4l2_async_unregister_subdev(sd);
-+ media_entity_cleanup(&sd->entity);
-+ imx219_free_controls(imx219);
-+
-+ pm_runtime_disable(&client->dev);
-+ if (!pm_runtime_status_suspended(&client->dev))
-+ imx219_power_off(&client->dev);
-+ pm_runtime_set_suspended(&client->dev);
-+
-+ return 0;
-+}
-+
-+static const struct of_device_id imx219_dt_ids[] = {
-+ { .compatible = "sony,imx219" },
-+ { /* sentinel */ }
-+};
-+MODULE_DEVICE_TABLE(of, imx219_dt_ids);
-+
-+static const struct dev_pm_ops imx219_pm_ops = {
-+ SET_SYSTEM_SLEEP_PM_OPS(imx219_suspend, imx219_resume)
-+ SET_RUNTIME_PM_OPS(imx219_power_off, imx219_power_on, NULL)
-+};
-+
-+static struct i2c_driver imx219_i2c_driver = {
-+ .driver = {
-+ .name = "imx219",
-+ .of_match_table = imx219_dt_ids,
-+ .pm = &imx219_pm_ops,
-+ },
-+ .probe_new = imx219_probe,
-+ .remove = imx219_remove,
-+};
-+
-+module_i2c_driver(imx219_i2c_driver);
-+
-+MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com");
-+MODULE_DESCRIPTION("Sony IMX219 sensor driver");
-+MODULE_LICENSE("GPL v2");
--- /dev/null
+From eae83133532e44adae2f632b2168119a4c7249a2 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 11 Mar 2020 12:07:57 +0000
+Subject: [PATCH] overlays: imx219: Correct link frequency to match the
+ upstream driver
+
+The upstream driver is checking the link frequency parameter, and
+the overlay had the wrong value.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/imx219-overlay.dts | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/imx219-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx219-overlay.dts
+@@ -40,7 +40,7 @@
+ data-lanes = <1 2>;
+ clock-noncontinuous;
+ link-frequencies =
+- /bits/ 64 <297000000>;
++ /bits/ 64 <456000000>;
+ };
+ };
+ };
--- /dev/null
+From 61113c5463166d734dc6560574f5fb1536bae795 Mon Sep 17 00:00:00 2001
+From: Nataliya Korovkina <malus.brandywine@gmail.com>
+Date: Thu, 12 Mar 2020 17:22:53 -0400
+Subject: [PATCH] Kbuild: Allow .dtbo overlays to be built, adjust.
+
+This is adjustment to commit
+d368ceaacdccd7732dc97d1d7987bdf7149d62e3 "kbuild: Allow .dtbo overlays to be built piecemeal"
+
+prepare3 target has gone from mainline tree in branch 5.4.y
+
+Signed-off-by: Nataliya Korovkina <malus.brandywine@gmail.com>
+---
+ Makefile | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/Makefile
++++ b/Makefile
+@@ -1238,7 +1238,7 @@ ifneq ($(dtstree),)
+ %.dtb: include/config/kernel.release scripts_dtc
+ $(Q)$(MAKE) $(build)=$(dtstree) $(dtstree)/$@
+
+-%.dtbo: prepare3 scripts_dtc
++%.dtbo: include/config/kernel.release scripts_dtc
+ $(Q)$(MAKE) $(build)=$(dtstree) $(dtstree)/$@
+
+ PHONY += dtbs dtbs_install dtbs_check
+++ /dev/null
-From eae83133532e44adae2f632b2168119a4c7249a2 Mon Sep 17 00:00:00 2001
-From: Dave Stevenson <dave.stevenson@raspberrypi.com>
-Date: Wed, 11 Mar 2020 12:07:57 +0000
-Subject: [PATCH] overlays: imx219: Correct link frequency to match the
- upstream driver
-
-The upstream driver is checking the link frequency parameter, and
-the overlay had the wrong value.
-
-Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
----
- arch/arm/boot/dts/overlays/imx219-overlay.dts | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/arch/arm/boot/dts/overlays/imx219-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/imx219-overlay.dts
-@@ -40,7 +40,7 @@
- data-lanes = <1 2>;
- clock-noncontinuous;
- link-frequencies =
-- /bits/ 64 <297000000>;
-+ /bits/ 64 <456000000>;
- };
- };
- };
+++ /dev/null
-From 61113c5463166d734dc6560574f5fb1536bae795 Mon Sep 17 00:00:00 2001
-From: Nataliya Korovkina <malus.brandywine@gmail.com>
-Date: Thu, 12 Mar 2020 17:22:53 -0400
-Subject: [PATCH] Kbuild: Allow .dtbo overlays to be built, adjust.
-
-This is adjustment to commit
-d368ceaacdccd7732dc97d1d7987bdf7149d62e3 "kbuild: Allow .dtbo overlays to be built piecemeal"
-
-prepare3 target has gone from mainline tree in branch 5.4.y
-
-Signed-off-by: Nataliya Korovkina <malus.brandywine@gmail.com>
----
- Makefile | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/Makefile
-+++ b/Makefile
-@@ -1238,7 +1238,7 @@ ifneq ($(dtstree),)
- %.dtb: include/config/kernel.release scripts_dtc
- $(Q)$(MAKE) $(build)=$(dtstree) $(dtstree)/$@
-
--%.dtbo: prepare3 scripts_dtc
-+%.dtbo: include/config/kernel.release scripts_dtc
- $(Q)$(MAKE) $(build)=$(dtstree) $(dtstree)/$@
-
- PHONY += dtbs dtbs_install dtbs_check
--- /dev/null
+From 23f717168dc55f69ad517d3ab5f412d04a5afc0e Mon Sep 17 00:00:00 2001
+From: David Plowman <david.plowman@raspberrypi.org>
+Date: Wed, 15 Jan 2020 13:40:38 +0000
+Subject: [PATCH] media: ov5647: Fix return codes from
+ ov5647_write/ov5647_read functions.
+
+Previously they were returning positive non-zero codes for success,
+which were getting passed up the call stack. Since release 4.19,
+do_dentry_open (fs/open.c) has been catching these and flagging an
+error. (So this driver has been broken since that date.)
+
+Fixes: 3c2472a [media] media: i2c: Add support for OV5647 sensor
+Signed-off-by: David Plowman <david.plowman@raspberrypi.org>
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 30 +++++++++++++++++++++++++++---
+ 1 file changed, 27 insertions(+), 3 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -214,9 +214,18 @@ static int ov5647_write(struct v4l2_subd
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ ret = i2c_master_send(client, data, 3);
+- if (ret < 0)
++ /*
++ * Writing the wrong number of bytes also needs to be flagged as an
++ * error. Success needs to produce a 0 return code.
++ */
++ if (ret == 3) {
++ ret = 0;
++ } else {
+ dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
+ __func__, reg);
++ if (ret >= 0)
++ ret = -EINVAL;
++ }
+
+ return ret;
+ }
+@@ -228,16 +237,31 @@ static int ov5647_read(struct v4l2_subde
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ ret = i2c_master_send(client, data_w, 2);
+- if (ret < 0) {
++ /*
++ * A negative return code, or sending the wrong number of bytes, both
++ * count as an error.
++ */
++ if (ret != 2) {
+ dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
+ __func__, reg);
++ if (ret >= 0)
++ ret = -EINVAL;
+ return ret;
+ }
+
+ ret = i2c_master_recv(client, val, 1);
+- if (ret < 0)
++ /*
++ * The only return value indicating success is 1. Anything else, even
++ * a non-negative value, indicates something went wrong.
++ */
++ if (ret == 1) {
++ ret = 0;
++ } else {
+ dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n",
+ __func__, reg);
++ if (ret >= 0)
++ ret = -EINVAL;
++ }
+
+ return ret;
+ }
--- /dev/null
+From 9e584d9de3387588bf455d3c45ec6a092bfa4266 Mon Sep 17 00:00:00 2001
+From: David Plowman <david.plowman@raspberrypi.com>
+Date: Wed, 29 Jan 2020 15:30:53 +0000
+Subject: [PATCH] media: ov5647: Add basic support for multiple sensor
+ modes.
+
+Specifically:
+
+Added a structure ov5647_mode and a list of supported_modes (though no
+actual new modes as yet). The state object points to the "current mode".
+
+ov5647_enum_mbus_code, ov5647_enum_frame_size, ov5647_set_fmt and
+ov5647_get_fmt all needed upgrading to cope with multiple modes.
+
+__sensor_init (which writes all the registers) is now called by
+ov5647_stream_on (once the mode is known) rather than by
+ov5647_sensor_power.
+
+Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 268 ++++++++++++++++++++++++++++---------
+ 1 file changed, 202 insertions(+), 66 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -86,13 +86,17 @@ struct regval_list {
+ u8 data;
+ };
+
++struct ov5647_mode {
++ struct v4l2_mbus_framefmt format;
++ struct regval_list *reg_list;
++ unsigned int num_regs;
++};
++
+ struct ov5647 {
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct mutex lock;
+- struct v4l2_mbus_framefmt format;
+- unsigned int width;
+- unsigned int height;
++ const struct ov5647_mode *mode;
+ int power_count;
+ struct clk *xclk;
+ struct gpio_desc *pwdn;
+@@ -207,6 +211,32 @@ static struct regval_list ov5647_640x480
+ {0x0100, 0x01},
+ };
+
++static struct ov5647_mode supported_modes_8bit[] = {
++ /*
++ * Original 8-bit VGA mode
++ * Uncentred crop (top left quarter) from 2x2 binned 1296x972 image.
++ */
++ {
++ {
++ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .field = V4L2_FIELD_NONE,
++ .width = 640,
++ .height = 480
++ },
++ ov5647_640x480,
++ ARRAY_SIZE(ov5647_640x480)
++ },
++ /* more modes below here... */
++};
++
++static struct ov5647_mode supported_modes_10bit[] = {
++ /* no 10-bit modes yet */
++};
++
++/* Use original 8-bit VGA mode as default. */
++#define OV5647_DEFAULT_MODE (&supported_modes_8bit[0])
++
+ static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
+ {
+ int ret;
+@@ -293,12 +323,55 @@ static int ov5647_set_virtual_channel(st
+ return ov5647_write(sd, OV5647_REG_MIPI_CTRL14, channel_id | (channel << 6));
+ }
+
++static int __sensor_init(struct v4l2_subdev *sd)
++{
++ int ret;
++ u8 resetval, rdval;
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct ov5647 *state = to_state(sd);
++
++ ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
++ if (ret < 0)
++ return ret;
++
++ ret = ov5647_write_array(sd, state->mode->reg_list,
++ state->mode->num_regs);
++ if (ret < 0) {
++ dev_err(&client->dev, "write sensor default regs error\n");
++ return ret;
++ }
++
++ ret = ov5647_set_virtual_channel(sd, 0);
++ if (ret < 0)
++ return ret;
++
++ ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval);
++ if (ret < 0)
++ return ret;
++
++ if (!(resetval & 0x01)) {
++ dev_err(&client->dev, "Device was in SW standby");
++ ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01);
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
++
+ static int ov5647_stream_on(struct v4l2_subdev *sd)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5647 *ov5647 = to_state(sd);
+ u8 val = MIPI_CTRL00_BUS_IDLE;
+ int ret;
+
++ ret = __sensor_init(sd);
++ if (ret < 0) {
++ dev_err(&client->dev, "sensor_init failed\n");
++ return ret;
++ }
++
+ if (ov5647->flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)
+ val |= MIPI_CTRL00_CLOCK_LANE_GATE |
+ MIPI_CTRL00_LINE_SYNC_ENABLE;
+@@ -347,44 +420,6 @@ static int set_sw_standby(struct v4l2_su
+ return ov5647_write(sd, OV5647_SW_STANDBY, rdval);
+ }
+
+-static int __sensor_init(struct v4l2_subdev *sd)
+-{
+- int ret;
+- u8 resetval, rdval;
+- struct i2c_client *client = v4l2_get_subdevdata(sd);
+-
+- ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
+- if (ret < 0)
+- return ret;
+-
+- ret = ov5647_write_array(sd, ov5647_640x480,
+- ARRAY_SIZE(ov5647_640x480));
+- if (ret < 0) {
+- dev_err(&client->dev, "write sensor default regs error\n");
+- return ret;
+- }
+-
+- ret = ov5647_set_virtual_channel(sd, 0);
+- if (ret < 0)
+- return ret;
+-
+- ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval);
+- if (ret < 0)
+- return ret;
+-
+- if (!(resetval & 0x01)) {
+- dev_err(&client->dev, "Device was in SW standby");
+- ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01);
+- if (ret < 0)
+- return ret;
+- }
+-
+- /*
+- * stream off to make the clock lane into LP-11 state.
+- */
+- return ov5647_stream_off(sd);
+-}
+-
+ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on)
+ {
+ int ret = 0;
+@@ -408,7 +443,7 @@ static int ov5647_sensor_power(struct v4
+ }
+
+ ret = ov5647_write_array(sd, sensor_oe_enable_regs,
+- ARRAY_SIZE(sensor_oe_enable_regs));
++ ARRAY_SIZE(sensor_oe_enable_regs));
+ if (ret < 0) {
+ clk_disable_unprepare(ov5647->xclk);
+ dev_err(&client->dev,
+@@ -416,7 +451,10 @@ static int ov5647_sensor_power(struct v4
+ goto out;
+ }
+
+- ret = __sensor_init(sd);
++ /*
++ * Ensure streaming off to make clock lane go into LP-11 state.
++ */
++ ret = ov5647_stream_off(sd);
+ if (ret < 0) {
+ clk_disable_unprepare(ov5647->xclk);
+ dev_err(&client->dev,
+@@ -427,7 +465,7 @@ static int ov5647_sensor_power(struct v4
+ dev_dbg(&client->dev, "OV5647 power off\n");
+
+ ret = ov5647_write_array(sd, sensor_oe_disable_regs,
+- ARRAY_SIZE(sensor_oe_disable_regs));
++ ARRAY_SIZE(sensor_oe_disable_regs));
+
+ if (ret < 0)
+ dev_dbg(&client->dev, "disable oe failed\n");
+@@ -489,10 +527,19 @@ static const struct v4l2_subdev_core_ops
+
+ static int ov5647_s_stream(struct v4l2_subdev *sd, int enable)
+ {
++ struct ov5647 *state = to_state(sd);
++ int ret = 0;
++
++ mutex_lock(&state->lock);
++
+ if (enable)
+- return ov5647_stream_on(sd);
++ ret = ov5647_stream_on(sd);
+ else
+- return ov5647_stream_off(sd);
++ ret = ov5647_stream_off(sd);
++
++ mutex_unlock(&state->lock);
++
++ return ret;
+ }
+
+ static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = {
+@@ -503,38 +550,127 @@ static int ov5647_enum_mbus_code(struct
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+ {
+- if (code->index > 0)
++ if (code->index == 0 && ARRAY_SIZE(supported_modes_8bit))
++ code->code = MEDIA_BUS_FMT_SBGGR8_1X8;
++ else if (code->index == 0 && ARRAY_SIZE(supported_modes_8bit) == 0 &&
++ ARRAY_SIZE(supported_modes_10bit))
++ code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
++ else if (code->index == 1 && ARRAY_SIZE(supported_modes_8bit) &&
++ ARRAY_SIZE(supported_modes_10bit))
++ code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
++ else
+ return -EINVAL;
+
+- code->code = MEDIA_BUS_FMT_SBGGR8_1X8;
++ return 0;
++}
++
++static int ov5647_enum_frame_size(struct v4l2_subdev *sd,
++ struct v4l2_subdev_pad_config *cfg,
++ struct v4l2_subdev_frame_size_enum *fse)
++{
++ struct ov5647_mode *mode = NULL;
++
++ if (fse->code == MEDIA_BUS_FMT_SBGGR8_1X8) {
++ if (fse->index >= ARRAY_SIZE(supported_modes_8bit))
++ return -EINVAL;
++ mode = &supported_modes_8bit[fse->index];
++ } else if (fse->code == MEDIA_BUS_FMT_SBGGR10_1X10) {
++ if (fse->index >= ARRAY_SIZE(supported_modes_10bit))
++ return -EINVAL;
++ mode = &supported_modes_10bit[fse->index];
++ } else {
++ return -EINVAL;
++ }
++
++ fse->min_width = mode->format.width;
++ fse->max_width = fse->min_width;
++ fse->min_height = mode->format.height;
++ fse->max_height = fse->min_height;
++
++ return 0;
++}
++
++static int ov5647_set_fmt(struct v4l2_subdev *sd,
++ struct v4l2_subdev_pad_config *cfg,
++ struct v4l2_subdev_format *format)
++{
++ struct v4l2_mbus_framefmt *fmt = &format->format;
++ struct ov5647 *state = to_state(sd);
++ struct v4l2_mbus_framefmt *framefmt;
++ const struct ov5647_mode *mode_8bit, *mode_10bit, *mode = NULL;
++
++ if (format->pad != 0)
++ return -EINVAL;
++
++ mutex_lock(&state->lock);
++
++ /*
++ * Try to respect any given pixel format, otherwise try for a 10-bit
++ * mode.
++ */
++ mode_8bit = v4l2_find_nearest_size(supported_modes_8bit,
++ ARRAY_SIZE(supported_modes_8bit),
++ format.width, format.height,
++ format->format.width,
++ format->format.height);
++ mode_10bit = v4l2_find_nearest_size(supported_modes_10bit,
++ ARRAY_SIZE(supported_modes_10bit),
++ format.width, format.height,
++ format->format.width,
++ format->format.height);
++ if (format->format.code == MEDIA_BUS_FMT_SBGGR8_1X8 && mode_8bit)
++ mode = mode_8bit;
++ else if (format->format.code == MEDIA_BUS_FMT_SBGGR10_1X10 &&
++ mode_10bit)
++ mode = mode_10bit;
++ else if (mode_10bit)
++ mode = mode_10bit;
++ else
++ mode = mode_8bit;
++
++ if (!mode)
++ return -EINVAL;
++
++ *fmt = mode->format;
++ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
++ framefmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
++ *framefmt = format->format;
++ } else {
++ state->mode = mode;
++ }
++
++ mutex_unlock(&state->lock);
+
+ return 0;
+ }
+
+-static int ov5647_set_get_fmt(struct v4l2_subdev *sd,
+- struct v4l2_subdev_pad_config *cfg,
+- struct v4l2_subdev_format *format)
++static int ov5647_get_fmt(struct v4l2_subdev *sd,
++ struct v4l2_subdev_pad_config *cfg,
++ struct v4l2_subdev_format *format)
+ {
+ struct v4l2_mbus_framefmt *fmt = &format->format;
++ struct ov5647 *state = to_state(sd);
+
+ if (format->pad != 0)
+ return -EINVAL;
+
+- /* Only one format is supported, so return that */
+- memset(fmt, 0, sizeof(*fmt));
+- fmt->code = MEDIA_BUS_FMT_SBGGR8_1X8;
+- fmt->colorspace = V4L2_COLORSPACE_SRGB;
+- fmt->field = V4L2_FIELD_NONE;
+- fmt->width = 640;
+- fmt->height = 480;
++ mutex_lock(&state->lock);
++
++ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
++ *fmt = *v4l2_subdev_get_try_format(sd, cfg, format->pad);
++ else
++ *fmt = state->mode->format;
++
++ mutex_unlock(&state->lock);
+
+ return 0;
+ }
+
+ static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = {
+ .enum_mbus_code = ov5647_enum_mbus_code,
+- .set_fmt = ov5647_set_get_fmt,
+- .get_fmt = ov5647_set_get_fmt,
++ .set_fmt = ov5647_set_fmt,
++ .get_fmt = ov5647_get_fmt,
++ .enum_frame_size = ov5647_enum_frame_size,
+ };
+
+ static const struct v4l2_subdev_ops ov5647_subdev_ops = {
+@@ -580,18 +716,15 @@ static int ov5647_open(struct v4l2_subde
+ v4l2_subdev_get_try_format(sd, fh->pad, 0);
+ struct v4l2_rect *crop =
+ v4l2_subdev_get_try_crop(sd, fh->pad, 0);
++ struct ov5647 *state = to_state(sd);
+
+ crop->left = OV5647_COLUMN_START_DEF;
+ crop->top = OV5647_ROW_START_DEF;
+ crop->width = OV5647_WINDOW_WIDTH_DEF;
+ crop->height = OV5647_WINDOW_HEIGHT_DEF;
+
+- format->code = MEDIA_BUS_FMT_SBGGR8_1X8;
+-
+- format->width = OV5647_WINDOW_WIDTH_DEF;
+- format->height = OV5647_WINDOW_HEIGHT_DEF;
+- format->field = V4L2_FIELD_NONE;
+- format->colorspace = V4L2_COLORSPACE_SRGB;
++ /* Set the default format to the same as the sensor. */
++ *format = state->mode->format;
+
+ return 0;
+ }
+@@ -660,6 +793,9 @@ static int ov5647_probe(struct i2c_clien
+
+ mutex_init(&sensor->lock);
+
++ /* Set the default mode before we init the subdev */
++ sensor->mode = OV5647_DEFAULT_MODE;
++
+ sd = &sensor->sd;
+ v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops);
+ sensor->sd.internal_ops = &ov5647_subdev_internal_ops;
+++ /dev/null
-From 23f717168dc55f69ad517d3ab5f412d04a5afc0e Mon Sep 17 00:00:00 2001
-From: David Plowman <david.plowman@raspberrypi.org>
-Date: Wed, 15 Jan 2020 13:40:38 +0000
-Subject: [PATCH] media: ov5647: Fix return codes from
- ov5647_write/ov5647_read functions.
-
-Previously they were returning positive non-zero codes for success,
-which were getting passed up the call stack. Since release 4.19,
-do_dentry_open (fs/open.c) has been catching these and flagging an
-error. (So this driver has been broken since that date.)
-
-Fixes: 3c2472a [media] media: i2c: Add support for OV5647 sensor
-Signed-off-by: David Plowman <david.plowman@raspberrypi.org>
-Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
----
- drivers/media/i2c/ov5647.c | 30 +++++++++++++++++++++++++++---
- 1 file changed, 27 insertions(+), 3 deletions(-)
-
---- a/drivers/media/i2c/ov5647.c
-+++ b/drivers/media/i2c/ov5647.c
-@@ -214,9 +214,18 @@ static int ov5647_write(struct v4l2_subd
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- ret = i2c_master_send(client, data, 3);
-- if (ret < 0)
-+ /*
-+ * Writing the wrong number of bytes also needs to be flagged as an
-+ * error. Success needs to produce a 0 return code.
-+ */
-+ if (ret == 3) {
-+ ret = 0;
-+ } else {
- dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
- __func__, reg);
-+ if (ret >= 0)
-+ ret = -EINVAL;
-+ }
-
- return ret;
- }
-@@ -228,16 +237,31 @@ static int ov5647_read(struct v4l2_subde
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- ret = i2c_master_send(client, data_w, 2);
-- if (ret < 0) {
-+ /*
-+ * A negative return code, or sending the wrong number of bytes, both
-+ * count as an error.
-+ */
-+ if (ret != 2) {
- dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
- __func__, reg);
-+ if (ret >= 0)
-+ ret = -EINVAL;
- return ret;
- }
-
- ret = i2c_master_recv(client, val, 1);
-- if (ret < 0)
-+ /*
-+ * The only return value indicating success is 1. Anything else, even
-+ * a non-negative value, indicates something went wrong.
-+ */
-+ if (ret == 1) {
-+ ret = 0;
-+ } else {
- dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n",
- __func__, reg);
-+ if (ret >= 0)
-+ ret = -EINVAL;
-+ }
-
- return ret;
- }
--- /dev/null
+From d9d333b717f220439868edd533994f2709b3a95f Mon Sep 17 00:00:00 2001
+From: David Plowman <david.plowman@raspberrypi.com>
+Date: Wed, 29 Jan 2020 15:31:23 +0000
+Subject: [PATCH] media: ov5647: Add V4L2 controls for analogue gain,
+ exposure and AWB
+
+Added basic v4l2_ctrl_handler infrastructure (there was none
+previously).
+
+Added controls to let AWB/AEC/AGC run in the sensor's auto mode or
+manually. Also controls to set exposure (in lines) and analogue gain
+(as a register code) from user code.
+
+Also delete registers (just the one) from the VGA mode register set
+that are now controlled by the new V4L2 controls.
+
+Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 175 ++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 174 insertions(+), 1 deletion(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -29,11 +29,13 @@
+ #include <linux/of_graph.h>
+ #include <linux/slab.h>
+ #include <linux/videodev2.h>
++#include <media/v4l2-ctrls.h>
+ #include <media/v4l2-device.h>
+ #include <media/v4l2-fwnode.h>
+ #include <media/v4l2-image-sizes.h>
+ #include <media/v4l2-mediabus.h>
+
++
+ #define SENSOR_NAME "ov5647"
+
+ /*
+@@ -53,9 +55,16 @@
+ #define OV5647_REG_CHIPID_H 0x300A
+ #define OV5647_REG_CHIPID_L 0x300B
+ #define OV5640_REG_PAD_OUT 0x300D
++#define OV5647_REG_EXP_HI 0x3500
++#define OV5647_REG_EXP_MID 0x3501
++#define OV5647_REG_EXP_LO 0x3502
++#define OV5647_REG_AEC_AGC 0x3503
++#define OV5647_REG_GAIN_HI 0x350A
++#define OV5647_REG_GAIN_LO 0x350B
+ #define OV5647_REG_FRAME_OFF_NUMBER 0x4202
+ #define OV5647_REG_MIPI_CTRL00 0x4800
+ #define OV5647_REG_MIPI_CTRL14 0x4814
++#define OV5647_REG_AWB 0x5001
+
+ #define REG_TERM 0xfffe
+ #define VAL_TERM 0xfe
+@@ -101,6 +110,7 @@ struct ov5647 {
+ struct clk *xclk;
+ struct gpio_desc *pwdn;
+ unsigned int flags;
++ struct v4l2_ctrl_handler ctrls;
+ };
+
+ static inline struct ov5647 *to_state(struct v4l2_subdev *sd)
+@@ -135,7 +145,6 @@ static struct regval_list ov5647_640x480
+ {0x3612, 0x59},
+ {0x3618, 0x00},
+ {0x5000, 0x06},
+- {0x5001, 0x01},
+ {0x5002, 0x41},
+ {0x5003, 0x08},
+ {0x5a00, 0x08},
+@@ -372,6 +381,11 @@ static int ov5647_stream_on(struct v4l2_
+ return ret;
+ }
+
++ /* Apply customized values from user when stream starts */
++ ret = __v4l2_ctrl_handler_setup(sd->ctrl_handler);
++ if (ret)
++ return ret;
++
+ if (ov5647->flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)
+ val |= MIPI_CTRL00_CLOCK_LANE_GATE |
+ MIPI_CTRL00_LINE_SYNC_ENABLE;
+@@ -753,6 +767,120 @@ static int ov5647_parse_dt(struct device
+ return ret;
+ }
+
++static int ov5647_s_auto_white_balance(struct v4l2_subdev *sd, u32 val)
++{
++ /* non-zero turns on AWB */
++ return ov5647_write(sd, OV5647_REG_AWB, val ? 1 : 0);
++}
++
++static int ov5647_s_autogain(struct v4l2_subdev *sd, u32 val)
++{
++ int ret;
++ u8 reg;
++
++ /* non-zero turns on AGC by clearing bit 1 */
++ ret = ov5647_read(sd, OV5647_REG_AEC_AGC, ®);
++ if (ret == 0)
++ ret = ov5647_write(sd, OV5647_REG_AEC_AGC,
++ val ? reg & ~2 : reg | 2);
++
++ return ret;
++}
++
++static int ov5647_s_exposure_auto(struct v4l2_subdev *sd, u32 val)
++{
++ int ret;
++ u8 reg;
++
++ /* Everything except V4L2_EXPOSURE_MANUAL turns on AEC by
++ * clearing bit 0
++ */
++ ret = ov5647_read(sd, OV5647_REG_AEC_AGC, ®);
++ if (ret == 0)
++ ret = ov5647_write(sd, OV5647_REG_AEC_AGC,
++ val == V4L2_EXPOSURE_MANUAL ?
++ reg | 1 : reg & ~1);
++
++ return ret;
++}
++
++static int ov5647_s_analogue_gain(struct v4l2_subdev *sd, u32 val)
++{
++ int ret;
++
++ /* 10 bits of gain, 2 in the high register */
++ ret = ov5647_write(sd, OV5647_REG_GAIN_HI, (val >> 8) & 3);
++ if (ret == 0)
++ ret = ov5647_write(sd, OV5647_REG_GAIN_LO, val & 0xff);
++
++ return ret;
++}
++
++static int ov5647_s_exposure(struct v4l2_subdev *sd, u32 val)
++{
++ int ret;
++
++ /* Sensor has 20 bits, but the bottom 4 bits are fractions of a line
++ * which we leave as zero (and don't receive in "val").
++ */
++ ret = ov5647_write(sd, OV5647_REG_EXP_HI, (val >> 12) & 0xf);
++ if (ret == 0)
++ ov5647_write(sd, OV5647_REG_EXP_MID, (val >> 4) & 0xff);
++ if (ret == 0)
++ ov5647_write(sd, OV5647_REG_EXP_LO, (val & 0xf) << 4);
++
++ return ret;
++}
++
++static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct ov5647 *state = container_of(ctrl->handler,
++ struct ov5647, ctrls);
++ struct v4l2_subdev *sd = &state->sd;
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ int ret = 0;
++
++ /* v4l2_ctrl_lock() locks our own mutex */
++
++ /*
++ * If the device is not powered up by the host driver do
++ * not apply any controls to H/W at this time. Instead
++ * the controls will be restored right after power-up.
++ */
++ if (state->power_count == 0)
++ return 0;
++
++ switch (ctrl->id) {
++ case V4L2_CID_AUTO_WHITE_BALANCE:
++ ret = ov5647_s_auto_white_balance(sd, ctrl->val);
++ break;
++ case V4L2_CID_AUTOGAIN:
++ ret = ov5647_s_autogain(sd, ctrl->val);
++ break;
++ case V4L2_CID_EXPOSURE_AUTO:
++ ret = ov5647_s_exposure_auto(sd, ctrl->val);
++ break;
++ case V4L2_CID_ANALOGUE_GAIN:
++ ret = ov5647_s_analogue_gain(sd, ctrl->val);
++ break;
++ case V4L2_CID_EXPOSURE:
++ ret = ov5647_s_exposure(sd, ctrl->val);
++ break;
++ default:
++ dev_info(&client->dev,
++ "ctrl(id:0x%x,val:0x%x) is not handled\n",
++ ctrl->id, ctrl->val);
++ ret = -EINVAL;
++ break;
++ }
++
++ return ret;
++}
++
++static const struct v4l2_ctrl_ops ov5647_ctrl_ops = {
++ .s_ctrl = ov5647_s_ctrl,
++};
++
+ static int ov5647_probe(struct i2c_client *client)
+ {
+ struct device *dev = &client->dev;
+@@ -761,6 +889,7 @@ static int ov5647_probe(struct i2c_clien
+ struct v4l2_subdev *sd;
+ struct device_node *np = client->dev.of_node;
+ u32 xclk_freq;
++ struct v4l2_ctrl *ctrl;
+
+ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+@@ -793,6 +922,48 @@ static int ov5647_probe(struct i2c_clien
+
+ mutex_init(&sensor->lock);
+
++ /* Initialise controls. */
++ v4l2_ctrl_handler_init(&sensor->ctrls, 3);
++ v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
++ V4L2_CID_AUTOGAIN,
++ 0, /* min */
++ 1, /* max */
++ 1, /* step */
++ 1); /* default */
++ v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
++ V4L2_CID_AUTO_WHITE_BALANCE,
++ 0, /* min */
++ 1, /* max */
++ 1, /* step */
++ 1); /* default */
++ v4l2_ctrl_new_std_menu(&sensor->ctrls, &ov5647_ctrl_ops,
++ V4L2_CID_EXPOSURE_AUTO,
++ V4L2_EXPOSURE_MANUAL, /* max */
++ 0, /* skip_mask */
++ V4L2_EXPOSURE_AUTO); /* default */
++ ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
++ V4L2_CID_EXPOSURE,
++ 4, /* min lines */
++ 65535, /* max lines (4+8+4 bits)*/
++ 1, /* step */
++ 1000); /* default number of lines */
++ ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
++ ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
++ V4L2_CID_ANALOGUE_GAIN,
++ 16, /* min, 16 = 1.0x */
++ 1023, /* max (10 bits) */
++ 1, /* step */
++ 32); /* default, 32 = 2.0x */
++ ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
++
++ if (sensor->ctrls.error) {
++ ret = sensor->ctrls.error;
++ dev_err(&client->dev, "%s control init failed (%d)\n",
++ __func__, ret);
++ goto error;
++ }
++ sensor->sd.ctrl_handler = &sensor->ctrls;
++
+ /* Set the default mode before we init the subdev */
+ sensor->mode = OV5647_DEFAULT_MODE;
+
+@@ -828,6 +999,7 @@ static int ov5647_probe(struct i2c_clien
+ error:
+ media_entity_cleanup(&sd->entity);
+ mutex_remove:
++ v4l2_ctrl_handler_free(&sensor->ctrls);
+ mutex_destroy(&sensor->lock);
+ return ret;
+ }
+@@ -839,6 +1011,7 @@ static int ov5647_remove(struct i2c_clie
+
+ v4l2_async_unregister_subdev(&ov5647->sd);
+ media_entity_cleanup(&ov5647->sd.entity);
++ v4l2_ctrl_handler_free(&ov5647->ctrls);
+ v4l2_device_unregister_subdev(sd);
+ mutex_destroy(&ov5647->lock);
+
+++ /dev/null
-From 9e584d9de3387588bf455d3c45ec6a092bfa4266 Mon Sep 17 00:00:00 2001
-From: David Plowman <david.plowman@raspberrypi.com>
-Date: Wed, 29 Jan 2020 15:30:53 +0000
-Subject: [PATCH] media: ov5647: Add basic support for multiple sensor
- modes.
-
-Specifically:
-
-Added a structure ov5647_mode and a list of supported_modes (though no
-actual new modes as yet). The state object points to the "current mode".
-
-ov5647_enum_mbus_code, ov5647_enum_frame_size, ov5647_set_fmt and
-ov5647_get_fmt all needed upgrading to cope with multiple modes.
-
-__sensor_init (which writes all the registers) is now called by
-ov5647_stream_on (once the mode is known) rather than by
-ov5647_sensor_power.
-
-Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
-Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
----
- drivers/media/i2c/ov5647.c | 268 ++++++++++++++++++++++++++++---------
- 1 file changed, 202 insertions(+), 66 deletions(-)
-
---- a/drivers/media/i2c/ov5647.c
-+++ b/drivers/media/i2c/ov5647.c
-@@ -86,13 +86,17 @@ struct regval_list {
- u8 data;
- };
-
-+struct ov5647_mode {
-+ struct v4l2_mbus_framefmt format;
-+ struct regval_list *reg_list;
-+ unsigned int num_regs;
-+};
-+
- struct ov5647 {
- struct v4l2_subdev sd;
- struct media_pad pad;
- struct mutex lock;
-- struct v4l2_mbus_framefmt format;
-- unsigned int width;
-- unsigned int height;
-+ const struct ov5647_mode *mode;
- int power_count;
- struct clk *xclk;
- struct gpio_desc *pwdn;
-@@ -207,6 +211,32 @@ static struct regval_list ov5647_640x480
- {0x0100, 0x01},
- };
-
-+static struct ov5647_mode supported_modes_8bit[] = {
-+ /*
-+ * Original 8-bit VGA mode
-+ * Uncentred crop (top left quarter) from 2x2 binned 1296x972 image.
-+ */
-+ {
-+ {
-+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
-+ .colorspace = V4L2_COLORSPACE_SRGB,
-+ .field = V4L2_FIELD_NONE,
-+ .width = 640,
-+ .height = 480
-+ },
-+ ov5647_640x480,
-+ ARRAY_SIZE(ov5647_640x480)
-+ },
-+ /* more modes below here... */
-+};
-+
-+static struct ov5647_mode supported_modes_10bit[] = {
-+ /* no 10-bit modes yet */
-+};
-+
-+/* Use original 8-bit VGA mode as default. */
-+#define OV5647_DEFAULT_MODE (&supported_modes_8bit[0])
-+
- static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
- {
- int ret;
-@@ -293,12 +323,55 @@ static int ov5647_set_virtual_channel(st
- return ov5647_write(sd, OV5647_REG_MIPI_CTRL14, channel_id | (channel << 6));
- }
-
-+static int __sensor_init(struct v4l2_subdev *sd)
-+{
-+ int ret;
-+ u8 resetval, rdval;
-+ struct i2c_client *client = v4l2_get_subdevdata(sd);
-+ struct ov5647 *state = to_state(sd);
-+
-+ ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = ov5647_write_array(sd, state->mode->reg_list,
-+ state->mode->num_regs);
-+ if (ret < 0) {
-+ dev_err(&client->dev, "write sensor default regs error\n");
-+ return ret;
-+ }
-+
-+ ret = ov5647_set_virtual_channel(sd, 0);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval);
-+ if (ret < 0)
-+ return ret;
-+
-+ if (!(resetval & 0x01)) {
-+ dev_err(&client->dev, "Device was in SW standby");
-+ ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01);
-+ if (ret < 0)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
- static int ov5647_stream_on(struct v4l2_subdev *sd)
- {
-+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5647 *ov5647 = to_state(sd);
- u8 val = MIPI_CTRL00_BUS_IDLE;
- int ret;
-
-+ ret = __sensor_init(sd);
-+ if (ret < 0) {
-+ dev_err(&client->dev, "sensor_init failed\n");
-+ return ret;
-+ }
-+
- if (ov5647->flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)
- val |= MIPI_CTRL00_CLOCK_LANE_GATE |
- MIPI_CTRL00_LINE_SYNC_ENABLE;
-@@ -347,44 +420,6 @@ static int set_sw_standby(struct v4l2_su
- return ov5647_write(sd, OV5647_SW_STANDBY, rdval);
- }
-
--static int __sensor_init(struct v4l2_subdev *sd)
--{
-- int ret;
-- u8 resetval, rdval;
-- struct i2c_client *client = v4l2_get_subdevdata(sd);
--
-- ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
-- if (ret < 0)
-- return ret;
--
-- ret = ov5647_write_array(sd, ov5647_640x480,
-- ARRAY_SIZE(ov5647_640x480));
-- if (ret < 0) {
-- dev_err(&client->dev, "write sensor default regs error\n");
-- return ret;
-- }
--
-- ret = ov5647_set_virtual_channel(sd, 0);
-- if (ret < 0)
-- return ret;
--
-- ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval);
-- if (ret < 0)
-- return ret;
--
-- if (!(resetval & 0x01)) {
-- dev_err(&client->dev, "Device was in SW standby");
-- ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01);
-- if (ret < 0)
-- return ret;
-- }
--
-- /*
-- * stream off to make the clock lane into LP-11 state.
-- */
-- return ov5647_stream_off(sd);
--}
--
- static int ov5647_sensor_power(struct v4l2_subdev *sd, int on)
- {
- int ret = 0;
-@@ -408,7 +443,7 @@ static int ov5647_sensor_power(struct v4
- }
-
- ret = ov5647_write_array(sd, sensor_oe_enable_regs,
-- ARRAY_SIZE(sensor_oe_enable_regs));
-+ ARRAY_SIZE(sensor_oe_enable_regs));
- if (ret < 0) {
- clk_disable_unprepare(ov5647->xclk);
- dev_err(&client->dev,
-@@ -416,7 +451,10 @@ static int ov5647_sensor_power(struct v4
- goto out;
- }
-
-- ret = __sensor_init(sd);
-+ /*
-+ * Ensure streaming off to make clock lane go into LP-11 state.
-+ */
-+ ret = ov5647_stream_off(sd);
- if (ret < 0) {
- clk_disable_unprepare(ov5647->xclk);
- dev_err(&client->dev,
-@@ -427,7 +465,7 @@ static int ov5647_sensor_power(struct v4
- dev_dbg(&client->dev, "OV5647 power off\n");
-
- ret = ov5647_write_array(sd, sensor_oe_disable_regs,
-- ARRAY_SIZE(sensor_oe_disable_regs));
-+ ARRAY_SIZE(sensor_oe_disable_regs));
-
- if (ret < 0)
- dev_dbg(&client->dev, "disable oe failed\n");
-@@ -489,10 +527,19 @@ static const struct v4l2_subdev_core_ops
-
- static int ov5647_s_stream(struct v4l2_subdev *sd, int enable)
- {
-+ struct ov5647 *state = to_state(sd);
-+ int ret = 0;
-+
-+ mutex_lock(&state->lock);
-+
- if (enable)
-- return ov5647_stream_on(sd);
-+ ret = ov5647_stream_on(sd);
- else
-- return ov5647_stream_off(sd);
-+ ret = ov5647_stream_off(sd);
-+
-+ mutex_unlock(&state->lock);
-+
-+ return ret;
- }
-
- static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = {
-@@ -503,38 +550,127 @@ static int ov5647_enum_mbus_code(struct
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_mbus_code_enum *code)
- {
-- if (code->index > 0)
-+ if (code->index == 0 && ARRAY_SIZE(supported_modes_8bit))
-+ code->code = MEDIA_BUS_FMT_SBGGR8_1X8;
-+ else if (code->index == 0 && ARRAY_SIZE(supported_modes_8bit) == 0 &&
-+ ARRAY_SIZE(supported_modes_10bit))
-+ code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
-+ else if (code->index == 1 && ARRAY_SIZE(supported_modes_8bit) &&
-+ ARRAY_SIZE(supported_modes_10bit))
-+ code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
-+ else
- return -EINVAL;
-
-- code->code = MEDIA_BUS_FMT_SBGGR8_1X8;
-+ return 0;
-+}
-+
-+static int ov5647_enum_frame_size(struct v4l2_subdev *sd,
-+ struct v4l2_subdev_pad_config *cfg,
-+ struct v4l2_subdev_frame_size_enum *fse)
-+{
-+ struct ov5647_mode *mode = NULL;
-+
-+ if (fse->code == MEDIA_BUS_FMT_SBGGR8_1X8) {
-+ if (fse->index >= ARRAY_SIZE(supported_modes_8bit))
-+ return -EINVAL;
-+ mode = &supported_modes_8bit[fse->index];
-+ } else if (fse->code == MEDIA_BUS_FMT_SBGGR10_1X10) {
-+ if (fse->index >= ARRAY_SIZE(supported_modes_10bit))
-+ return -EINVAL;
-+ mode = &supported_modes_10bit[fse->index];
-+ } else {
-+ return -EINVAL;
-+ }
-+
-+ fse->min_width = mode->format.width;
-+ fse->max_width = fse->min_width;
-+ fse->min_height = mode->format.height;
-+ fse->max_height = fse->min_height;
-+
-+ return 0;
-+}
-+
-+static int ov5647_set_fmt(struct v4l2_subdev *sd,
-+ struct v4l2_subdev_pad_config *cfg,
-+ struct v4l2_subdev_format *format)
-+{
-+ struct v4l2_mbus_framefmt *fmt = &format->format;
-+ struct ov5647 *state = to_state(sd);
-+ struct v4l2_mbus_framefmt *framefmt;
-+ const struct ov5647_mode *mode_8bit, *mode_10bit, *mode = NULL;
-+
-+ if (format->pad != 0)
-+ return -EINVAL;
-+
-+ mutex_lock(&state->lock);
-+
-+ /*
-+ * Try to respect any given pixel format, otherwise try for a 10-bit
-+ * mode.
-+ */
-+ mode_8bit = v4l2_find_nearest_size(supported_modes_8bit,
-+ ARRAY_SIZE(supported_modes_8bit),
-+ format.width, format.height,
-+ format->format.width,
-+ format->format.height);
-+ mode_10bit = v4l2_find_nearest_size(supported_modes_10bit,
-+ ARRAY_SIZE(supported_modes_10bit),
-+ format.width, format.height,
-+ format->format.width,
-+ format->format.height);
-+ if (format->format.code == MEDIA_BUS_FMT_SBGGR8_1X8 && mode_8bit)
-+ mode = mode_8bit;
-+ else if (format->format.code == MEDIA_BUS_FMT_SBGGR10_1X10 &&
-+ mode_10bit)
-+ mode = mode_10bit;
-+ else if (mode_10bit)
-+ mode = mode_10bit;
-+ else
-+ mode = mode_8bit;
-+
-+ if (!mode)
-+ return -EINVAL;
-+
-+ *fmt = mode->format;
-+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
-+ framefmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
-+ *framefmt = format->format;
-+ } else {
-+ state->mode = mode;
-+ }
-+
-+ mutex_unlock(&state->lock);
-
- return 0;
- }
-
--static int ov5647_set_get_fmt(struct v4l2_subdev *sd,
-- struct v4l2_subdev_pad_config *cfg,
-- struct v4l2_subdev_format *format)
-+static int ov5647_get_fmt(struct v4l2_subdev *sd,
-+ struct v4l2_subdev_pad_config *cfg,
-+ struct v4l2_subdev_format *format)
- {
- struct v4l2_mbus_framefmt *fmt = &format->format;
-+ struct ov5647 *state = to_state(sd);
-
- if (format->pad != 0)
- return -EINVAL;
-
-- /* Only one format is supported, so return that */
-- memset(fmt, 0, sizeof(*fmt));
-- fmt->code = MEDIA_BUS_FMT_SBGGR8_1X8;
-- fmt->colorspace = V4L2_COLORSPACE_SRGB;
-- fmt->field = V4L2_FIELD_NONE;
-- fmt->width = 640;
-- fmt->height = 480;
-+ mutex_lock(&state->lock);
-+
-+ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
-+ *fmt = *v4l2_subdev_get_try_format(sd, cfg, format->pad);
-+ else
-+ *fmt = state->mode->format;
-+
-+ mutex_unlock(&state->lock);
-
- return 0;
- }
-
- static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = {
- .enum_mbus_code = ov5647_enum_mbus_code,
-- .set_fmt = ov5647_set_get_fmt,
-- .get_fmt = ov5647_set_get_fmt,
-+ .set_fmt = ov5647_set_fmt,
-+ .get_fmt = ov5647_get_fmt,
-+ .enum_frame_size = ov5647_enum_frame_size,
- };
-
- static const struct v4l2_subdev_ops ov5647_subdev_ops = {
-@@ -580,18 +716,15 @@ static int ov5647_open(struct v4l2_subde
- v4l2_subdev_get_try_format(sd, fh->pad, 0);
- struct v4l2_rect *crop =
- v4l2_subdev_get_try_crop(sd, fh->pad, 0);
-+ struct ov5647 *state = to_state(sd);
-
- crop->left = OV5647_COLUMN_START_DEF;
- crop->top = OV5647_ROW_START_DEF;
- crop->width = OV5647_WINDOW_WIDTH_DEF;
- crop->height = OV5647_WINDOW_HEIGHT_DEF;
-
-- format->code = MEDIA_BUS_FMT_SBGGR8_1X8;
--
-- format->width = OV5647_WINDOW_WIDTH_DEF;
-- format->height = OV5647_WINDOW_HEIGHT_DEF;
-- format->field = V4L2_FIELD_NONE;
-- format->colorspace = V4L2_COLORSPACE_SRGB;
-+ /* Set the default format to the same as the sensor. */
-+ *format = state->mode->format;
-
- return 0;
- }
-@@ -660,6 +793,9 @@ static int ov5647_probe(struct i2c_clien
-
- mutex_init(&sensor->lock);
-
-+ /* Set the default mode before we init the subdev */
-+ sensor->mode = OV5647_DEFAULT_MODE;
-+
- sd = &sensor->sd;
- v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops);
- sensor->sd.internal_ops = &ov5647_subdev_internal_ops;
+++ /dev/null
-From d9d333b717f220439868edd533994f2709b3a95f Mon Sep 17 00:00:00 2001
-From: David Plowman <david.plowman@raspberrypi.com>
-Date: Wed, 29 Jan 2020 15:31:23 +0000
-Subject: [PATCH] media: ov5647: Add V4L2 controls for analogue gain,
- exposure and AWB
-
-Added basic v4l2_ctrl_handler infrastructure (there was none
-previously).
-
-Added controls to let AWB/AEC/AGC run in the sensor's auto mode or
-manually. Also controls to set exposure (in lines) and analogue gain
-(as a register code) from user code.
-
-Also delete registers (just the one) from the VGA mode register set
-that are now controlled by the new V4L2 controls.
-
-Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
-Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
----
- drivers/media/i2c/ov5647.c | 175 ++++++++++++++++++++++++++++++++++++-
- 1 file changed, 174 insertions(+), 1 deletion(-)
-
---- a/drivers/media/i2c/ov5647.c
-+++ b/drivers/media/i2c/ov5647.c
-@@ -29,11 +29,13 @@
- #include <linux/of_graph.h>
- #include <linux/slab.h>
- #include <linux/videodev2.h>
-+#include <media/v4l2-ctrls.h>
- #include <media/v4l2-device.h>
- #include <media/v4l2-fwnode.h>
- #include <media/v4l2-image-sizes.h>
- #include <media/v4l2-mediabus.h>
-
-+
- #define SENSOR_NAME "ov5647"
-
- /*
-@@ -53,9 +55,16 @@
- #define OV5647_REG_CHIPID_H 0x300A
- #define OV5647_REG_CHIPID_L 0x300B
- #define OV5640_REG_PAD_OUT 0x300D
-+#define OV5647_REG_EXP_HI 0x3500
-+#define OV5647_REG_EXP_MID 0x3501
-+#define OV5647_REG_EXP_LO 0x3502
-+#define OV5647_REG_AEC_AGC 0x3503
-+#define OV5647_REG_GAIN_HI 0x350A
-+#define OV5647_REG_GAIN_LO 0x350B
- #define OV5647_REG_FRAME_OFF_NUMBER 0x4202
- #define OV5647_REG_MIPI_CTRL00 0x4800
- #define OV5647_REG_MIPI_CTRL14 0x4814
-+#define OV5647_REG_AWB 0x5001
-
- #define REG_TERM 0xfffe
- #define VAL_TERM 0xfe
-@@ -101,6 +110,7 @@ struct ov5647 {
- struct clk *xclk;
- struct gpio_desc *pwdn;
- unsigned int flags;
-+ struct v4l2_ctrl_handler ctrls;
- };
-
- static inline struct ov5647 *to_state(struct v4l2_subdev *sd)
-@@ -135,7 +145,6 @@ static struct regval_list ov5647_640x480
- {0x3612, 0x59},
- {0x3618, 0x00},
- {0x5000, 0x06},
-- {0x5001, 0x01},
- {0x5002, 0x41},
- {0x5003, 0x08},
- {0x5a00, 0x08},
-@@ -372,6 +381,11 @@ static int ov5647_stream_on(struct v4l2_
- return ret;
- }
-
-+ /* Apply customized values from user when stream starts */
-+ ret = __v4l2_ctrl_handler_setup(sd->ctrl_handler);
-+ if (ret)
-+ return ret;
-+
- if (ov5647->flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)
- val |= MIPI_CTRL00_CLOCK_LANE_GATE |
- MIPI_CTRL00_LINE_SYNC_ENABLE;
-@@ -753,6 +767,120 @@ static int ov5647_parse_dt(struct device
- return ret;
- }
-
-+static int ov5647_s_auto_white_balance(struct v4l2_subdev *sd, u32 val)
-+{
-+ /* non-zero turns on AWB */
-+ return ov5647_write(sd, OV5647_REG_AWB, val ? 1 : 0);
-+}
-+
-+static int ov5647_s_autogain(struct v4l2_subdev *sd, u32 val)
-+{
-+ int ret;
-+ u8 reg;
-+
-+ /* non-zero turns on AGC by clearing bit 1 */
-+ ret = ov5647_read(sd, OV5647_REG_AEC_AGC, ®);
-+ if (ret == 0)
-+ ret = ov5647_write(sd, OV5647_REG_AEC_AGC,
-+ val ? reg & ~2 : reg | 2);
-+
-+ return ret;
-+}
-+
-+static int ov5647_s_exposure_auto(struct v4l2_subdev *sd, u32 val)
-+{
-+ int ret;
-+ u8 reg;
-+
-+ /* Everything except V4L2_EXPOSURE_MANUAL turns on AEC by
-+ * clearing bit 0
-+ */
-+ ret = ov5647_read(sd, OV5647_REG_AEC_AGC, ®);
-+ if (ret == 0)
-+ ret = ov5647_write(sd, OV5647_REG_AEC_AGC,
-+ val == V4L2_EXPOSURE_MANUAL ?
-+ reg | 1 : reg & ~1);
-+
-+ return ret;
-+}
-+
-+static int ov5647_s_analogue_gain(struct v4l2_subdev *sd, u32 val)
-+{
-+ int ret;
-+
-+ /* 10 bits of gain, 2 in the high register */
-+ ret = ov5647_write(sd, OV5647_REG_GAIN_HI, (val >> 8) & 3);
-+ if (ret == 0)
-+ ret = ov5647_write(sd, OV5647_REG_GAIN_LO, val & 0xff);
-+
-+ return ret;
-+}
-+
-+static int ov5647_s_exposure(struct v4l2_subdev *sd, u32 val)
-+{
-+ int ret;
-+
-+ /* Sensor has 20 bits, but the bottom 4 bits are fractions of a line
-+ * which we leave as zero (and don't receive in "val").
-+ */
-+ ret = ov5647_write(sd, OV5647_REG_EXP_HI, (val >> 12) & 0xf);
-+ if (ret == 0)
-+ ov5647_write(sd, OV5647_REG_EXP_MID, (val >> 4) & 0xff);
-+ if (ret == 0)
-+ ov5647_write(sd, OV5647_REG_EXP_LO, (val & 0xf) << 4);
-+
-+ return ret;
-+}
-+
-+static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl)
-+{
-+ struct ov5647 *state = container_of(ctrl->handler,
-+ struct ov5647, ctrls);
-+ struct v4l2_subdev *sd = &state->sd;
-+ struct i2c_client *client = v4l2_get_subdevdata(sd);
-+ int ret = 0;
-+
-+ /* v4l2_ctrl_lock() locks our own mutex */
-+
-+ /*
-+ * If the device is not powered up by the host driver do
-+ * not apply any controls to H/W at this time. Instead
-+ * the controls will be restored right after power-up.
-+ */
-+ if (state->power_count == 0)
-+ return 0;
-+
-+ switch (ctrl->id) {
-+ case V4L2_CID_AUTO_WHITE_BALANCE:
-+ ret = ov5647_s_auto_white_balance(sd, ctrl->val);
-+ break;
-+ case V4L2_CID_AUTOGAIN:
-+ ret = ov5647_s_autogain(sd, ctrl->val);
-+ break;
-+ case V4L2_CID_EXPOSURE_AUTO:
-+ ret = ov5647_s_exposure_auto(sd, ctrl->val);
-+ break;
-+ case V4L2_CID_ANALOGUE_GAIN:
-+ ret = ov5647_s_analogue_gain(sd, ctrl->val);
-+ break;
-+ case V4L2_CID_EXPOSURE:
-+ ret = ov5647_s_exposure(sd, ctrl->val);
-+ break;
-+ default:
-+ dev_info(&client->dev,
-+ "ctrl(id:0x%x,val:0x%x) is not handled\n",
-+ ctrl->id, ctrl->val);
-+ ret = -EINVAL;
-+ break;
-+ }
-+
-+ return ret;
-+}
-+
-+static const struct v4l2_ctrl_ops ov5647_ctrl_ops = {
-+ .s_ctrl = ov5647_s_ctrl,
-+};
-+
- static int ov5647_probe(struct i2c_client *client)
- {
- struct device *dev = &client->dev;
-@@ -761,6 +889,7 @@ static int ov5647_probe(struct i2c_clien
- struct v4l2_subdev *sd;
- struct device_node *np = client->dev.of_node;
- u32 xclk_freq;
-+ struct v4l2_ctrl *ctrl;
-
- sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
- if (!sensor)
-@@ -793,6 +922,48 @@ static int ov5647_probe(struct i2c_clien
-
- mutex_init(&sensor->lock);
-
-+ /* Initialise controls. */
-+ v4l2_ctrl_handler_init(&sensor->ctrls, 3);
-+ v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
-+ V4L2_CID_AUTOGAIN,
-+ 0, /* min */
-+ 1, /* max */
-+ 1, /* step */
-+ 1); /* default */
-+ v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
-+ V4L2_CID_AUTO_WHITE_BALANCE,
-+ 0, /* min */
-+ 1, /* max */
-+ 1, /* step */
-+ 1); /* default */
-+ v4l2_ctrl_new_std_menu(&sensor->ctrls, &ov5647_ctrl_ops,
-+ V4L2_CID_EXPOSURE_AUTO,
-+ V4L2_EXPOSURE_MANUAL, /* max */
-+ 0, /* skip_mask */
-+ V4L2_EXPOSURE_AUTO); /* default */
-+ ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
-+ V4L2_CID_EXPOSURE,
-+ 4, /* min lines */
-+ 65535, /* max lines (4+8+4 bits)*/
-+ 1, /* step */
-+ 1000); /* default number of lines */
-+ ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
-+ ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
-+ V4L2_CID_ANALOGUE_GAIN,
-+ 16, /* min, 16 = 1.0x */
-+ 1023, /* max (10 bits) */
-+ 1, /* step */
-+ 32); /* default, 32 = 2.0x */
-+ ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
-+
-+ if (sensor->ctrls.error) {
-+ ret = sensor->ctrls.error;
-+ dev_err(&client->dev, "%s control init failed (%d)\n",
-+ __func__, ret);
-+ goto error;
-+ }
-+ sensor->sd.ctrl_handler = &sensor->ctrls;
-+
- /* Set the default mode before we init the subdev */
- sensor->mode = OV5647_DEFAULT_MODE;
-
-@@ -828,6 +999,7 @@ static int ov5647_probe(struct i2c_clien
- error:
- media_entity_cleanup(&sd->entity);
- mutex_remove:
-+ v4l2_ctrl_handler_free(&sensor->ctrls);
- mutex_destroy(&sensor->lock);
- return ret;
- }
-@@ -839,6 +1011,7 @@ static int ov5647_remove(struct i2c_clie
-
- v4l2_async_unregister_subdev(&ov5647->sd);
- media_entity_cleanup(&ov5647->sd.entity);
-+ v4l2_ctrl_handler_free(&ov5647->ctrls);
- v4l2_device_unregister_subdev(sd);
- mutex_destroy(&ov5647->lock);
-
--- /dev/null
+From 28c0004a54ce9b2c5862b38408952583b07458f9 Mon Sep 17 00:00:00 2001
+From: David Plowman <david.plowman@raspberrypi.com>
+Date: Wed, 29 Jan 2020 15:31:28 +0000
+Subject: [PATCH] media: ov5647: Add extra 10-bit sensor modes.
+
+The 8-bit VGA mode remains, we add the following 10-bit modes:
+
+Mode 0: 2592x1944 full resolution.
+
+Mode 1: 1920x1080 full resolution, but centre-cropped.
+(This mode achieves 30fps, mode 0 does not.)
+
+Mode 2: 1296x972 full field-of-view 2x2 binned mode.
+
+Mode 3: VGA full field of view mode.
+
+Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 463 ++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 452 insertions(+), 11 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -111,6 +111,7 @@ struct ov5647 {
+ struct gpio_desc *pwdn;
+ unsigned int flags;
+ struct v4l2_ctrl_handler ctrls;
++ bool write_mode_regs;
+ };
+
+ static inline struct ov5647 *to_state(struct v4l2_subdev *sd)
+@@ -130,7 +131,7 @@ static struct regval_list sensor_oe_enab
+ {0x3002, 0xe4},
+ };
+
+-static struct regval_list ov5647_640x480[] = {
++static struct regval_list ov5647_640x480_8bit[] = {
+ {0x0100, 0x00},
+ {0x0103, 0x01},
+ {0x3034, 0x08},
+@@ -220,9 +221,378 @@ static struct regval_list ov5647_640x480
+ {0x0100, 0x01},
+ };
+
++static struct regval_list ov5647_2592x1944_10bit[] = {
++ {0x0100, 0x00},
++ {0x0103, 0x01},
++ {0x3034, 0x1a},
++ {0x3035, 0x21},
++ {0x3036, 0x69},
++ {0x303c, 0x11},
++ {0x3106, 0xf5},
++ {0x3821, 0x06},
++ {0x3820, 0x00},
++ {0x3827, 0xec},
++ {0x370c, 0x03},
++ {0x3612, 0x5b},
++ {0x3618, 0x04},
++ {0x5000, 0x06},
++ {0x5002, 0x41},
++ {0x5003, 0x08},
++ {0x5a00, 0x08},
++ {0x3000, 0x00},
++ {0x3001, 0x00},
++ {0x3002, 0x00},
++ {0x3016, 0x08},
++ {0x3017, 0xe0},
++ {0x3018, 0x44},
++ {0x301c, 0xf8},
++ {0x301d, 0xf0},
++ {0x3a18, 0x00},
++ {0x3a19, 0xf8},
++ {0x3c01, 0x80},
++ {0x3b07, 0x0c},
++ {0x380c, 0x0b},
++ {0x380d, 0x1c},
++ {0x380e, 0x07},
++ {0x380f, 0xb0},
++ {0x3814, 0x11},
++ {0x3815, 0x11},
++ {0x3708, 0x64},
++ {0x3709, 0x12},
++ {0x3808, 0x0a},
++ {0x3809, 0x20},
++ {0x380a, 0x07},
++ {0x380b, 0x98},
++ {0x3800, 0x00},
++ {0x3801, 0x00},
++ {0x3802, 0x00},
++ {0x3803, 0x00},
++ {0x3804, 0x0a},
++ {0x3805, 0x3f},
++ {0x3806, 0x07},
++ {0x3807, 0xa3},
++ {0x3811, 0x10},
++ {0x3813, 0x06},
++ {0x3630, 0x2e},
++ {0x3632, 0xe2},
++ {0x3633, 0x23},
++ {0x3634, 0x44},
++ {0x3636, 0x06},
++ {0x3620, 0x64},
++ {0x3621, 0xe0},
++ {0x3600, 0x37},
++ {0x3704, 0xa0},
++ {0x3703, 0x5a},
++ {0x3715, 0x78},
++ {0x3717, 0x01},
++ {0x3731, 0x02},
++ {0x370b, 0x60},
++ {0x3705, 0x1a},
++ {0x3f05, 0x02},
++ {0x3f06, 0x10},
++ {0x3f01, 0x0a},
++ {0x3a08, 0x01},
++ {0x3a09, 0x28},
++ {0x3a0a, 0x00},
++ {0x3a0b, 0xf6},
++ {0x3a0d, 0x08},
++ {0x3a0e, 0x06},
++ {0x3a0f, 0x58},
++ {0x3a10, 0x50},
++ {0x3a1b, 0x58},
++ {0x3a1e, 0x50},
++ {0x3a11, 0x60},
++ {0x3a1f, 0x28},
++ {0x4001, 0x02},
++ {0x4004, 0x04},
++ {0x4000, 0x09},
++ {0x4837, 0x19},
++ {0x4800, 0x24},
++ {0x3503, 0x03},
++ {0x0100, 0x01},
++};
++
++static struct regval_list ov5647_1080p30_10bit[] = {
++ {0x0100, 0x00},
++ {0x0103, 0x01},
++ {0x3034, 0x1a},
++ {0x3035, 0x21},
++ {0x3036, 0x62},
++ {0x303c, 0x11},
++ {0x3106, 0xf5},
++ {0x3821, 0x06},
++ {0x3820, 0x00},
++ {0x3827, 0xec},
++ {0x370c, 0x03},
++ {0x3612, 0x5b},
++ {0x3618, 0x04},
++ {0x5000, 0x06},
++ {0x5002, 0x41},
++ {0x5003, 0x08},
++ {0x5a00, 0x08},
++ {0x3000, 0x00},
++ {0x3001, 0x00},
++ {0x3002, 0x00},
++ {0x3016, 0x08},
++ {0x3017, 0xe0},
++ {0x3018, 0x44},
++ {0x301c, 0xf8},
++ {0x301d, 0xf0},
++ {0x3a18, 0x00},
++ {0x3a19, 0xf8},
++ {0x3c01, 0x80},
++ {0x3b07, 0x0c},
++ {0x380c, 0x09},
++ {0x380d, 0x70},
++ {0x380e, 0x04},
++ {0x380f, 0x50},
++ {0x3814, 0x11},
++ {0x3815, 0x11},
++ {0x3708, 0x64},
++ {0x3709, 0x12},
++ {0x3808, 0x07},
++ {0x3809, 0x80},
++ {0x380a, 0x04},
++ {0x380b, 0x38},
++ {0x3800, 0x01},
++ {0x3801, 0x5c},
++ {0x3802, 0x01},
++ {0x3803, 0xb2},
++ {0x3804, 0x08},
++ {0x3805, 0xe3},
++ {0x3806, 0x05},
++ {0x3807, 0xf1},
++ {0x3811, 0x04},
++ {0x3813, 0x02},
++ {0x3630, 0x2e},
++ {0x3632, 0xe2},
++ {0x3633, 0x23},
++ {0x3634, 0x44},
++ {0x3636, 0x06},
++ {0x3620, 0x64},
++ {0x3621, 0xe0},
++ {0x3600, 0x37},
++ {0x3704, 0xa0},
++ {0x3703, 0x5a},
++ {0x3715, 0x78},
++ {0x3717, 0x01},
++ {0x3731, 0x02},
++ {0x370b, 0x60},
++ {0x3705, 0x1a},
++ {0x3f05, 0x02},
++ {0x3f06, 0x10},
++ {0x3f01, 0x0a},
++ {0x3a08, 0x01},
++ {0x3a09, 0x4b},
++ {0x3a0a, 0x01},
++ {0x3a0b, 0x13},
++ {0x3a0d, 0x04},
++ {0x3a0e, 0x03},
++ {0x3a0f, 0x58},
++ {0x3a10, 0x50},
++ {0x3a1b, 0x58},
++ {0x3a1e, 0x50},
++ {0x3a11, 0x60},
++ {0x3a1f, 0x28},
++ {0x4001, 0x02},
++ {0x4004, 0x04},
++ {0x4000, 0x09},
++ {0x4837, 0x19},
++ {0x4800, 0x34},
++ {0x3503, 0x03},
++ {0x0100, 0x01},
++};
++
++static struct regval_list ov5647_2x2binned_10bit[] = {
++ {0x0100, 0x00},
++ {0x0103, 0x01},
++ {0x3034, 0x1A},
++ {0x3035, 0x21},
++ {0x3036, 0x62},
++ {0x303C, 0x11},
++ {0x3106, 0xF5},
++ {0x3827, 0xEC},
++ {0x370C, 0x03},
++ {0x3612, 0x59},
++ {0x3618, 0x00},
++ {0x5000, 0x06},
++ {0x5002, 0x41},
++ {0x5003, 0x08},
++ {0x5A00, 0x08},
++ {0x3000, 0x00},
++ {0x3001, 0x00},
++ {0x3002, 0x00},
++ {0x3016, 0x08},
++ {0x3017, 0xE0},
++ {0x3018, 0x44},
++ {0x301C, 0xF8},
++ {0x301D, 0xF0},
++ {0x3A18, 0x00},
++ {0x3A19, 0xF8},
++ {0x3C01, 0x80},
++ {0x3B07, 0x0C},
++ {0x3800, 0x00},
++ {0x3801, 0x00},
++ {0x3802, 0x00},
++ {0x3803, 0x00},
++ {0x3804, 0x0A},
++ {0x3805, 0x3F},
++ {0x3806, 0x07},
++ {0x3807, 0xA3},
++ {0x3808, 0x05},
++ {0x3809, 0x10},
++ {0x380A, 0x03},
++ {0x380B, 0xCC},
++ {0x380C, 0x07},
++ {0x380D, 0x68},
++ {0x3811, 0x0c},
++ {0x3813, 0x06},
++ {0x3814, 0x31},
++ {0x3815, 0x31},
++ {0x3630, 0x2E},
++ {0x3632, 0xE2},
++ {0x3633, 0x23},
++ {0x3634, 0x44},
++ {0x3636, 0x06},
++ {0x3620, 0x64},
++ {0x3621, 0xE0},
++ {0x3600, 0x37},
++ {0x3704, 0xA0},
++ {0x3703, 0x5A},
++ {0x3715, 0x78},
++ {0x3717, 0x01},
++ {0x3731, 0x02},
++ {0x370B, 0x60},
++ {0x3705, 0x1A},
++ {0x3F05, 0x02},
++ {0x3F06, 0x10},
++ {0x3F01, 0x0A},
++ {0x3A08, 0x01},
++ {0x3A09, 0x28},
++ {0x3A0A, 0x00},
++ {0x3A0B, 0xF6},
++ {0x3A0D, 0x08},
++ {0x3A0E, 0x06},
++ {0x3A0F, 0x58},
++ {0x3A10, 0x50},
++ {0x3A1B, 0x58},
++ {0x3A1E, 0x50},
++ {0x3A11, 0x60},
++ {0x3A1F, 0x28},
++ {0x4001, 0x02},
++ {0x4004, 0x04},
++ {0x4000, 0x09},
++ {0x4837, 0x16},
++ {0x4800, 0x24},
++ {0x3503, 0x03},
++ {0x3820, 0x41},
++ {0x3821, 0x07},
++ {0x380E, 0x05},
++ {0x380F, 0x9B},
++ {0x350A, 0x00},
++ {0x350B, 0x10},
++ {0x3500, 0x00},
++ {0x3501, 0x1A},
++ {0x3502, 0xF0},
++ {0x3212, 0xA0},
++ {0x0100, 0x01},
++};
++
++static struct regval_list ov5647_640x480_10bit[] = {
++ {0x0100, 0x00},
++ {0x0103, 0x01},
++ {0x3035, 0x11},
++ {0x3036, 0x46},
++ {0x303c, 0x11},
++ {0x3821, 0x07},
++ {0x3820, 0x41},
++ {0x370c, 0x03},
++ {0x3612, 0x59},
++ {0x3618, 0x00},
++ {0x5000, 0x06},
++ {0x5003, 0x08},
++ {0x5a00, 0x08},
++ {0x3000, 0xff},
++ {0x3001, 0xff},
++ {0x3002, 0xff},
++ {0x301d, 0xf0},
++ {0x3a18, 0x00},
++ {0x3a19, 0xf8},
++ {0x3c01, 0x80},
++ {0x3b07, 0x0c},
++ {0x380c, 0x07},
++ {0x380d, 0x3c},
++ {0x380e, 0x01},
++ {0x380f, 0xf8},
++ {0x3814, 0x35},
++ {0x3815, 0x35},
++ {0x3708, 0x64},
++ {0x3709, 0x52},
++ {0x3808, 0x02},
++ {0x3809, 0x80},
++ {0x380a, 0x01},
++ {0x380b, 0xe0},
++ {0x3800, 0x00},
++ {0x3801, 0x10},
++ {0x3802, 0x00},
++ {0x3803, 0x00},
++ {0x3804, 0x0a},
++ {0x3805, 0x2f},
++ {0x3806, 0x07},
++ {0x3807, 0x9f},
++ {0x3630, 0x2e},
++ {0x3632, 0xe2},
++ {0x3633, 0x23},
++ {0x3634, 0x44},
++ {0x3620, 0x64},
++ {0x3621, 0xe0},
++ {0x3600, 0x37},
++ {0x3704, 0xa0},
++ {0x3703, 0x5a},
++ {0x3715, 0x78},
++ {0x3717, 0x01},
++ {0x3731, 0x02},
++ {0x370b, 0x60},
++ {0x3705, 0x1a},
++ {0x3f05, 0x02},
++ {0x3f06, 0x10},
++ {0x3f01, 0x0a},
++ {0x3a08, 0x01},
++ {0x3a09, 0x2e},
++ {0x3a0a, 0x00},
++ {0x3a0b, 0xfb},
++ {0x3a0d, 0x02},
++ {0x3a0e, 0x01},
++ {0x3a0f, 0x58},
++ {0x3a10, 0x50},
++ {0x3a1b, 0x58},
++ {0x3a1e, 0x50},
++ {0x3a11, 0x60},
++ {0x3a1f, 0x28},
++ {0x4001, 0x02},
++ {0x4004, 0x02},
++ {0x4000, 0x09},
++ {0x3000, 0x00},
++ {0x3001, 0x00},
++ {0x3002, 0x00},
++ {0x3017, 0xe0},
++ {0x301c, 0xfc},
++ {0x3636, 0x06},
++ {0x3016, 0x08},
++ {0x3827, 0xec},
++ {0x3018, 0x44},
++ {0x3035, 0x21},
++ {0x3106, 0xf5},
++ {0x3034, 0x1a},
++ {0x301c, 0xf8},
++ {0x4800, 0x34},
++ {0x3503, 0x03},
++ {0x0100, 0x01},
++};
++
+ static struct ov5647_mode supported_modes_8bit[] = {
+ /*
+- * Original 8-bit VGA mode
++ * MODE 0: Original 8-bit VGA mode.
+ * Uncentred crop (top left quarter) from 2x2 binned 1296x972 image.
+ */
+ {
+@@ -233,14 +603,70 @@ static struct ov5647_mode supported_mode
+ .width = 640,
+ .height = 480
+ },
+- ov5647_640x480,
+- ARRAY_SIZE(ov5647_640x480)
++ ov5647_640x480_8bit,
++ ARRAY_SIZE(ov5647_640x480_8bit)
+ },
+- /* more modes below here... */
+ };
+
+ static struct ov5647_mode supported_modes_10bit[] = {
+- /* no 10-bit modes yet */
++ /*
++ * MODE 0: 2592x1944 full resolution full FOV 10-bit mode.
++ */
++ {
++ {
++ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .field = V4L2_FIELD_NONE,
++ .width = 2592,
++ .height = 1944
++ },
++ ov5647_2592x1944_10bit,
++ ARRAY_SIZE(ov5647_2592x1944_10bit)
++ },
++ /*
++ * MODE 1: 1080p30 10-bit mode.
++ * Full resolution centre-cropped down to 1080p.
++ */
++ {
++ {
++ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .field = V4L2_FIELD_NONE,
++ .width = 1920,
++ .height = 1080
++ },
++ ov5647_1080p30_10bit,
++ ARRAY_SIZE(ov5647_1080p30_10bit)
++ },
++ /*
++ * MODE 2: 2x2 binned full FOV 10-bit mode.
++ */
++ {
++ {
++ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .field = V4L2_FIELD_NONE,
++ .width = 1296,
++ .height = 972
++ },
++ ov5647_2x2binned_10bit,
++ ARRAY_SIZE(ov5647_2x2binned_10bit)
++ },
++ /*
++ * MODE 3: 10-bit VGA full FOV mode 60fps.
++ * 2x2 binned and subsampled down to VGA.
++ */
++ {
++ {
++ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .field = V4L2_FIELD_NONE,
++ .width = 640,
++ .height = 480
++ },
++ ov5647_640x480_10bit,
++ ARRAY_SIZE(ov5647_640x480_10bit)
++ },
+ };
+
+ /* Use original 8-bit VGA mode as default. */
+@@ -343,11 +769,14 @@ static int __sensor_init(struct v4l2_sub
+ if (ret < 0)
+ return ret;
+
+- ret = ov5647_write_array(sd, state->mode->reg_list,
+- state->mode->num_regs);
+- if (ret < 0) {
+- dev_err(&client->dev, "write sensor default regs error\n");
+- return ret;
++ if (state->write_mode_regs) {
++ ret = ov5647_write_array(sd, state->mode->reg_list,
++ state->mode->num_regs);
++ if (ret < 0) {
++ dev_err(&client->dev, "write sensor default regs error\n");
++ return ret;
++ }
++ state->write_mode_regs = false;
+ }
+
+ ret = ov5647_set_virtual_channel(sd, 0);
+@@ -475,6 +904,9 @@ static int ov5647_sensor_power(struct v4
+ "Camera not available, check Power\n");
+ goto out;
+ }
++
++ /* Write out the register set over I2C on stream-on. */
++ ov5647->write_mode_regs = true;
+ } else if (!on && ov5647->power_count == 1) {
+ dev_dbg(&client->dev, "OV5647 power off\n");
+
+@@ -650,6 +1082,12 @@ static int ov5647_set_fmt(struct v4l2_su
+ framefmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
+ *framefmt = format->format;
+ } else {
++ /*
++ * If we have changed modes, write the I2C register list on
++ * a stream_on().
++ */
++ if (state->mode != mode)
++ state->write_mode_regs = true;
+ state->mode = mode;
+ }
+
+@@ -967,6 +1405,9 @@ static int ov5647_probe(struct i2c_clien
+ /* Set the default mode before we init the subdev */
+ sensor->mode = OV5647_DEFAULT_MODE;
+
++ /* Write out the register set over I2C on stream-on. */
++ sensor->write_mode_regs = true;
++
+ sd = &sensor->sd;
+ v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops);
+ sensor->sd.internal_ops = &ov5647_subdev_internal_ops;
+++ /dev/null
-From 28c0004a54ce9b2c5862b38408952583b07458f9 Mon Sep 17 00:00:00 2001
-From: David Plowman <david.plowman@raspberrypi.com>
-Date: Wed, 29 Jan 2020 15:31:28 +0000
-Subject: [PATCH] media: ov5647: Add extra 10-bit sensor modes.
-
-The 8-bit VGA mode remains, we add the following 10-bit modes:
-
-Mode 0: 2592x1944 full resolution.
-
-Mode 1: 1920x1080 full resolution, but centre-cropped.
-(This mode achieves 30fps, mode 0 does not.)
-
-Mode 2: 1296x972 full field-of-view 2x2 binned mode.
-
-Mode 3: VGA full field of view mode.
-
-Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
-Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
----
- drivers/media/i2c/ov5647.c | 463 ++++++++++++++++++++++++++++++++++++-
- 1 file changed, 452 insertions(+), 11 deletions(-)
-
---- a/drivers/media/i2c/ov5647.c
-+++ b/drivers/media/i2c/ov5647.c
-@@ -111,6 +111,7 @@ struct ov5647 {
- struct gpio_desc *pwdn;
- unsigned int flags;
- struct v4l2_ctrl_handler ctrls;
-+ bool write_mode_regs;
- };
-
- static inline struct ov5647 *to_state(struct v4l2_subdev *sd)
-@@ -130,7 +131,7 @@ static struct regval_list sensor_oe_enab
- {0x3002, 0xe4},
- };
-
--static struct regval_list ov5647_640x480[] = {
-+static struct regval_list ov5647_640x480_8bit[] = {
- {0x0100, 0x00},
- {0x0103, 0x01},
- {0x3034, 0x08},
-@@ -220,9 +221,378 @@ static struct regval_list ov5647_640x480
- {0x0100, 0x01},
- };
-
-+static struct regval_list ov5647_2592x1944_10bit[] = {
-+ {0x0100, 0x00},
-+ {0x0103, 0x01},
-+ {0x3034, 0x1a},
-+ {0x3035, 0x21},
-+ {0x3036, 0x69},
-+ {0x303c, 0x11},
-+ {0x3106, 0xf5},
-+ {0x3821, 0x06},
-+ {0x3820, 0x00},
-+ {0x3827, 0xec},
-+ {0x370c, 0x03},
-+ {0x3612, 0x5b},
-+ {0x3618, 0x04},
-+ {0x5000, 0x06},
-+ {0x5002, 0x41},
-+ {0x5003, 0x08},
-+ {0x5a00, 0x08},
-+ {0x3000, 0x00},
-+ {0x3001, 0x00},
-+ {0x3002, 0x00},
-+ {0x3016, 0x08},
-+ {0x3017, 0xe0},
-+ {0x3018, 0x44},
-+ {0x301c, 0xf8},
-+ {0x301d, 0xf0},
-+ {0x3a18, 0x00},
-+ {0x3a19, 0xf8},
-+ {0x3c01, 0x80},
-+ {0x3b07, 0x0c},
-+ {0x380c, 0x0b},
-+ {0x380d, 0x1c},
-+ {0x380e, 0x07},
-+ {0x380f, 0xb0},
-+ {0x3814, 0x11},
-+ {0x3815, 0x11},
-+ {0x3708, 0x64},
-+ {0x3709, 0x12},
-+ {0x3808, 0x0a},
-+ {0x3809, 0x20},
-+ {0x380a, 0x07},
-+ {0x380b, 0x98},
-+ {0x3800, 0x00},
-+ {0x3801, 0x00},
-+ {0x3802, 0x00},
-+ {0x3803, 0x00},
-+ {0x3804, 0x0a},
-+ {0x3805, 0x3f},
-+ {0x3806, 0x07},
-+ {0x3807, 0xa3},
-+ {0x3811, 0x10},
-+ {0x3813, 0x06},
-+ {0x3630, 0x2e},
-+ {0x3632, 0xe2},
-+ {0x3633, 0x23},
-+ {0x3634, 0x44},
-+ {0x3636, 0x06},
-+ {0x3620, 0x64},
-+ {0x3621, 0xe0},
-+ {0x3600, 0x37},
-+ {0x3704, 0xa0},
-+ {0x3703, 0x5a},
-+ {0x3715, 0x78},
-+ {0x3717, 0x01},
-+ {0x3731, 0x02},
-+ {0x370b, 0x60},
-+ {0x3705, 0x1a},
-+ {0x3f05, 0x02},
-+ {0x3f06, 0x10},
-+ {0x3f01, 0x0a},
-+ {0x3a08, 0x01},
-+ {0x3a09, 0x28},
-+ {0x3a0a, 0x00},
-+ {0x3a0b, 0xf6},
-+ {0x3a0d, 0x08},
-+ {0x3a0e, 0x06},
-+ {0x3a0f, 0x58},
-+ {0x3a10, 0x50},
-+ {0x3a1b, 0x58},
-+ {0x3a1e, 0x50},
-+ {0x3a11, 0x60},
-+ {0x3a1f, 0x28},
-+ {0x4001, 0x02},
-+ {0x4004, 0x04},
-+ {0x4000, 0x09},
-+ {0x4837, 0x19},
-+ {0x4800, 0x24},
-+ {0x3503, 0x03},
-+ {0x0100, 0x01},
-+};
-+
-+static struct regval_list ov5647_1080p30_10bit[] = {
-+ {0x0100, 0x00},
-+ {0x0103, 0x01},
-+ {0x3034, 0x1a},
-+ {0x3035, 0x21},
-+ {0x3036, 0x62},
-+ {0x303c, 0x11},
-+ {0x3106, 0xf5},
-+ {0x3821, 0x06},
-+ {0x3820, 0x00},
-+ {0x3827, 0xec},
-+ {0x370c, 0x03},
-+ {0x3612, 0x5b},
-+ {0x3618, 0x04},
-+ {0x5000, 0x06},
-+ {0x5002, 0x41},
-+ {0x5003, 0x08},
-+ {0x5a00, 0x08},
-+ {0x3000, 0x00},
-+ {0x3001, 0x00},
-+ {0x3002, 0x00},
-+ {0x3016, 0x08},
-+ {0x3017, 0xe0},
-+ {0x3018, 0x44},
-+ {0x301c, 0xf8},
-+ {0x301d, 0xf0},
-+ {0x3a18, 0x00},
-+ {0x3a19, 0xf8},
-+ {0x3c01, 0x80},
-+ {0x3b07, 0x0c},
-+ {0x380c, 0x09},
-+ {0x380d, 0x70},
-+ {0x380e, 0x04},
-+ {0x380f, 0x50},
-+ {0x3814, 0x11},
-+ {0x3815, 0x11},
-+ {0x3708, 0x64},
-+ {0x3709, 0x12},
-+ {0x3808, 0x07},
-+ {0x3809, 0x80},
-+ {0x380a, 0x04},
-+ {0x380b, 0x38},
-+ {0x3800, 0x01},
-+ {0x3801, 0x5c},
-+ {0x3802, 0x01},
-+ {0x3803, 0xb2},
-+ {0x3804, 0x08},
-+ {0x3805, 0xe3},
-+ {0x3806, 0x05},
-+ {0x3807, 0xf1},
-+ {0x3811, 0x04},
-+ {0x3813, 0x02},
-+ {0x3630, 0x2e},
-+ {0x3632, 0xe2},
-+ {0x3633, 0x23},
-+ {0x3634, 0x44},
-+ {0x3636, 0x06},
-+ {0x3620, 0x64},
-+ {0x3621, 0xe0},
-+ {0x3600, 0x37},
-+ {0x3704, 0xa0},
-+ {0x3703, 0x5a},
-+ {0x3715, 0x78},
-+ {0x3717, 0x01},
-+ {0x3731, 0x02},
-+ {0x370b, 0x60},
-+ {0x3705, 0x1a},
-+ {0x3f05, 0x02},
-+ {0x3f06, 0x10},
-+ {0x3f01, 0x0a},
-+ {0x3a08, 0x01},
-+ {0x3a09, 0x4b},
-+ {0x3a0a, 0x01},
-+ {0x3a0b, 0x13},
-+ {0x3a0d, 0x04},
-+ {0x3a0e, 0x03},
-+ {0x3a0f, 0x58},
-+ {0x3a10, 0x50},
-+ {0x3a1b, 0x58},
-+ {0x3a1e, 0x50},
-+ {0x3a11, 0x60},
-+ {0x3a1f, 0x28},
-+ {0x4001, 0x02},
-+ {0x4004, 0x04},
-+ {0x4000, 0x09},
-+ {0x4837, 0x19},
-+ {0x4800, 0x34},
-+ {0x3503, 0x03},
-+ {0x0100, 0x01},
-+};
-+
-+static struct regval_list ov5647_2x2binned_10bit[] = {
-+ {0x0100, 0x00},
-+ {0x0103, 0x01},
-+ {0x3034, 0x1A},
-+ {0x3035, 0x21},
-+ {0x3036, 0x62},
-+ {0x303C, 0x11},
-+ {0x3106, 0xF5},
-+ {0x3827, 0xEC},
-+ {0x370C, 0x03},
-+ {0x3612, 0x59},
-+ {0x3618, 0x00},
-+ {0x5000, 0x06},
-+ {0x5002, 0x41},
-+ {0x5003, 0x08},
-+ {0x5A00, 0x08},
-+ {0x3000, 0x00},
-+ {0x3001, 0x00},
-+ {0x3002, 0x00},
-+ {0x3016, 0x08},
-+ {0x3017, 0xE0},
-+ {0x3018, 0x44},
-+ {0x301C, 0xF8},
-+ {0x301D, 0xF0},
-+ {0x3A18, 0x00},
-+ {0x3A19, 0xF8},
-+ {0x3C01, 0x80},
-+ {0x3B07, 0x0C},
-+ {0x3800, 0x00},
-+ {0x3801, 0x00},
-+ {0x3802, 0x00},
-+ {0x3803, 0x00},
-+ {0x3804, 0x0A},
-+ {0x3805, 0x3F},
-+ {0x3806, 0x07},
-+ {0x3807, 0xA3},
-+ {0x3808, 0x05},
-+ {0x3809, 0x10},
-+ {0x380A, 0x03},
-+ {0x380B, 0xCC},
-+ {0x380C, 0x07},
-+ {0x380D, 0x68},
-+ {0x3811, 0x0c},
-+ {0x3813, 0x06},
-+ {0x3814, 0x31},
-+ {0x3815, 0x31},
-+ {0x3630, 0x2E},
-+ {0x3632, 0xE2},
-+ {0x3633, 0x23},
-+ {0x3634, 0x44},
-+ {0x3636, 0x06},
-+ {0x3620, 0x64},
-+ {0x3621, 0xE0},
-+ {0x3600, 0x37},
-+ {0x3704, 0xA0},
-+ {0x3703, 0x5A},
-+ {0x3715, 0x78},
-+ {0x3717, 0x01},
-+ {0x3731, 0x02},
-+ {0x370B, 0x60},
-+ {0x3705, 0x1A},
-+ {0x3F05, 0x02},
-+ {0x3F06, 0x10},
-+ {0x3F01, 0x0A},
-+ {0x3A08, 0x01},
-+ {0x3A09, 0x28},
-+ {0x3A0A, 0x00},
-+ {0x3A0B, 0xF6},
-+ {0x3A0D, 0x08},
-+ {0x3A0E, 0x06},
-+ {0x3A0F, 0x58},
-+ {0x3A10, 0x50},
-+ {0x3A1B, 0x58},
-+ {0x3A1E, 0x50},
-+ {0x3A11, 0x60},
-+ {0x3A1F, 0x28},
-+ {0x4001, 0x02},
-+ {0x4004, 0x04},
-+ {0x4000, 0x09},
-+ {0x4837, 0x16},
-+ {0x4800, 0x24},
-+ {0x3503, 0x03},
-+ {0x3820, 0x41},
-+ {0x3821, 0x07},
-+ {0x380E, 0x05},
-+ {0x380F, 0x9B},
-+ {0x350A, 0x00},
-+ {0x350B, 0x10},
-+ {0x3500, 0x00},
-+ {0x3501, 0x1A},
-+ {0x3502, 0xF0},
-+ {0x3212, 0xA0},
-+ {0x0100, 0x01},
-+};
-+
-+static struct regval_list ov5647_640x480_10bit[] = {
-+ {0x0100, 0x00},
-+ {0x0103, 0x01},
-+ {0x3035, 0x11},
-+ {0x3036, 0x46},
-+ {0x303c, 0x11},
-+ {0x3821, 0x07},
-+ {0x3820, 0x41},
-+ {0x370c, 0x03},
-+ {0x3612, 0x59},
-+ {0x3618, 0x00},
-+ {0x5000, 0x06},
-+ {0x5003, 0x08},
-+ {0x5a00, 0x08},
-+ {0x3000, 0xff},
-+ {0x3001, 0xff},
-+ {0x3002, 0xff},
-+ {0x301d, 0xf0},
-+ {0x3a18, 0x00},
-+ {0x3a19, 0xf8},
-+ {0x3c01, 0x80},
-+ {0x3b07, 0x0c},
-+ {0x380c, 0x07},
-+ {0x380d, 0x3c},
-+ {0x380e, 0x01},
-+ {0x380f, 0xf8},
-+ {0x3814, 0x35},
-+ {0x3815, 0x35},
-+ {0x3708, 0x64},
-+ {0x3709, 0x52},
-+ {0x3808, 0x02},
-+ {0x3809, 0x80},
-+ {0x380a, 0x01},
-+ {0x380b, 0xe0},
-+ {0x3800, 0x00},
-+ {0x3801, 0x10},
-+ {0x3802, 0x00},
-+ {0x3803, 0x00},
-+ {0x3804, 0x0a},
-+ {0x3805, 0x2f},
-+ {0x3806, 0x07},
-+ {0x3807, 0x9f},
-+ {0x3630, 0x2e},
-+ {0x3632, 0xe2},
-+ {0x3633, 0x23},
-+ {0x3634, 0x44},
-+ {0x3620, 0x64},
-+ {0x3621, 0xe0},
-+ {0x3600, 0x37},
-+ {0x3704, 0xa0},
-+ {0x3703, 0x5a},
-+ {0x3715, 0x78},
-+ {0x3717, 0x01},
-+ {0x3731, 0x02},
-+ {0x370b, 0x60},
-+ {0x3705, 0x1a},
-+ {0x3f05, 0x02},
-+ {0x3f06, 0x10},
-+ {0x3f01, 0x0a},
-+ {0x3a08, 0x01},
-+ {0x3a09, 0x2e},
-+ {0x3a0a, 0x00},
-+ {0x3a0b, 0xfb},
-+ {0x3a0d, 0x02},
-+ {0x3a0e, 0x01},
-+ {0x3a0f, 0x58},
-+ {0x3a10, 0x50},
-+ {0x3a1b, 0x58},
-+ {0x3a1e, 0x50},
-+ {0x3a11, 0x60},
-+ {0x3a1f, 0x28},
-+ {0x4001, 0x02},
-+ {0x4004, 0x02},
-+ {0x4000, 0x09},
-+ {0x3000, 0x00},
-+ {0x3001, 0x00},
-+ {0x3002, 0x00},
-+ {0x3017, 0xe0},
-+ {0x301c, 0xfc},
-+ {0x3636, 0x06},
-+ {0x3016, 0x08},
-+ {0x3827, 0xec},
-+ {0x3018, 0x44},
-+ {0x3035, 0x21},
-+ {0x3106, 0xf5},
-+ {0x3034, 0x1a},
-+ {0x301c, 0xf8},
-+ {0x4800, 0x34},
-+ {0x3503, 0x03},
-+ {0x0100, 0x01},
-+};
-+
- static struct ov5647_mode supported_modes_8bit[] = {
- /*
-- * Original 8-bit VGA mode
-+ * MODE 0: Original 8-bit VGA mode.
- * Uncentred crop (top left quarter) from 2x2 binned 1296x972 image.
- */
- {
-@@ -233,14 +603,70 @@ static struct ov5647_mode supported_mode
- .width = 640,
- .height = 480
- },
-- ov5647_640x480,
-- ARRAY_SIZE(ov5647_640x480)
-+ ov5647_640x480_8bit,
-+ ARRAY_SIZE(ov5647_640x480_8bit)
- },
-- /* more modes below here... */
- };
-
- static struct ov5647_mode supported_modes_10bit[] = {
-- /* no 10-bit modes yet */
-+ /*
-+ * MODE 0: 2592x1944 full resolution full FOV 10-bit mode.
-+ */
-+ {
-+ {
-+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
-+ .colorspace = V4L2_COLORSPACE_SRGB,
-+ .field = V4L2_FIELD_NONE,
-+ .width = 2592,
-+ .height = 1944
-+ },
-+ ov5647_2592x1944_10bit,
-+ ARRAY_SIZE(ov5647_2592x1944_10bit)
-+ },
-+ /*
-+ * MODE 1: 1080p30 10-bit mode.
-+ * Full resolution centre-cropped down to 1080p.
-+ */
-+ {
-+ {
-+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
-+ .colorspace = V4L2_COLORSPACE_SRGB,
-+ .field = V4L2_FIELD_NONE,
-+ .width = 1920,
-+ .height = 1080
-+ },
-+ ov5647_1080p30_10bit,
-+ ARRAY_SIZE(ov5647_1080p30_10bit)
-+ },
-+ /*
-+ * MODE 2: 2x2 binned full FOV 10-bit mode.
-+ */
-+ {
-+ {
-+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
-+ .colorspace = V4L2_COLORSPACE_SRGB,
-+ .field = V4L2_FIELD_NONE,
-+ .width = 1296,
-+ .height = 972
-+ },
-+ ov5647_2x2binned_10bit,
-+ ARRAY_SIZE(ov5647_2x2binned_10bit)
-+ },
-+ /*
-+ * MODE 3: 10-bit VGA full FOV mode 60fps.
-+ * 2x2 binned and subsampled down to VGA.
-+ */
-+ {
-+ {
-+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
-+ .colorspace = V4L2_COLORSPACE_SRGB,
-+ .field = V4L2_FIELD_NONE,
-+ .width = 640,
-+ .height = 480
-+ },
-+ ov5647_640x480_10bit,
-+ ARRAY_SIZE(ov5647_640x480_10bit)
-+ },
- };
-
- /* Use original 8-bit VGA mode as default. */
-@@ -343,11 +769,14 @@ static int __sensor_init(struct v4l2_sub
- if (ret < 0)
- return ret;
-
-- ret = ov5647_write_array(sd, state->mode->reg_list,
-- state->mode->num_regs);
-- if (ret < 0) {
-- dev_err(&client->dev, "write sensor default regs error\n");
-- return ret;
-+ if (state->write_mode_regs) {
-+ ret = ov5647_write_array(sd, state->mode->reg_list,
-+ state->mode->num_regs);
-+ if (ret < 0) {
-+ dev_err(&client->dev, "write sensor default regs error\n");
-+ return ret;
-+ }
-+ state->write_mode_regs = false;
- }
-
- ret = ov5647_set_virtual_channel(sd, 0);
-@@ -475,6 +904,9 @@ static int ov5647_sensor_power(struct v4
- "Camera not available, check Power\n");
- goto out;
- }
-+
-+ /* Write out the register set over I2C on stream-on. */
-+ ov5647->write_mode_regs = true;
- } else if (!on && ov5647->power_count == 1) {
- dev_dbg(&client->dev, "OV5647 power off\n");
-
-@@ -650,6 +1082,12 @@ static int ov5647_set_fmt(struct v4l2_su
- framefmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
- *framefmt = format->format;
- } else {
-+ /*
-+ * If we have changed modes, write the I2C register list on
-+ * a stream_on().
-+ */
-+ if (state->mode != mode)
-+ state->write_mode_regs = true;
- state->mode = mode;
- }
-
-@@ -967,6 +1405,9 @@ static int ov5647_probe(struct i2c_clien
- /* Set the default mode before we init the subdev */
- sensor->mode = OV5647_DEFAULT_MODE;
-
-+ /* Write out the register set over I2C on stream-on. */
-+ sensor->write_mode_regs = true;
-+
- sd = &sensor->sd;
- v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops);
- sensor->sd.internal_ops = &ov5647_subdev_internal_ops;
--- /dev/null
+From 8bc19baeeca276374bed2d2ec95029d34fd93d7d Mon Sep 17 00:00:00 2001
+From: David Plowman <david.plowman@raspberrypi.com>
+Date: Wed, 29 Jan 2020 15:31:32 +0000
+Subject: [PATCH] media: ov5647: change defaults to better match raw
+ camera applications.
+
+Specifically:
+
+* AWB is now off by default.
+
+* AEC/AGC is also off by default.
+
+* The default mode is changed to the 10-bit 2x2 binned mode.
+
+AWB and AEC/AGC can be re-enabled using the usual V4L2 controls. The
+original 8-bit mode will be respected if an application requests the
+8-bit format.
+
+Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -669,8 +669,8 @@ static struct ov5647_mode supported_mode
+ },
+ };
+
+-/* Use original 8-bit VGA mode as default. */
+-#define OV5647_DEFAULT_MODE (&supported_modes_8bit[0])
++/* Use 2x2 binned 10-bit mode as default. */
++#define OV5647_DEFAULT_MODE (&supported_modes_10bit[2])
+
+ static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
+ {
+@@ -1367,18 +1367,18 @@ static int ov5647_probe(struct i2c_clien
+ 0, /* min */
+ 1, /* max */
+ 1, /* step */
+- 1); /* default */
++ 0); /* default */
+ v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE,
+ 0, /* min */
+ 1, /* max */
+ 1, /* step */
+- 1); /* default */
++ 0); /* default */
+ v4l2_ctrl_new_std_menu(&sensor->ctrls, &ov5647_ctrl_ops,
+ V4L2_CID_EXPOSURE_AUTO,
+ V4L2_EXPOSURE_MANUAL, /* max */
+ 0, /* skip_mask */
+- V4L2_EXPOSURE_AUTO); /* default */
++ V4L2_EXPOSURE_MANUAL); /* default */
+ ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+ V4L2_CID_EXPOSURE,
+ 4, /* min lines */
--- /dev/null
+From c0b2ca6abdde60a111fd6d3257be78c7f44e16ff Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 26 Dec 2019 15:44:31 +0100
+Subject: [PATCH] drm/vc4: fkms: Change crtc_state structure name to
+ avoid conflict
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_firmware_kms.c | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
+@@ -260,7 +260,7 @@ static inline struct vc4_crtc *to_vc4_cr
+ return container_of(crtc, struct vc4_crtc, base);
+ }
+
+-struct vc4_crtc_state {
++struct fkms_crtc_state {
+ struct drm_crtc_state base;
+
+ struct {
+@@ -271,10 +271,10 @@ struct vc4_crtc_state {
+ } margins;
+ };
+
+-static inline struct vc4_crtc_state *
+-to_vc4_crtc_state(struct drm_crtc_state *crtc_state)
++static inline struct fkms_crtc_state *
++to_fkms_crtc_state(struct drm_crtc_state *crtc_state)
+ {
+- return (struct vc4_crtc_state *)crtc_state;
++ return (struct fkms_crtc_state *)crtc_state;
+ }
+
+ struct vc4_fkms_encoder {
+@@ -410,7 +410,7 @@ static void vc4_fkms_crtc_get_margins(st
+ unsigned int *left, unsigned int *right,
+ unsigned int *top, unsigned int *bottom)
+ {
+- struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
++ struct fkms_crtc_state *vc4_state = to_fkms_crtc_state(state);
+ struct drm_connector_state *conn_state;
+ struct drm_connector *conn;
+ int i;
+@@ -423,7 +423,7 @@ static void vc4_fkms_crtc_get_margins(st
+ /* We have to interate over all new connector states because
+ * vc4_fkms_crtc_get_margins() might be called before
+ * vc4_fkms_crtc_atomic_check() which means margins info in
+- * vc4_crtc_state might be outdated.
++ * fkms_crtc_state might be outdated.
+ */
+ for_each_new_connector_in_state(state->state, conn, conn_state, i) {
+ if (conn_state->crtc != state->crtc)
+@@ -1068,7 +1068,7 @@ vc4_crtc_mode_valid(struct drm_crtc *crt
+ static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+ {
+- struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
++ struct fkms_crtc_state *vc4_state = to_fkms_crtc_state(state);
+ struct drm_connector *conn;
+ struct drm_connector_state *conn_state;
+ int i;
+@@ -1178,13 +1178,13 @@ static int vc4_page_flip(struct drm_crtc
+ static struct drm_crtc_state *
+ vc4_crtc_duplicate_state(struct drm_crtc *crtc)
+ {
+- struct vc4_crtc_state *vc4_state, *old_vc4_state;
++ struct fkms_crtc_state *vc4_state, *old_vc4_state;
+
+ vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
+ if (!vc4_state)
+ return NULL;
+
+- old_vc4_state = to_vc4_crtc_state(crtc->state);
++ old_vc4_state = to_fkms_crtc_state(crtc->state);
+ vc4_state->margins = old_vc4_state->margins;
+
+ __drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base);
+++ /dev/null
-From 8bc19baeeca276374bed2d2ec95029d34fd93d7d Mon Sep 17 00:00:00 2001
-From: David Plowman <david.plowman@raspberrypi.com>
-Date: Wed, 29 Jan 2020 15:31:32 +0000
-Subject: [PATCH] media: ov5647: change defaults to better match raw
- camera applications.
-
-Specifically:
-
-* AWB is now off by default.
-
-* AEC/AGC is also off by default.
-
-* The default mode is changed to the 10-bit 2x2 binned mode.
-
-AWB and AEC/AGC can be re-enabled using the usual V4L2 controls. The
-original 8-bit mode will be respected if an application requests the
-8-bit format.
-
-Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
-Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
----
- drivers/media/i2c/ov5647.c | 10 +++++-----
- 1 file changed, 5 insertions(+), 5 deletions(-)
-
---- a/drivers/media/i2c/ov5647.c
-+++ b/drivers/media/i2c/ov5647.c
-@@ -669,8 +669,8 @@ static struct ov5647_mode supported_mode
- },
- };
-
--/* Use original 8-bit VGA mode as default. */
--#define OV5647_DEFAULT_MODE (&supported_modes_8bit[0])
-+/* Use 2x2 binned 10-bit mode as default. */
-+#define OV5647_DEFAULT_MODE (&supported_modes_10bit[2])
-
- static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
- {
-@@ -1367,18 +1367,18 @@ static int ov5647_probe(struct i2c_clien
- 0, /* min */
- 1, /* max */
- 1, /* step */
-- 1); /* default */
-+ 0); /* default */
- v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
- V4L2_CID_AUTO_WHITE_BALANCE,
- 0, /* min */
- 1, /* max */
- 1, /* step */
-- 1); /* default */
-+ 0); /* default */
- v4l2_ctrl_new_std_menu(&sensor->ctrls, &ov5647_ctrl_ops,
- V4L2_CID_EXPOSURE_AUTO,
- V4L2_EXPOSURE_MANUAL, /* max */
- 0, /* skip_mask */
-- V4L2_EXPOSURE_AUTO); /* default */
-+ V4L2_EXPOSURE_MANUAL); /* default */
- ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
- V4L2_CID_EXPOSURE,
- 4, /* min lines */
--- /dev/null
+From 0d392a430d7dc84d8654972e9dbfa4d13009d3e8 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 24 Jan 2020 14:22:06 +0000
+Subject: [PATCH] drm/fourcc: Add packed 10bit YUV 4:2:0 format
+
+Adds a format that is 3 10bit YUV 4:2:0 samples packed into
+a 32bit work (with 2 spare bits).
+
+Supported on Broadcom BCM2711 chips.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/drm_fourcc.c | 3 +++
+ include/uapi/drm/drm_fourcc.h | 11 +++++++++++
+ 2 files changed, 14 insertions(+)
+
+--- a/drivers/gpu/drm/drm_fourcc.c
++++ b/drivers/gpu/drm/drm_fourcc.c
+@@ -274,6 +274,9 @@ const struct drm_format_info *__drm_form
+ { .format = DRM_FORMAT_YUV420_10BIT, .depth = 0,
+ .num_planes = 1, .cpp = { 0, 0, 0 }, .hsub = 2, .vsub = 2,
+ .is_yuv = true },
++ { .format = DRM_FORMAT_P030, .depth = 0, .num_planes = 2,
++ .char_per_block = { 4, 4, 0 }, .block_w = { 3, 0, 0 }, .block_h = { 1, 0, 0 },
++ .hsub = 2, .vsub = 2, .is_yuv = true},
+ };
+
+ unsigned int i;
+--- a/include/uapi/drm/drm_fourcc.h
++++ b/include/uapi/drm/drm_fourcc.h
+@@ -266,6 +266,13 @@ extern "C" {
+ #define DRM_FORMAT_P016 fourcc_code('P', '0', '1', '6') /* 2x2 subsampled Cr:Cb plane 16 bits per channel */
+
+ /*
++ * 2 plane YCbCr MSB aligned, 3 pixels packed into 4 bytes.
++ * index 0 = Y plane, [31:0] x:Y2:Y1:Y0 2:10:10:10 little endian
++ * index 1 = Cr:Cb plane, [63:0] x:Cr2:Cb2:Cr1:x:Cb1:Cr0:Cb0 [2:10:10:10:2:10:10:10] little endian
++ */
++#define DRM_FORMAT_P030 fourcc_code('P', '0', '3', '0') /* 2x2 subsampled Cr:Cb plane 10 bits per channel packed */
++
++/*
+ * 3 plane YCbCr
+ * index 0: Y plane, [7:0] Y
+ * index 1: Cb plane, [7:0] Cb
+@@ -593,6 +600,10 @@ extern "C" {
+ * and UV. Some SAND-using hardware stores UV in a separate tiled
+ * image from Y to reduce the column height, which is not supported
+ * with these modifiers.
++ *
++ * The DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT modifier is also
++ * supported for DRM_FORMAT_P030 where the columns remain as 128 bytes
++ * wide, but as this is a 10 bpp format that translates to 96 pixels.
+ */
+
+ #define DRM_FORMAT_MOD_BROADCOM_SAND32_COL_HEIGHT(v) \
+++ /dev/null
-From c0b2ca6abdde60a111fd6d3257be78c7f44e16ff Mon Sep 17 00:00:00 2001
-From: Maxime Ripard <maxime@cerno.tech>
-Date: Thu, 26 Dec 2019 15:44:31 +0100
-Subject: [PATCH] drm/vc4: fkms: Change crtc_state structure name to
- avoid conflict
-
-Signed-off-by: Maxime Ripard <maxime@cerno.tech>
----
- drivers/gpu/drm/vc4/vc4_firmware_kms.c | 18 +++++++++---------
- 1 file changed, 9 insertions(+), 9 deletions(-)
-
---- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
-+++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
-@@ -260,7 +260,7 @@ static inline struct vc4_crtc *to_vc4_cr
- return container_of(crtc, struct vc4_crtc, base);
- }
-
--struct vc4_crtc_state {
-+struct fkms_crtc_state {
- struct drm_crtc_state base;
-
- struct {
-@@ -271,10 +271,10 @@ struct vc4_crtc_state {
- } margins;
- };
-
--static inline struct vc4_crtc_state *
--to_vc4_crtc_state(struct drm_crtc_state *crtc_state)
-+static inline struct fkms_crtc_state *
-+to_fkms_crtc_state(struct drm_crtc_state *crtc_state)
- {
-- return (struct vc4_crtc_state *)crtc_state;
-+ return (struct fkms_crtc_state *)crtc_state;
- }
-
- struct vc4_fkms_encoder {
-@@ -410,7 +410,7 @@ static void vc4_fkms_crtc_get_margins(st
- unsigned int *left, unsigned int *right,
- unsigned int *top, unsigned int *bottom)
- {
-- struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
-+ struct fkms_crtc_state *vc4_state = to_fkms_crtc_state(state);
- struct drm_connector_state *conn_state;
- struct drm_connector *conn;
- int i;
-@@ -423,7 +423,7 @@ static void vc4_fkms_crtc_get_margins(st
- /* We have to interate over all new connector states because
- * vc4_fkms_crtc_get_margins() might be called before
- * vc4_fkms_crtc_atomic_check() which means margins info in
-- * vc4_crtc_state might be outdated.
-+ * fkms_crtc_state might be outdated.
- */
- for_each_new_connector_in_state(state->state, conn, conn_state, i) {
- if (conn_state->crtc != state->crtc)
-@@ -1068,7 +1068,7 @@ vc4_crtc_mode_valid(struct drm_crtc *crt
- static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
- struct drm_crtc_state *state)
- {
-- struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
-+ struct fkms_crtc_state *vc4_state = to_fkms_crtc_state(state);
- struct drm_connector *conn;
- struct drm_connector_state *conn_state;
- int i;
-@@ -1178,13 +1178,13 @@ static int vc4_page_flip(struct drm_crtc
- static struct drm_crtc_state *
- vc4_crtc_duplicate_state(struct drm_crtc *crtc)
- {
-- struct vc4_crtc_state *vc4_state, *old_vc4_state;
-+ struct fkms_crtc_state *vc4_state, *old_vc4_state;
-
- vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
- if (!vc4_state)
- return NULL;
-
-- old_vc4_state = to_vc4_crtc_state(crtc->state);
-+ old_vc4_state = to_fkms_crtc_state(crtc->state);
- vc4_state->margins = old_vc4_state->margins;
-
- __drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base);
+++ /dev/null
-From 0d392a430d7dc84d8654972e9dbfa4d13009d3e8 Mon Sep 17 00:00:00 2001
-From: Dave Stevenson <dave.stevenson@raspberrypi.com>
-Date: Fri, 24 Jan 2020 14:22:06 +0000
-Subject: [PATCH] drm/fourcc: Add packed 10bit YUV 4:2:0 format
-
-Adds a format that is 3 10bit YUV 4:2:0 samples packed into
-a 32bit work (with 2 spare bits).
-
-Supported on Broadcom BCM2711 chips.
-
-Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
----
- drivers/gpu/drm/drm_fourcc.c | 3 +++
- include/uapi/drm/drm_fourcc.h | 11 +++++++++++
- 2 files changed, 14 insertions(+)
-
---- a/drivers/gpu/drm/drm_fourcc.c
-+++ b/drivers/gpu/drm/drm_fourcc.c
-@@ -274,6 +274,9 @@ const struct drm_format_info *__drm_form
- { .format = DRM_FORMAT_YUV420_10BIT, .depth = 0,
- .num_planes = 1, .cpp = { 0, 0, 0 }, .hsub = 2, .vsub = 2,
- .is_yuv = true },
-+ { .format = DRM_FORMAT_P030, .depth = 0, .num_planes = 2,
-+ .char_per_block = { 4, 4, 0 }, .block_w = { 3, 0, 0 }, .block_h = { 1, 0, 0 },
-+ .hsub = 2, .vsub = 2, .is_yuv = true},
- };
-
- unsigned int i;
---- a/include/uapi/drm/drm_fourcc.h
-+++ b/include/uapi/drm/drm_fourcc.h
-@@ -266,6 +266,13 @@ extern "C" {
- #define DRM_FORMAT_P016 fourcc_code('P', '0', '1', '6') /* 2x2 subsampled Cr:Cb plane 16 bits per channel */
-
- /*
-+ * 2 plane YCbCr MSB aligned, 3 pixels packed into 4 bytes.
-+ * index 0 = Y plane, [31:0] x:Y2:Y1:Y0 2:10:10:10 little endian
-+ * index 1 = Cr:Cb plane, [63:0] x:Cr2:Cb2:Cr1:x:Cb1:Cr0:Cb0 [2:10:10:10:2:10:10:10] little endian
-+ */
-+#define DRM_FORMAT_P030 fourcc_code('P', '0', '3', '0') /* 2x2 subsampled Cr:Cb plane 10 bits per channel packed */
-+
-+/*
- * 3 plane YCbCr
- * index 0: Y plane, [7:0] Y
- * index 1: Cb plane, [7:0] Cb
-@@ -593,6 +600,10 @@ extern "C" {
- * and UV. Some SAND-using hardware stores UV in a separate tiled
- * image from Y to reduce the column height, which is not supported
- * with these modifiers.
-+ *
-+ * The DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT modifier is also
-+ * supported for DRM_FORMAT_P030 where the columns remain as 128 bytes
-+ * wide, but as this is a 10 bpp format that translates to 96 pixels.
- */
-
- #define DRM_FORMAT_MOD_BROADCOM_SAND32_COL_HEIGHT(v) \
--- /dev/null
+From 531d3d5c89825bade52f4257d264bbb06775a6fa Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 24 Jan 2020 14:24:33 +0000
+Subject: [PATCH] drm/vc4: Add DRM_FORMAT_P030 support to firmware-kms
+
+Adds support for this format which is 3 10bit samples packed into
+4 bytes, as used by the HEVC codec block.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_firmware_kms.c | 21 ++++++++++++++++++++-
+ drivers/gpu/drm/vc4/vc_image_types.h | 4 ++++
+ 2 files changed, 24 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
+@@ -216,6 +216,10 @@ static const struct vc_image_format {
+ .vc_image = VC_IMAGE_YUV420SP,
+ .is_vu = 1,
+ },
++ {
++ .drm = DRM_FORMAT_P030,
++ .vc_image = VC_IMAGE_YUV10COL,
++ },
+ };
+
+ static const struct vc_image_format *vc4_get_vc_image_fmt(u32 drm_format)
+@@ -622,7 +626,15 @@ static int vc4_plane_to_mb(struct drm_pl
+ }
+ break;
+ case DRM_FORMAT_MOD_BROADCOM_SAND128:
+- mb->plane.vc_image_type = VC_IMAGE_YUV_UV;
++ switch (mb->plane.vc_image_type) {
++ case VC_IMAGE_YUV420SP:
++ mb->plane.vc_image_type = VC_IMAGE_YUV_UV;
++ break;
++ /* VC_IMAGE_YUV10COL could be included in here, but it is only
++ * valid as a SAND128 format, so the table at the top will have
++ * already set the correct format.
++ */
++ }
+ /* Note that the column pitch is passed across in lines, not
+ * bytes.
+ */
+@@ -707,6 +719,13 @@ static bool vc4_fkms_format_mod_supporte
+ case DRM_FORMAT_MOD_BROADCOM_SAND128:
+ return true;
+ default:
++ return false;
++ }
++ case DRM_FORMAT_P030:
++ switch (fourcc_mod_broadcom_mod(modifier)) {
++ case DRM_FORMAT_MOD_BROADCOM_SAND128:
++ return true;
++ default:
+ return false;
+ }
+ case DRM_FORMAT_NV21:
+--- a/drivers/gpu/drm/vc4/vc_image_types.h
++++ b/drivers/gpu/drm/vc4/vc_image_types.h
+@@ -139,6 +139,10 @@ enum {
+ VC_IMAGE_YUV_UV_16,
+ /* YUV4:2:0 with U,V in side-by-side format */
+ VC_IMAGE_YUV420_S,
++ /* 10-bit YUV 420 column image format */
++ VC_IMAGE_YUV10COL,
++ /* 32-bpp, 10-bit R/G/B, 2-bit Alpha */
++ VC_IMAGE_RGBA1010102,
+
+ VC_IMAGE_MAX, /* bounds for error checking */
+ VC_IMAGE_FORCE_ENUM_16BIT = 0xffff,
+++ /dev/null
-From 531d3d5c89825bade52f4257d264bbb06775a6fa Mon Sep 17 00:00:00 2001
-From: Dave Stevenson <dave.stevenson@raspberrypi.com>
-Date: Fri, 24 Jan 2020 14:24:33 +0000
-Subject: [PATCH] drm/vc4: Add DRM_FORMAT_P030 support to firmware-kms
-
-Adds support for this format which is 3 10bit samples packed into
-4 bytes, as used by the HEVC codec block.
-
-Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
----
- drivers/gpu/drm/vc4/vc4_firmware_kms.c | 21 ++++++++++++++++++++-
- drivers/gpu/drm/vc4/vc_image_types.h | 4 ++++
- 2 files changed, 24 insertions(+), 1 deletion(-)
-
---- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
-+++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
-@@ -216,6 +216,10 @@ static const struct vc_image_format {
- .vc_image = VC_IMAGE_YUV420SP,
- .is_vu = 1,
- },
-+ {
-+ .drm = DRM_FORMAT_P030,
-+ .vc_image = VC_IMAGE_YUV10COL,
-+ },
- };
-
- static const struct vc_image_format *vc4_get_vc_image_fmt(u32 drm_format)
-@@ -622,7 +626,15 @@ static int vc4_plane_to_mb(struct drm_pl
- }
- break;
- case DRM_FORMAT_MOD_BROADCOM_SAND128:
-- mb->plane.vc_image_type = VC_IMAGE_YUV_UV;
-+ switch (mb->plane.vc_image_type) {
-+ case VC_IMAGE_YUV420SP:
-+ mb->plane.vc_image_type = VC_IMAGE_YUV_UV;
-+ break;
-+ /* VC_IMAGE_YUV10COL could be included in here, but it is only
-+ * valid as a SAND128 format, so the table at the top will have
-+ * already set the correct format.
-+ */
-+ }
- /* Note that the column pitch is passed across in lines, not
- * bytes.
- */
-@@ -707,6 +719,13 @@ static bool vc4_fkms_format_mod_supporte
- case DRM_FORMAT_MOD_BROADCOM_SAND128:
- return true;
- default:
-+ return false;
-+ }
-+ case DRM_FORMAT_P030:
-+ switch (fourcc_mod_broadcom_mod(modifier)) {
-+ case DRM_FORMAT_MOD_BROADCOM_SAND128:
-+ return true;
-+ default:
- return false;
- }
- case DRM_FORMAT_NV21:
---- a/drivers/gpu/drm/vc4/vc_image_types.h
-+++ b/drivers/gpu/drm/vc4/vc_image_types.h
-@@ -139,6 +139,10 @@ enum {
- VC_IMAGE_YUV_UV_16,
- /* YUV4:2:0 with U,V in side-by-side format */
- VC_IMAGE_YUV420_S,
-+ /* 10-bit YUV 420 column image format */
-+ VC_IMAGE_YUV10COL,
-+ /* 32-bpp, 10-bit R/G/B, 2-bit Alpha */
-+ VC_IMAGE_RGBA1010102,
-
- VC_IMAGE_MAX, /* bounds for error checking */
- VC_IMAGE_FORCE_ENUM_16BIT = 0xffff,
--- /dev/null
+From e253d03936265dc4ab8ae9ae89d2a885e80a45a6 Mon Sep 17 00:00:00 2001
+From: Matthias Reichl <hias@horus.com>
+Date: Fri, 6 Mar 2020 11:08:10 +0100
+Subject: [PATCH] gpio-ir-overlay: add parameter to configure signal
+ polarity (#3490)
+
+Standard IR receivers use inverted / active-low signalling
+and the gpio-ir overlay configures the GPIO appropriately
+as GPIO_ACTIVE_LOW (1).
+
+In order to support (rather rare) non-inverted / active-high
+signalling the GPIO needs to be configured as GPIO_ACTIVE_HIGH (0).
+
+Add an "invert" parameter to override this like in the gpio-ir-tx
+overlay.
+
+Signed-off-by: Matthias Reichl <hias@horus.com>
+---
+ arch/arm/boot/dts/overlays/README | 4 ++++
+ arch/arm/boot/dts/overlays/gpio-ir-overlay.dts | 1 +
+ 2 files changed, 5 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -754,6 +754,10 @@ Params: gpio_pin Input pi
+ gpio_pull Desired pull-up/down state (off, down, up)
+ Default is "up".
+
++ invert "1" = invert the input (active-low signalling).
++ "0" = non-inverted input (active-high
++ signalling). Default is "1".
++
+ rc-map-name Default rc keymap (can also be changed by
+ ir-keytable), defaults to "rc-rc6-mce"
+
+--- a/arch/arm/boot/dts/overlays/gpio-ir-overlay.dts
++++ b/arch/arm/boot/dts/overlays/gpio-ir-overlay.dts
+@@ -42,6 +42,7 @@
+ <&gpio_ir_pins>,"brcm,pins:0",
+ <&gpio_ir_pins>,"reg:0";
+ gpio_pull = <&gpio_ir_pins>,"brcm,pull:0"; // pull-up/down state
++ invert = <&gpio_ir>,"gpios:8"; // 0 = active high input
+
+ rc-map-name = <&gpio_ir>,"linux,rc-map-name"; // default rc map
+ };
--- /dev/null
+From 76e0edf9676388c58bb5f0d7dda8eb8029926c6d Mon Sep 17 00:00:00 2001
+From: AMuszkat <ariel.muszkat@gmail.com>
+Date: Mon, 24 Feb 2020 22:56:59 +0100
+Subject: [PATCH] Add support for merus-amp soundcard and ma120x0p
+ codec
+
+correct checkpatch warnings and errors
+
+Signed-off-by: AMuszkat <ariel.muszkat@gmail.com>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 6 +
+ .../boot/dts/overlays/merus-amp-overlay.dts | 60 +
+ sound/soc/bcm/rpi-simple-soundcard.c | 28 +
+ sound/soc/codecs/Kconfig | 8 +
+ sound/soc/codecs/Makefile | 2 +
+ sound/soc/codecs/ma120x0p.c | 1384 +++++++++++++++++
+ 7 files changed, 1489 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/merus-amp-overlay.dts
+ create mode 100644 sound/soc/codecs/ma120x0p.c
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -103,6 +103,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ mcp3202.dtbo \
+ mcp342x.dtbo \
+ media-center.dtbo \
++ merus-amp.dtbo \
+ midi-uart0.dtbo \
+ midi-uart1.dtbo \
+ miniuart-bt.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -1662,6 +1662,12 @@ Params: speed Display
+ (default "off")
+
+
++Name: merus-amp
++Info: Configures the merus-amp audio card
++Load: dtoverlay=merus-amp
++Params: <None>
++
++
+ Name: midi-uart0
+ Info: Configures UART0 (ttyAMA0) so that a requested 38.4kbaud actually gets
+ 31.25kbaud, the frequency required for MIDI
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/merus-amp-overlay.dts
+@@ -0,0 +1,60 @@
++// SPDX-License-Identifier: GPL-2.0-only
++// Definitions for Infineon Merus-Amp
++/dts-v1/;
++/plugin/;
++#include <dt-bindings/pinctrl/bcm2835.h>
++#include <dt-bindings/gpio/gpio.h>
++
++
++/ {
++ compatible = "brcm,bcm2835";
++
++ fragment@0 {
++ target = <&i2s>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@1 {
++ target = <&gpio>;
++ __overlay__ {
++ merus_amp_pins: merus_amp_pins {
++ brcm,pins = <23>;
++ brcm,function = <0>; /* in */
++ brcm,pull = <2>; /* up */
++ };
++ };
++ };
++
++ fragment@2 {
++ target = <&i2c1>;
++ __overlay__ {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "okay";
++
++ merus_amp: ma120x0p@20 {
++ #sound-dai-cells = <0>;
++ compatible = "ma,ma120x0p";
++ reg = <0x20>;
++ status = "okay";
++ pinctrl-names = "default";
++ pinctrl-0 = <&merus_amp_pins>;
++ enable_gp-gpios = <&gpio 14 GPIO_ACTIVE_HIGH>;
++ mute_gp-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>;
++ booster_gp-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>;
++ error_gp-gpios = <&gpio 23 GPIO_ACTIVE_HIGH>;
++ };
++ };
++ };
++
++ fragment@3 {
++ target = <&sound>;
++ __overlay__ {
++ compatible = "merus,merus-amp";
++ i2s-controller = <&i2s>;
++ status = "okay";
++ };
++ };
++};
+--- a/sound/soc/bcm/rpi-simple-soundcard.c
++++ b/sound/soc/bcm/rpi-simple-soundcard.c
+@@ -16,6 +16,10 @@
+ * adau1977-adc.c
+ * by Andrey Grodzovsky <andrey2805@gmail.com>
+ *
++ * merus-amp.c
++ * by Ariel Muszkat <ariel.muszkat@gmail.com>
++ * Jorgen Kragh Jakobsen <jorgen.kraghjakobsen@infineon.com>
++ *
+ * 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.
+@@ -229,6 +233,28 @@ static struct snd_rpi_simple_drvdata drv
+ .fixed_bclk_ratio = 64,
+ };
+
++SND_SOC_DAILINK_DEFS(merus_amp,
++ DAILINK_COMP_ARRAY(COMP_EMPTY()),
++ DAILINK_COMP_ARRAY(COMP_CODEC("ma120x0p-amp", "ma120x0p.1-0020")),
++ DAILINK_COMP_ARRAY(COMP_EMPTY()));
++
++static struct snd_soc_dai_link snd_merus_amp_dai[] = {
++ {
++ .name = "MerusAmp",
++ .stream_name = "Merus Audio Amp",
++ .dai_fmt = SND_SOC_DAIFMT_I2S |
++ SND_SOC_DAIFMT_NB_NF |
++ SND_SOC_DAIFMT_CBS_CFS,
++ SND_SOC_DAILINK_REG(merus_amp),
++ },
++};
++
++static struct snd_rpi_simple_drvdata drvdata_merus_amp = {
++ .card_name = "snd_rpi_merus_amp",
++ .dai = snd_merus_amp_dai,
++ .fixed_bclk_ratio = 64,
++};
++
+ static const struct of_device_id snd_rpi_simple_of_match[] = {
+ { .compatible = "adi,adau1977-adc",
+ .data = (void *) &drvdata_adau1977 },
+@@ -241,6 +267,8 @@ static const struct of_device_id snd_rpi
+ { .compatible = "hifiberry,hifiberry-dac",
+ .data = (void *) &drvdata_hifiberry_dac },
+ { .compatible = "rpi,rpi-dac", &drvdata_rpi_dac},
++ { .compatible = "merus,merus-amp",
++ .data = (void *) &drvdata_merus_amp },
+ {},
+ };
+
+--- a/sound/soc/codecs/Kconfig
++++ b/sound/soc/codecs/Kconfig
+@@ -103,6 +103,7 @@ config SND_SOC_ALL_CODECS
+ select SND_SOC_LM4857 if I2C
+ select SND_SOC_LM49453 if I2C
+ select SND_SOC_LOCHNAGAR_SC if MFD_LOCHNAGAR
++ select SND_SOC_MA120X0P if I2C
+ select SND_SOC_MAX98088 if I2C
+ select SND_SOC_MAX98090 if I2C
+ select SND_SOC_MAX98095 if I2C
+@@ -732,6 +733,13 @@ config SND_SOC_LOCHNAGAR_SC
+ This driver support the sound card functionality of the Cirrus
+ Logic Lochnagar audio development board.
+
++config SND_SOC_MA120X0P
++ tristate "Infineon Merus(TM) MA120X0P Multilevel Class-D Audio amplifiers"
++ depends on I2C
++ help
++ Enable support for Infineon MA120X0P Multilevel Class-D audio power
++ amplifiers.
++
+ config SND_SOC_MADERA
+ tristate
+ default y if SND_SOC_CS47L15=y
+--- a/sound/soc/codecs/Makefile
++++ b/sound/soc/codecs/Makefile
+@@ -99,6 +99,7 @@ snd-soc-l3-objs := l3.o
+ snd-soc-lm4857-objs := lm4857.o
+ snd-soc-lm49453-objs := lm49453.o
+ snd-soc-lochnagar-sc-objs := lochnagar-sc.o
++snd-soc-ma120x0p-objs := ma120x0p.o
+ snd-soc-madera-objs := madera.o
+ snd-soc-max9759-objs := max9759.o
+ snd-soc-max9768-objs := max9768.o
+@@ -386,6 +387,7 @@ obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
+ obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o
+ obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o
+ obj-$(CONFIG_SND_SOC_LOCHNAGAR_SC) += snd-soc-lochnagar-sc.o
++obj-$(CONFIG_SND_SOC_MA120X0P) += snd-soc-ma120x0p.o
+ obj-$(CONFIG_SND_SOC_MADERA) += snd-soc-madera.o
+ obj-$(CONFIG_SND_SOC_MAX9759) += snd-soc-max9759.o
+ obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o
+--- /dev/null
++++ b/sound/soc/codecs/ma120x0p.c
+@@ -0,0 +1,1384 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * ASoC Driver for Infineon Merus(TM) ma120x0p multi-level class-D amplifier
++ *
++ * Authors: Ariel Muszkat <ariel.muszkat@gmail.com>
++ * Jorgen Kragh Jakobsen <jorgen.kraghjakobsen@infineon.com>
++ *
++ * Copyright (C) 2019 Infineon Technologies AG
++ *
++ */
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/pm_runtime.h>
++#include <linux/i2c.h>
++#include <linux/of_device.h>
++#include <linux/spi/spi.h>
++#include <linux/regmap.h>
++#include <linux/regulator/consumer.h>
++#include <linux/slab.h>
++#include <linux/gpio/consumer.h>
++#include <linux/gpio.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/initval.h>
++#include <sound/tlv.h>
++#include <linux/interrupt.h>
++
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include <linux/fs.h>
++#include <linux/uaccess.h>
++
++#ifndef _MA120X0P_
++#define _MA120X0P_
++//------------------------------------------------------------------manualPM---
++// Select Manual PowerMode control
++#define ma_manualpm__a 0
++#define ma_manualpm__len 1
++#define ma_manualpm__mask 0x40
++#define ma_manualpm__shift 0x06
++#define ma_manualpm__reset 0x00
++//--------------------------------------------------------------------pm_man---
++// manual selected power mode
++#define ma_pm_man__a 0
++#define ma_pm_man__len 2
++#define ma_pm_man__mask 0x30
++#define ma_pm_man__shift 0x04
++#define ma_pm_man__reset 0x03
++//------------------------------------------ ----------------------mthr_1to2---
++// mod. index threshold value for pm1=>pm2 change.
++#define ma_mthr_1to2__a 1
++#define ma_mthr_1to2__len 8
++#define ma_mthr_1to2__mask 0xff
++#define ma_mthr_1to2__shift 0x00
++#define ma_mthr_1to2__reset 0x3c
++//-----------------------------------------------------------------mthr_2to1---
++// mod. index threshold value for pm2=>pm1 change.
++#define ma_mthr_2to1__a 2
++#define ma_mthr_2to1__len 8
++#define ma_mthr_2to1__mask 0xff
++#define ma_mthr_2to1__shift 0x00
++#define ma_mthr_2to1__reset 0x32
++//-----------------------------------------------------------------mthr_2to3---
++// mod. index threshold value for pm2=>pm3 change.
++#define ma_mthr_2to3__a 3
++#define ma_mthr_2to3__len 8
++#define ma_mthr_2to3__mask 0xff
++#define ma_mthr_2to3__shift 0x00
++#define ma_mthr_2to3__reset 0x5a
++//-----------------------------------------------------------------mthr_3to2---
++// mod. index threshold value for pm3=>pm2 change.
++#define ma_mthr_3to2__a 4
++#define ma_mthr_3to2__len 8
++#define ma_mthr_3to2__mask 0xff
++#define ma_mthr_3to2__shift 0x00
++#define ma_mthr_3to2__reset 0x50
++//-------------------------------------------------------------pwmclkdiv_nom---
++// pwm default clock divider value
++#define ma_pwmclkdiv_nom__a 8
++#define ma_pwmclkdiv_nom__len 8
++#define ma_pwmclkdiv_nom__mask 0xff
++#define ma_pwmclkdiv_nom__shift 0x00
++#define ma_pwmclkdiv_nom__reset 0x26
++//--------- ----------------------------------------------------ocp_latch_en---
++// high to use permanently latching level-2 ocp
++#define ma_ocp_latch_en__a 10
++#define ma_ocp_latch_en__len 1
++#define ma_ocp_latch_en__mask 0x02
++#define ma_ocp_latch_en__shift 0x01
++#define ma_ocp_latch_en__reset 0x00
++//---------------------------------------------------------------lf_clamp_en---
++// high (default) to enable lf int2+3 clamping on clip
++#define ma_lf_clamp_en__a 10
++#define ma_lf_clamp_en__len 1
++#define ma_lf_clamp_en__mask 0x80
++#define ma_lf_clamp_en__shift 0x07
++#define ma_lf_clamp_en__reset 0x00
++//-------------------------------------------------------pmcfg_btl_b.modtype---
++//
++#define ma_pmcfg_btl_b__modtype__a 18
++#define ma_pmcfg_btl_b__modtype__len 2
++#define ma_pmcfg_btl_b__modtype__mask 0x18
++#define ma_pmcfg_btl_b__modtype__shift 0x03
++#define ma_pmcfg_btl_b__modtype__reset 0x02
++//-------------------------------------------------------pmcfg_btl_b.freqdiv---
++#define ma_pmcfg_btl_b__freqdiv__a 18
++#define ma_pmcfg_btl_b__freqdiv__len 2
++#define ma_pmcfg_btl_b__freqdiv__mask 0x06
++#define ma_pmcfg_btl_b__freqdiv__shift 0x01
++#define ma_pmcfg_btl_b__freqdiv__reset 0x01
++//----------------------------------------------------pmcfg_btl_b.lf_gain_ol---
++//
++#define ma_pmcfg_btl_b__lf_gain_ol__a 18
++#define ma_pmcfg_btl_b__lf_gain_ol__len 1
++#define ma_pmcfg_btl_b__lf_gain_ol__mask 0x01
++#define ma_pmcfg_btl_b__lf_gain_ol__shift 0x00
++#define ma_pmcfg_btl_b__lf_gain_ol__reset 0x01
++//-------------------------------------------------------pmcfg_btl_c.freqdiv---
++//
++#define ma_pmcfg_btl_c__freqdiv__a 19
++#define ma_pmcfg_btl_c__freqdiv__len 2
++#define ma_pmcfg_btl_c__freqdiv__mask 0x06
++#define ma_pmcfg_btl_c__freqdiv__shift 0x01
++#define ma_pmcfg_btl_c__freqdiv__reset 0x01
++//-------------------------------------------------------pmcfg_btl_c.modtype---
++//
++#define ma_pmcfg_btl_c__modtype__a 19
++#define ma_pmcfg_btl_c__modtype__len 2
++#define ma_pmcfg_btl_c__modtype__mask 0x18
++#define ma_pmcfg_btl_c__modtype__shift 0x03
++#define ma_pmcfg_btl_c__modtype__reset 0x01
++//----------------------------------------------------pmcfg_btl_c.lf_gain_ol---
++//
++#define ma_pmcfg_btl_c__lf_gain_ol__a 19
++#define ma_pmcfg_btl_c__lf_gain_ol__len 1
++#define ma_pmcfg_btl_c__lf_gain_ol__mask 0x01
++#define ma_pmcfg_btl_c__lf_gain_ol__shift 0x00
++#define ma_pmcfg_btl_c__lf_gain_ol__reset 0x00
++//-------------------------------------------------------pmcfg_btl_d.modtype---
++//
++#define ma_pmcfg_btl_d__modtype__a 20
++#define ma_pmcfg_btl_d__modtype__len 2
++#define ma_pmcfg_btl_d__modtype__mask 0x18
++#define ma_pmcfg_btl_d__modtype__shift 0x03
++#define ma_pmcfg_btl_d__modtype__reset 0x02
++//-------------------------------------------------------pmcfg_btl_d.freqdiv---
++//
++#define ma_pmcfg_btl_d__freqdiv__a 20
++#define ma_pmcfg_btl_d__freqdiv__len 2
++#define ma_pmcfg_btl_d__freqdiv__mask 0x06
++#define ma_pmcfg_btl_d__freqdiv__shift 0x01
++#define ma_pmcfg_btl_d__freqdiv__reset 0x02
++//----------------------------------------------------pmcfg_btl_d.lf_gain_ol---
++//
++#define ma_pmcfg_btl_d__lf_gain_ol__a 20
++#define ma_pmcfg_btl_d__lf_gain_ol__len 1
++#define ma_pmcfg_btl_d__lf_gain_ol__mask 0x01
++#define ma_pmcfg_btl_d__lf_gain_ol__shift 0x00
++#define ma_pmcfg_btl_d__lf_gain_ol__reset 0x00
++//------------ -------------------------------------------pmcfg_se_a.modtype---
++//
++#define ma_pmcfg_se_a__modtype__a 21
++#define ma_pmcfg_se_a__modtype__len 2
++#define ma_pmcfg_se_a__modtype__mask 0x18
++#define ma_pmcfg_se_a__modtype__shift 0x03
++#define ma_pmcfg_se_a__modtype__reset 0x01
++//--------------------------------------------------------pmcfg_se_a.freqdiv---
++//
++#define ma_pmcfg_se_a__freqdiv__a 21
++#define ma_pmcfg_se_a__freqdiv__len 2
++#define ma_pmcfg_se_a__freqdiv__mask 0x06
++#define ma_pmcfg_se_a__freqdiv__shift 0x01
++#define ma_pmcfg_se_a__freqdiv__reset 0x00
++//-----------------------------------------------------pmcfg_se_a.lf_gain_ol---
++//
++#define ma_pmcfg_se_a__lf_gain_ol__a 21
++#define ma_pmcfg_se_a__lf_gain_ol__len 1
++#define ma_pmcfg_se_a__lf_gain_ol__mask 0x01
++#define ma_pmcfg_se_a__lf_gain_ol__shift 0x00
++#define ma_pmcfg_se_a__lf_gain_ol__reset 0x01
++//-----------------------------------------------------pmcfg_se_b.lf_gain_ol---
++//
++#define ma_pmcfg_se_b__lf_gain_ol__a 22
++#define ma_pmcfg_se_b__lf_gain_ol__len 1
++#define ma_pmcfg_se_b__lf_gain_ol__mask 0x01
++#define ma_pmcfg_se_b__lf_gain_ol__shift 0x00
++#define ma_pmcfg_se_b__lf_gain_ol__reset 0x00
++//--------------------------------------------------------pmcfg_se_b.freqdiv---
++//
++#define ma_pmcfg_se_b__freqdiv__a 22
++#define ma_pmcfg_se_b__freqdiv__len 2
++#define ma_pmcfg_se_b__freqdiv__mask 0x06
++#define ma_pmcfg_se_b__freqdiv__shift 0x01
++#define ma_pmcfg_se_b__freqdiv__reset 0x01
++//--------------------------------------------------------pmcfg_se_b.modtype---
++//
++#define ma_pmcfg_se_b__modtype__a 22
++#define ma_pmcfg_se_b__modtype__len 2
++#define ma_pmcfg_se_b__modtype__mask 0x18
++#define ma_pmcfg_se_b__modtype__shift 0x03
++#define ma_pmcfg_se_b__modtype__reset 0x01
++//----------------------------------------------------------balwaitcount_pm1---
++// pm1 balancing period.
++#define ma_balwaitcount_pm1__a 23
++#define ma_balwaitcount_pm1__len 8
++#define ma_balwaitcount_pm1__mask 0xff
++#define ma_balwaitcount_pm1__shift 0x00
++#define ma_balwaitcount_pm1__reset 0x14
++//----------------------------------------------------------balwaitcount_pm2---
++// pm2 balancing period.
++#define ma_balwaitcount_pm2__a 24
++#define ma_balwaitcount_pm2__len 8
++#define ma_balwaitcount_pm2__mask 0xff
++#define ma_balwaitcount_pm2__shift 0x00
++#define ma_balwaitcount_pm2__reset 0x14
++//----------------------------------------------------------balwaitcount_pm3---
++// pm3 balancing period.
++#define ma_balwaitcount_pm3__a 25
++#define ma_balwaitcount_pm3__len 8
++#define ma_balwaitcount_pm3__mask 0xff
++#define ma_balwaitcount_pm3__shift 0x00
++#define ma_balwaitcount_pm3__reset 0x1a
++//-------------------------------------------------------------usespread_pm1---
++// pm1 pwm spread-spectrum mode on/off.
++#define ma_usespread_pm1__a 26
++#define ma_usespread_pm1__len 1
++#define ma_usespread_pm1__mask 0x40
++#define ma_usespread_pm1__shift 0x06
++#define ma_usespread_pm1__reset 0x00
++//---------------------------------------------------------------dtsteps_pm1---
++// pm1 dead time setting [10ns steps].
++#define ma_dtsteps_pm1__a 26
++#define ma_dtsteps_pm1__len 3
++#define ma_dtsteps_pm1__mask 0x38
++#define ma_dtsteps_pm1__shift 0x03
++#define ma_dtsteps_pm1__reset 0x04
++//---------------------------------------------------------------baltype_pm1---
++// pm1 balancing sensor scheme.
++#define ma_baltype_pm1__a 26
++#define ma_baltype_pm1__len 3
++#define ma_baltype_pm1__mask 0x07
++#define ma_baltype_pm1__shift 0x00
++#define ma_baltype_pm1__reset 0x00
++//-------------------------------------------------------------usespread_pm2---
++// pm2 pwm spread-spectrum mode on/off.
++#define ma_usespread_pm2__a 27
++#define ma_usespread_pm2__len 1
++#define ma_usespread_pm2__mask 0x40
++#define ma_usespread_pm2__shift 0x06
++#define ma_usespread_pm2__reset 0x00
++//---------------------------------------------------------------dtsteps_pm2---
++// pm2 dead time setting [10ns steps].
++#define ma_dtsteps_pm2__a 27
++#define ma_dtsteps_pm2__len 3
++#define ma_dtsteps_pm2__mask 0x38
++#define ma_dtsteps_pm2__shift 0x03
++#define ma_dtsteps_pm2__reset 0x03
++//---------------------------------------------------------------baltype_pm2---
++// pm2 balancing sensor scheme.
++#define ma_baltype_pm2__a 27
++#define ma_baltype_pm2__len 3
++#define ma_baltype_pm2__mask 0x07
++#define ma_baltype_pm2__shift 0x00
++#define ma_baltype_pm2__reset 0x01
++//-------------------------------------------------------------usespread_pm3---
++// pm3 pwm spread-spectrum mode on/off.
++#define ma_usespread_pm3__a 28
++#define ma_usespread_pm3__len 1
++#define ma_usespread_pm3__mask 0x40
++#define ma_usespread_pm3__shift 0x06
++#define ma_usespread_pm3__reset 0x00
++//---------------------------------------------------------------dtsteps_pm3---
++// pm3 dead time setting [10ns steps].
++#define ma_dtsteps_pm3__a 28
++#define ma_dtsteps_pm3__len 3
++#define ma_dtsteps_pm3__mask 0x38
++#define ma_dtsteps_pm3__shift 0x03
++#define ma_dtsteps_pm3__reset 0x01
++//---------------------------------------------------------------baltype_pm3---
++// pm3 balancing sensor scheme.
++#define ma_baltype_pm3__a 28
++#define ma_baltype_pm3__len 3
++#define ma_baltype_pm3__mask 0x07
++#define ma_baltype_pm3__shift 0x00
++#define ma_baltype_pm3__reset 0x03
++//-----------------------------------------------------------------pmprofile---
++// pm profile select. valid presets: 0-1-2-3-4. 5=> custom profile.
++#define ma_pmprofile__a 29
++#define ma_pmprofile__len 3
++#define ma_pmprofile__mask 0x07
++#define ma_pmprofile__shift 0x00
++#define ma_pmprofile__reset 0x00
++//-------------------------------------------------------------------pm3_man---
++// custom profile pm3 contents. 0=>a, 1=>b, 2=>c, 3=>d
++#define ma_pm3_man__a 30
++#define ma_pm3_man__len 2
++#define ma_pm3_man__mask 0x30
++#define ma_pm3_man__shift 0x04
++#define ma_pm3_man__reset 0x02
++//-------------------------------------------------------------------pm2_man---
++// custom profile pm2 contents. 0=>a, 1=>b, 2=>c, 3=>d
++#define ma_pm2_man__a 30
++#define ma_pm2_man__len 2
++#define ma_pm2_man__mask 0x0c
++#define ma_pm2_man__shift 0x02
++#define ma_pm2_man__reset 0x03
++//-------------------------------------------------------------------pm1_man---
++// custom profile pm1 contents. 0=>a, 1=>b, 2=>c, 3=>d
++#define ma_pm1_man__a 30
++#define ma_pm1_man__len 2
++#define ma_pm1_man__mask 0x03
++#define ma_pm1_man__shift 0x00
++#define ma_pm1_man__reset 0x03
++//-----------------------------------------------------------ocp_latch_clear---
++// low-high clears current ocp latched condition.
++#define ma_ocp_latch_clear__a 32
++#define ma_ocp_latch_clear__len 1
++#define ma_ocp_latch_clear__mask 0x80
++#define ma_ocp_latch_clear__shift 0x07
++#define ma_ocp_latch_clear__reset 0x00
++//-------------------------------------------------------------audio_in_mode---
++// audio input mode; 0-1-2-3-4-5
++#define ma_audio_in_mode__a 37
++#define ma_audio_in_mode__len 3
++#define ma_audio_in_mode__mask 0xe0
++#define ma_audio_in_mode__shift 0x05
++#define ma_audio_in_mode__reset 0x00
++//-----------------------------------------------------------------eh_dcshdn---
++// high to enable dc protection
++#define ma_eh_dcshdn__a 38
++#define ma_eh_dcshdn__len 1
++#define ma_eh_dcshdn__mask 0x04
++#define ma_eh_dcshdn__shift 0x02
++#define ma_eh_dcshdn__reset 0x01
++//---------------------------------------------------------audio_in_mode_ext---
++// if set, audio_in_mode is controlled from audio_in_mode register. if not set
++//audio_in_mode is set from fuse bank setting
++#define ma_audio_in_mode_ext__a 39
++#define ma_audio_in_mode_ext__len 1
++#define ma_audio_in_mode_ext__mask 0x20
++#define ma_audio_in_mode_ext__shift 0x05
++#define ma_audio_in_mode_ext__reset 0x00
++//------------------------------------------------------------------eh_clear---
++// flip to clear error registers
++#define ma_eh_clear__a 45
++#define ma_eh_clear__len 1
++#define ma_eh_clear__mask 0x04
++#define ma_eh_clear__shift 0x02
++#define ma_eh_clear__reset 0x00
++//----------------------------------------------------------thermal_compr_en---
++// enable otw-contr. input compression?
++#define ma_thermal_compr_en__a 45
++#define ma_thermal_compr_en__len 1
++#define ma_thermal_compr_en__mask 0x20
++#define ma_thermal_compr_en__shift 0x05
++#define ma_thermal_compr_en__reset 0x01
++//---------------------------------------------------------------system_mute---
++// 1 = mute system, 0 = normal operation
++#define ma_system_mute__a 45
++#define ma_system_mute__len 1
++#define ma_system_mute__mask 0x40
++#define ma_system_mute__shift 0x06
++#define ma_system_mute__reset 0x00
++//------------------------------------------------------thermal_compr_max_db---
++// audio limiter max thermal reduction
++#define ma_thermal_compr_max_db__a 46
++#define ma_thermal_compr_max_db__len 3
++#define ma_thermal_compr_max_db__mask 0x07
++#define ma_thermal_compr_max_db__shift 0x00
++#define ma_thermal_compr_max_db__reset 0x04
++//---------------------------------------------------------audio_proc_enable---
++// enable audio proc, bypass if not enabled
++#define ma_audio_proc_enable__a 53
++#define ma_audio_proc_enable__len 1
++#define ma_audio_proc_enable__mask 0x08
++#define ma_audio_proc_enable__shift 0x03
++#define ma_audio_proc_enable__reset 0x00
++//--------------------------------------------------------audio_proc_release---
++// 00:slow, 01:normal, 10:fast
++#define ma_audio_proc_release__a 53
++#define ma_audio_proc_release__len 2
++#define ma_audio_proc_release__mask 0x30
++#define ma_audio_proc_release__shift 0x04
++#define ma_audio_proc_release__reset 0x00
++//---------------------------------------------------------audio_proc_attack---
++// 00:slow, 01:normal, 10:fast
++#define ma_audio_proc_attack__a 53
++#define ma_audio_proc_attack__len 2
++#define ma_audio_proc_attack__mask 0xc0
++#define ma_audio_proc_attack__shift 0x06
++#define ma_audio_proc_attack__reset 0x00
++//----------------------------------------------------------------i2s_format---
++// i2s basic data format, 000 = std. i2s, 001 = left justified (default)
++#define ma_i2s_format__a 53
++#define ma_i2s_format__len 3
++#define ma_i2s_format__mask 0x07
++#define ma_i2s_format__shift 0x00
++#define ma_i2s_format__reset 0x01
++//--------------------------------------------------audio_proc_limiterenable---
++// 1: enable limiter, 0: disable limiter
++#define ma_audio_proc_limiterenable__a 54
++#define ma_audio_proc_limiterenable__len 1
++#define ma_audio_proc_limiterenable__mask 0x40
++#define ma_audio_proc_limiterenable__shift 0x06
++#define ma_audio_proc_limiterenable__reset 0x00
++//-----------------------------------------------------------audio_proc_mute---
++// 1: mute, 0: unmute
++#define ma_audio_proc_mute__a 54
++#define ma_audio_proc_mute__len 1
++#define ma_audio_proc_mute__mask 0x80
++#define ma_audio_proc_mute__shift 0x07
++#define ma_audio_proc_mute__reset 0x00
++//---------------------------------------------------------------i2s_sck_pol---
++// i2s sck polarity cfg. 0 = rising edge data change
++#define ma_i2s_sck_pol__a 54
++#define ma_i2s_sck_pol__len 1
++#define ma_i2s_sck_pol__mask 0x01
++#define ma_i2s_sck_pol__shift 0x00
++#define ma_i2s_sck_pol__reset 0x01
++//-------------------------------------------------------------i2s_framesize---
++// i2s word length. 00 = 32bit, 01 = 24bit
++#define ma_i2s_framesize__a 54
++#define ma_i2s_framesize__len 2
++#define ma_i2s_framesize__mask 0x18
++#define ma_i2s_framesize__shift 0x03
++#define ma_i2s_framesize__reset 0x00
++//----------------------------------------------------------------i2s_ws_pol---
++// i2s ws polarity. 0 = low first
++#define ma_i2s_ws_pol__a 54
++#define ma_i2s_ws_pol__len 1
++#define ma_i2s_ws_pol__mask 0x02
++#define ma_i2s_ws_pol__shift 0x01
++#define ma_i2s_ws_pol__reset 0x00
++//-----------------------------------------------------------------i2s_order---
++// i2s word bit order. 0 = msb first
++#define ma_i2s_order__a 54
++#define ma_i2s_order__len 1
++#define ma_i2s_order__mask 0x04
++#define ma_i2s_order__shift 0x02
++#define ma_i2s_order__reset 0x00
++//------------------------------------------------------------i2s_rightfirst---
++// i2s l/r word order; 0 = left first
++#define ma_i2s_rightfirst__a 54
++#define ma_i2s_rightfirst__len 1
++#define ma_i2s_rightfirst__mask 0x20
++#define ma_i2s_rightfirst__shift 0x05
++#define ma_i2s_rightfirst__reset 0x00
++//-------------------------------------------------------------vol_db_master---
++// master volume db
++#define ma_vol_db_master__a 64
++#define ma_vol_db_master__len 8
++#define ma_vol_db_master__mask 0xff
++#define ma_vol_db_master__shift 0x00
++#define ma_vol_db_master__reset 0x18
++//------------------------------------------------------------vol_lsb_master---
++// master volume lsb 1/4 steps
++#define ma_vol_lsb_master__a 65
++#define ma_vol_lsb_master__len 2
++#define ma_vol_lsb_master__mask 0x03
++#define ma_vol_lsb_master__shift 0x00
++#define ma_vol_lsb_master__reset 0x00
++//----------------------------------------------------------------vol_db_ch0---
++// volume channel 0
++#define ma_vol_db_ch0__a 66
++#define ma_vol_db_ch0__len 8
++#define ma_vol_db_ch0__mask 0xff
++#define ma_vol_db_ch0__shift 0x00
++#define ma_vol_db_ch0__reset 0x18
++//----------------------------------------------------------------vol_db_ch1---
++// volume channel 1
++#define ma_vol_db_ch1__a 67
++#define ma_vol_db_ch1__len 8
++#define ma_vol_db_ch1__mask 0xff
++#define ma_vol_db_ch1__shift 0x00
++#define ma_vol_db_ch1__reset 0x18
++//----------------------------------------------------------------vol_db_ch2---
++// volume channel 2
++#define ma_vol_db_ch2__a 68
++#define ma_vol_db_ch2__len 8
++#define ma_vol_db_ch2__mask 0xff
++#define ma_vol_db_ch2__shift 0x00
++#define ma_vol_db_ch2__reset 0x18
++//----------------------------------------------------------------vol_db_ch3---
++// volume channel 3
++#define ma_vol_db_ch3__a 69
++#define ma_vol_db_ch3__len 8
++#define ma_vol_db_ch3__mask 0xff
++#define ma_vol_db_ch3__shift 0x00
++#define ma_vol_db_ch3__reset 0x18
++//---------------------------------------------------------------vol_lsb_ch0---
++// volume channel 1 - 1/4 steps
++#define ma_vol_lsb_ch0__a 70
++#define ma_vol_lsb_ch0__len 2
++#define ma_vol_lsb_ch0__mask 0x03
++#define ma_vol_lsb_ch0__shift 0x00
++#define ma_vol_lsb_ch0__reset 0x00
++//---------------------------------------------------------------vol_lsb_ch1---
++// volume channel 3 - 1/4 steps
++#define ma_vol_lsb_ch1__a 70
++#define ma_vol_lsb_ch1__len 2
++#define ma_vol_lsb_ch1__mask 0x0c
++#define ma_vol_lsb_ch1__shift 0x02
++#define ma_vol_lsb_ch1__reset 0x00
++//---------------------------------------------------------------vol_lsb_ch2---
++// volume channel 2 - 1/4 steps
++#define ma_vol_lsb_ch2__a 70
++#define ma_vol_lsb_ch2__len 2
++#define ma_vol_lsb_ch2__mask 0x30
++#define ma_vol_lsb_ch2__shift 0x04
++#define ma_vol_lsb_ch2__reset 0x00
++//---------------------------------------------------------------vol_lsb_ch3---
++// volume channel 3 - 1/4 steps
++#define ma_vol_lsb_ch3__a 70
++#define ma_vol_lsb_ch3__len 2
++#define ma_vol_lsb_ch3__mask 0xc0
++#define ma_vol_lsb_ch3__shift 0x06
++#define ma_vol_lsb_ch3__reset 0x00
++//----------------------------------------------------------------thr_db_ch0---
++// thr_db channel 0
++#define ma_thr_db_ch0__a 71
++#define ma_thr_db_ch0__len 8
++#define ma_thr_db_ch0__mask 0xff
++#define ma_thr_db_ch0__shift 0x00
++#define ma_thr_db_ch0__reset 0x18
++//----------------------------------------------------------------thr_db_ch1---
++// thr db ch1
++#define ma_thr_db_ch1__a 72
++#define ma_thr_db_ch1__len 8
++#define ma_thr_db_ch1__mask 0xff
++#define ma_thr_db_ch1__shift 0x00
++#define ma_thr_db_ch1__reset 0x18
++//----------------------------------------------------------------thr_db_ch2---
++// thr db ch2
++#define ma_thr_db_ch2__a 73
++#define ma_thr_db_ch2__len 8
++#define ma_thr_db_ch2__mask 0xff
++#define ma_thr_db_ch2__shift 0x00
++#define ma_thr_db_ch2__reset 0x18
++//----------------------------------------------------------------thr_db_ch3---
++// threshold db ch3
++#define ma_thr_db_ch3__a 74
++#define ma_thr_db_ch3__len 8
++#define ma_thr_db_ch3__mask 0xff
++#define ma_thr_db_ch3__shift 0x00
++#define ma_thr_db_ch3__reset 0x18
++//---------------------------------------------------------------thr_lsb_ch0---
++// thr lsb ch0
++#define ma_thr_lsb_ch0__a 75
++#define ma_thr_lsb_ch0__len 2
++#define ma_thr_lsb_ch0__mask 0x03
++#define ma_thr_lsb_ch0__shift 0x00
++#define ma_thr_lsb_ch0__reset 0x00
++//---------------------------------------------------------------thr_lsb_ch1---
++// thr lsb ch1
++#define ma_thr_lsb_ch1__a 75
++#define ma_thr_lsb_ch1__len 2
++#define ma_thr_lsb_ch1__mask 0x0c
++#define ma_thr_lsb_ch1__shift 0x02
++#define ma_thr_lsb_ch1__reset 0x00
++//---------------------------------------------------------------thr_lsb_ch2---
++// thr lsb ch2 1/4 db step
++#define ma_thr_lsb_ch2__a 75
++#define ma_thr_lsb_ch2__len 2
++#define ma_thr_lsb_ch2__mask 0x30
++#define ma_thr_lsb_ch2__shift 0x04
++#define ma_thr_lsb_ch2__reset 0x00
++//---------------------------------------------------------------thr_lsb_ch3---
++// threshold lsb ch3
++#define ma_thr_lsb_ch3__a 75
++#define ma_thr_lsb_ch3__len 2
++#define ma_thr_lsb_ch3__mask 0xc0
++#define ma_thr_lsb_ch3__shift 0x06
++#define ma_thr_lsb_ch3__reset 0x00
++//-----------------------------------------------------------dcu_mon0.pm_mon---
++// power mode monitor channel 0
++#define ma_dcu_mon0__pm_mon__a 96
++#define ma_dcu_mon0__pm_mon__len 2
++#define ma_dcu_mon0__pm_mon__mask 0x03
++#define ma_dcu_mon0__pm_mon__shift 0x00
++#define ma_dcu_mon0__pm_mon__reset 0x00
++//-----------------------------------------------------dcu_mon0.freqmode_mon---
++// frequence mode monitor channel 0
++#define ma_dcu_mon0__freqmode_mon__a 96
++#define ma_dcu_mon0__freqmode_mon__len 3
++#define ma_dcu_mon0__freqmode_mon__mask 0x70
++#define ma_dcu_mon0__freqmode_mon__shift 0x04
++#define ma_dcu_mon0__freqmode_mon__reset 0x00
++//-------------------------------------------------------dcu_mon0.pps_passed---
++// dcu0 pps completion indicator
++#define ma_dcu_mon0__pps_passed__a 96
++#define ma_dcu_mon0__pps_passed__len 1
++#define ma_dcu_mon0__pps_passed__mask 0x80
++#define ma_dcu_mon0__pps_passed__shift 0x07
++#define ma_dcu_mon0__pps_passed__reset 0x00
++//----------------------------------------------------------dcu_mon0.ocp_mon---
++// ocp monitor channel 0
++#define ma_dcu_mon0__ocp_mon__a 97
++#define ma_dcu_mon0__ocp_mon__len 1
++#define ma_dcu_mon0__ocp_mon__mask 0x01
++#define ma_dcu_mon0__ocp_mon__shift 0x00
++#define ma_dcu_mon0__ocp_mon__reset 0x00
++//--------------------------------------------------------dcu_mon0.vcfly1_ok---
++// cfly1 protection monitor channel 0.
++#define ma_dcu_mon0__vcfly1_ok__a 97
++#define ma_dcu_mon0__vcfly1_ok__len 1
++#define ma_dcu_mon0__vcfly1_ok__mask 0x02
++#define ma_dcu_mon0__vcfly1_ok__shift 0x01
++#define ma_dcu_mon0__vcfly1_ok__reset 0x00
++//--------------------------------------------------------dcu_mon0.vcfly2_ok---
++// cfly2 protection monitor channel 0.
++#define ma_dcu_mon0__vcfly2_ok__a 97
++#define ma_dcu_mon0__vcfly2_ok__len 1
++#define ma_dcu_mon0__vcfly2_ok__mask 0x04
++#define ma_dcu_mon0__vcfly2_ok__shift 0x02
++#define ma_dcu_mon0__vcfly2_ok__reset 0x00
++//----------------------------------------------------------dcu_mon0.pvdd_ok---
++// dcu0 pvdd monitor
++#define ma_dcu_mon0__pvdd_ok__a 97
++#define ma_dcu_mon0__pvdd_ok__len 1
++#define ma_dcu_mon0__pvdd_ok__mask 0x08
++#define ma_dcu_mon0__pvdd_ok__shift 0x03
++#define ma_dcu_mon0__pvdd_ok__reset 0x00
++//-----------------------------------------------------------dcu_mon0.vdd_ok---
++// dcu0 vdd monitor
++#define ma_dcu_mon0__vdd_ok__a 97
++#define ma_dcu_mon0__vdd_ok__len 1
++#define ma_dcu_mon0__vdd_ok__mask 0x10
++#define ma_dcu_mon0__vdd_ok__shift 0x04
++#define ma_dcu_mon0__vdd_ok__reset 0x00
++//-------------------------------------------------------------dcu_mon0.mute---
++// dcu0 mute monitor
++#define ma_dcu_mon0__mute__a 97
++#define ma_dcu_mon0__mute__len 1
++#define ma_dcu_mon0__mute__mask 0x20
++#define ma_dcu_mon0__mute__shift 0x05
++#define ma_dcu_mon0__mute__reset 0x00
++//------------------------------------------------------------dcu_mon0.m_mon---
++// m sense monitor channel 0
++#define ma_dcu_mon0__m_mon__a 98
++#define ma_dcu_mon0__m_mon__len 8
++#define ma_dcu_mon0__m_mon__mask 0xff
++#define ma_dcu_mon0__m_mon__shift 0x00
++#define ma_dcu_mon0__m_mon__reset 0x00
++//-----------------------------------------------------------dcu_mon1.pm_mon---
++// power mode monitor channel 1
++#define ma_dcu_mon1__pm_mon__a 100
++#define ma_dcu_mon1__pm_mon__len 2
++#define ma_dcu_mon1__pm_mon__mask 0x03
++#define ma_dcu_mon1__pm_mon__shift 0x00
++#define ma_dcu_mon1__pm_mon__reset 0x00
++//-----------------------------------------------------dcu_mon1.freqmode_mon---
++// frequence mode monitor channel 1
++#define ma_dcu_mon1__freqmode_mon__a 100
++#define ma_dcu_mon1__freqmode_mon__len 3
++#define ma_dcu_mon1__freqmode_mon__mask 0x70
++#define ma_dcu_mon1__freqmode_mon__shift 0x04
++#define ma_dcu_mon1__freqmode_mon__reset 0x00
++//-------------------------------------------------------dcu_mon1.pps_passed---
++// dcu1 pps completion indicator
++#define ma_dcu_mon1__pps_passed__a 100
++#define ma_dcu_mon1__pps_passed__len 1
++#define ma_dcu_mon1__pps_passed__mask 0x80
++#define ma_dcu_mon1__pps_passed__shift 0x07
++#define ma_dcu_mon1__pps_passed__reset 0x00
++//----------------------------------------------------------dcu_mon1.ocp_mon---
++// ocp monitor channel 1
++#define ma_dcu_mon1__ocp_mon__a 101
++#define ma_dcu_mon1__ocp_mon__len 1
++#define ma_dcu_mon1__ocp_mon__mask 0x01
++#define ma_dcu_mon1__ocp_mon__shift 0x00
++#define ma_dcu_mon1__ocp_mon__reset 0x00
++//--------------------------------------------------------dcu_mon1.vcfly1_ok---
++// cfly1 protcetion monitor channel 1
++#define ma_dcu_mon1__vcfly1_ok__a 101
++#define ma_dcu_mon1__vcfly1_ok__len 1
++#define ma_dcu_mon1__vcfly1_ok__mask 0x02
++#define ma_dcu_mon1__vcfly1_ok__shift 0x01
++#define ma_dcu_mon1__vcfly1_ok__reset 0x00
++//--------------------------------------------------------dcu_mon1.vcfly2_ok---
++// cfly2 protection monitor channel 1
++#define ma_dcu_mon1__vcfly2_ok__a 101
++#define ma_dcu_mon1__vcfly2_ok__len 1
++#define ma_dcu_mon1__vcfly2_ok__mask 0x04
++#define ma_dcu_mon1__vcfly2_ok__shift 0x02
++#define ma_dcu_mon1__vcfly2_ok__reset 0x00
++//----------------------------------------------------------dcu_mon1.pvdd_ok---
++// dcu1 pvdd monitor
++#define ma_dcu_mon1__pvdd_ok__a 101
++#define ma_dcu_mon1__pvdd_ok__len 1
++#define ma_dcu_mon1__pvdd_ok__mask 0x08
++#define ma_dcu_mon1__pvdd_ok__shift 0x03
++#define ma_dcu_mon1__pvdd_ok__reset 0x00
++//-----------------------------------------------------------dcu_mon1.vdd_ok---
++// dcu1 vdd monitor
++#define ma_dcu_mon1__vdd_ok__a 101
++#define ma_dcu_mon1__vdd_ok__len 1
++#define ma_dcu_mon1__vdd_ok__mask 0x10
++#define ma_dcu_mon1__vdd_ok__shift 0x04
++#define ma_dcu_mon1__vdd_ok__reset 0x00
++//-------------------------------------------------------------dcu_mon1.mute---
++// dcu1 mute monitor
++#define ma_dcu_mon1__mute__a 101
++#define ma_dcu_mon1__mute__len 1
++#define ma_dcu_mon1__mute__mask 0x20
++#define ma_dcu_mon1__mute__shift 0x05
++#define ma_dcu_mon1__mute__reset 0x00
++//------------------------------------------------------------dcu_mon1.m_mon---
++// m sense monitor channel 1
++#define ma_dcu_mon1__m_mon__a 102
++#define ma_dcu_mon1__m_mon__len 8
++#define ma_dcu_mon1__m_mon__mask 0xff
++#define ma_dcu_mon1__m_mon__shift 0x00
++#define ma_dcu_mon1__m_mon__reset 0x00
++//--------------------------------------------------------dcu_mon0.sw_enable---
++// dcu0 switch enable monitor
++#define ma_dcu_mon0__sw_enable__a 104
++#define ma_dcu_mon0__sw_enable__len 1
++#define ma_dcu_mon0__sw_enable__mask 0x40
++#define ma_dcu_mon0__sw_enable__shift 0x06
++#define ma_dcu_mon0__sw_enable__reset 0x00
++//--------------------------------------------------------dcu_mon1.sw_enable---
++// dcu1 switch enable monitor
++#define ma_dcu_mon1__sw_enable__a 104
++#define ma_dcu_mon1__sw_enable__len 1
++#define ma_dcu_mon1__sw_enable__mask 0x80
++#define ma_dcu_mon1__sw_enable__shift 0x07
++#define ma_dcu_mon1__sw_enable__reset 0x00
++//------------------------------------------------------------hvboot0_ok_mon---
++// hvboot0_ok for test/debug
++#define ma_hvboot0_ok_mon__a 105
++#define ma_hvboot0_ok_mon__len 1
++#define ma_hvboot0_ok_mon__mask 0x40
++#define ma_hvboot0_ok_mon__shift 0x06
++#define ma_hvboot0_ok_mon__reset 0x00
++//------------------------------------------------------------hvboot1_ok_mon---
++// hvboot1_ok for test/debug
++#define ma_hvboot1_ok_mon__a 105
++#define ma_hvboot1_ok_mon__len 1
++#define ma_hvboot1_ok_mon__mask 0x80
++#define ma_hvboot1_ok_mon__shift 0x07
++#define ma_hvboot1_ok_mon__reset 0x00
++//-----------------------------------------------------------------error_acc---
++// accumulated errors, at and after triggering
++#define ma_error_acc__a 109
++#define ma_error_acc__len 8
++#define ma_error_acc__mask 0xff
++#define ma_error_acc__shift 0x00
++#define ma_error_acc__reset 0x00
++//-------------------------------------------------------------i2s_data_rate---
++// detected i2s data rate: 00/01/10 = x1/x2/x4
++#define ma_i2s_data_rate__a 116
++#define ma_i2s_data_rate__len 2
++#define ma_i2s_data_rate__mask 0x03
++#define ma_i2s_data_rate__shift 0x00
++#define ma_i2s_data_rate__reset 0x00
++//---------------------------------------------------------audio_in_mode_mon---
++// audio input mode monitor
++#define ma_audio_in_mode_mon__a 116
++#define ma_audio_in_mode_mon__len 3
++#define ma_audio_in_mode_mon__mask 0x1c
++#define ma_audio_in_mode_mon__shift 0x02
++#define ma_audio_in_mode_mon__reset 0x00
++//------------------------------------------------------------------msel_mon---
++// msel[2:0] monitor register
++#define ma_msel_mon__a 117
++#define ma_msel_mon__len 3
++#define ma_msel_mon__mask 0x07
++#define ma_msel_mon__shift 0x00
++#define ma_msel_mon__reset 0x00
++//---------------------------------------------------------------------error---
++// current error flag monitor reg - for app. ctrl.
++#define ma_error__a 124
++#define ma_error__len 8
++#define ma_error__mask 0xff
++#define ma_error__shift 0x00
++#define ma_error__reset 0x00
++//----------------------------------------------------audio_proc_limiter_mon---
++// b7-b4: channel 3-0 limiter active
++#define ma_audio_proc_limiter_mon__a 126
++#define ma_audio_proc_limiter_mon__len 4
++#define ma_audio_proc_limiter_mon__mask 0xf0
++#define ma_audio_proc_limiter_mon__shift 0x04
++#define ma_audio_proc_limiter_mon__reset 0x00
++//-------------------------------------------------------audio_proc_clip_mon---
++// b3-b0: channel 3-0 clipping monitor
++#define ma_audio_proc_clip_mon__a 126
++#define ma_audio_proc_clip_mon__len 4
++#define ma_audio_proc_clip_mon__mask 0x0f
++#define ma_audio_proc_clip_mon__shift 0x00
++#define ma_audio_proc_clip_mon__reset 0x00
++#endif
++
++#define SOC_ENUM_ERR(xname, xenum)\
++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
++ .access = SNDRV_CTL_ELEM_ACCESS_READ,\
++ .info = snd_soc_info_enum_double,\
++ .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double,\
++ .private_value = (unsigned long)&(xenum) }
++
++static struct i2c_client *i2c;
++
++struct ma120x0p_priv {
++ struct regmap *regmap;
++ int mclk_div;
++ struct snd_soc_component *component;
++ struct gpio_desc *enable_gpio;
++ struct gpio_desc *mute_gpio;
++ struct gpio_desc *booster_gpio;
++ struct gpio_desc *error_gpio;
++};
++
++static struct ma120x0p_priv *priv_data;
++
++//Used to share the IRQ number within this file
++static unsigned int irqNumber;
++
++// Function prototype for the custom IRQ handler function
++static irqreturn_t ma120x0p_irq_handler(int irq, void *data);
++
++//Alsa Controls
++static const char * const limenable_text[] = {"Bypassed", "Enabled"};
++static const char * const limatack_text[] = {"Slow", "Normal", "Fast"};
++static const char * const limrelease_text[] = {"Slow", "Normal", "Fast"};
++
++static const char * const err_flycap_text[] = {"Ok", "Error"};
++static const char * const err_overcurr_text[] = {"Ok", "Error"};
++static const char * const err_pllerr_text[] = {"Ok", "Error"};
++static const char * const err_pvddunder_text[] = {"Ok", "Error"};
++static const char * const err_overtempw_text[] = {"Ok", "Error"};
++static const char * const err_overtempe_text[] = {"Ok", "Error"};
++static const char * const err_pinlowimp_text[] = {"Ok", "Error"};
++static const char * const err_dcprot_text[] = {"Ok", "Error"};
++
++static const char * const pwr_mode_prof_text[] = {"PMF0", "PMF1", "PMF2",
++"PMF3", "PMF4"};
++
++static const struct soc_enum lim_enable_ctrl =
++ SOC_ENUM_SINGLE(ma_audio_proc_limiterenable__a,
++ ma_audio_proc_limiterenable__shift,
++ ma_audio_proc_limiterenable__len + 1,
++ limenable_text);
++static const struct soc_enum limatack_ctrl =
++ SOC_ENUM_SINGLE(ma_audio_proc_attack__a,
++ ma_audio_proc_attack__shift,
++ ma_audio_proc_attack__len + 1,
++ limatack_text);
++static const struct soc_enum limrelease_ctrl =
++ SOC_ENUM_SINGLE(ma_audio_proc_release__a,
++ ma_audio_proc_release__shift,
++ ma_audio_proc_release__len + 1,
++ limrelease_text);
++static const struct soc_enum err_flycap_ctrl =
++ SOC_ENUM_SINGLE(ma_error__a, 0, 3, err_flycap_text);
++static const struct soc_enum err_overcurr_ctrl =
++ SOC_ENUM_SINGLE(ma_error__a, 1, 3, err_overcurr_text);
++static const struct soc_enum err_pllerr_ctrl =
++ SOC_ENUM_SINGLE(ma_error__a, 2, 3, err_pllerr_text);
++static const struct soc_enum err_pvddunder_ctrl =
++ SOC_ENUM_SINGLE(ma_error__a, 3, 3, err_pvddunder_text);
++static const struct soc_enum err_overtempw_ctrl =
++ SOC_ENUM_SINGLE(ma_error__a, 4, 3, err_overtempw_text);
++static const struct soc_enum err_overtempe_ctrl =
++ SOC_ENUM_SINGLE(ma_error__a, 5, 3, err_overtempe_text);
++static const struct soc_enum err_pinlowimp_ctrl =
++ SOC_ENUM_SINGLE(ma_error__a, 6, 3, err_pinlowimp_text);
++static const struct soc_enum err_dcprot_ctrl =
++ SOC_ENUM_SINGLE(ma_error__a, 7, 3, err_dcprot_text);
++static const struct soc_enum pwr_mode_prof_ctrl =
++ SOC_ENUM_SINGLE(ma_pmprofile__a, ma_pmprofile__shift, 5,
++ pwr_mode_prof_text);
++
++static const char * const pwr_mode_texts[] = {
++ "Dynamic power mode",
++ "Power mode 1",
++ "Power mode 2",
++ "Power mode 3",
++ };
++
++static const int pwr_mode_values[] = {
++ 0x10,
++ 0x50,
++ 0x60,
++ 0x70,
++ };
++
++static const SOC_VALUE_ENUM_SINGLE_DECL(pwr_mode_ctrl,
++ ma_pm_man__a, 0, 0x70,
++ pwr_mode_texts,
++ pwr_mode_values);
++
++static const DECLARE_TLV_DB_SCALE(ma120x0p_vol_tlv, -5000, 100, 0);
++static const DECLARE_TLV_DB_SCALE(ma120x0p_lim_tlv, -5000, 100, 0);
++static const DECLARE_TLV_DB_SCALE(ma120x0p_lr_tlv, -5000, 100, 0);
++
++static const struct snd_kcontrol_new ma120x0p_snd_controls[] = {
++ //Master Volume
++ SOC_SINGLE_RANGE_TLV("A.Mstr Vol Volume",
++ ma_vol_db_master__a, 0, 0x18, 0x4a, 1, ma120x0p_vol_tlv),
++
++ //L-R Volume ch0
++ SOC_SINGLE_RANGE_TLV("B.L Vol Volume",
++ ma_vol_db_ch0__a, 0, 0x18, 0x4a, 1, ma120x0p_lr_tlv),
++ SOC_SINGLE_RANGE_TLV("C.R Vol Volume",
++ ma_vol_db_ch1__a, 0, 0x18, 0x4a, 1, ma120x0p_lr_tlv),
++
++ //L-R Limiter Threshold ch0-ch1
++ SOC_DOUBLE_R_RANGE_TLV("D.Lim thresh Volume",
++ ma_thr_db_ch0__a, ma_thr_db_ch1__a, 0, 0x0e, 0x4a, 1,
++ ma120x0p_lim_tlv),
++
++ //Enum Switches/Selectors
++ //SOC_ENUM("E.AudioProc Mute", audioproc_mute_ctrl),
++ SOC_ENUM("F.Limiter Enable", lim_enable_ctrl),
++ SOC_ENUM("G.Limiter Attck", limatack_ctrl),
++ SOC_ENUM("H.Limiter Rls", limrelease_ctrl),
++
++ //Enum Error Monitor (read-only)
++ SOC_ENUM_ERR("I.Err flycap", err_flycap_ctrl),
++ SOC_ENUM_ERR("J.Err overcurr", err_overcurr_ctrl),
++ SOC_ENUM_ERR("K.Err pllerr", err_pllerr_ctrl),
++ SOC_ENUM_ERR("L.Err pvddunder", err_pvddunder_ctrl),
++ SOC_ENUM_ERR("M.Err overtempw", err_overtempw_ctrl),
++ SOC_ENUM_ERR("N.Err overtempe", err_overtempe_ctrl),
++ SOC_ENUM_ERR("O.Err pinlowimp", err_pinlowimp_ctrl),
++ SOC_ENUM_ERR("P.Err dcprot", err_dcprot_ctrl),
++
++ //Power modes profiles
++ SOC_ENUM("Q.PM Prof", pwr_mode_prof_ctrl),
++
++ // Power mode selection (Dynamic,1,2,3)
++ SOC_ENUM("R.Power Mode", pwr_mode_ctrl),
++};
++
++//Machine Driver
++static int ma120x0p_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
++{
++ u16 blen = 0x00;
++
++ struct snd_soc_component *component = dai->component;
++
++ priv_data->component = component;
++
++ switch (params_format(params)) {
++ case SNDRV_PCM_FORMAT_S16_LE:
++ blen = 0x10;
++ break;
++ case SNDRV_PCM_FORMAT_S24_LE:
++ blen = 0x00;
++ break;
++ case SNDRV_PCM_FORMAT_S32_LE:
++ blen = 0x00;
++ break;
++ default:
++ dev_err(dai->dev, "Unsupported word length: %u\n",
++ params_format(params));
++ return -EINVAL;
++ }
++
++ // set word length
++ snd_soc_component_update_bits(component, ma_i2s_framesize__a,
++ ma_i2s_framesize__mask, blen);
++
++ return 0;
++}
++
++static int ma120x0p_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
++{
++ int val = 0;
++
++ struct ma120x0p_priv *ma120x0p;
++
++ struct snd_soc_component *component = dai->component;
++
++ ma120x0p = snd_soc_component_get_drvdata(component);
++
++ if (mute)
++ val = 0;
++ else
++ val = 1;
++
++ gpiod_set_value_cansleep(priv_data->mute_gpio, val);
++
++ return 0;
++}
++
++static const struct snd_soc_dai_ops ma120x0p_dai_ops = {
++ .hw_params = ma120x0p_hw_params,
++ .mute_stream = ma120x0p_mute_stream,
++};
++
++static struct snd_soc_dai_driver ma120x0p_dai = {
++ .name = "ma120x0p-amp",
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 2,
++ .channels_max = 2,
++ .rates = SNDRV_PCM_RATE_CONTINUOUS,
++ .rate_min = 44100,
++ .rate_max = 48000,
++ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE
++ },
++ .ops = &ma120x0p_dai_ops,
++};
++
++//Codec Driver
++static int ma120x0p_clear_err(struct snd_soc_component *component)
++{
++ int ret = 0;
++
++ struct ma120x0p_priv *ma120x0p;
++
++ ma120x0p = snd_soc_component_get_drvdata(component);
++
++ ret = snd_soc_component_update_bits(component,
++ ma_eh_clear__a, ma_eh_clear__mask, 0x00);
++ if (ret < 0)
++ return ret;
++
++ ret = snd_soc_component_update_bits(component,
++ ma_eh_clear__a, ma_eh_clear__mask, 0x04);
++ if (ret < 0)
++ return ret;
++
++ ret = snd_soc_component_update_bits(component,
++ ma_eh_clear__a, ma_eh_clear__mask, 0x00);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static void ma120x0p_remove(struct snd_soc_component *component)
++{
++ struct ma120x0p_priv *ma120x0p;
++
++ ma120x0p = snd_soc_component_get_drvdata(component);
++}
++
++static int ma120x0p_probe(struct snd_soc_component *component)
++{
++ struct ma120x0p_priv *ma120x0p;
++
++ int ret = 0;
++
++ i2c = container_of(component->dev, struct i2c_client, dev);
++
++ ma120x0p = snd_soc_component_get_drvdata(component);
++
++ //Reset error
++ ma120x0p_clear_err(component);
++ if (ret < 0)
++ return ret;
++
++ // set serial audio format I2S and enable audio processor
++ ret = snd_soc_component_write(component, ma_i2s_format__a, 0x08);
++ if (ret < 0)
++ return ret;
++
++ // Enable audio limiter
++ ret = snd_soc_component_update_bits(component,
++ ma_audio_proc_limiterenable__a,
++ ma_audio_proc_limiterenable__mask, 0x40);
++ if (ret < 0)
++ return ret;
++
++ // Set lim attack to fast
++ ret = snd_soc_component_update_bits(component,
++ ma_audio_proc_attack__a, ma_audio_proc_attack__mask, 0x80);
++ if (ret < 0)
++ return ret;
++
++ // Set lim attack to low
++ ret = snd_soc_component_update_bits(component,
++ ma_audio_proc_release__a, ma_audio_proc_release__mask, 0x00);
++ if (ret < 0)
++ return ret;
++
++ // set volume to 0dB
++ ret = snd_soc_component_write(component, ma_vol_db_master__a, 0x18);
++ if (ret < 0)
++ return ret;
++
++ // set ch0 lim thresh to -15dB
++ ret = snd_soc_component_write(component, ma_thr_db_ch0__a, 0x27);
++ if (ret < 0)
++ return ret;
++
++ // set ch1 lim thresh to -15dB
++ ret = snd_soc_component_write(component, ma_thr_db_ch1__a, 0x27);
++ if (ret < 0)
++ return ret;
++
++ //Check for errors
++ ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x00, 0);
++ if (ret < 0)
++ return ret;
++ ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x01, 0);
++ if (ret < 0)
++ return ret;
++ ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x02, 0);
++ if (ret < 0)
++ return ret;
++ ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x08, 0);
++ if (ret < 0)
++ return ret;
++ ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x10, 0);
++ if (ret < 0)
++ return ret;
++ ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x20, 0);
++ if (ret < 0)
++ return ret;
++ ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x40, 0);
++ if (ret < 0)
++ return ret;
++ ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x80, 0);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static int ma120x0p_set_bias_level(struct snd_soc_component *component,
++ enum snd_soc_bias_level level)
++{
++ int ret = 0;
++
++ struct ma120x0p_priv *ma120x0p;
++
++ ma120x0p = snd_soc_component_get_drvdata(component);
++
++ switch (level) {
++ case SND_SOC_BIAS_ON:
++ break;
++
++ case SND_SOC_BIAS_PREPARE:
++ break;
++
++ case SND_SOC_BIAS_STANDBY:
++ ret = gpiod_get_value_cansleep(priv_data->enable_gpio);
++ if (ret != 0) {
++ dev_err(component->dev, "Device ma120x0p disabled in STANDBY BIAS: %d\n",
++ ret);
++ return ret;
++ }
++ break;
++
++ case SND_SOC_BIAS_OFF:
++ break;
++ }
++
++ return 0;
++}
++
++static const struct snd_soc_dapm_widget ma120x0p_dapm_widgets[] = {
++ SND_SOC_DAPM_OUTPUT("OUT_A"),
++ SND_SOC_DAPM_OUTPUT("OUT_B"),
++};
++
++static const struct snd_soc_dapm_route ma120x0p_dapm_routes[] = {
++ { "OUT_B", NULL, "Playback" },
++ { "OUT_A", NULL, "Playback" },
++};
++
++static const struct snd_soc_component_driver ma120x0p_component_driver = {
++ .probe = ma120x0p_probe,
++ .remove = ma120x0p_remove,
++ .set_bias_level = ma120x0p_set_bias_level,
++ .dapm_widgets = ma120x0p_dapm_widgets,
++ .num_dapm_widgets = ARRAY_SIZE(ma120x0p_dapm_widgets),
++ .dapm_routes = ma120x0p_dapm_routes,
++ .num_dapm_routes = ARRAY_SIZE(ma120x0p_dapm_routes),
++ .controls = ma120x0p_snd_controls,
++ .num_controls = ARRAY_SIZE(ma120x0p_snd_controls),
++ .use_pmdown_time = 1,
++ .endianness = 1,
++ .non_legacy_dai_naming = 1,
++};
++
++//I2C Driver
++static const struct reg_default ma120x0p_reg_defaults[] = {
++ { 0x01, 0x3c },
++};
++
++static bool ma120x0p_reg_volatile(struct device *dev, unsigned int reg)
++{
++ switch (reg) {
++ case ma_error__a:
++ return true;
++ default:
++ return false;
++ }
++}
++
++static const struct of_device_id ma120x0p_of_match[] = {
++ { .compatible = "ma,ma120x0p", },
++ { }
++};
++
++MODULE_DEVICE_TABLE(of, ma120x0p_of_match);
++
++static struct regmap_config ma120x0p_regmap_config = {
++ .reg_bits = 8,
++ .val_bits = 8,
++
++ .max_register = 255,
++ .volatile_reg = ma120x0p_reg_volatile,
++
++ .cache_type = REGCACHE_RBTREE,
++ .reg_defaults = ma120x0p_reg_defaults,
++ .num_reg_defaults = ARRAY_SIZE(ma120x0p_reg_defaults),
++};
++
++static int ma120x0p_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;
++ i2c_set_clientdata(i2c, priv_data);
++
++ priv_data->regmap = devm_regmap_init_i2c(i2c, &ma120x0p_regmap_config);
++ if (IS_ERR(priv_data->regmap)) {
++ ret = PTR_ERR(priv_data->regmap);
++ return ret;
++ }
++
++ //Startup sequence
++
++ //Make sure the device is muted
++ priv_data->mute_gpio = devm_gpiod_get(&i2c->dev, "mute_gp",
++ GPIOD_OUT_LOW);
++ if (IS_ERR(priv_data->mute_gpio)) {
++ ret = PTR_ERR(priv_data->mute_gpio);
++ dev_err(&i2c->dev, "Failed to get mute gpio line: %d\n", ret);
++ return ret;
++ }
++ msleep(50);
++
++// MA120xx0P devices are usually powered by an integrated boost converter.
++// An option GPIO control line is provided to enable the booster properly and
++// in sync with the enable and mute GPIO lines.
++ priv_data->booster_gpio = devm_gpiod_get_optional(&i2c->dev,
++ "booster_gp", GPIOD_OUT_LOW);
++ if (IS_ERR(priv_data->booster_gpio)) {
++ ret = PTR_ERR(priv_data->booster_gpio);
++ dev_err(&i2c->dev,
++ "Failed to get booster enable gpio line: %d\n", ret);
++ return ret;
++ }
++ msleep(50);
++
++ //Enable booster and wait 200ms until stable PVDD
++ gpiod_set_value_cansleep(priv_data->booster_gpio, 1);
++ msleep(200);
++
++ //Enable ma120x0pp
++ priv_data->enable_gpio = devm_gpiod_get(&i2c->dev,
++ "enable_gp", GPIOD_OUT_LOW);
++ if (IS_ERR(priv_data->enable_gpio)) {
++ ret = PTR_ERR(priv_data->enable_gpio);
++ dev_err(&i2c->dev,
++ "Failed to get ma120x0p enable gpio line: %d\n", ret);
++ return ret;
++ }
++ msleep(50);
++
++ //Optional use of ma120x0pp error line as an interrupt trigger to
++ //platform GPIO.
++ //Get error input gpio ma120x0p
++ priv_data->error_gpio = devm_gpiod_get_optional(&i2c->dev,
++ "error_gp", GPIOD_IN);
++ if (IS_ERR(priv_data->error_gpio)) {
++ ret = PTR_ERR(priv_data->error_gpio);
++ dev_err(&i2c->dev,
++ "Failed to get ma120x0p error gpio line: %d\n", ret);
++ return ret;
++ }
++
++ if (priv_data->error_gpio != NULL) {
++ irqNumber = gpiod_to_irq(priv_data->error_gpio);
++
++ ret = devm_request_threaded_irq(&i2c->dev,
++ irqNumber, ma120x0p_irq_handler,
++ NULL, IRQF_TRIGGER_FALLING,
++ "ma120x0p", priv_data);
++ if (ret != 0)
++ dev_warn(&i2c->dev, "Failed to request IRQ: %d\n",
++ ret);
++ }
++
++ ret = devm_snd_soc_register_component(&i2c->dev,
++ &ma120x0p_component_driver, &ma120x0p_dai, 1);
++
++ return ret;
++}
++
++static irqreturn_t ma120x0p_irq_handler(int irq, void *data)
++{
++ gpiod_set_value_cansleep(priv_data->mute_gpio, 0);
++ gpiod_set_value_cansleep(priv_data->enable_gpio, 1);
++ return IRQ_HANDLED;
++}
++
++static int ma120x0p_i2c_remove(struct i2c_client *i2c)
++{
++ snd_soc_unregister_component(&i2c->dev);
++ i2c_set_clientdata(i2c, NULL);
++
++ gpiod_set_value_cansleep(priv_data->mute_gpio, 0);
++ msleep(30);
++ gpiod_set_value_cansleep(priv_data->enable_gpio, 1);
++ msleep(200);
++ gpiod_set_value_cansleep(priv_data->booster_gpio, 0);
++ msleep(200);
++
++ kfree(priv_data);
++
++ return 0;
++}
++
++static void ma120x0p_i2c_shutdown(struct i2c_client *i2c)
++{
++ snd_soc_unregister_component(&i2c->dev);
++ i2c_set_clientdata(i2c, NULL);
++
++ gpiod_set_value_cansleep(priv_data->mute_gpio, 0);
++ msleep(30);
++ gpiod_set_value_cansleep(priv_data->enable_gpio, 1);
++ msleep(200);
++ gpiod_set_value_cansleep(priv_data->booster_gpio, 0);
++ msleep(200);
++
++ kfree(priv_data);
++}
++
++static const struct i2c_device_id ma120x0p_i2c_id[] = {
++ { "ma120x0p", 0 },
++ { }
++};
++
++MODULE_DEVICE_TABLE(i2c, ma120x0p_i2c_id);
++
++static struct i2c_driver ma120x0p_i2c_driver = {
++ .driver = {
++ .name = "ma120x0p",
++ .owner = THIS_MODULE,
++ .of_match_table = ma120x0p_of_match,
++ },
++ .probe = ma120x0p_i2c_probe,
++ .remove = ma120x0p_i2c_remove,
++ .shutdown = ma120x0p_i2c_shutdown,
++ .id_table = ma120x0p_i2c_id
++};
++
++static int __init ma120x0p_modinit(void)
++{
++ int ret = 0;
++
++ ret = i2c_add_driver(&ma120x0p_i2c_driver);
++ if (ret != 0) {
++ pr_err("Failed to register MA120X0P I2C driver: %d\n", ret);
++ return ret;
++ }
++ return ret;
++}
++module_init(ma120x0p_modinit);
++
++static void __exit ma120x0p_exit(void)
++{
++ i2c_del_driver(&ma120x0p_i2c_driver);
++}
++module_exit(ma120x0p_exit);
++
++MODULE_AUTHOR("Ariel Muszkat ariel.muszkat@gmail.com>");
++MODULE_DESCRIPTION("ASoC driver for ma120x0p");
++MODULE_LICENSE("GPL v2");
+++ /dev/null
-From e253d03936265dc4ab8ae9ae89d2a885e80a45a6 Mon Sep 17 00:00:00 2001
-From: Matthias Reichl <hias@horus.com>
-Date: Fri, 6 Mar 2020 11:08:10 +0100
-Subject: [PATCH] gpio-ir-overlay: add parameter to configure signal
- polarity (#3490)
-
-Standard IR receivers use inverted / active-low signalling
-and the gpio-ir overlay configures the GPIO appropriately
-as GPIO_ACTIVE_LOW (1).
-
-In order to support (rather rare) non-inverted / active-high
-signalling the GPIO needs to be configured as GPIO_ACTIVE_HIGH (0).
-
-Add an "invert" parameter to override this like in the gpio-ir-tx
-overlay.
-
-Signed-off-by: Matthias Reichl <hias@horus.com>
----
- arch/arm/boot/dts/overlays/README | 4 ++++
- arch/arm/boot/dts/overlays/gpio-ir-overlay.dts | 1 +
- 2 files changed, 5 insertions(+)
-
---- a/arch/arm/boot/dts/overlays/README
-+++ b/arch/arm/boot/dts/overlays/README
-@@ -754,6 +754,10 @@ Params: gpio_pin Input pi
- gpio_pull Desired pull-up/down state (off, down, up)
- Default is "up".
-
-+ invert "1" = invert the input (active-low signalling).
-+ "0" = non-inverted input (active-high
-+ signalling). Default is "1".
-+
- rc-map-name Default rc keymap (can also be changed by
- ir-keytable), defaults to "rc-rc6-mce"
-
---- a/arch/arm/boot/dts/overlays/gpio-ir-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/gpio-ir-overlay.dts
-@@ -42,6 +42,7 @@
- <&gpio_ir_pins>,"brcm,pins:0",
- <&gpio_ir_pins>,"reg:0";
- gpio_pull = <&gpio_ir_pins>,"brcm,pull:0"; // pull-up/down state
-+ invert = <&gpio_ir>,"gpios:8"; // 0 = active high input
-
- rc-map-name = <&gpio_ir>,"linux,rc-map-name"; // default rc map
- };
--- /dev/null
+From e25d9a93812847b4ddc9e883d0cd45b32f8e2f76 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 17 Mar 2020 16:39:07 +0000
+Subject: [PATCH] ARM: dts: bcm2711: Add 32-bit PMU compatibility
+
+The "arm" architecture has no support for the cortex-a72 as such, but
+the performance and measurement unit from the cortex-a15 seems to be
+compatible.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2711-rpi.dtsi | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi
+@@ -12,6 +12,10 @@
+ sd_poll_once = <&emmc2>, "non-removable?";
+ };
+
++ arm-pmu {
++ compatible = "arm,cortex-a72-pmu", "arm,cortex-a15-pmu";
++ };
++
+ v3dbus {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+++ /dev/null
-From 76e0edf9676388c58bb5f0d7dda8eb8029926c6d Mon Sep 17 00:00:00 2001
-From: AMuszkat <ariel.muszkat@gmail.com>
-Date: Mon, 24 Feb 2020 22:56:59 +0100
-Subject: [PATCH] Add support for merus-amp soundcard and ma120x0p
- codec
-
-correct checkpatch warnings and errors
-
-Signed-off-by: AMuszkat <ariel.muszkat@gmail.com>
----
- arch/arm/boot/dts/overlays/Makefile | 1 +
- arch/arm/boot/dts/overlays/README | 6 +
- .../boot/dts/overlays/merus-amp-overlay.dts | 60 +
- sound/soc/bcm/rpi-simple-soundcard.c | 28 +
- sound/soc/codecs/Kconfig | 8 +
- sound/soc/codecs/Makefile | 2 +
- sound/soc/codecs/ma120x0p.c | 1384 +++++++++++++++++
- 7 files changed, 1489 insertions(+)
- create mode 100644 arch/arm/boot/dts/overlays/merus-amp-overlay.dts
- create mode 100644 sound/soc/codecs/ma120x0p.c
-
---- a/arch/arm/boot/dts/overlays/Makefile
-+++ b/arch/arm/boot/dts/overlays/Makefile
-@@ -103,6 +103,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
- mcp3202.dtbo \
- mcp342x.dtbo \
- media-center.dtbo \
-+ merus-amp.dtbo \
- midi-uart0.dtbo \
- midi-uart1.dtbo \
- miniuart-bt.dtbo \
---- a/arch/arm/boot/dts/overlays/README
-+++ b/arch/arm/boot/dts/overlays/README
-@@ -1662,6 +1662,12 @@ Params: speed Display
- (default "off")
-
-
-+Name: merus-amp
-+Info: Configures the merus-amp audio card
-+Load: dtoverlay=merus-amp
-+Params: <None>
-+
-+
- Name: midi-uart0
- Info: Configures UART0 (ttyAMA0) so that a requested 38.4kbaud actually gets
- 31.25kbaud, the frequency required for MIDI
---- /dev/null
-+++ b/arch/arm/boot/dts/overlays/merus-amp-overlay.dts
-@@ -0,0 +1,60 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+// Definitions for Infineon Merus-Amp
-+/dts-v1/;
-+/plugin/;
-+#include <dt-bindings/pinctrl/bcm2835.h>
-+#include <dt-bindings/gpio/gpio.h>
-+
-+
-+/ {
-+ compatible = "brcm,bcm2835";
-+
-+ fragment@0 {
-+ target = <&i2s>;
-+ __overlay__ {
-+ status = "okay";
-+ };
-+ };
-+
-+ fragment@1 {
-+ target = <&gpio>;
-+ __overlay__ {
-+ merus_amp_pins: merus_amp_pins {
-+ brcm,pins = <23>;
-+ brcm,function = <0>; /* in */
-+ brcm,pull = <2>; /* up */
-+ };
-+ };
-+ };
-+
-+ fragment@2 {
-+ target = <&i2c1>;
-+ __overlay__ {
-+ #address-cells = <1>;
-+ #size-cells = <0>;
-+ status = "okay";
-+
-+ merus_amp: ma120x0p@20 {
-+ #sound-dai-cells = <0>;
-+ compatible = "ma,ma120x0p";
-+ reg = <0x20>;
-+ status = "okay";
-+ pinctrl-names = "default";
-+ pinctrl-0 = <&merus_amp_pins>;
-+ enable_gp-gpios = <&gpio 14 GPIO_ACTIVE_HIGH>;
-+ mute_gp-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>;
-+ booster_gp-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>;
-+ error_gp-gpios = <&gpio 23 GPIO_ACTIVE_HIGH>;
-+ };
-+ };
-+ };
-+
-+ fragment@3 {
-+ target = <&sound>;
-+ __overlay__ {
-+ compatible = "merus,merus-amp";
-+ i2s-controller = <&i2s>;
-+ status = "okay";
-+ };
-+ };
-+};
---- a/sound/soc/bcm/rpi-simple-soundcard.c
-+++ b/sound/soc/bcm/rpi-simple-soundcard.c
-@@ -16,6 +16,10 @@
- * adau1977-adc.c
- * by Andrey Grodzovsky <andrey2805@gmail.com>
- *
-+ * merus-amp.c
-+ * by Ariel Muszkat <ariel.muszkat@gmail.com>
-+ * Jorgen Kragh Jakobsen <jorgen.kraghjakobsen@infineon.com>
-+ *
- * 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.
-@@ -229,6 +233,28 @@ static struct snd_rpi_simple_drvdata drv
- .fixed_bclk_ratio = 64,
- };
-
-+SND_SOC_DAILINK_DEFS(merus_amp,
-+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
-+ DAILINK_COMP_ARRAY(COMP_CODEC("ma120x0p-amp", "ma120x0p.1-0020")),
-+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
-+
-+static struct snd_soc_dai_link snd_merus_amp_dai[] = {
-+ {
-+ .name = "MerusAmp",
-+ .stream_name = "Merus Audio Amp",
-+ .dai_fmt = SND_SOC_DAIFMT_I2S |
-+ SND_SOC_DAIFMT_NB_NF |
-+ SND_SOC_DAIFMT_CBS_CFS,
-+ SND_SOC_DAILINK_REG(merus_amp),
-+ },
-+};
-+
-+static struct snd_rpi_simple_drvdata drvdata_merus_amp = {
-+ .card_name = "snd_rpi_merus_amp",
-+ .dai = snd_merus_amp_dai,
-+ .fixed_bclk_ratio = 64,
-+};
-+
- static const struct of_device_id snd_rpi_simple_of_match[] = {
- { .compatible = "adi,adau1977-adc",
- .data = (void *) &drvdata_adau1977 },
-@@ -241,6 +267,8 @@ static const struct of_device_id snd_rpi
- { .compatible = "hifiberry,hifiberry-dac",
- .data = (void *) &drvdata_hifiberry_dac },
- { .compatible = "rpi,rpi-dac", &drvdata_rpi_dac},
-+ { .compatible = "merus,merus-amp",
-+ .data = (void *) &drvdata_merus_amp },
- {},
- };
-
---- a/sound/soc/codecs/Kconfig
-+++ b/sound/soc/codecs/Kconfig
-@@ -103,6 +103,7 @@ config SND_SOC_ALL_CODECS
- select SND_SOC_LM4857 if I2C
- select SND_SOC_LM49453 if I2C
- select SND_SOC_LOCHNAGAR_SC if MFD_LOCHNAGAR
-+ select SND_SOC_MA120X0P if I2C
- select SND_SOC_MAX98088 if I2C
- select SND_SOC_MAX98090 if I2C
- select SND_SOC_MAX98095 if I2C
-@@ -732,6 +733,13 @@ config SND_SOC_LOCHNAGAR_SC
- This driver support the sound card functionality of the Cirrus
- Logic Lochnagar audio development board.
-
-+config SND_SOC_MA120X0P
-+ tristate "Infineon Merus(TM) MA120X0P Multilevel Class-D Audio amplifiers"
-+ depends on I2C
-+ help
-+ Enable support for Infineon MA120X0P Multilevel Class-D audio power
-+ amplifiers.
-+
- config SND_SOC_MADERA
- tristate
- default y if SND_SOC_CS47L15=y
---- a/sound/soc/codecs/Makefile
-+++ b/sound/soc/codecs/Makefile
-@@ -99,6 +99,7 @@ snd-soc-l3-objs := l3.o
- snd-soc-lm4857-objs := lm4857.o
- snd-soc-lm49453-objs := lm49453.o
- snd-soc-lochnagar-sc-objs := lochnagar-sc.o
-+snd-soc-ma120x0p-objs := ma120x0p.o
- snd-soc-madera-objs := madera.o
- snd-soc-max9759-objs := max9759.o
- snd-soc-max9768-objs := max9768.o
-@@ -386,6 +387,7 @@ obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
- obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o
- obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o
- obj-$(CONFIG_SND_SOC_LOCHNAGAR_SC) += snd-soc-lochnagar-sc.o
-+obj-$(CONFIG_SND_SOC_MA120X0P) += snd-soc-ma120x0p.o
- obj-$(CONFIG_SND_SOC_MADERA) += snd-soc-madera.o
- obj-$(CONFIG_SND_SOC_MAX9759) += snd-soc-max9759.o
- obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o
---- /dev/null
-+++ b/sound/soc/codecs/ma120x0p.c
-@@ -0,0 +1,1384 @@
-+// SPDX-License-Identifier: GPL-2.0-or-later
-+/*
-+ * ASoC Driver for Infineon Merus(TM) ma120x0p multi-level class-D amplifier
-+ *
-+ * Authors: Ariel Muszkat <ariel.muszkat@gmail.com>
-+ * Jorgen Kragh Jakobsen <jorgen.kraghjakobsen@infineon.com>
-+ *
-+ * Copyright (C) 2019 Infineon Technologies AG
-+ *
-+ */
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/init.h>
-+#include <linux/delay.h>
-+#include <linux/pm_runtime.h>
-+#include <linux/i2c.h>
-+#include <linux/of_device.h>
-+#include <linux/spi/spi.h>
-+#include <linux/regmap.h>
-+#include <linux/regulator/consumer.h>
-+#include <linux/slab.h>
-+#include <linux/gpio/consumer.h>
-+#include <linux/gpio.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+#include <sound/initval.h>
-+#include <sound/tlv.h>
-+#include <linux/interrupt.h>
-+
-+#include <linux/kernel.h>
-+#include <linux/string.h>
-+#include <linux/fs.h>
-+#include <linux/uaccess.h>
-+
-+#ifndef _MA120X0P_
-+#define _MA120X0P_
-+//------------------------------------------------------------------manualPM---
-+// Select Manual PowerMode control
-+#define ma_manualpm__a 0
-+#define ma_manualpm__len 1
-+#define ma_manualpm__mask 0x40
-+#define ma_manualpm__shift 0x06
-+#define ma_manualpm__reset 0x00
-+//--------------------------------------------------------------------pm_man---
-+// manual selected power mode
-+#define ma_pm_man__a 0
-+#define ma_pm_man__len 2
-+#define ma_pm_man__mask 0x30
-+#define ma_pm_man__shift 0x04
-+#define ma_pm_man__reset 0x03
-+//------------------------------------------ ----------------------mthr_1to2---
-+// mod. index threshold value for pm1=>pm2 change.
-+#define ma_mthr_1to2__a 1
-+#define ma_mthr_1to2__len 8
-+#define ma_mthr_1to2__mask 0xff
-+#define ma_mthr_1to2__shift 0x00
-+#define ma_mthr_1to2__reset 0x3c
-+//-----------------------------------------------------------------mthr_2to1---
-+// mod. index threshold value for pm2=>pm1 change.
-+#define ma_mthr_2to1__a 2
-+#define ma_mthr_2to1__len 8
-+#define ma_mthr_2to1__mask 0xff
-+#define ma_mthr_2to1__shift 0x00
-+#define ma_mthr_2to1__reset 0x32
-+//-----------------------------------------------------------------mthr_2to3---
-+// mod. index threshold value for pm2=>pm3 change.
-+#define ma_mthr_2to3__a 3
-+#define ma_mthr_2to3__len 8
-+#define ma_mthr_2to3__mask 0xff
-+#define ma_mthr_2to3__shift 0x00
-+#define ma_mthr_2to3__reset 0x5a
-+//-----------------------------------------------------------------mthr_3to2---
-+// mod. index threshold value for pm3=>pm2 change.
-+#define ma_mthr_3to2__a 4
-+#define ma_mthr_3to2__len 8
-+#define ma_mthr_3to2__mask 0xff
-+#define ma_mthr_3to2__shift 0x00
-+#define ma_mthr_3to2__reset 0x50
-+//-------------------------------------------------------------pwmclkdiv_nom---
-+// pwm default clock divider value
-+#define ma_pwmclkdiv_nom__a 8
-+#define ma_pwmclkdiv_nom__len 8
-+#define ma_pwmclkdiv_nom__mask 0xff
-+#define ma_pwmclkdiv_nom__shift 0x00
-+#define ma_pwmclkdiv_nom__reset 0x26
-+//--------- ----------------------------------------------------ocp_latch_en---
-+// high to use permanently latching level-2 ocp
-+#define ma_ocp_latch_en__a 10
-+#define ma_ocp_latch_en__len 1
-+#define ma_ocp_latch_en__mask 0x02
-+#define ma_ocp_latch_en__shift 0x01
-+#define ma_ocp_latch_en__reset 0x00
-+//---------------------------------------------------------------lf_clamp_en---
-+// high (default) to enable lf int2+3 clamping on clip
-+#define ma_lf_clamp_en__a 10
-+#define ma_lf_clamp_en__len 1
-+#define ma_lf_clamp_en__mask 0x80
-+#define ma_lf_clamp_en__shift 0x07
-+#define ma_lf_clamp_en__reset 0x00
-+//-------------------------------------------------------pmcfg_btl_b.modtype---
-+//
-+#define ma_pmcfg_btl_b__modtype__a 18
-+#define ma_pmcfg_btl_b__modtype__len 2
-+#define ma_pmcfg_btl_b__modtype__mask 0x18
-+#define ma_pmcfg_btl_b__modtype__shift 0x03
-+#define ma_pmcfg_btl_b__modtype__reset 0x02
-+//-------------------------------------------------------pmcfg_btl_b.freqdiv---
-+#define ma_pmcfg_btl_b__freqdiv__a 18
-+#define ma_pmcfg_btl_b__freqdiv__len 2
-+#define ma_pmcfg_btl_b__freqdiv__mask 0x06
-+#define ma_pmcfg_btl_b__freqdiv__shift 0x01
-+#define ma_pmcfg_btl_b__freqdiv__reset 0x01
-+//----------------------------------------------------pmcfg_btl_b.lf_gain_ol---
-+//
-+#define ma_pmcfg_btl_b__lf_gain_ol__a 18
-+#define ma_pmcfg_btl_b__lf_gain_ol__len 1
-+#define ma_pmcfg_btl_b__lf_gain_ol__mask 0x01
-+#define ma_pmcfg_btl_b__lf_gain_ol__shift 0x00
-+#define ma_pmcfg_btl_b__lf_gain_ol__reset 0x01
-+//-------------------------------------------------------pmcfg_btl_c.freqdiv---
-+//
-+#define ma_pmcfg_btl_c__freqdiv__a 19
-+#define ma_pmcfg_btl_c__freqdiv__len 2
-+#define ma_pmcfg_btl_c__freqdiv__mask 0x06
-+#define ma_pmcfg_btl_c__freqdiv__shift 0x01
-+#define ma_pmcfg_btl_c__freqdiv__reset 0x01
-+//-------------------------------------------------------pmcfg_btl_c.modtype---
-+//
-+#define ma_pmcfg_btl_c__modtype__a 19
-+#define ma_pmcfg_btl_c__modtype__len 2
-+#define ma_pmcfg_btl_c__modtype__mask 0x18
-+#define ma_pmcfg_btl_c__modtype__shift 0x03
-+#define ma_pmcfg_btl_c__modtype__reset 0x01
-+//----------------------------------------------------pmcfg_btl_c.lf_gain_ol---
-+//
-+#define ma_pmcfg_btl_c__lf_gain_ol__a 19
-+#define ma_pmcfg_btl_c__lf_gain_ol__len 1
-+#define ma_pmcfg_btl_c__lf_gain_ol__mask 0x01
-+#define ma_pmcfg_btl_c__lf_gain_ol__shift 0x00
-+#define ma_pmcfg_btl_c__lf_gain_ol__reset 0x00
-+//-------------------------------------------------------pmcfg_btl_d.modtype---
-+//
-+#define ma_pmcfg_btl_d__modtype__a 20
-+#define ma_pmcfg_btl_d__modtype__len 2
-+#define ma_pmcfg_btl_d__modtype__mask 0x18
-+#define ma_pmcfg_btl_d__modtype__shift 0x03
-+#define ma_pmcfg_btl_d__modtype__reset 0x02
-+//-------------------------------------------------------pmcfg_btl_d.freqdiv---
-+//
-+#define ma_pmcfg_btl_d__freqdiv__a 20
-+#define ma_pmcfg_btl_d__freqdiv__len 2
-+#define ma_pmcfg_btl_d__freqdiv__mask 0x06
-+#define ma_pmcfg_btl_d__freqdiv__shift 0x01
-+#define ma_pmcfg_btl_d__freqdiv__reset 0x02
-+//----------------------------------------------------pmcfg_btl_d.lf_gain_ol---
-+//
-+#define ma_pmcfg_btl_d__lf_gain_ol__a 20
-+#define ma_pmcfg_btl_d__lf_gain_ol__len 1
-+#define ma_pmcfg_btl_d__lf_gain_ol__mask 0x01
-+#define ma_pmcfg_btl_d__lf_gain_ol__shift 0x00
-+#define ma_pmcfg_btl_d__lf_gain_ol__reset 0x00
-+//------------ -------------------------------------------pmcfg_se_a.modtype---
-+//
-+#define ma_pmcfg_se_a__modtype__a 21
-+#define ma_pmcfg_se_a__modtype__len 2
-+#define ma_pmcfg_se_a__modtype__mask 0x18
-+#define ma_pmcfg_se_a__modtype__shift 0x03
-+#define ma_pmcfg_se_a__modtype__reset 0x01
-+//--------------------------------------------------------pmcfg_se_a.freqdiv---
-+//
-+#define ma_pmcfg_se_a__freqdiv__a 21
-+#define ma_pmcfg_se_a__freqdiv__len 2
-+#define ma_pmcfg_se_a__freqdiv__mask 0x06
-+#define ma_pmcfg_se_a__freqdiv__shift 0x01
-+#define ma_pmcfg_se_a__freqdiv__reset 0x00
-+//-----------------------------------------------------pmcfg_se_a.lf_gain_ol---
-+//
-+#define ma_pmcfg_se_a__lf_gain_ol__a 21
-+#define ma_pmcfg_se_a__lf_gain_ol__len 1
-+#define ma_pmcfg_se_a__lf_gain_ol__mask 0x01
-+#define ma_pmcfg_se_a__lf_gain_ol__shift 0x00
-+#define ma_pmcfg_se_a__lf_gain_ol__reset 0x01
-+//-----------------------------------------------------pmcfg_se_b.lf_gain_ol---
-+//
-+#define ma_pmcfg_se_b__lf_gain_ol__a 22
-+#define ma_pmcfg_se_b__lf_gain_ol__len 1
-+#define ma_pmcfg_se_b__lf_gain_ol__mask 0x01
-+#define ma_pmcfg_se_b__lf_gain_ol__shift 0x00
-+#define ma_pmcfg_se_b__lf_gain_ol__reset 0x00
-+//--------------------------------------------------------pmcfg_se_b.freqdiv---
-+//
-+#define ma_pmcfg_se_b__freqdiv__a 22
-+#define ma_pmcfg_se_b__freqdiv__len 2
-+#define ma_pmcfg_se_b__freqdiv__mask 0x06
-+#define ma_pmcfg_se_b__freqdiv__shift 0x01
-+#define ma_pmcfg_se_b__freqdiv__reset 0x01
-+//--------------------------------------------------------pmcfg_se_b.modtype---
-+//
-+#define ma_pmcfg_se_b__modtype__a 22
-+#define ma_pmcfg_se_b__modtype__len 2
-+#define ma_pmcfg_se_b__modtype__mask 0x18
-+#define ma_pmcfg_se_b__modtype__shift 0x03
-+#define ma_pmcfg_se_b__modtype__reset 0x01
-+//----------------------------------------------------------balwaitcount_pm1---
-+// pm1 balancing period.
-+#define ma_balwaitcount_pm1__a 23
-+#define ma_balwaitcount_pm1__len 8
-+#define ma_balwaitcount_pm1__mask 0xff
-+#define ma_balwaitcount_pm1__shift 0x00
-+#define ma_balwaitcount_pm1__reset 0x14
-+//----------------------------------------------------------balwaitcount_pm2---
-+// pm2 balancing period.
-+#define ma_balwaitcount_pm2__a 24
-+#define ma_balwaitcount_pm2__len 8
-+#define ma_balwaitcount_pm2__mask 0xff
-+#define ma_balwaitcount_pm2__shift 0x00
-+#define ma_balwaitcount_pm2__reset 0x14
-+//----------------------------------------------------------balwaitcount_pm3---
-+// pm3 balancing period.
-+#define ma_balwaitcount_pm3__a 25
-+#define ma_balwaitcount_pm3__len 8
-+#define ma_balwaitcount_pm3__mask 0xff
-+#define ma_balwaitcount_pm3__shift 0x00
-+#define ma_balwaitcount_pm3__reset 0x1a
-+//-------------------------------------------------------------usespread_pm1---
-+// pm1 pwm spread-spectrum mode on/off.
-+#define ma_usespread_pm1__a 26
-+#define ma_usespread_pm1__len 1
-+#define ma_usespread_pm1__mask 0x40
-+#define ma_usespread_pm1__shift 0x06
-+#define ma_usespread_pm1__reset 0x00
-+//---------------------------------------------------------------dtsteps_pm1---
-+// pm1 dead time setting [10ns steps].
-+#define ma_dtsteps_pm1__a 26
-+#define ma_dtsteps_pm1__len 3
-+#define ma_dtsteps_pm1__mask 0x38
-+#define ma_dtsteps_pm1__shift 0x03
-+#define ma_dtsteps_pm1__reset 0x04
-+//---------------------------------------------------------------baltype_pm1---
-+// pm1 balancing sensor scheme.
-+#define ma_baltype_pm1__a 26
-+#define ma_baltype_pm1__len 3
-+#define ma_baltype_pm1__mask 0x07
-+#define ma_baltype_pm1__shift 0x00
-+#define ma_baltype_pm1__reset 0x00
-+//-------------------------------------------------------------usespread_pm2---
-+// pm2 pwm spread-spectrum mode on/off.
-+#define ma_usespread_pm2__a 27
-+#define ma_usespread_pm2__len 1
-+#define ma_usespread_pm2__mask 0x40
-+#define ma_usespread_pm2__shift 0x06
-+#define ma_usespread_pm2__reset 0x00
-+//---------------------------------------------------------------dtsteps_pm2---
-+// pm2 dead time setting [10ns steps].
-+#define ma_dtsteps_pm2__a 27
-+#define ma_dtsteps_pm2__len 3
-+#define ma_dtsteps_pm2__mask 0x38
-+#define ma_dtsteps_pm2__shift 0x03
-+#define ma_dtsteps_pm2__reset 0x03
-+//---------------------------------------------------------------baltype_pm2---
-+// pm2 balancing sensor scheme.
-+#define ma_baltype_pm2__a 27
-+#define ma_baltype_pm2__len 3
-+#define ma_baltype_pm2__mask 0x07
-+#define ma_baltype_pm2__shift 0x00
-+#define ma_baltype_pm2__reset 0x01
-+//-------------------------------------------------------------usespread_pm3---
-+// pm3 pwm spread-spectrum mode on/off.
-+#define ma_usespread_pm3__a 28
-+#define ma_usespread_pm3__len 1
-+#define ma_usespread_pm3__mask 0x40
-+#define ma_usespread_pm3__shift 0x06
-+#define ma_usespread_pm3__reset 0x00
-+//---------------------------------------------------------------dtsteps_pm3---
-+// pm3 dead time setting [10ns steps].
-+#define ma_dtsteps_pm3__a 28
-+#define ma_dtsteps_pm3__len 3
-+#define ma_dtsteps_pm3__mask 0x38
-+#define ma_dtsteps_pm3__shift 0x03
-+#define ma_dtsteps_pm3__reset 0x01
-+//---------------------------------------------------------------baltype_pm3---
-+// pm3 balancing sensor scheme.
-+#define ma_baltype_pm3__a 28
-+#define ma_baltype_pm3__len 3
-+#define ma_baltype_pm3__mask 0x07
-+#define ma_baltype_pm3__shift 0x00
-+#define ma_baltype_pm3__reset 0x03
-+//-----------------------------------------------------------------pmprofile---
-+// pm profile select. valid presets: 0-1-2-3-4. 5=> custom profile.
-+#define ma_pmprofile__a 29
-+#define ma_pmprofile__len 3
-+#define ma_pmprofile__mask 0x07
-+#define ma_pmprofile__shift 0x00
-+#define ma_pmprofile__reset 0x00
-+//-------------------------------------------------------------------pm3_man---
-+// custom profile pm3 contents. 0=>a, 1=>b, 2=>c, 3=>d
-+#define ma_pm3_man__a 30
-+#define ma_pm3_man__len 2
-+#define ma_pm3_man__mask 0x30
-+#define ma_pm3_man__shift 0x04
-+#define ma_pm3_man__reset 0x02
-+//-------------------------------------------------------------------pm2_man---
-+// custom profile pm2 contents. 0=>a, 1=>b, 2=>c, 3=>d
-+#define ma_pm2_man__a 30
-+#define ma_pm2_man__len 2
-+#define ma_pm2_man__mask 0x0c
-+#define ma_pm2_man__shift 0x02
-+#define ma_pm2_man__reset 0x03
-+//-------------------------------------------------------------------pm1_man---
-+// custom profile pm1 contents. 0=>a, 1=>b, 2=>c, 3=>d
-+#define ma_pm1_man__a 30
-+#define ma_pm1_man__len 2
-+#define ma_pm1_man__mask 0x03
-+#define ma_pm1_man__shift 0x00
-+#define ma_pm1_man__reset 0x03
-+//-----------------------------------------------------------ocp_latch_clear---
-+// low-high clears current ocp latched condition.
-+#define ma_ocp_latch_clear__a 32
-+#define ma_ocp_latch_clear__len 1
-+#define ma_ocp_latch_clear__mask 0x80
-+#define ma_ocp_latch_clear__shift 0x07
-+#define ma_ocp_latch_clear__reset 0x00
-+//-------------------------------------------------------------audio_in_mode---
-+// audio input mode; 0-1-2-3-4-5
-+#define ma_audio_in_mode__a 37
-+#define ma_audio_in_mode__len 3
-+#define ma_audio_in_mode__mask 0xe0
-+#define ma_audio_in_mode__shift 0x05
-+#define ma_audio_in_mode__reset 0x00
-+//-----------------------------------------------------------------eh_dcshdn---
-+// high to enable dc protection
-+#define ma_eh_dcshdn__a 38
-+#define ma_eh_dcshdn__len 1
-+#define ma_eh_dcshdn__mask 0x04
-+#define ma_eh_dcshdn__shift 0x02
-+#define ma_eh_dcshdn__reset 0x01
-+//---------------------------------------------------------audio_in_mode_ext---
-+// if set, audio_in_mode is controlled from audio_in_mode register. if not set
-+//audio_in_mode is set from fuse bank setting
-+#define ma_audio_in_mode_ext__a 39
-+#define ma_audio_in_mode_ext__len 1
-+#define ma_audio_in_mode_ext__mask 0x20
-+#define ma_audio_in_mode_ext__shift 0x05
-+#define ma_audio_in_mode_ext__reset 0x00
-+//------------------------------------------------------------------eh_clear---
-+// flip to clear error registers
-+#define ma_eh_clear__a 45
-+#define ma_eh_clear__len 1
-+#define ma_eh_clear__mask 0x04
-+#define ma_eh_clear__shift 0x02
-+#define ma_eh_clear__reset 0x00
-+//----------------------------------------------------------thermal_compr_en---
-+// enable otw-contr. input compression?
-+#define ma_thermal_compr_en__a 45
-+#define ma_thermal_compr_en__len 1
-+#define ma_thermal_compr_en__mask 0x20
-+#define ma_thermal_compr_en__shift 0x05
-+#define ma_thermal_compr_en__reset 0x01
-+//---------------------------------------------------------------system_mute---
-+// 1 = mute system, 0 = normal operation
-+#define ma_system_mute__a 45
-+#define ma_system_mute__len 1
-+#define ma_system_mute__mask 0x40
-+#define ma_system_mute__shift 0x06
-+#define ma_system_mute__reset 0x00
-+//------------------------------------------------------thermal_compr_max_db---
-+// audio limiter max thermal reduction
-+#define ma_thermal_compr_max_db__a 46
-+#define ma_thermal_compr_max_db__len 3
-+#define ma_thermal_compr_max_db__mask 0x07
-+#define ma_thermal_compr_max_db__shift 0x00
-+#define ma_thermal_compr_max_db__reset 0x04
-+//---------------------------------------------------------audio_proc_enable---
-+// enable audio proc, bypass if not enabled
-+#define ma_audio_proc_enable__a 53
-+#define ma_audio_proc_enable__len 1
-+#define ma_audio_proc_enable__mask 0x08
-+#define ma_audio_proc_enable__shift 0x03
-+#define ma_audio_proc_enable__reset 0x00
-+//--------------------------------------------------------audio_proc_release---
-+// 00:slow, 01:normal, 10:fast
-+#define ma_audio_proc_release__a 53
-+#define ma_audio_proc_release__len 2
-+#define ma_audio_proc_release__mask 0x30
-+#define ma_audio_proc_release__shift 0x04
-+#define ma_audio_proc_release__reset 0x00
-+//---------------------------------------------------------audio_proc_attack---
-+// 00:slow, 01:normal, 10:fast
-+#define ma_audio_proc_attack__a 53
-+#define ma_audio_proc_attack__len 2
-+#define ma_audio_proc_attack__mask 0xc0
-+#define ma_audio_proc_attack__shift 0x06
-+#define ma_audio_proc_attack__reset 0x00
-+//----------------------------------------------------------------i2s_format---
-+// i2s basic data format, 000 = std. i2s, 001 = left justified (default)
-+#define ma_i2s_format__a 53
-+#define ma_i2s_format__len 3
-+#define ma_i2s_format__mask 0x07
-+#define ma_i2s_format__shift 0x00
-+#define ma_i2s_format__reset 0x01
-+//--------------------------------------------------audio_proc_limiterenable---
-+// 1: enable limiter, 0: disable limiter
-+#define ma_audio_proc_limiterenable__a 54
-+#define ma_audio_proc_limiterenable__len 1
-+#define ma_audio_proc_limiterenable__mask 0x40
-+#define ma_audio_proc_limiterenable__shift 0x06
-+#define ma_audio_proc_limiterenable__reset 0x00
-+//-----------------------------------------------------------audio_proc_mute---
-+// 1: mute, 0: unmute
-+#define ma_audio_proc_mute__a 54
-+#define ma_audio_proc_mute__len 1
-+#define ma_audio_proc_mute__mask 0x80
-+#define ma_audio_proc_mute__shift 0x07
-+#define ma_audio_proc_mute__reset 0x00
-+//---------------------------------------------------------------i2s_sck_pol---
-+// i2s sck polarity cfg. 0 = rising edge data change
-+#define ma_i2s_sck_pol__a 54
-+#define ma_i2s_sck_pol__len 1
-+#define ma_i2s_sck_pol__mask 0x01
-+#define ma_i2s_sck_pol__shift 0x00
-+#define ma_i2s_sck_pol__reset 0x01
-+//-------------------------------------------------------------i2s_framesize---
-+// i2s word length. 00 = 32bit, 01 = 24bit
-+#define ma_i2s_framesize__a 54
-+#define ma_i2s_framesize__len 2
-+#define ma_i2s_framesize__mask 0x18
-+#define ma_i2s_framesize__shift 0x03
-+#define ma_i2s_framesize__reset 0x00
-+//----------------------------------------------------------------i2s_ws_pol---
-+// i2s ws polarity. 0 = low first
-+#define ma_i2s_ws_pol__a 54
-+#define ma_i2s_ws_pol__len 1
-+#define ma_i2s_ws_pol__mask 0x02
-+#define ma_i2s_ws_pol__shift 0x01
-+#define ma_i2s_ws_pol__reset 0x00
-+//-----------------------------------------------------------------i2s_order---
-+// i2s word bit order. 0 = msb first
-+#define ma_i2s_order__a 54
-+#define ma_i2s_order__len 1
-+#define ma_i2s_order__mask 0x04
-+#define ma_i2s_order__shift 0x02
-+#define ma_i2s_order__reset 0x00
-+//------------------------------------------------------------i2s_rightfirst---
-+// i2s l/r word order; 0 = left first
-+#define ma_i2s_rightfirst__a 54
-+#define ma_i2s_rightfirst__len 1
-+#define ma_i2s_rightfirst__mask 0x20
-+#define ma_i2s_rightfirst__shift 0x05
-+#define ma_i2s_rightfirst__reset 0x00
-+//-------------------------------------------------------------vol_db_master---
-+// master volume db
-+#define ma_vol_db_master__a 64
-+#define ma_vol_db_master__len 8
-+#define ma_vol_db_master__mask 0xff
-+#define ma_vol_db_master__shift 0x00
-+#define ma_vol_db_master__reset 0x18
-+//------------------------------------------------------------vol_lsb_master---
-+// master volume lsb 1/4 steps
-+#define ma_vol_lsb_master__a 65
-+#define ma_vol_lsb_master__len 2
-+#define ma_vol_lsb_master__mask 0x03
-+#define ma_vol_lsb_master__shift 0x00
-+#define ma_vol_lsb_master__reset 0x00
-+//----------------------------------------------------------------vol_db_ch0---
-+// volume channel 0
-+#define ma_vol_db_ch0__a 66
-+#define ma_vol_db_ch0__len 8
-+#define ma_vol_db_ch0__mask 0xff
-+#define ma_vol_db_ch0__shift 0x00
-+#define ma_vol_db_ch0__reset 0x18
-+//----------------------------------------------------------------vol_db_ch1---
-+// volume channel 1
-+#define ma_vol_db_ch1__a 67
-+#define ma_vol_db_ch1__len 8
-+#define ma_vol_db_ch1__mask 0xff
-+#define ma_vol_db_ch1__shift 0x00
-+#define ma_vol_db_ch1__reset 0x18
-+//----------------------------------------------------------------vol_db_ch2---
-+// volume channel 2
-+#define ma_vol_db_ch2__a 68
-+#define ma_vol_db_ch2__len 8
-+#define ma_vol_db_ch2__mask 0xff
-+#define ma_vol_db_ch2__shift 0x00
-+#define ma_vol_db_ch2__reset 0x18
-+//----------------------------------------------------------------vol_db_ch3---
-+// volume channel 3
-+#define ma_vol_db_ch3__a 69
-+#define ma_vol_db_ch3__len 8
-+#define ma_vol_db_ch3__mask 0xff
-+#define ma_vol_db_ch3__shift 0x00
-+#define ma_vol_db_ch3__reset 0x18
-+//---------------------------------------------------------------vol_lsb_ch0---
-+// volume channel 1 - 1/4 steps
-+#define ma_vol_lsb_ch0__a 70
-+#define ma_vol_lsb_ch0__len 2
-+#define ma_vol_lsb_ch0__mask 0x03
-+#define ma_vol_lsb_ch0__shift 0x00
-+#define ma_vol_lsb_ch0__reset 0x00
-+//---------------------------------------------------------------vol_lsb_ch1---
-+// volume channel 3 - 1/4 steps
-+#define ma_vol_lsb_ch1__a 70
-+#define ma_vol_lsb_ch1__len 2
-+#define ma_vol_lsb_ch1__mask 0x0c
-+#define ma_vol_lsb_ch1__shift 0x02
-+#define ma_vol_lsb_ch1__reset 0x00
-+//---------------------------------------------------------------vol_lsb_ch2---
-+// volume channel 2 - 1/4 steps
-+#define ma_vol_lsb_ch2__a 70
-+#define ma_vol_lsb_ch2__len 2
-+#define ma_vol_lsb_ch2__mask 0x30
-+#define ma_vol_lsb_ch2__shift 0x04
-+#define ma_vol_lsb_ch2__reset 0x00
-+//---------------------------------------------------------------vol_lsb_ch3---
-+// volume channel 3 - 1/4 steps
-+#define ma_vol_lsb_ch3__a 70
-+#define ma_vol_lsb_ch3__len 2
-+#define ma_vol_lsb_ch3__mask 0xc0
-+#define ma_vol_lsb_ch3__shift 0x06
-+#define ma_vol_lsb_ch3__reset 0x00
-+//----------------------------------------------------------------thr_db_ch0---
-+// thr_db channel 0
-+#define ma_thr_db_ch0__a 71
-+#define ma_thr_db_ch0__len 8
-+#define ma_thr_db_ch0__mask 0xff
-+#define ma_thr_db_ch0__shift 0x00
-+#define ma_thr_db_ch0__reset 0x18
-+//----------------------------------------------------------------thr_db_ch1---
-+// thr db ch1
-+#define ma_thr_db_ch1__a 72
-+#define ma_thr_db_ch1__len 8
-+#define ma_thr_db_ch1__mask 0xff
-+#define ma_thr_db_ch1__shift 0x00
-+#define ma_thr_db_ch1__reset 0x18
-+//----------------------------------------------------------------thr_db_ch2---
-+// thr db ch2
-+#define ma_thr_db_ch2__a 73
-+#define ma_thr_db_ch2__len 8
-+#define ma_thr_db_ch2__mask 0xff
-+#define ma_thr_db_ch2__shift 0x00
-+#define ma_thr_db_ch2__reset 0x18
-+//----------------------------------------------------------------thr_db_ch3---
-+// threshold db ch3
-+#define ma_thr_db_ch3__a 74
-+#define ma_thr_db_ch3__len 8
-+#define ma_thr_db_ch3__mask 0xff
-+#define ma_thr_db_ch3__shift 0x00
-+#define ma_thr_db_ch3__reset 0x18
-+//---------------------------------------------------------------thr_lsb_ch0---
-+// thr lsb ch0
-+#define ma_thr_lsb_ch0__a 75
-+#define ma_thr_lsb_ch0__len 2
-+#define ma_thr_lsb_ch0__mask 0x03
-+#define ma_thr_lsb_ch0__shift 0x00
-+#define ma_thr_lsb_ch0__reset 0x00
-+//---------------------------------------------------------------thr_lsb_ch1---
-+// thr lsb ch1
-+#define ma_thr_lsb_ch1__a 75
-+#define ma_thr_lsb_ch1__len 2
-+#define ma_thr_lsb_ch1__mask 0x0c
-+#define ma_thr_lsb_ch1__shift 0x02
-+#define ma_thr_lsb_ch1__reset 0x00
-+//---------------------------------------------------------------thr_lsb_ch2---
-+// thr lsb ch2 1/4 db step
-+#define ma_thr_lsb_ch2__a 75
-+#define ma_thr_lsb_ch2__len 2
-+#define ma_thr_lsb_ch2__mask 0x30
-+#define ma_thr_lsb_ch2__shift 0x04
-+#define ma_thr_lsb_ch2__reset 0x00
-+//---------------------------------------------------------------thr_lsb_ch3---
-+// threshold lsb ch3
-+#define ma_thr_lsb_ch3__a 75
-+#define ma_thr_lsb_ch3__len 2
-+#define ma_thr_lsb_ch3__mask 0xc0
-+#define ma_thr_lsb_ch3__shift 0x06
-+#define ma_thr_lsb_ch3__reset 0x00
-+//-----------------------------------------------------------dcu_mon0.pm_mon---
-+// power mode monitor channel 0
-+#define ma_dcu_mon0__pm_mon__a 96
-+#define ma_dcu_mon0__pm_mon__len 2
-+#define ma_dcu_mon0__pm_mon__mask 0x03
-+#define ma_dcu_mon0__pm_mon__shift 0x00
-+#define ma_dcu_mon0__pm_mon__reset 0x00
-+//-----------------------------------------------------dcu_mon0.freqmode_mon---
-+// frequence mode monitor channel 0
-+#define ma_dcu_mon0__freqmode_mon__a 96
-+#define ma_dcu_mon0__freqmode_mon__len 3
-+#define ma_dcu_mon0__freqmode_mon__mask 0x70
-+#define ma_dcu_mon0__freqmode_mon__shift 0x04
-+#define ma_dcu_mon0__freqmode_mon__reset 0x00
-+//-------------------------------------------------------dcu_mon0.pps_passed---
-+// dcu0 pps completion indicator
-+#define ma_dcu_mon0__pps_passed__a 96
-+#define ma_dcu_mon0__pps_passed__len 1
-+#define ma_dcu_mon0__pps_passed__mask 0x80
-+#define ma_dcu_mon0__pps_passed__shift 0x07
-+#define ma_dcu_mon0__pps_passed__reset 0x00
-+//----------------------------------------------------------dcu_mon0.ocp_mon---
-+// ocp monitor channel 0
-+#define ma_dcu_mon0__ocp_mon__a 97
-+#define ma_dcu_mon0__ocp_mon__len 1
-+#define ma_dcu_mon0__ocp_mon__mask 0x01
-+#define ma_dcu_mon0__ocp_mon__shift 0x00
-+#define ma_dcu_mon0__ocp_mon__reset 0x00
-+//--------------------------------------------------------dcu_mon0.vcfly1_ok---
-+// cfly1 protection monitor channel 0.
-+#define ma_dcu_mon0__vcfly1_ok__a 97
-+#define ma_dcu_mon0__vcfly1_ok__len 1
-+#define ma_dcu_mon0__vcfly1_ok__mask 0x02
-+#define ma_dcu_mon0__vcfly1_ok__shift 0x01
-+#define ma_dcu_mon0__vcfly1_ok__reset 0x00
-+//--------------------------------------------------------dcu_mon0.vcfly2_ok---
-+// cfly2 protection monitor channel 0.
-+#define ma_dcu_mon0__vcfly2_ok__a 97
-+#define ma_dcu_mon0__vcfly2_ok__len 1
-+#define ma_dcu_mon0__vcfly2_ok__mask 0x04
-+#define ma_dcu_mon0__vcfly2_ok__shift 0x02
-+#define ma_dcu_mon0__vcfly2_ok__reset 0x00
-+//----------------------------------------------------------dcu_mon0.pvdd_ok---
-+// dcu0 pvdd monitor
-+#define ma_dcu_mon0__pvdd_ok__a 97
-+#define ma_dcu_mon0__pvdd_ok__len 1
-+#define ma_dcu_mon0__pvdd_ok__mask 0x08
-+#define ma_dcu_mon0__pvdd_ok__shift 0x03
-+#define ma_dcu_mon0__pvdd_ok__reset 0x00
-+//-----------------------------------------------------------dcu_mon0.vdd_ok---
-+// dcu0 vdd monitor
-+#define ma_dcu_mon0__vdd_ok__a 97
-+#define ma_dcu_mon0__vdd_ok__len 1
-+#define ma_dcu_mon0__vdd_ok__mask 0x10
-+#define ma_dcu_mon0__vdd_ok__shift 0x04
-+#define ma_dcu_mon0__vdd_ok__reset 0x00
-+//-------------------------------------------------------------dcu_mon0.mute---
-+// dcu0 mute monitor
-+#define ma_dcu_mon0__mute__a 97
-+#define ma_dcu_mon0__mute__len 1
-+#define ma_dcu_mon0__mute__mask 0x20
-+#define ma_dcu_mon0__mute__shift 0x05
-+#define ma_dcu_mon0__mute__reset 0x00
-+//------------------------------------------------------------dcu_mon0.m_mon---
-+// m sense monitor channel 0
-+#define ma_dcu_mon0__m_mon__a 98
-+#define ma_dcu_mon0__m_mon__len 8
-+#define ma_dcu_mon0__m_mon__mask 0xff
-+#define ma_dcu_mon0__m_mon__shift 0x00
-+#define ma_dcu_mon0__m_mon__reset 0x00
-+//-----------------------------------------------------------dcu_mon1.pm_mon---
-+// power mode monitor channel 1
-+#define ma_dcu_mon1__pm_mon__a 100
-+#define ma_dcu_mon1__pm_mon__len 2
-+#define ma_dcu_mon1__pm_mon__mask 0x03
-+#define ma_dcu_mon1__pm_mon__shift 0x00
-+#define ma_dcu_mon1__pm_mon__reset 0x00
-+//-----------------------------------------------------dcu_mon1.freqmode_mon---
-+// frequence mode monitor channel 1
-+#define ma_dcu_mon1__freqmode_mon__a 100
-+#define ma_dcu_mon1__freqmode_mon__len 3
-+#define ma_dcu_mon1__freqmode_mon__mask 0x70
-+#define ma_dcu_mon1__freqmode_mon__shift 0x04
-+#define ma_dcu_mon1__freqmode_mon__reset 0x00
-+//-------------------------------------------------------dcu_mon1.pps_passed---
-+// dcu1 pps completion indicator
-+#define ma_dcu_mon1__pps_passed__a 100
-+#define ma_dcu_mon1__pps_passed__len 1
-+#define ma_dcu_mon1__pps_passed__mask 0x80
-+#define ma_dcu_mon1__pps_passed__shift 0x07
-+#define ma_dcu_mon1__pps_passed__reset 0x00
-+//----------------------------------------------------------dcu_mon1.ocp_mon---
-+// ocp monitor channel 1
-+#define ma_dcu_mon1__ocp_mon__a 101
-+#define ma_dcu_mon1__ocp_mon__len 1
-+#define ma_dcu_mon1__ocp_mon__mask 0x01
-+#define ma_dcu_mon1__ocp_mon__shift 0x00
-+#define ma_dcu_mon1__ocp_mon__reset 0x00
-+//--------------------------------------------------------dcu_mon1.vcfly1_ok---
-+// cfly1 protcetion monitor channel 1
-+#define ma_dcu_mon1__vcfly1_ok__a 101
-+#define ma_dcu_mon1__vcfly1_ok__len 1
-+#define ma_dcu_mon1__vcfly1_ok__mask 0x02
-+#define ma_dcu_mon1__vcfly1_ok__shift 0x01
-+#define ma_dcu_mon1__vcfly1_ok__reset 0x00
-+//--------------------------------------------------------dcu_mon1.vcfly2_ok---
-+// cfly2 protection monitor channel 1
-+#define ma_dcu_mon1__vcfly2_ok__a 101
-+#define ma_dcu_mon1__vcfly2_ok__len 1
-+#define ma_dcu_mon1__vcfly2_ok__mask 0x04
-+#define ma_dcu_mon1__vcfly2_ok__shift 0x02
-+#define ma_dcu_mon1__vcfly2_ok__reset 0x00
-+//----------------------------------------------------------dcu_mon1.pvdd_ok---
-+// dcu1 pvdd monitor
-+#define ma_dcu_mon1__pvdd_ok__a 101
-+#define ma_dcu_mon1__pvdd_ok__len 1
-+#define ma_dcu_mon1__pvdd_ok__mask 0x08
-+#define ma_dcu_mon1__pvdd_ok__shift 0x03
-+#define ma_dcu_mon1__pvdd_ok__reset 0x00
-+//-----------------------------------------------------------dcu_mon1.vdd_ok---
-+// dcu1 vdd monitor
-+#define ma_dcu_mon1__vdd_ok__a 101
-+#define ma_dcu_mon1__vdd_ok__len 1
-+#define ma_dcu_mon1__vdd_ok__mask 0x10
-+#define ma_dcu_mon1__vdd_ok__shift 0x04
-+#define ma_dcu_mon1__vdd_ok__reset 0x00
-+//-------------------------------------------------------------dcu_mon1.mute---
-+// dcu1 mute monitor
-+#define ma_dcu_mon1__mute__a 101
-+#define ma_dcu_mon1__mute__len 1
-+#define ma_dcu_mon1__mute__mask 0x20
-+#define ma_dcu_mon1__mute__shift 0x05
-+#define ma_dcu_mon1__mute__reset 0x00
-+//------------------------------------------------------------dcu_mon1.m_mon---
-+// m sense monitor channel 1
-+#define ma_dcu_mon1__m_mon__a 102
-+#define ma_dcu_mon1__m_mon__len 8
-+#define ma_dcu_mon1__m_mon__mask 0xff
-+#define ma_dcu_mon1__m_mon__shift 0x00
-+#define ma_dcu_mon1__m_mon__reset 0x00
-+//--------------------------------------------------------dcu_mon0.sw_enable---
-+// dcu0 switch enable monitor
-+#define ma_dcu_mon0__sw_enable__a 104
-+#define ma_dcu_mon0__sw_enable__len 1
-+#define ma_dcu_mon0__sw_enable__mask 0x40
-+#define ma_dcu_mon0__sw_enable__shift 0x06
-+#define ma_dcu_mon0__sw_enable__reset 0x00
-+//--------------------------------------------------------dcu_mon1.sw_enable---
-+// dcu1 switch enable monitor
-+#define ma_dcu_mon1__sw_enable__a 104
-+#define ma_dcu_mon1__sw_enable__len 1
-+#define ma_dcu_mon1__sw_enable__mask 0x80
-+#define ma_dcu_mon1__sw_enable__shift 0x07
-+#define ma_dcu_mon1__sw_enable__reset 0x00
-+//------------------------------------------------------------hvboot0_ok_mon---
-+// hvboot0_ok for test/debug
-+#define ma_hvboot0_ok_mon__a 105
-+#define ma_hvboot0_ok_mon__len 1
-+#define ma_hvboot0_ok_mon__mask 0x40
-+#define ma_hvboot0_ok_mon__shift 0x06
-+#define ma_hvboot0_ok_mon__reset 0x00
-+//------------------------------------------------------------hvboot1_ok_mon---
-+// hvboot1_ok for test/debug
-+#define ma_hvboot1_ok_mon__a 105
-+#define ma_hvboot1_ok_mon__len 1
-+#define ma_hvboot1_ok_mon__mask 0x80
-+#define ma_hvboot1_ok_mon__shift 0x07
-+#define ma_hvboot1_ok_mon__reset 0x00
-+//-----------------------------------------------------------------error_acc---
-+// accumulated errors, at and after triggering
-+#define ma_error_acc__a 109
-+#define ma_error_acc__len 8
-+#define ma_error_acc__mask 0xff
-+#define ma_error_acc__shift 0x00
-+#define ma_error_acc__reset 0x00
-+//-------------------------------------------------------------i2s_data_rate---
-+// detected i2s data rate: 00/01/10 = x1/x2/x4
-+#define ma_i2s_data_rate__a 116
-+#define ma_i2s_data_rate__len 2
-+#define ma_i2s_data_rate__mask 0x03
-+#define ma_i2s_data_rate__shift 0x00
-+#define ma_i2s_data_rate__reset 0x00
-+//---------------------------------------------------------audio_in_mode_mon---
-+// audio input mode monitor
-+#define ma_audio_in_mode_mon__a 116
-+#define ma_audio_in_mode_mon__len 3
-+#define ma_audio_in_mode_mon__mask 0x1c
-+#define ma_audio_in_mode_mon__shift 0x02
-+#define ma_audio_in_mode_mon__reset 0x00
-+//------------------------------------------------------------------msel_mon---
-+// msel[2:0] monitor register
-+#define ma_msel_mon__a 117
-+#define ma_msel_mon__len 3
-+#define ma_msel_mon__mask 0x07
-+#define ma_msel_mon__shift 0x00
-+#define ma_msel_mon__reset 0x00
-+//---------------------------------------------------------------------error---
-+// current error flag monitor reg - for app. ctrl.
-+#define ma_error__a 124
-+#define ma_error__len 8
-+#define ma_error__mask 0xff
-+#define ma_error__shift 0x00
-+#define ma_error__reset 0x00
-+//----------------------------------------------------audio_proc_limiter_mon---
-+// b7-b4: channel 3-0 limiter active
-+#define ma_audio_proc_limiter_mon__a 126
-+#define ma_audio_proc_limiter_mon__len 4
-+#define ma_audio_proc_limiter_mon__mask 0xf0
-+#define ma_audio_proc_limiter_mon__shift 0x04
-+#define ma_audio_proc_limiter_mon__reset 0x00
-+//-------------------------------------------------------audio_proc_clip_mon---
-+// b3-b0: channel 3-0 clipping monitor
-+#define ma_audio_proc_clip_mon__a 126
-+#define ma_audio_proc_clip_mon__len 4
-+#define ma_audio_proc_clip_mon__mask 0x0f
-+#define ma_audio_proc_clip_mon__shift 0x00
-+#define ma_audio_proc_clip_mon__reset 0x00
-+#endif
-+
-+#define SOC_ENUM_ERR(xname, xenum)\
-+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
-+ .access = SNDRV_CTL_ELEM_ACCESS_READ,\
-+ .info = snd_soc_info_enum_double,\
-+ .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double,\
-+ .private_value = (unsigned long)&(xenum) }
-+
-+static struct i2c_client *i2c;
-+
-+struct ma120x0p_priv {
-+ struct regmap *regmap;
-+ int mclk_div;
-+ struct snd_soc_component *component;
-+ struct gpio_desc *enable_gpio;
-+ struct gpio_desc *mute_gpio;
-+ struct gpio_desc *booster_gpio;
-+ struct gpio_desc *error_gpio;
-+};
-+
-+static struct ma120x0p_priv *priv_data;
-+
-+//Used to share the IRQ number within this file
-+static unsigned int irqNumber;
-+
-+// Function prototype for the custom IRQ handler function
-+static irqreturn_t ma120x0p_irq_handler(int irq, void *data);
-+
-+//Alsa Controls
-+static const char * const limenable_text[] = {"Bypassed", "Enabled"};
-+static const char * const limatack_text[] = {"Slow", "Normal", "Fast"};
-+static const char * const limrelease_text[] = {"Slow", "Normal", "Fast"};
-+
-+static const char * const err_flycap_text[] = {"Ok", "Error"};
-+static const char * const err_overcurr_text[] = {"Ok", "Error"};
-+static const char * const err_pllerr_text[] = {"Ok", "Error"};
-+static const char * const err_pvddunder_text[] = {"Ok", "Error"};
-+static const char * const err_overtempw_text[] = {"Ok", "Error"};
-+static const char * const err_overtempe_text[] = {"Ok", "Error"};
-+static const char * const err_pinlowimp_text[] = {"Ok", "Error"};
-+static const char * const err_dcprot_text[] = {"Ok", "Error"};
-+
-+static const char * const pwr_mode_prof_text[] = {"PMF0", "PMF1", "PMF2",
-+"PMF3", "PMF4"};
-+
-+static const struct soc_enum lim_enable_ctrl =
-+ SOC_ENUM_SINGLE(ma_audio_proc_limiterenable__a,
-+ ma_audio_proc_limiterenable__shift,
-+ ma_audio_proc_limiterenable__len + 1,
-+ limenable_text);
-+static const struct soc_enum limatack_ctrl =
-+ SOC_ENUM_SINGLE(ma_audio_proc_attack__a,
-+ ma_audio_proc_attack__shift,
-+ ma_audio_proc_attack__len + 1,
-+ limatack_text);
-+static const struct soc_enum limrelease_ctrl =
-+ SOC_ENUM_SINGLE(ma_audio_proc_release__a,
-+ ma_audio_proc_release__shift,
-+ ma_audio_proc_release__len + 1,
-+ limrelease_text);
-+static const struct soc_enum err_flycap_ctrl =
-+ SOC_ENUM_SINGLE(ma_error__a, 0, 3, err_flycap_text);
-+static const struct soc_enum err_overcurr_ctrl =
-+ SOC_ENUM_SINGLE(ma_error__a, 1, 3, err_overcurr_text);
-+static const struct soc_enum err_pllerr_ctrl =
-+ SOC_ENUM_SINGLE(ma_error__a, 2, 3, err_pllerr_text);
-+static const struct soc_enum err_pvddunder_ctrl =
-+ SOC_ENUM_SINGLE(ma_error__a, 3, 3, err_pvddunder_text);
-+static const struct soc_enum err_overtempw_ctrl =
-+ SOC_ENUM_SINGLE(ma_error__a, 4, 3, err_overtempw_text);
-+static const struct soc_enum err_overtempe_ctrl =
-+ SOC_ENUM_SINGLE(ma_error__a, 5, 3, err_overtempe_text);
-+static const struct soc_enum err_pinlowimp_ctrl =
-+ SOC_ENUM_SINGLE(ma_error__a, 6, 3, err_pinlowimp_text);
-+static const struct soc_enum err_dcprot_ctrl =
-+ SOC_ENUM_SINGLE(ma_error__a, 7, 3, err_dcprot_text);
-+static const struct soc_enum pwr_mode_prof_ctrl =
-+ SOC_ENUM_SINGLE(ma_pmprofile__a, ma_pmprofile__shift, 5,
-+ pwr_mode_prof_text);
-+
-+static const char * const pwr_mode_texts[] = {
-+ "Dynamic power mode",
-+ "Power mode 1",
-+ "Power mode 2",
-+ "Power mode 3",
-+ };
-+
-+static const int pwr_mode_values[] = {
-+ 0x10,
-+ 0x50,
-+ 0x60,
-+ 0x70,
-+ };
-+
-+static const SOC_VALUE_ENUM_SINGLE_DECL(pwr_mode_ctrl,
-+ ma_pm_man__a, 0, 0x70,
-+ pwr_mode_texts,
-+ pwr_mode_values);
-+
-+static const DECLARE_TLV_DB_SCALE(ma120x0p_vol_tlv, -5000, 100, 0);
-+static const DECLARE_TLV_DB_SCALE(ma120x0p_lim_tlv, -5000, 100, 0);
-+static const DECLARE_TLV_DB_SCALE(ma120x0p_lr_tlv, -5000, 100, 0);
-+
-+static const struct snd_kcontrol_new ma120x0p_snd_controls[] = {
-+ //Master Volume
-+ SOC_SINGLE_RANGE_TLV("A.Mstr Vol Volume",
-+ ma_vol_db_master__a, 0, 0x18, 0x4a, 1, ma120x0p_vol_tlv),
-+
-+ //L-R Volume ch0
-+ SOC_SINGLE_RANGE_TLV("B.L Vol Volume",
-+ ma_vol_db_ch0__a, 0, 0x18, 0x4a, 1, ma120x0p_lr_tlv),
-+ SOC_SINGLE_RANGE_TLV("C.R Vol Volume",
-+ ma_vol_db_ch1__a, 0, 0x18, 0x4a, 1, ma120x0p_lr_tlv),
-+
-+ //L-R Limiter Threshold ch0-ch1
-+ SOC_DOUBLE_R_RANGE_TLV("D.Lim thresh Volume",
-+ ma_thr_db_ch0__a, ma_thr_db_ch1__a, 0, 0x0e, 0x4a, 1,
-+ ma120x0p_lim_tlv),
-+
-+ //Enum Switches/Selectors
-+ //SOC_ENUM("E.AudioProc Mute", audioproc_mute_ctrl),
-+ SOC_ENUM("F.Limiter Enable", lim_enable_ctrl),
-+ SOC_ENUM("G.Limiter Attck", limatack_ctrl),
-+ SOC_ENUM("H.Limiter Rls", limrelease_ctrl),
-+
-+ //Enum Error Monitor (read-only)
-+ SOC_ENUM_ERR("I.Err flycap", err_flycap_ctrl),
-+ SOC_ENUM_ERR("J.Err overcurr", err_overcurr_ctrl),
-+ SOC_ENUM_ERR("K.Err pllerr", err_pllerr_ctrl),
-+ SOC_ENUM_ERR("L.Err pvddunder", err_pvddunder_ctrl),
-+ SOC_ENUM_ERR("M.Err overtempw", err_overtempw_ctrl),
-+ SOC_ENUM_ERR("N.Err overtempe", err_overtempe_ctrl),
-+ SOC_ENUM_ERR("O.Err pinlowimp", err_pinlowimp_ctrl),
-+ SOC_ENUM_ERR("P.Err dcprot", err_dcprot_ctrl),
-+
-+ //Power modes profiles
-+ SOC_ENUM("Q.PM Prof", pwr_mode_prof_ctrl),
-+
-+ // Power mode selection (Dynamic,1,2,3)
-+ SOC_ENUM("R.Power Mode", pwr_mode_ctrl),
-+};
-+
-+//Machine Driver
-+static int ma120x0p_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
-+{
-+ u16 blen = 0x00;
-+
-+ struct snd_soc_component *component = dai->component;
-+
-+ priv_data->component = component;
-+
-+ switch (params_format(params)) {
-+ case SNDRV_PCM_FORMAT_S16_LE:
-+ blen = 0x10;
-+ break;
-+ case SNDRV_PCM_FORMAT_S24_LE:
-+ blen = 0x00;
-+ break;
-+ case SNDRV_PCM_FORMAT_S32_LE:
-+ blen = 0x00;
-+ break;
-+ default:
-+ dev_err(dai->dev, "Unsupported word length: %u\n",
-+ params_format(params));
-+ return -EINVAL;
-+ }
-+
-+ // set word length
-+ snd_soc_component_update_bits(component, ma_i2s_framesize__a,
-+ ma_i2s_framesize__mask, blen);
-+
-+ return 0;
-+}
-+
-+static int ma120x0p_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
-+{
-+ int val = 0;
-+
-+ struct ma120x0p_priv *ma120x0p;
-+
-+ struct snd_soc_component *component = dai->component;
-+
-+ ma120x0p = snd_soc_component_get_drvdata(component);
-+
-+ if (mute)
-+ val = 0;
-+ else
-+ val = 1;
-+
-+ gpiod_set_value_cansleep(priv_data->mute_gpio, val);
-+
-+ return 0;
-+}
-+
-+static const struct snd_soc_dai_ops ma120x0p_dai_ops = {
-+ .hw_params = ma120x0p_hw_params,
-+ .mute_stream = ma120x0p_mute_stream,
-+};
-+
-+static struct snd_soc_dai_driver ma120x0p_dai = {
-+ .name = "ma120x0p-amp",
-+ .playback = {
-+ .stream_name = "Playback",
-+ .channels_min = 2,
-+ .channels_max = 2,
-+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
-+ .rate_min = 44100,
-+ .rate_max = 48000,
-+ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE
-+ },
-+ .ops = &ma120x0p_dai_ops,
-+};
-+
-+//Codec Driver
-+static int ma120x0p_clear_err(struct snd_soc_component *component)
-+{
-+ int ret = 0;
-+
-+ struct ma120x0p_priv *ma120x0p;
-+
-+ ma120x0p = snd_soc_component_get_drvdata(component);
-+
-+ ret = snd_soc_component_update_bits(component,
-+ ma_eh_clear__a, ma_eh_clear__mask, 0x00);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = snd_soc_component_update_bits(component,
-+ ma_eh_clear__a, ma_eh_clear__mask, 0x04);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = snd_soc_component_update_bits(component,
-+ ma_eh_clear__a, ma_eh_clear__mask, 0x00);
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static void ma120x0p_remove(struct snd_soc_component *component)
-+{
-+ struct ma120x0p_priv *ma120x0p;
-+
-+ ma120x0p = snd_soc_component_get_drvdata(component);
-+}
-+
-+static int ma120x0p_probe(struct snd_soc_component *component)
-+{
-+ struct ma120x0p_priv *ma120x0p;
-+
-+ int ret = 0;
-+
-+ i2c = container_of(component->dev, struct i2c_client, dev);
-+
-+ ma120x0p = snd_soc_component_get_drvdata(component);
-+
-+ //Reset error
-+ ma120x0p_clear_err(component);
-+ if (ret < 0)
-+ return ret;
-+
-+ // set serial audio format I2S and enable audio processor
-+ ret = snd_soc_component_write(component, ma_i2s_format__a, 0x08);
-+ if (ret < 0)
-+ return ret;
-+
-+ // Enable audio limiter
-+ ret = snd_soc_component_update_bits(component,
-+ ma_audio_proc_limiterenable__a,
-+ ma_audio_proc_limiterenable__mask, 0x40);
-+ if (ret < 0)
-+ return ret;
-+
-+ // Set lim attack to fast
-+ ret = snd_soc_component_update_bits(component,
-+ ma_audio_proc_attack__a, ma_audio_proc_attack__mask, 0x80);
-+ if (ret < 0)
-+ return ret;
-+
-+ // Set lim attack to low
-+ ret = snd_soc_component_update_bits(component,
-+ ma_audio_proc_release__a, ma_audio_proc_release__mask, 0x00);
-+ if (ret < 0)
-+ return ret;
-+
-+ // set volume to 0dB
-+ ret = snd_soc_component_write(component, ma_vol_db_master__a, 0x18);
-+ if (ret < 0)
-+ return ret;
-+
-+ // set ch0 lim thresh to -15dB
-+ ret = snd_soc_component_write(component, ma_thr_db_ch0__a, 0x27);
-+ if (ret < 0)
-+ return ret;
-+
-+ // set ch1 lim thresh to -15dB
-+ ret = snd_soc_component_write(component, ma_thr_db_ch1__a, 0x27);
-+ if (ret < 0)
-+ return ret;
-+
-+ //Check for errors
-+ ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x00, 0);
-+ if (ret < 0)
-+ return ret;
-+ ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x01, 0);
-+ if (ret < 0)
-+ return ret;
-+ ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x02, 0);
-+ if (ret < 0)
-+ return ret;
-+ ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x08, 0);
-+ if (ret < 0)
-+ return ret;
-+ ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x10, 0);
-+ if (ret < 0)
-+ return ret;
-+ ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x20, 0);
-+ if (ret < 0)
-+ return ret;
-+ ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x40, 0);
-+ if (ret < 0)
-+ return ret;
-+ ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x80, 0);
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int ma120x0p_set_bias_level(struct snd_soc_component *component,
-+ enum snd_soc_bias_level level)
-+{
-+ int ret = 0;
-+
-+ struct ma120x0p_priv *ma120x0p;
-+
-+ ma120x0p = snd_soc_component_get_drvdata(component);
-+
-+ switch (level) {
-+ case SND_SOC_BIAS_ON:
-+ break;
-+
-+ case SND_SOC_BIAS_PREPARE:
-+ break;
-+
-+ case SND_SOC_BIAS_STANDBY:
-+ ret = gpiod_get_value_cansleep(priv_data->enable_gpio);
-+ if (ret != 0) {
-+ dev_err(component->dev, "Device ma120x0p disabled in STANDBY BIAS: %d\n",
-+ ret);
-+ return ret;
-+ }
-+ break;
-+
-+ case SND_SOC_BIAS_OFF:
-+ break;
-+ }
-+
-+ return 0;
-+}
-+
-+static const struct snd_soc_dapm_widget ma120x0p_dapm_widgets[] = {
-+ SND_SOC_DAPM_OUTPUT("OUT_A"),
-+ SND_SOC_DAPM_OUTPUT("OUT_B"),
-+};
-+
-+static const struct snd_soc_dapm_route ma120x0p_dapm_routes[] = {
-+ { "OUT_B", NULL, "Playback" },
-+ { "OUT_A", NULL, "Playback" },
-+};
-+
-+static const struct snd_soc_component_driver ma120x0p_component_driver = {
-+ .probe = ma120x0p_probe,
-+ .remove = ma120x0p_remove,
-+ .set_bias_level = ma120x0p_set_bias_level,
-+ .dapm_widgets = ma120x0p_dapm_widgets,
-+ .num_dapm_widgets = ARRAY_SIZE(ma120x0p_dapm_widgets),
-+ .dapm_routes = ma120x0p_dapm_routes,
-+ .num_dapm_routes = ARRAY_SIZE(ma120x0p_dapm_routes),
-+ .controls = ma120x0p_snd_controls,
-+ .num_controls = ARRAY_SIZE(ma120x0p_snd_controls),
-+ .use_pmdown_time = 1,
-+ .endianness = 1,
-+ .non_legacy_dai_naming = 1,
-+};
-+
-+//I2C Driver
-+static const struct reg_default ma120x0p_reg_defaults[] = {
-+ { 0x01, 0x3c },
-+};
-+
-+static bool ma120x0p_reg_volatile(struct device *dev, unsigned int reg)
-+{
-+ switch (reg) {
-+ case ma_error__a:
-+ return true;
-+ default:
-+ return false;
-+ }
-+}
-+
-+static const struct of_device_id ma120x0p_of_match[] = {
-+ { .compatible = "ma,ma120x0p", },
-+ { }
-+};
-+
-+MODULE_DEVICE_TABLE(of, ma120x0p_of_match);
-+
-+static struct regmap_config ma120x0p_regmap_config = {
-+ .reg_bits = 8,
-+ .val_bits = 8,
-+
-+ .max_register = 255,
-+ .volatile_reg = ma120x0p_reg_volatile,
-+
-+ .cache_type = REGCACHE_RBTREE,
-+ .reg_defaults = ma120x0p_reg_defaults,
-+ .num_reg_defaults = ARRAY_SIZE(ma120x0p_reg_defaults),
-+};
-+
-+static int ma120x0p_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;
-+ i2c_set_clientdata(i2c, priv_data);
-+
-+ priv_data->regmap = devm_regmap_init_i2c(i2c, &ma120x0p_regmap_config);
-+ if (IS_ERR(priv_data->regmap)) {
-+ ret = PTR_ERR(priv_data->regmap);
-+ return ret;
-+ }
-+
-+ //Startup sequence
-+
-+ //Make sure the device is muted
-+ priv_data->mute_gpio = devm_gpiod_get(&i2c->dev, "mute_gp",
-+ GPIOD_OUT_LOW);
-+ if (IS_ERR(priv_data->mute_gpio)) {
-+ ret = PTR_ERR(priv_data->mute_gpio);
-+ dev_err(&i2c->dev, "Failed to get mute gpio line: %d\n", ret);
-+ return ret;
-+ }
-+ msleep(50);
-+
-+// MA120xx0P devices are usually powered by an integrated boost converter.
-+// An option GPIO control line is provided to enable the booster properly and
-+// in sync with the enable and mute GPIO lines.
-+ priv_data->booster_gpio = devm_gpiod_get_optional(&i2c->dev,
-+ "booster_gp", GPIOD_OUT_LOW);
-+ if (IS_ERR(priv_data->booster_gpio)) {
-+ ret = PTR_ERR(priv_data->booster_gpio);
-+ dev_err(&i2c->dev,
-+ "Failed to get booster enable gpio line: %d\n", ret);
-+ return ret;
-+ }
-+ msleep(50);
-+
-+ //Enable booster and wait 200ms until stable PVDD
-+ gpiod_set_value_cansleep(priv_data->booster_gpio, 1);
-+ msleep(200);
-+
-+ //Enable ma120x0pp
-+ priv_data->enable_gpio = devm_gpiod_get(&i2c->dev,
-+ "enable_gp", GPIOD_OUT_LOW);
-+ if (IS_ERR(priv_data->enable_gpio)) {
-+ ret = PTR_ERR(priv_data->enable_gpio);
-+ dev_err(&i2c->dev,
-+ "Failed to get ma120x0p enable gpio line: %d\n", ret);
-+ return ret;
-+ }
-+ msleep(50);
-+
-+ //Optional use of ma120x0pp error line as an interrupt trigger to
-+ //platform GPIO.
-+ //Get error input gpio ma120x0p
-+ priv_data->error_gpio = devm_gpiod_get_optional(&i2c->dev,
-+ "error_gp", GPIOD_IN);
-+ if (IS_ERR(priv_data->error_gpio)) {
-+ ret = PTR_ERR(priv_data->error_gpio);
-+ dev_err(&i2c->dev,
-+ "Failed to get ma120x0p error gpio line: %d\n", ret);
-+ return ret;
-+ }
-+
-+ if (priv_data->error_gpio != NULL) {
-+ irqNumber = gpiod_to_irq(priv_data->error_gpio);
-+
-+ ret = devm_request_threaded_irq(&i2c->dev,
-+ irqNumber, ma120x0p_irq_handler,
-+ NULL, IRQF_TRIGGER_FALLING,
-+ "ma120x0p", priv_data);
-+ if (ret != 0)
-+ dev_warn(&i2c->dev, "Failed to request IRQ: %d\n",
-+ ret);
-+ }
-+
-+ ret = devm_snd_soc_register_component(&i2c->dev,
-+ &ma120x0p_component_driver, &ma120x0p_dai, 1);
-+
-+ return ret;
-+}
-+
-+static irqreturn_t ma120x0p_irq_handler(int irq, void *data)
-+{
-+ gpiod_set_value_cansleep(priv_data->mute_gpio, 0);
-+ gpiod_set_value_cansleep(priv_data->enable_gpio, 1);
-+ return IRQ_HANDLED;
-+}
-+
-+static int ma120x0p_i2c_remove(struct i2c_client *i2c)
-+{
-+ snd_soc_unregister_component(&i2c->dev);
-+ i2c_set_clientdata(i2c, NULL);
-+
-+ gpiod_set_value_cansleep(priv_data->mute_gpio, 0);
-+ msleep(30);
-+ gpiod_set_value_cansleep(priv_data->enable_gpio, 1);
-+ msleep(200);
-+ gpiod_set_value_cansleep(priv_data->booster_gpio, 0);
-+ msleep(200);
-+
-+ kfree(priv_data);
-+
-+ return 0;
-+}
-+
-+static void ma120x0p_i2c_shutdown(struct i2c_client *i2c)
-+{
-+ snd_soc_unregister_component(&i2c->dev);
-+ i2c_set_clientdata(i2c, NULL);
-+
-+ gpiod_set_value_cansleep(priv_data->mute_gpio, 0);
-+ msleep(30);
-+ gpiod_set_value_cansleep(priv_data->enable_gpio, 1);
-+ msleep(200);
-+ gpiod_set_value_cansleep(priv_data->booster_gpio, 0);
-+ msleep(200);
-+
-+ kfree(priv_data);
-+}
-+
-+static const struct i2c_device_id ma120x0p_i2c_id[] = {
-+ { "ma120x0p", 0 },
-+ { }
-+};
-+
-+MODULE_DEVICE_TABLE(i2c, ma120x0p_i2c_id);
-+
-+static struct i2c_driver ma120x0p_i2c_driver = {
-+ .driver = {
-+ .name = "ma120x0p",
-+ .owner = THIS_MODULE,
-+ .of_match_table = ma120x0p_of_match,
-+ },
-+ .probe = ma120x0p_i2c_probe,
-+ .remove = ma120x0p_i2c_remove,
-+ .shutdown = ma120x0p_i2c_shutdown,
-+ .id_table = ma120x0p_i2c_id
-+};
-+
-+static int __init ma120x0p_modinit(void)
-+{
-+ int ret = 0;
-+
-+ ret = i2c_add_driver(&ma120x0p_i2c_driver);
-+ if (ret != 0) {
-+ pr_err("Failed to register MA120X0P I2C driver: %d\n", ret);
-+ return ret;
-+ }
-+ return ret;
-+}
-+module_init(ma120x0p_modinit);
-+
-+static void __exit ma120x0p_exit(void)
-+{
-+ i2c_del_driver(&ma120x0p_i2c_driver);
-+}
-+module_exit(ma120x0p_exit);
-+
-+MODULE_AUTHOR("Ariel Muszkat ariel.muszkat@gmail.com>");
-+MODULE_DESCRIPTION("ASoC driver for ma120x0p");
-+MODULE_LICENSE("GPL v2");
+++ /dev/null
-From e25d9a93812847b4ddc9e883d0cd45b32f8e2f76 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Tue, 17 Mar 2020 16:39:07 +0000
-Subject: [PATCH] ARM: dts: bcm2711: Add 32-bit PMU compatibility
-
-The "arm" architecture has no support for the cortex-a72 as such, but
-the performance and measurement unit from the cortex-a15 seems to be
-compatible.
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- arch/arm/boot/dts/bcm2711-rpi.dtsi | 4 ++++
- 1 file changed, 4 insertions(+)
-
---- a/arch/arm/boot/dts/bcm2711-rpi.dtsi
-+++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi
-@@ -12,6 +12,10 @@
- sd_poll_once = <&emmc2>, "non-removable?";
- };
-
-+ arm-pmu {
-+ compatible = "arm,cortex-a72-pmu", "arm,cortex-a15-pmu";
-+ };
-+
- v3dbus {
- compatible = "simple-bus";
- #address-cells = <1>;
--- /dev/null
+From 70b0d5d07426e1b9c34ddd6ab4ee99b8c2fb81a6 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 19 Mar 2020 10:04:46 +0000
+Subject: [PATCH] ARM: dts: bcm271x: Use a53 pmu, drop RPI364
+
+The upstream bcm2837.dtsi uses cortex-a53-pmu, so we can do the same
+but with a fallback to the cortex-a7-pmu which is supported by the
+32-bit kernel.
+
+Now that we're using the natural fallback mechanism of compatible
+strings, the RPI364 macro no longer serves any purpose - remove it.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2710.dtsi | 6 +-----
+ arch/arm64/boot/dts/broadcom/bcm2710-rpi-2-b.dts | 2 --
+ arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts | 2 --
+ arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b.dts | 2 --
+ arch/arm64/boot/dts/broadcom/bcm2710-rpi-cm3.dts | 2 --
+ arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dts | 2 --
+ 6 files changed, 1 insertion(+), 15 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2710.dtsi
++++ b/arch/arm/boot/dts/bcm2710.dtsi
+@@ -5,11 +5,7 @@
+ compatible = "brcm,bcm2837", "brcm,bcm2836";
+
+ arm-pmu {
+-#ifdef RPI364
+- compatible = "arm,armv8-pmuv3", "arm,cortex-a7-pmu";
+-#else
+- compatible = "arm,cortex-a7-pmu";
+-#endif
++ compatible = "arm,cortex-a53-pmu", "arm,cortex-a7-pmu";
+ };
+
+ soc {
+--- a/arch/arm64/boot/dts/broadcom/bcm2710-rpi-2-b.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2710-rpi-2-b.dts
+@@ -1,3 +1 @@
+-#define RPI364
+-
+ #include "../../../../arm/boot/dts/bcm2710-rpi-2-b.dts"
+--- a/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts
+@@ -1,3 +1 @@
+-#define RPI364
+-
+ #include "../../../../arm/boot/dts/bcm2710-rpi-3-b-plus.dts"
+--- a/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b.dts
+@@ -1,3 +1 @@
+-#define RPI364
+-
+ #include "../../../../arm/boot/dts/bcm2710-rpi-3-b.dts"
+--- a/arch/arm64/boot/dts/broadcom/bcm2710-rpi-cm3.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2710-rpi-cm3.dts
+@@ -1,3 +1 @@
+-#define RPI364
+-
+ #include "../../../../arm/boot/dts/bcm2710-rpi-cm3.dts"
+--- a/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dts
+@@ -1,3 +1 @@
+-#define RPI364
+-
+ #include "../../../../arm/boot/dts/bcm2711-rpi-4-b.dts"
+++ /dev/null
-From 70b0d5d07426e1b9c34ddd6ab4ee99b8c2fb81a6 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Thu, 19 Mar 2020 10:04:46 +0000
-Subject: [PATCH] ARM: dts: bcm271x: Use a53 pmu, drop RPI364
-
-The upstream bcm2837.dtsi uses cortex-a53-pmu, so we can do the same
-but with a fallback to the cortex-a7-pmu which is supported by the
-32-bit kernel.
-
-Now that we're using the natural fallback mechanism of compatible
-strings, the RPI364 macro no longer serves any purpose - remove it.
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- arch/arm/boot/dts/bcm2710.dtsi | 6 +-----
- arch/arm64/boot/dts/broadcom/bcm2710-rpi-2-b.dts | 2 --
- arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts | 2 --
- arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b.dts | 2 --
- arch/arm64/boot/dts/broadcom/bcm2710-rpi-cm3.dts | 2 --
- arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dts | 2 --
- 6 files changed, 1 insertion(+), 15 deletions(-)
-
---- a/arch/arm/boot/dts/bcm2710.dtsi
-+++ b/arch/arm/boot/dts/bcm2710.dtsi
-@@ -5,11 +5,7 @@
- compatible = "brcm,bcm2837", "brcm,bcm2836";
-
- arm-pmu {
--#ifdef RPI364
-- compatible = "arm,armv8-pmuv3", "arm,cortex-a7-pmu";
--#else
-- compatible = "arm,cortex-a7-pmu";
--#endif
-+ compatible = "arm,cortex-a53-pmu", "arm,cortex-a7-pmu";
- };
-
- soc {
---- a/arch/arm64/boot/dts/broadcom/bcm2710-rpi-2-b.dts
-+++ b/arch/arm64/boot/dts/broadcom/bcm2710-rpi-2-b.dts
-@@ -1,3 +1 @@
--#define RPI364
--
- #include "../../../../arm/boot/dts/bcm2710-rpi-2-b.dts"
---- a/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts
-+++ b/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts
-@@ -1,3 +1 @@
--#define RPI364
--
- #include "../../../../arm/boot/dts/bcm2710-rpi-3-b-plus.dts"
---- a/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b.dts
-+++ b/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b.dts
-@@ -1,3 +1 @@
--#define RPI364
--
- #include "../../../../arm/boot/dts/bcm2710-rpi-3-b.dts"
---- a/arch/arm64/boot/dts/broadcom/bcm2710-rpi-cm3.dts
-+++ b/arch/arm64/boot/dts/broadcom/bcm2710-rpi-cm3.dts
-@@ -1,3 +1 @@
--#define RPI364
--
- #include "../../../../arm/boot/dts/bcm2710-rpi-cm3.dts"
---- a/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dts
-+++ b/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dts
-@@ -1,3 +1 @@
--#define RPI364
--
- #include "../../../../arm/boot/dts/bcm2711-rpi-4-b.dts"
--- /dev/null
+From cff8c5c2a95a4afd65bfa3198258d03bc790cddb Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Tue, 25 Feb 2020 14:11:59 +0100
+Subject: [PATCH] net: bcmgenet: Clear ID_MODE_DIS in
+ EXT_RGMII_OOB_CTRL when not needed
+
+commit 402482a6a78e5c61d8a2ec6311fc5b4aca392cd6 upstream.
+
+Outdated Raspberry Pi 4 firmware might configure the external PHY as
+rgmii although the kernel currently sets it as rgmii-rxid. This makes
+connections unreliable as ID_MODE_DIS is left enabled. To avoid this,
+explicitly clear that bit whenever we don't need it.
+
+Fixes: da38802211cc ("net: bcmgenet: Add RGMII_RXID support")
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Acked-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Matthias Reichl <hias@horus.com>
+---
+ drivers/net/ethernet/broadcom/genet/bcmmii.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/ethernet/broadcom/genet/bcmmii.c
++++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c
+@@ -292,6 +292,7 @@ int bcmgenet_mii_config(struct net_devic
+ */
+ if (priv->ext_phy) {
+ reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
++ reg &= ~ID_MODE_DIS;
+ reg |= id_mode_dis;
+ if (GENET_IS_V1(priv) || GENET_IS_V2(priv) || GENET_IS_V3(priv))
+ reg |= RGMII_MODE_EN_V123;
--- /dev/null
+From fade8b3cf37785297b4f8a9bbd13ab107208af5a Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:22 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Fix possible reference past
+ end of string
+
+Commit 8582e244e5fe72d2e9ace186fa8f3ed3bb4122e1 upstream.
+
+Before this commit, if the last option of a video=... option is for
+example "rotate" without a "=<value>" after it then delim will point to
+the terminating 0 of the string, and value which is sets to <delim + 1>
+will point one position past the end of the string.
+
+This commit fixes this by enforcing that the contents of delim equals '='
+as it should be for options which take a value, this check is done in a
+new drm_mode_parse_cmdline_int helper function which factors out the
+common integer parsing code for all the options which take an int.
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-1-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c | 68 ++++++++++++++++---------------------
+ 1 file changed, 30 insertions(+), 38 deletions(-)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1568,11 +1568,34 @@ static int drm_mode_parse_cmdline_res_mo
+ return 0;
+ }
+
++static int drm_mode_parse_cmdline_int(const char *delim, unsigned int *int_ret)
++{
++ const char *value;
++ char *endp;
++
++ /*
++ * delim must point to the '=', otherwise it is a syntax error and
++ * if delim points to the terminating zero, then delim + 1 wil point
++ * past the end of the string.
++ */
++ if (*delim != '=')
++ return -EINVAL;
++
++ value = delim + 1;
++ *int_ret = simple_strtol(value, &endp, 10);
++
++ /* Make sure we have parsed something */
++ if (endp == value)
++ return -EINVAL;
++
++ return 0;
++}
++
+ static int drm_mode_parse_cmdline_options(char *str, size_t len,
+ const struct drm_connector *connector,
+ struct drm_cmdline_mode *mode)
+ {
+- unsigned int rotation = 0;
++ unsigned int deg, margin, rotation = 0;
+ char *sep = str;
+
+ while ((sep = strchr(sep, ','))) {
+@@ -1588,13 +1611,7 @@ static int drm_mode_parse_cmdline_option
+ }
+
+ if (!strncmp(option, "rotate", delim - option)) {
+- const char *value = delim + 1;
+- unsigned int deg;
+-
+- deg = simple_strtol(value, &sep, 10);
+-
+- /* Make sure we have parsed something */
+- if (sep == value)
++ if (drm_mode_parse_cmdline_int(delim, °))
+ return -EINVAL;
+
+ switch (deg) {
+@@ -1619,57 +1636,32 @@ static int drm_mode_parse_cmdline_option
+ }
+ } else if (!strncmp(option, "reflect_x", delim - option)) {
+ rotation |= DRM_MODE_REFLECT_X;
+- sep = delim;
+ } else if (!strncmp(option, "reflect_y", delim - option)) {
+ rotation |= DRM_MODE_REFLECT_Y;
+- sep = delim;
+ } else if (!strncmp(option, "margin_right", delim - option)) {
+- const char *value = delim + 1;
+- unsigned int margin;
+-
+- margin = simple_strtol(value, &sep, 10);
+-
+- /* Make sure we have parsed something */
+- if (sep == value)
++ if (drm_mode_parse_cmdline_int(delim, &margin))
+ return -EINVAL;
+
+ mode->tv_margins.right = margin;
+ } else if (!strncmp(option, "margin_left", delim - option)) {
+- const char *value = delim + 1;
+- unsigned int margin;
+-
+- margin = simple_strtol(value, &sep, 10);
+-
+- /* Make sure we have parsed something */
+- if (sep == value)
++ if (drm_mode_parse_cmdline_int(delim, &margin))
+ return -EINVAL;
+
+ mode->tv_margins.left = margin;
+ } else if (!strncmp(option, "margin_top", delim - option)) {
+- const char *value = delim + 1;
+- unsigned int margin;
+-
+- margin = simple_strtol(value, &sep, 10);
+-
+- /* Make sure we have parsed something */
+- if (sep == value)
++ if (drm_mode_parse_cmdline_int(delim, &margin))
+ return -EINVAL;
+
+ mode->tv_margins.top = margin;
+ } else if (!strncmp(option, "margin_bottom", delim - option)) {
+- const char *value = delim + 1;
+- unsigned int margin;
+-
+- margin = simple_strtol(value, &sep, 10);
+-
+- /* Make sure we have parsed something */
+- if (sep == value)
++ if (drm_mode_parse_cmdline_int(delim, &margin))
+ return -EINVAL;
+
+ mode->tv_margins.bottom = margin;
+ } else {
+ return -EINVAL;
+ }
++ sep = delim;
+ }
+
+ if (!(rotation & DRM_MODE_ROTATE_MASK))
+++ /dev/null
-From cff8c5c2a95a4afd65bfa3198258d03bc790cddb Mon Sep 17 00:00:00 2001
-From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Date: Tue, 25 Feb 2020 14:11:59 +0100
-Subject: [PATCH] net: bcmgenet: Clear ID_MODE_DIS in
- EXT_RGMII_OOB_CTRL when not needed
-
-commit 402482a6a78e5c61d8a2ec6311fc5b4aca392cd6 upstream.
-
-Outdated Raspberry Pi 4 firmware might configure the external PHY as
-rgmii although the kernel currently sets it as rgmii-rxid. This makes
-connections unreliable as ID_MODE_DIS is left enabled. To avoid this,
-explicitly clear that bit whenever we don't need it.
-
-Fixes: da38802211cc ("net: bcmgenet: Add RGMII_RXID support")
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Acked-by: Florian Fainelli <f.fainelli@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
-Signed-off-by: Matthias Reichl <hias@horus.com>
----
- drivers/net/ethernet/broadcom/genet/bcmmii.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/net/ethernet/broadcom/genet/bcmmii.c
-+++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c
-@@ -292,6 +292,7 @@ int bcmgenet_mii_config(struct net_devic
- */
- if (priv->ext_phy) {
- reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
-+ reg &= ~ID_MODE_DIS;
- reg |= id_mode_dis;
- if (GENET_IS_V1(priv) || GENET_IS_V2(priv) || GENET_IS_V3(priv))
- reg |= RGMII_MODE_EN_V123;
+++ /dev/null
-From fade8b3cf37785297b4f8a9bbd13ab107208af5a Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:22 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Fix possible reference past
- end of string
-
-Commit 8582e244e5fe72d2e9ace186fa8f3ed3bb4122e1 upstream.
-
-Before this commit, if the last option of a video=... option is for
-example "rotate" without a "=<value>" after it then delim will point to
-the terminating 0 of the string, and value which is sets to <delim + 1>
-will point one position past the end of the string.
-
-This commit fixes this by enforcing that the contents of delim equals '='
-as it should be for options which take a value, this check is done in a
-new drm_mode_parse_cmdline_int helper function which factors out the
-common integer parsing code for all the options which take an int.
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-1-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c | 68 ++++++++++++++++---------------------
- 1 file changed, 30 insertions(+), 38 deletions(-)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1568,11 +1568,34 @@ static int drm_mode_parse_cmdline_res_mo
- return 0;
- }
-
-+static int drm_mode_parse_cmdline_int(const char *delim, unsigned int *int_ret)
-+{
-+ const char *value;
-+ char *endp;
-+
-+ /*
-+ * delim must point to the '=', otherwise it is a syntax error and
-+ * if delim points to the terminating zero, then delim + 1 wil point
-+ * past the end of the string.
-+ */
-+ if (*delim != '=')
-+ return -EINVAL;
-+
-+ value = delim + 1;
-+ *int_ret = simple_strtol(value, &endp, 10);
-+
-+ /* Make sure we have parsed something */
-+ if (endp == value)
-+ return -EINVAL;
-+
-+ return 0;
-+}
-+
- static int drm_mode_parse_cmdline_options(char *str, size_t len,
- const struct drm_connector *connector,
- struct drm_cmdline_mode *mode)
- {
-- unsigned int rotation = 0;
-+ unsigned int deg, margin, rotation = 0;
- char *sep = str;
-
- while ((sep = strchr(sep, ','))) {
-@@ -1588,13 +1611,7 @@ static int drm_mode_parse_cmdline_option
- }
-
- if (!strncmp(option, "rotate", delim - option)) {
-- const char *value = delim + 1;
-- unsigned int deg;
--
-- deg = simple_strtol(value, &sep, 10);
--
-- /* Make sure we have parsed something */
-- if (sep == value)
-+ if (drm_mode_parse_cmdline_int(delim, °))
- return -EINVAL;
-
- switch (deg) {
-@@ -1619,57 +1636,32 @@ static int drm_mode_parse_cmdline_option
- }
- } else if (!strncmp(option, "reflect_x", delim - option)) {
- rotation |= DRM_MODE_REFLECT_X;
-- sep = delim;
- } else if (!strncmp(option, "reflect_y", delim - option)) {
- rotation |= DRM_MODE_REFLECT_Y;
-- sep = delim;
- } else if (!strncmp(option, "margin_right", delim - option)) {
-- const char *value = delim + 1;
-- unsigned int margin;
--
-- margin = simple_strtol(value, &sep, 10);
--
-- /* Make sure we have parsed something */
-- if (sep == value)
-+ if (drm_mode_parse_cmdline_int(delim, &margin))
- return -EINVAL;
-
- mode->tv_margins.right = margin;
- } else if (!strncmp(option, "margin_left", delim - option)) {
-- const char *value = delim + 1;
-- unsigned int margin;
--
-- margin = simple_strtol(value, &sep, 10);
--
-- /* Make sure we have parsed something */
-- if (sep == value)
-+ if (drm_mode_parse_cmdline_int(delim, &margin))
- return -EINVAL;
-
- mode->tv_margins.left = margin;
- } else if (!strncmp(option, "margin_top", delim - option)) {
-- const char *value = delim + 1;
-- unsigned int margin;
--
-- margin = simple_strtol(value, &sep, 10);
--
-- /* Make sure we have parsed something */
-- if (sep == value)
-+ if (drm_mode_parse_cmdline_int(delim, &margin))
- return -EINVAL;
-
- mode->tv_margins.top = margin;
- } else if (!strncmp(option, "margin_bottom", delim - option)) {
-- const char *value = delim + 1;
-- unsigned int margin;
--
-- margin = simple_strtol(value, &sep, 10);
--
-- /* Make sure we have parsed something */
-- if (sep == value)
-+ if (drm_mode_parse_cmdline_int(delim, &margin))
- return -EINVAL;
-
- mode->tv_margins.bottom = margin;
- } else {
- return -EINVAL;
- }
-+ sep = delim;
- }
-
- if (!(rotation & DRM_MODE_ROTATE_MASK))
--- /dev/null
+From 250363a413cd08e723789e1b8821608ff5eebfe6 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:23 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Make various char pointers
+ const
+
+Commit 83e14ea3a64f00897cc31974d3ae4e27e5a7405b upstream.
+
+We are not supposed to modify the passed in string, make char pointers
+used in drm_mode_parse_cmdline_options() const char * where possible.
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-2-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1591,15 +1591,15 @@ static int drm_mode_parse_cmdline_int(co
+ return 0;
+ }
+
+-static int drm_mode_parse_cmdline_options(char *str, size_t len,
++static int drm_mode_parse_cmdline_options(const char *str, size_t len,
+ const struct drm_connector *connector,
+ struct drm_cmdline_mode *mode)
+ {
+ unsigned int deg, margin, rotation = 0;
+- char *sep = str;
++ const char *sep = str;
+
+ while ((sep = strchr(sep, ','))) {
+- char *delim, *option;
++ const char *delim, *option;
+
+ option = sep + 1;
+ delim = strchr(option, '=');
+@@ -1725,8 +1725,8 @@ bool drm_mode_parse_command_line_for_con
+ bool named_mode = false, parse_extras = false;
+ unsigned int bpp_off = 0, refresh_off = 0, options_off = 0;
+ unsigned int mode_end = 0;
+- char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
+- char *options_ptr = NULL;
++ const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
++ const char *options_ptr = NULL;
+ char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
+ int ret;
+
+++ /dev/null
-From 250363a413cd08e723789e1b8821608ff5eebfe6 Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:23 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Make various char pointers
- const
-
-Commit 83e14ea3a64f00897cc31974d3ae4e27e5a7405b upstream.
-
-We are not supposed to modify the passed in string, make char pointers
-used in drm_mode_parse_cmdline_options() const char * where possible.
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-2-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c | 10 +++++-----
- 1 file changed, 5 insertions(+), 5 deletions(-)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1591,15 +1591,15 @@ static int drm_mode_parse_cmdline_int(co
- return 0;
- }
-
--static int drm_mode_parse_cmdline_options(char *str, size_t len,
-+static int drm_mode_parse_cmdline_options(const char *str, size_t len,
- const struct drm_connector *connector,
- struct drm_cmdline_mode *mode)
- {
- unsigned int deg, margin, rotation = 0;
-- char *sep = str;
-+ const char *sep = str;
-
- while ((sep = strchr(sep, ','))) {
-- char *delim, *option;
-+ const char *delim, *option;
-
- option = sep + 1;
- delim = strchr(option, '=');
-@@ -1725,8 +1725,8 @@ bool drm_mode_parse_command_line_for_con
- bool named_mode = false, parse_extras = false;
- unsigned int bpp_off = 0, refresh_off = 0, options_off = 0;
- unsigned int mode_end = 0;
-- char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
-- char *options_ptr = NULL;
-+ const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
-+ const char *options_ptr = NULL;
- char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
- int ret;
-
--- /dev/null
+From 0e7c5e80d8d310a881d723a426762e8822d5bf35 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:24 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Stop parsing extras after
+ bpp / refresh at ', '
+
+Commit c2ed3e941901810ad3d55ce1935fa22c5007fee4 upstream.
+
+Before this commit it was impossible to add an extra mode argument after
+a bpp or refresh specifier, combined with an option, e.g.
+video=HDMI-1:720x480-24e,rotate=180 would not work, either the "e" to
+force enable would need to be dropped or the ",rotate=180", otherwise
+the mode_option would not be accepted.
+
+This commit fixes this by fixing the length calculation if extras_ptr
+is set to stop the extra parsing at the start of the options (stop at the
+',' options_ptr points to).
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-3-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c | 10 ++++---
+ .../gpu/drm/selftests/drm_cmdline_selftests.h | 1 +
+ .../drm/selftests/test-drm_cmdline_parser.c | 26 +++++++++++++++++++
+ 3 files changed, 33 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1728,7 +1728,7 @@ bool drm_mode_parse_command_line_for_con
+ const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
+ const char *options_ptr = NULL;
+ char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
+- int ret;
++ int i, len, ret;
+
+ #ifdef CONFIG_FB
+ if (!mode_option)
+@@ -1848,9 +1848,11 @@ bool drm_mode_parse_command_line_for_con
+ else if (refresh_ptr)
+ extra_ptr = refresh_end_ptr;
+
+- if (extra_ptr &&
+- extra_ptr != options_ptr) {
+- int len = strlen(name) - (extra_ptr - name);
++ if (extra_ptr) {
++ if (options_ptr)
++ len = options_ptr - extra_ptr;
++ else
++ len = strlen(extra_ptr);
+
+ ret = drm_mode_parse_cmdline_extra(extra_ptr, len, false,
+ connector, mode);
+--- a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
++++ b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
+@@ -61,3 +61,4 @@ cmdline_test(drm_cmdline_test_vmirror)
+ cmdline_test(drm_cmdline_test_margin_options)
+ cmdline_test(drm_cmdline_test_multiple_options)
+ cmdline_test(drm_cmdline_test_invalid_option)
++cmdline_test(drm_cmdline_test_bpp_extra_and_option)
+--- a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
++++ b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
+@@ -1003,6 +1003,32 @@ static int drm_cmdline_test_invalid_opti
+ return 0;
+ }
+
++static int drm_cmdline_test_bpp_extra_and_option(void *ignored)
++{
++ struct drm_cmdline_mode mode = { };
++
++ FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24e,rotate=180",
++ &no_connector,
++ &mode));
++ FAIL_ON(!mode.specified);
++ FAIL_ON(mode.xres != 720);
++ FAIL_ON(mode.yres != 480);
++ FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180);
++
++ FAIL_ON(mode.refresh_specified);
++
++ FAIL_ON(!mode.bpp_specified);
++ FAIL_ON(mode.bpp != 24);
++
++ FAIL_ON(mode.rb);
++ FAIL_ON(mode.cvt);
++ FAIL_ON(mode.interlace);
++ FAIL_ON(mode.margins);
++ FAIL_ON(mode.force != DRM_FORCE_ON);
++
++ return 0;
++}
++
+ #include "drm_selftest.c"
+
+ static int __init test_drm_cmdline_init(void)
--- /dev/null
+From bc4d8c5519c74b9bdf4d35369ba76bac01be8ca2 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:25 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Accept extras directly
+ after mode combined with options
+
+Commit cfb0881b8f621b656a9e23b31944a5db94cf5842 upstream.
+
+Before this commit it was impossible to combine an extra mode argument
+specified directly after the resolution with an option, e.g.
+video=HDMI-1:720x480e,rotate=180 would not work, either the "e" to force
+enable would need to be dropped or the ",rotate=180", otherwise the
+mode_option would not be accepted.
+
+This commit fixes this by setting parse_extras to true in this case, so
+that drm_mode_parse_cmdline_res_mode() parses the extra arguments directly
+after the resolution.
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-4-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c | 1 +
+ .../gpu/drm/selftests/drm_cmdline_selftests.h | 1 +
+ .../drm/selftests/test-drm_cmdline_parser.c | 24 +++++++++++++++++++
+ 3 files changed, 26 insertions(+)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1801,6 +1801,7 @@ bool drm_mode_parse_command_line_for_con
+ mode_end = refresh_off;
+ } else if (options_ptr) {
+ mode_end = options_off;
++ parse_extras = true;
+ } else {
+ mode_end = strlen(name);
+ parse_extras = true;
+--- a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
++++ b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
+@@ -62,3 +62,4 @@ cmdline_test(drm_cmdline_test_margin_opt
+ cmdline_test(drm_cmdline_test_multiple_options)
+ cmdline_test(drm_cmdline_test_invalid_option)
+ cmdline_test(drm_cmdline_test_bpp_extra_and_option)
++cmdline_test(drm_cmdline_test_extra_and_option)
+--- a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
++++ b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
+@@ -1029,6 +1029,30 @@ static int drm_cmdline_test_bpp_extra_an
+ return 0;
+ }
+
++static int drm_cmdline_test_extra_and_option(void *ignored)
++{
++ struct drm_cmdline_mode mode = { };
++
++ FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480e,rotate=180",
++ &no_connector,
++ &mode));
++ FAIL_ON(!mode.specified);
++ FAIL_ON(mode.xres != 720);
++ FAIL_ON(mode.yres != 480);
++ FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180);
++
++ FAIL_ON(mode.refresh_specified);
++ FAIL_ON(mode.bpp_specified);
++
++ FAIL_ON(mode.rb);
++ FAIL_ON(mode.cvt);
++ FAIL_ON(mode.interlace);
++ FAIL_ON(mode.margins);
++ FAIL_ON(mode.force != DRM_FORCE_ON);
++
++ return 0;
++}
++
+ #include "drm_selftest.c"
+
+ static int __init test_drm_cmdline_init(void)
+++ /dev/null
-From 0e7c5e80d8d310a881d723a426762e8822d5bf35 Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:24 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Stop parsing extras after
- bpp / refresh at ', '
-
-Commit c2ed3e941901810ad3d55ce1935fa22c5007fee4 upstream.
-
-Before this commit it was impossible to add an extra mode argument after
-a bpp or refresh specifier, combined with an option, e.g.
-video=HDMI-1:720x480-24e,rotate=180 would not work, either the "e" to
-force enable would need to be dropped or the ",rotate=180", otherwise
-the mode_option would not be accepted.
-
-This commit fixes this by fixing the length calculation if extras_ptr
-is set to stop the extra parsing at the start of the options (stop at the
-',' options_ptr points to).
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-3-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c | 10 ++++---
- .../gpu/drm/selftests/drm_cmdline_selftests.h | 1 +
- .../drm/selftests/test-drm_cmdline_parser.c | 26 +++++++++++++++++++
- 3 files changed, 33 insertions(+), 4 deletions(-)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1728,7 +1728,7 @@ bool drm_mode_parse_command_line_for_con
- const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
- const char *options_ptr = NULL;
- char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
-- int ret;
-+ int i, len, ret;
-
- #ifdef CONFIG_FB
- if (!mode_option)
-@@ -1848,9 +1848,11 @@ bool drm_mode_parse_command_line_for_con
- else if (refresh_ptr)
- extra_ptr = refresh_end_ptr;
-
-- if (extra_ptr &&
-- extra_ptr != options_ptr) {
-- int len = strlen(name) - (extra_ptr - name);
-+ if (extra_ptr) {
-+ if (options_ptr)
-+ len = options_ptr - extra_ptr;
-+ else
-+ len = strlen(extra_ptr);
-
- ret = drm_mode_parse_cmdline_extra(extra_ptr, len, false,
- connector, mode);
---- a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
-+++ b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
-@@ -61,3 +61,4 @@ cmdline_test(drm_cmdline_test_vmirror)
- cmdline_test(drm_cmdline_test_margin_options)
- cmdline_test(drm_cmdline_test_multiple_options)
- cmdline_test(drm_cmdline_test_invalid_option)
-+cmdline_test(drm_cmdline_test_bpp_extra_and_option)
---- a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
-+++ b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
-@@ -1003,6 +1003,32 @@ static int drm_cmdline_test_invalid_opti
- return 0;
- }
-
-+static int drm_cmdline_test_bpp_extra_and_option(void *ignored)
-+{
-+ struct drm_cmdline_mode mode = { };
-+
-+ FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24e,rotate=180",
-+ &no_connector,
-+ &mode));
-+ FAIL_ON(!mode.specified);
-+ FAIL_ON(mode.xres != 720);
-+ FAIL_ON(mode.yres != 480);
-+ FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180);
-+
-+ FAIL_ON(mode.refresh_specified);
-+
-+ FAIL_ON(!mode.bpp_specified);
-+ FAIL_ON(mode.bpp != 24);
-+
-+ FAIL_ON(mode.rb);
-+ FAIL_ON(mode.cvt);
-+ FAIL_ON(mode.interlace);
-+ FAIL_ON(mode.margins);
-+ FAIL_ON(mode.force != DRM_FORCE_ON);
-+
-+ return 0;
-+}
-+
- #include "drm_selftest.c"
-
- static int __init test_drm_cmdline_init(void)
+++ /dev/null
-From bc4d8c5519c74b9bdf4d35369ba76bac01be8ca2 Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:25 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Accept extras directly
- after mode combined with options
-
-Commit cfb0881b8f621b656a9e23b31944a5db94cf5842 upstream.
-
-Before this commit it was impossible to combine an extra mode argument
-specified directly after the resolution with an option, e.g.
-video=HDMI-1:720x480e,rotate=180 would not work, either the "e" to force
-enable would need to be dropped or the ",rotate=180", otherwise the
-mode_option would not be accepted.
-
-This commit fixes this by setting parse_extras to true in this case, so
-that drm_mode_parse_cmdline_res_mode() parses the extra arguments directly
-after the resolution.
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-4-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c | 1 +
- .../gpu/drm/selftests/drm_cmdline_selftests.h | 1 +
- .../drm/selftests/test-drm_cmdline_parser.c | 24 +++++++++++++++++++
- 3 files changed, 26 insertions(+)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1801,6 +1801,7 @@ bool drm_mode_parse_command_line_for_con
- mode_end = refresh_off;
- } else if (options_ptr) {
- mode_end = options_off;
-+ parse_extras = true;
- } else {
- mode_end = strlen(name);
- parse_extras = true;
---- a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
-+++ b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
-@@ -62,3 +62,4 @@ cmdline_test(drm_cmdline_test_margin_opt
- cmdline_test(drm_cmdline_test_multiple_options)
- cmdline_test(drm_cmdline_test_invalid_option)
- cmdline_test(drm_cmdline_test_bpp_extra_and_option)
-+cmdline_test(drm_cmdline_test_extra_and_option)
---- a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
-+++ b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
-@@ -1029,6 +1029,30 @@ static int drm_cmdline_test_bpp_extra_an
- return 0;
- }
-
-+static int drm_cmdline_test_extra_and_option(void *ignored)
-+{
-+ struct drm_cmdline_mode mode = { };
-+
-+ FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480e,rotate=180",
-+ &no_connector,
-+ &mode));
-+ FAIL_ON(!mode.specified);
-+ FAIL_ON(mode.xres != 720);
-+ FAIL_ON(mode.yres != 480);
-+ FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180);
-+
-+ FAIL_ON(mode.refresh_specified);
-+ FAIL_ON(mode.bpp_specified);
-+
-+ FAIL_ON(mode.rb);
-+ FAIL_ON(mode.cvt);
-+ FAIL_ON(mode.interlace);
-+ FAIL_ON(mode.margins);
-+ FAIL_ON(mode.force != DRM_FORCE_ON);
-+
-+ return 0;
-+}
-+
- #include "drm_selftest.c"
-
- static int __init test_drm_cmdline_init(void)
--- /dev/null
+From 5b6257773b43e7a7b28f86359d2e9ebe15346b78 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:26 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Rework
+ drm_mode_parse_cmdline_options()
+
+Commit 739b200c2edcaaa7a86f37b0c11db57956433dfb upstream.
+
+Refactor drm_mode_parse_cmdline_options() so that it takes a pointer
+to the first option, rather then a pointer to the ',' before the first
+option.
+
+This is a preparation patch for allowing parsing of stand-alone options
+without a mode before them, e.g.: video=HDMI-1:margin_right=14,...
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-5-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c | 21 +++++++++------------
+ 1 file changed, 9 insertions(+), 12 deletions(-)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1591,23 +1591,21 @@ static int drm_mode_parse_cmdline_int(co
+ return 0;
+ }
+
+-static int drm_mode_parse_cmdline_options(const char *str, size_t len,
++static int drm_mode_parse_cmdline_options(const char *str,
+ const struct drm_connector *connector,
+ struct drm_cmdline_mode *mode)
+ {
+ unsigned int deg, margin, rotation = 0;
+- const char *sep = str;
++ const char *delim, *option, *sep;
+
+- while ((sep = strchr(sep, ','))) {
+- const char *delim, *option;
+-
+- option = sep + 1;
++ option = str;
++ do {
+ delim = strchr(option, '=');
+ if (!delim) {
+ delim = strchr(option, ',');
+
+ if (!delim)
+- delim = str + len;
++ delim = option + strlen(option);
+ }
+
+ if (!strncmp(option, "rotate", delim - option)) {
+@@ -1661,8 +1659,9 @@ static int drm_mode_parse_cmdline_option
+ } else {
+ return -EINVAL;
+ }
+- sep = delim;
+- }
++ sep = strchr(delim, ',');
++ option = sep + 1;
++ } while (sep);
+
+ if (!(rotation & DRM_MODE_ROTATE_MASK))
+ rotation |= DRM_MODE_ROTATE_0;
+@@ -1862,9 +1861,7 @@ bool drm_mode_parse_command_line_for_con
+ }
+
+ if (options_ptr) {
+- int len = strlen(name) - (options_ptr - name);
+-
+- ret = drm_mode_parse_cmdline_options(options_ptr, len,
++ ret = drm_mode_parse_cmdline_options(options_ptr + 1,
+ connector, mode);
+ if (ret)
+ return false;
--- /dev/null
+From d3c76025a7de614fade5ffcaa8c1d88d8d64213e Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:27 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Add freestanding argument
+ to drm_mode_parse_cmdline_options()
+
+Commit 99e2716e053734b70434502867be24d20a3e2d84 upstream.
+
+Add a freestanding function argument to drm_mode_parse_cmdline_options()
+similar to how drm_mode_parse_cmdline_extra() already has this.
+
+This is a preparation patch for allowing parsing of stand-alone options
+without a mode before them, e.g.: video=HDMI-1:margin_right=14,...
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-6-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1592,6 +1592,7 @@ static int drm_mode_parse_cmdline_int(co
+ }
+
+ static int drm_mode_parse_cmdline_options(const char *str,
++ bool freestanding,
+ const struct drm_connector *connector,
+ struct drm_cmdline_mode *mode)
+ {
+@@ -1670,6 +1671,9 @@ static int drm_mode_parse_cmdline_option
+ if (!is_power_of_2(rotation & DRM_MODE_ROTATE_MASK))
+ return -EINVAL;
+
++ if (rotation && freestanding)
++ return -EINVAL;
++
+ mode->rotation_reflection = rotation;
+
+ return 0;
+@@ -1862,6 +1866,7 @@ bool drm_mode_parse_command_line_for_con
+
+ if (options_ptr) {
+ ret = drm_mode_parse_cmdline_options(options_ptr + 1,
++ false,
+ connector, mode);
+ if (ret)
+ return false;
+++ /dev/null
-From 5b6257773b43e7a7b28f86359d2e9ebe15346b78 Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:26 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Rework
- drm_mode_parse_cmdline_options()
-
-Commit 739b200c2edcaaa7a86f37b0c11db57956433dfb upstream.
-
-Refactor drm_mode_parse_cmdline_options() so that it takes a pointer
-to the first option, rather then a pointer to the ',' before the first
-option.
-
-This is a preparation patch for allowing parsing of stand-alone options
-without a mode before them, e.g.: video=HDMI-1:margin_right=14,...
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-5-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c | 21 +++++++++------------
- 1 file changed, 9 insertions(+), 12 deletions(-)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1591,23 +1591,21 @@ static int drm_mode_parse_cmdline_int(co
- return 0;
- }
-
--static int drm_mode_parse_cmdline_options(const char *str, size_t len,
-+static int drm_mode_parse_cmdline_options(const char *str,
- const struct drm_connector *connector,
- struct drm_cmdline_mode *mode)
- {
- unsigned int deg, margin, rotation = 0;
-- const char *sep = str;
-+ const char *delim, *option, *sep;
-
-- while ((sep = strchr(sep, ','))) {
-- const char *delim, *option;
--
-- option = sep + 1;
-+ option = str;
-+ do {
- delim = strchr(option, '=');
- if (!delim) {
- delim = strchr(option, ',');
-
- if (!delim)
-- delim = str + len;
-+ delim = option + strlen(option);
- }
-
- if (!strncmp(option, "rotate", delim - option)) {
-@@ -1661,8 +1659,9 @@ static int drm_mode_parse_cmdline_option
- } else {
- return -EINVAL;
- }
-- sep = delim;
-- }
-+ sep = strchr(delim, ',');
-+ option = sep + 1;
-+ } while (sep);
-
- if (!(rotation & DRM_MODE_ROTATE_MASK))
- rotation |= DRM_MODE_ROTATE_0;
-@@ -1862,9 +1861,7 @@ bool drm_mode_parse_command_line_for_con
- }
-
- if (options_ptr) {
-- int len = strlen(name) - (options_ptr - name);
--
-- ret = drm_mode_parse_cmdline_options(options_ptr, len,
-+ ret = drm_mode_parse_cmdline_options(options_ptr + 1,
- connector, mode);
- if (ret)
- return false;
+++ /dev/null
-From d3c76025a7de614fade5ffcaa8c1d88d8d64213e Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:27 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Add freestanding argument
- to drm_mode_parse_cmdline_options()
-
-Commit 99e2716e053734b70434502867be24d20a3e2d84 upstream.
-
-Add a freestanding function argument to drm_mode_parse_cmdline_options()
-similar to how drm_mode_parse_cmdline_extra() already has this.
-
-This is a preparation patch for allowing parsing of stand-alone options
-without a mode before them, e.g.: video=HDMI-1:margin_right=14,...
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-6-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1592,6 +1592,7 @@ static int drm_mode_parse_cmdline_int(co
- }
-
- static int drm_mode_parse_cmdline_options(const char *str,
-+ bool freestanding,
- const struct drm_connector *connector,
- struct drm_cmdline_mode *mode)
- {
-@@ -1670,6 +1671,9 @@ static int drm_mode_parse_cmdline_option
- if (!is_power_of_2(rotation & DRM_MODE_ROTATE_MASK))
- return -EINVAL;
-
-+ if (rotation && freestanding)
-+ return -EINVAL;
-+
- mode->rotation_reflection = rotation;
-
- return 0;
-@@ -1862,6 +1866,7 @@ bool drm_mode_parse_command_line_for_con
-
- if (options_ptr) {
- ret = drm_mode_parse_cmdline_options(options_ptr + 1,
-+ false,
- connector, mode);
- if (ret)
- return false;
--- /dev/null
+From 5b7efd2fa0c75164373d6faf28fec4b89065d39c Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:28 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Set bpp/refresh_specified
+ after successful parsing
+
+Commit 6a2d163756545aa3180d7851d5f8322b865e72be upstream.
+
+drm_connector_get_cmdline_mode() calls
+drm_mode_parse_command_line_for_connector() with &connector->cmdline_mode
+as mode argument, so anything which we store in the mode arguments gets
+kept even if we return false.
+
+Avoid storing a possibly false-postive bpp/refresh_specified setting
+in connector->cmdline_mode by moving the setting of these to after
+successful parsing of the bpp/refresh parts of the video= argument.
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-7-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1778,10 +1778,8 @@ bool drm_mode_parse_command_line_for_con
+
+ /* Try to locate the bpp and refresh specifiers, if any */
+ bpp_ptr = strchr(name, '-');
+- if (bpp_ptr) {
++ if (bpp_ptr)
+ bpp_off = bpp_ptr - name;
+- mode->bpp_specified = true;
+- }
+
+ refresh_ptr = strchr(name, '@');
+ if (refresh_ptr) {
+@@ -1789,7 +1787,6 @@ bool drm_mode_parse_command_line_for_con
+ return false;
+
+ refresh_off = refresh_ptr - name;
+- mode->refresh_specified = true;
+ }
+
+ /* Locate the start of named options */
+@@ -1832,6 +1829,8 @@ bool drm_mode_parse_command_line_for_con
+ ret = drm_mode_parse_cmdline_bpp(bpp_ptr, &bpp_end_ptr, mode);
+ if (ret)
+ return false;
++
++ mode->bpp_specified = true;
+ }
+
+ if (refresh_ptr) {
+@@ -1839,6 +1838,8 @@ bool drm_mode_parse_command_line_for_con
+ &refresh_end_ptr, mode);
+ if (ret)
+ return false;
++
++ mode->refresh_specified = true;
+ }
+
+ /*
--- /dev/null
+From b3212eba63b541206e12c8dc31fc0d99d916b210 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:29 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Allow specifying
+ stand-alone options
+
+Commit 7b1cce760afe38b40f0989cdf10b2190dccf9815 upstream.
+
+Some options which can be specified on the commandline, such as
+margin_right=..., margin_left=..., etc. are applied not only to the
+specified mode, but to all modes. As such it would be nice if the user
+can simply say e.g.
+video=HDMI-1:margin_right=14,margin_left=24,margin_bottom=36,margin_top=42
+
+This commit refactors drm_mode_parse_command_line_for_connector() to
+add support for this, and as a nice side effect also cleans up the
+function a bit.
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-8-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c | 92 +++++++------------
+ .../gpu/drm/selftests/drm_cmdline_selftests.h | 2 +
+ .../drm/selftests/test-drm_cmdline_parser.c | 50 ++++++++++
+ 3 files changed, 86 insertions(+), 58 deletions(-)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1684,17 +1684,6 @@ static const char * const drm_named_mode
+ "PAL",
+ };
+
+-static bool drm_named_mode_is_in_whitelist(const char *mode, unsigned int size)
+-{
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++)
+- if (!strncmp(mode, drm_named_modes_whitelist[i], size))
+- return true;
+-
+- return false;
+-}
+-
+ /**
+ * drm_mode_parse_command_line_for_connector - parse command line modeline for connector
+ * @mode_option: optional per connector mode option
+@@ -1725,7 +1714,7 @@ bool drm_mode_parse_command_line_for_con
+ struct drm_cmdline_mode *mode)
+ {
+ const char *name;
+- bool named_mode = false, parse_extras = false;
++ bool freestanding = false, parse_extras = false;
+ unsigned int bpp_off = 0, refresh_off = 0, options_off = 0;
+ unsigned int mode_end = 0;
+ const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
+@@ -1745,49 +1734,14 @@ bool drm_mode_parse_command_line_for_con
+
+ name = mode_option;
+
+- /*
+- * This is a bit convoluted. To differentiate between the
+- * named modes and poorly formatted resolutions, we need a
+- * bunch of things:
+- * - We need to make sure that the first character (which
+- * would be our resolution in X) is a digit.
+- * - If not, then it's either a named mode or a force on/off.
+- * To distinguish between the two, we need to run the
+- * extra parsing function, and if not, then we consider it
+- * a named mode.
+- *
+- * If this isn't enough, we should add more heuristics here,
+- * and matching unit-tests.
+- */
+- if (!isdigit(name[0]) && name[0] != 'x') {
+- unsigned int namelen = strlen(name);
+-
+- /*
+- * Only the force on/off options can be in that case,
+- * and they all take a single character.
+- */
+- if (namelen == 1) {
+- ret = drm_mode_parse_cmdline_extra(name, namelen, true,
+- connector, mode);
+- if (!ret)
+- return true;
+- }
+-
+- named_mode = true;
+- }
+-
+ /* Try to locate the bpp and refresh specifiers, if any */
+ bpp_ptr = strchr(name, '-');
+ if (bpp_ptr)
+ bpp_off = bpp_ptr - name;
+
+ refresh_ptr = strchr(name, '@');
+- if (refresh_ptr) {
+- if (named_mode)
+- return false;
+-
++ if (refresh_ptr)
+ refresh_off = refresh_ptr - name;
+- }
+
+ /* Locate the start of named options */
+ options_ptr = strchr(name, ',');
+@@ -1807,23 +1761,45 @@ bool drm_mode_parse_command_line_for_con
+ parse_extras = true;
+ }
+
+- if (named_mode) {
+- if (mode_end + 1 > DRM_DISPLAY_MODE_LEN)
+- return false;
+-
+- if (!drm_named_mode_is_in_whitelist(name, mode_end))
+- return false;
++ /* First check for a named mode */
++ for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++) {
++ ret = str_has_prefix(name, drm_named_modes_whitelist[i]);
++ if (ret == mode_end) {
++ if (refresh_ptr)
++ return false; /* named + refresh is invalid */
++
++ strcpy(mode->name, drm_named_modes_whitelist[i]);
++ mode->specified = true;
++ break;
++ }
++ }
+
+- strscpy(mode->name, name, mode_end + 1);
+- } else {
++ /* No named mode? Check for a normal mode argument, e.g. 1024x768 */
++ if (!mode->specified && isdigit(name[0])) {
+ ret = drm_mode_parse_cmdline_res_mode(name, mode_end,
+ parse_extras,
+ connector,
+ mode);
+ if (ret)
+ return false;
++
++ mode->specified = true;
++ }
++
++ /* No mode? Check for freestanding extras and/or options */
++ if (!mode->specified) {
++ unsigned int len = strlen(mode_option);
++
++ if (bpp_ptr || refresh_ptr)
++ return false; /* syntax error */
++
++ if (len == 1 || (len >= 2 && mode_option[1] == ','))
++ extra_ptr = mode_option;
++ else
++ options_ptr = mode_option - 1;
++
++ freestanding = true;
+ }
+- mode->specified = true;
+
+ if (bpp_ptr) {
+ ret = drm_mode_parse_cmdline_bpp(bpp_ptr, &bpp_end_ptr, mode);
+@@ -1859,7 +1835,7 @@ bool drm_mode_parse_command_line_for_con
+ else
+ len = strlen(extra_ptr);
+
+- ret = drm_mode_parse_cmdline_extra(extra_ptr, len, false,
++ ret = drm_mode_parse_cmdline_extra(extra_ptr, len, freestanding,
+ connector, mode);
+ if (ret)
+ return false;
+@@ -1867,7 +1843,7 @@ bool drm_mode_parse_command_line_for_con
+
+ if (options_ptr) {
+ ret = drm_mode_parse_cmdline_options(options_ptr + 1,
+- false,
++ freestanding,
+ connector, mode);
+ if (ret)
+ return false;
+--- a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
++++ b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
+@@ -63,3 +63,5 @@ cmdline_test(drm_cmdline_test_multiple_o
+ cmdline_test(drm_cmdline_test_invalid_option)
+ cmdline_test(drm_cmdline_test_bpp_extra_and_option)
+ cmdline_test(drm_cmdline_test_extra_and_option)
++cmdline_test(drm_cmdline_test_freestanding_options)
++cmdline_test(drm_cmdline_test_freestanding_force_e_and_options)
+--- a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
++++ b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
+@@ -1053,6 +1053,56 @@ static int drm_cmdline_test_extra_and_op
+ return 0;
+ }
+
++static int drm_cmdline_test_freestanding_options(void *ignored)
++{
++ struct drm_cmdline_mode mode = { };
++
++ FAIL_ON(!drm_mode_parse_command_line_for_connector("margin_right=14,margin_left=24,margin_bottom=36,margin_top=42",
++ &no_connector,
++ &mode));
++ FAIL_ON(mode.specified);
++ FAIL_ON(mode.refresh_specified);
++ FAIL_ON(mode.bpp_specified);
++
++ FAIL_ON(mode.tv_margins.right != 14);
++ FAIL_ON(mode.tv_margins.left != 24);
++ FAIL_ON(mode.tv_margins.bottom != 36);
++ FAIL_ON(mode.tv_margins.top != 42);
++
++ FAIL_ON(mode.rb);
++ FAIL_ON(mode.cvt);
++ FAIL_ON(mode.interlace);
++ FAIL_ON(mode.margins);
++ FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
++
++ return 0;
++}
++
++static int drm_cmdline_test_freestanding_force_e_and_options(void *ignored)
++{
++ struct drm_cmdline_mode mode = { };
++
++ FAIL_ON(!drm_mode_parse_command_line_for_connector("e,margin_right=14,margin_left=24,margin_bottom=36,margin_top=42",
++ &no_connector,
++ &mode));
++ FAIL_ON(mode.specified);
++ FAIL_ON(mode.refresh_specified);
++ FAIL_ON(mode.bpp_specified);
++
++ FAIL_ON(mode.tv_margins.right != 14);
++ FAIL_ON(mode.tv_margins.left != 24);
++ FAIL_ON(mode.tv_margins.bottom != 36);
++ FAIL_ON(mode.tv_margins.top != 42);
++
++ FAIL_ON(mode.rb);
++ FAIL_ON(mode.cvt);
++ FAIL_ON(mode.interlace);
++ FAIL_ON(mode.margins);
++ FAIL_ON(mode.force != DRM_FORCE_ON);
++
++ return 0;
++}
++
+ #include "drm_selftest.c"
+
+ static int __init test_drm_cmdline_init(void)
+++ /dev/null
-From 5b7efd2fa0c75164373d6faf28fec4b89065d39c Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:28 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Set bpp/refresh_specified
- after successful parsing
-
-Commit 6a2d163756545aa3180d7851d5f8322b865e72be upstream.
-
-drm_connector_get_cmdline_mode() calls
-drm_mode_parse_command_line_for_connector() with &connector->cmdline_mode
-as mode argument, so anything which we store in the mode arguments gets
-kept even if we return false.
-
-Avoid storing a possibly false-postive bpp/refresh_specified setting
-in connector->cmdline_mode by moving the setting of these to after
-successful parsing of the bpp/refresh parts of the video= argument.
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-7-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c | 9 +++++----
- 1 file changed, 5 insertions(+), 4 deletions(-)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1778,10 +1778,8 @@ bool drm_mode_parse_command_line_for_con
-
- /* Try to locate the bpp and refresh specifiers, if any */
- bpp_ptr = strchr(name, '-');
-- if (bpp_ptr) {
-+ if (bpp_ptr)
- bpp_off = bpp_ptr - name;
-- mode->bpp_specified = true;
-- }
-
- refresh_ptr = strchr(name, '@');
- if (refresh_ptr) {
-@@ -1789,7 +1787,6 @@ bool drm_mode_parse_command_line_for_con
- return false;
-
- refresh_off = refresh_ptr - name;
-- mode->refresh_specified = true;
- }
-
- /* Locate the start of named options */
-@@ -1832,6 +1829,8 @@ bool drm_mode_parse_command_line_for_con
- ret = drm_mode_parse_cmdline_bpp(bpp_ptr, &bpp_end_ptr, mode);
- if (ret)
- return false;
-+
-+ mode->bpp_specified = true;
- }
-
- if (refresh_ptr) {
-@@ -1839,6 +1838,8 @@ bool drm_mode_parse_command_line_for_con
- &refresh_end_ptr, mode);
- if (ret)
- return false;
-+
-+ mode->refresh_specified = true;
- }
-
- /*
--- /dev/null
+From 7d395633947fa6595a117f40e0f27ba87be77d6c Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:30 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Add support for specifying
+ panel_orientation (v2)
+
+Commit 4e7a4a6fbdc669c44e6079f9d5eb25673749455f upstream.
+
+Sometimes we want to override a connector's panel_orientation from the
+kernel commandline. Either for testing and for special cases, e.g. a kiosk
+like setup which uses a TV mounted in portrait mode.
+
+Users can already specify a "rotate" option through a video= kernel cmdline
+option. But that only supports 0/180 degrees (see drm_client_modeset TODO)
+and only works for in kernel modeset clients, not for userspace kms users.
+
+The "panel-orientation" connector property OTOH does support 90/270 degrees
+as it leaves dealing with the rotation up to userspace and this does work
+for userspace kms clients (at least those which support this property).
+
+Changes in v2:
+-Add missing ':' after @panel_orientation (reported by kbuild test robot)
+
+BugLink: https://gitlab.freedesktop.org/plymouth/plymouth/merge_requests/83
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-9-hdegoede@redhat.com
+---
+ Documentation/fb/modedb.rst | 3 ++
+ drivers/gpu/drm/drm_modes.c | 32 +++++++++++++++++++
+ .../gpu/drm/selftests/drm_cmdline_selftests.h | 1 +
+ .../drm/selftests/test-drm_cmdline_parser.c | 22 +++++++++++++
+ include/drm/drm_connector.h | 8 +++++
+ 5 files changed, 66 insertions(+)
+
+--- a/Documentation/fb/modedb.rst
++++ b/Documentation/fb/modedb.rst
+@@ -65,6 +65,9 @@ Valid options are::
+ - reflect_y (boolean): Perform an axial symmetry on the Y axis
+ - rotate (integer): Rotate the initial framebuffer by x
+ degrees. Valid values are 0, 90, 180 and 270.
++ - panel_orientation, one of "normal", "upside_down", "left_side_up", or
++ "right_side_up". For KMS drivers only, this sets the "panel orientation"
++ property on the kms connector as hint for kms users.
+
+
+ -----------------------------------------------------------------------------
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1591,6 +1591,33 @@ static int drm_mode_parse_cmdline_int(co
+ return 0;
+ }
+
++static int drm_mode_parse_panel_orientation(const char *delim,
++ struct drm_cmdline_mode *mode)
++{
++ const char *value;
++
++ if (*delim != '=')
++ return -EINVAL;
++
++ value = delim + 1;
++ delim = strchr(value, ',');
++ if (!delim)
++ delim = value + strlen(value);
++
++ if (!strncmp(value, "normal", delim - value))
++ mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL;
++ else if (!strncmp(value, "upside_down", delim - value))
++ mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP;
++ else if (!strncmp(value, "left_side_up", delim - value))
++ mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP;
++ else if (!strncmp(value, "right_side_up", delim - value))
++ mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP;
++ else
++ return -EINVAL;
++
++ return 0;
++}
++
+ static int drm_mode_parse_cmdline_options(const char *str,
+ bool freestanding,
+ const struct drm_connector *connector,
+@@ -1657,6 +1684,9 @@ static int drm_mode_parse_cmdline_option
+ return -EINVAL;
+
+ mode->tv_margins.bottom = margin;
++ } else if (!strncmp(option, "panel_orientation", delim - option)) {
++ if (drm_mode_parse_panel_orientation(delim, mode))
++ return -EINVAL;
+ } else {
+ return -EINVAL;
+ }
+@@ -1722,6 +1752,8 @@ bool drm_mode_parse_command_line_for_con
+ char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
+ int i, len, ret;
+
++ mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
++
+ #ifdef CONFIG_FB
+ if (!mode_option)
+ mode_option = fb_mode_option;
+--- a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
++++ b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
+@@ -65,3 +65,4 @@ cmdline_test(drm_cmdline_test_bpp_extra_
+ cmdline_test(drm_cmdline_test_extra_and_option)
+ cmdline_test(drm_cmdline_test_freestanding_options)
+ cmdline_test(drm_cmdline_test_freestanding_force_e_and_options)
++cmdline_test(drm_cmdline_test_panel_orientation)
+--- a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
++++ b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
+@@ -1103,6 +1103,28 @@ static int drm_cmdline_test_freestanding
+ return 0;
+ }
+
++static int drm_cmdline_test_panel_orientation(void *ignored)
++{
++ struct drm_cmdline_mode mode = { };
++
++ FAIL_ON(!drm_mode_parse_command_line_for_connector("panel_orientation=upside_down",
++ &no_connector,
++ &mode));
++ FAIL_ON(mode.specified);
++ FAIL_ON(mode.refresh_specified);
++ FAIL_ON(mode.bpp_specified);
++
++ FAIL_ON(mode.panel_orientation != DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP);
++
++ FAIL_ON(mode.rb);
++ FAIL_ON(mode.cvt);
++ FAIL_ON(mode.interlace);
++ FAIL_ON(mode.margins);
++ FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
++
++ return 0;
++}
++
+ #include "drm_selftest.c"
+
+ static int __init test_drm_cmdline_init(void)
+--- a/include/drm/drm_connector.h
++++ b/include/drm/drm_connector.h
+@@ -1066,6 +1066,14 @@ struct drm_cmdline_mode {
+ unsigned int rotation_reflection;
+
+ /**
++ * @panel_orientation:
++ *
++ * drm-connector "panel orientation" property override value,
++ * DRM_MODE_PANEL_ORIENTATION_UNKNOWN if not set.
++ */
++ enum drm_panel_orientation panel_orientation;
++
++ /**
+ * @tv_margins: TV margins to apply to the mode.
+ */
+ struct drm_connector_tv_margins tv_margins;
+++ /dev/null
-From b3212eba63b541206e12c8dc31fc0d99d916b210 Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:29 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Allow specifying
- stand-alone options
-
-Commit 7b1cce760afe38b40f0989cdf10b2190dccf9815 upstream.
-
-Some options which can be specified on the commandline, such as
-margin_right=..., margin_left=..., etc. are applied not only to the
-specified mode, but to all modes. As such it would be nice if the user
-can simply say e.g.
-video=HDMI-1:margin_right=14,margin_left=24,margin_bottom=36,margin_top=42
-
-This commit refactors drm_mode_parse_command_line_for_connector() to
-add support for this, and as a nice side effect also cleans up the
-function a bit.
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-8-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c | 92 +++++++------------
- .../gpu/drm/selftests/drm_cmdline_selftests.h | 2 +
- .../drm/selftests/test-drm_cmdline_parser.c | 50 ++++++++++
- 3 files changed, 86 insertions(+), 58 deletions(-)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1684,17 +1684,6 @@ static const char * const drm_named_mode
- "PAL",
- };
-
--static bool drm_named_mode_is_in_whitelist(const char *mode, unsigned int size)
--{
-- int i;
--
-- for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++)
-- if (!strncmp(mode, drm_named_modes_whitelist[i], size))
-- return true;
--
-- return false;
--}
--
- /**
- * drm_mode_parse_command_line_for_connector - parse command line modeline for connector
- * @mode_option: optional per connector mode option
-@@ -1725,7 +1714,7 @@ bool drm_mode_parse_command_line_for_con
- struct drm_cmdline_mode *mode)
- {
- const char *name;
-- bool named_mode = false, parse_extras = false;
-+ bool freestanding = false, parse_extras = false;
- unsigned int bpp_off = 0, refresh_off = 0, options_off = 0;
- unsigned int mode_end = 0;
- const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
-@@ -1745,49 +1734,14 @@ bool drm_mode_parse_command_line_for_con
-
- name = mode_option;
-
-- /*
-- * This is a bit convoluted. To differentiate between the
-- * named modes and poorly formatted resolutions, we need a
-- * bunch of things:
-- * - We need to make sure that the first character (which
-- * would be our resolution in X) is a digit.
-- * - If not, then it's either a named mode or a force on/off.
-- * To distinguish between the two, we need to run the
-- * extra parsing function, and if not, then we consider it
-- * a named mode.
-- *
-- * If this isn't enough, we should add more heuristics here,
-- * and matching unit-tests.
-- */
-- if (!isdigit(name[0]) && name[0] != 'x') {
-- unsigned int namelen = strlen(name);
--
-- /*
-- * Only the force on/off options can be in that case,
-- * and they all take a single character.
-- */
-- if (namelen == 1) {
-- ret = drm_mode_parse_cmdline_extra(name, namelen, true,
-- connector, mode);
-- if (!ret)
-- return true;
-- }
--
-- named_mode = true;
-- }
--
- /* Try to locate the bpp and refresh specifiers, if any */
- bpp_ptr = strchr(name, '-');
- if (bpp_ptr)
- bpp_off = bpp_ptr - name;
-
- refresh_ptr = strchr(name, '@');
-- if (refresh_ptr) {
-- if (named_mode)
-- return false;
--
-+ if (refresh_ptr)
- refresh_off = refresh_ptr - name;
-- }
-
- /* Locate the start of named options */
- options_ptr = strchr(name, ',');
-@@ -1807,23 +1761,45 @@ bool drm_mode_parse_command_line_for_con
- parse_extras = true;
- }
-
-- if (named_mode) {
-- if (mode_end + 1 > DRM_DISPLAY_MODE_LEN)
-- return false;
--
-- if (!drm_named_mode_is_in_whitelist(name, mode_end))
-- return false;
-+ /* First check for a named mode */
-+ for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++) {
-+ ret = str_has_prefix(name, drm_named_modes_whitelist[i]);
-+ if (ret == mode_end) {
-+ if (refresh_ptr)
-+ return false; /* named + refresh is invalid */
-+
-+ strcpy(mode->name, drm_named_modes_whitelist[i]);
-+ mode->specified = true;
-+ break;
-+ }
-+ }
-
-- strscpy(mode->name, name, mode_end + 1);
-- } else {
-+ /* No named mode? Check for a normal mode argument, e.g. 1024x768 */
-+ if (!mode->specified && isdigit(name[0])) {
- ret = drm_mode_parse_cmdline_res_mode(name, mode_end,
- parse_extras,
- connector,
- mode);
- if (ret)
- return false;
-+
-+ mode->specified = true;
-+ }
-+
-+ /* No mode? Check for freestanding extras and/or options */
-+ if (!mode->specified) {
-+ unsigned int len = strlen(mode_option);
-+
-+ if (bpp_ptr || refresh_ptr)
-+ return false; /* syntax error */
-+
-+ if (len == 1 || (len >= 2 && mode_option[1] == ','))
-+ extra_ptr = mode_option;
-+ else
-+ options_ptr = mode_option - 1;
-+
-+ freestanding = true;
- }
-- mode->specified = true;
-
- if (bpp_ptr) {
- ret = drm_mode_parse_cmdline_bpp(bpp_ptr, &bpp_end_ptr, mode);
-@@ -1859,7 +1835,7 @@ bool drm_mode_parse_command_line_for_con
- else
- len = strlen(extra_ptr);
-
-- ret = drm_mode_parse_cmdline_extra(extra_ptr, len, false,
-+ ret = drm_mode_parse_cmdline_extra(extra_ptr, len, freestanding,
- connector, mode);
- if (ret)
- return false;
-@@ -1867,7 +1843,7 @@ bool drm_mode_parse_command_line_for_con
-
- if (options_ptr) {
- ret = drm_mode_parse_cmdline_options(options_ptr + 1,
-- false,
-+ freestanding,
- connector, mode);
- if (ret)
- return false;
---- a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
-+++ b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
-@@ -63,3 +63,5 @@ cmdline_test(drm_cmdline_test_multiple_o
- cmdline_test(drm_cmdline_test_invalid_option)
- cmdline_test(drm_cmdline_test_bpp_extra_and_option)
- cmdline_test(drm_cmdline_test_extra_and_option)
-+cmdline_test(drm_cmdline_test_freestanding_options)
-+cmdline_test(drm_cmdline_test_freestanding_force_e_and_options)
---- a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
-+++ b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
-@@ -1053,6 +1053,56 @@ static int drm_cmdline_test_extra_and_op
- return 0;
- }
-
-+static int drm_cmdline_test_freestanding_options(void *ignored)
-+{
-+ struct drm_cmdline_mode mode = { };
-+
-+ FAIL_ON(!drm_mode_parse_command_line_for_connector("margin_right=14,margin_left=24,margin_bottom=36,margin_top=42",
-+ &no_connector,
-+ &mode));
-+ FAIL_ON(mode.specified);
-+ FAIL_ON(mode.refresh_specified);
-+ FAIL_ON(mode.bpp_specified);
-+
-+ FAIL_ON(mode.tv_margins.right != 14);
-+ FAIL_ON(mode.tv_margins.left != 24);
-+ FAIL_ON(mode.tv_margins.bottom != 36);
-+ FAIL_ON(mode.tv_margins.top != 42);
-+
-+ FAIL_ON(mode.rb);
-+ FAIL_ON(mode.cvt);
-+ FAIL_ON(mode.interlace);
-+ FAIL_ON(mode.margins);
-+ FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
-+
-+ return 0;
-+}
-+
-+static int drm_cmdline_test_freestanding_force_e_and_options(void *ignored)
-+{
-+ struct drm_cmdline_mode mode = { };
-+
-+ FAIL_ON(!drm_mode_parse_command_line_for_connector("e,margin_right=14,margin_left=24,margin_bottom=36,margin_top=42",
-+ &no_connector,
-+ &mode));
-+ FAIL_ON(mode.specified);
-+ FAIL_ON(mode.refresh_specified);
-+ FAIL_ON(mode.bpp_specified);
-+
-+ FAIL_ON(mode.tv_margins.right != 14);
-+ FAIL_ON(mode.tv_margins.left != 24);
-+ FAIL_ON(mode.tv_margins.bottom != 36);
-+ FAIL_ON(mode.tv_margins.top != 42);
-+
-+ FAIL_ON(mode.rb);
-+ FAIL_ON(mode.cvt);
-+ FAIL_ON(mode.interlace);
-+ FAIL_ON(mode.margins);
-+ FAIL_ON(mode.force != DRM_FORCE_ON);
-+
-+ return 0;
-+}
-+
- #include "drm_selftest.c"
-
- static int __init test_drm_cmdline_init(void)
+++ /dev/null
-From 7d395633947fa6595a117f40e0f27ba87be77d6c Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:30 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Add support for specifying
- panel_orientation (v2)
-
-Commit 4e7a4a6fbdc669c44e6079f9d5eb25673749455f upstream.
-
-Sometimes we want to override a connector's panel_orientation from the
-kernel commandline. Either for testing and for special cases, e.g. a kiosk
-like setup which uses a TV mounted in portrait mode.
-
-Users can already specify a "rotate" option through a video= kernel cmdline
-option. But that only supports 0/180 degrees (see drm_client_modeset TODO)
-and only works for in kernel modeset clients, not for userspace kms users.
-
-The "panel-orientation" connector property OTOH does support 90/270 degrees
-as it leaves dealing with the rotation up to userspace and this does work
-for userspace kms clients (at least those which support this property).
-
-Changes in v2:
--Add missing ':' after @panel_orientation (reported by kbuild test robot)
-
-BugLink: https://gitlab.freedesktop.org/plymouth/plymouth/merge_requests/83
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-9-hdegoede@redhat.com
----
- Documentation/fb/modedb.rst | 3 ++
- drivers/gpu/drm/drm_modes.c | 32 +++++++++++++++++++
- .../gpu/drm/selftests/drm_cmdline_selftests.h | 1 +
- .../drm/selftests/test-drm_cmdline_parser.c | 22 +++++++++++++
- include/drm/drm_connector.h | 8 +++++
- 5 files changed, 66 insertions(+)
-
---- a/Documentation/fb/modedb.rst
-+++ b/Documentation/fb/modedb.rst
-@@ -65,6 +65,9 @@ Valid options are::
- - reflect_y (boolean): Perform an axial symmetry on the Y axis
- - rotate (integer): Rotate the initial framebuffer by x
- degrees. Valid values are 0, 90, 180 and 270.
-+ - panel_orientation, one of "normal", "upside_down", "left_side_up", or
-+ "right_side_up". For KMS drivers only, this sets the "panel orientation"
-+ property on the kms connector as hint for kms users.
-
-
- -----------------------------------------------------------------------------
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1591,6 +1591,33 @@ static int drm_mode_parse_cmdline_int(co
- return 0;
- }
-
-+static int drm_mode_parse_panel_orientation(const char *delim,
-+ struct drm_cmdline_mode *mode)
-+{
-+ const char *value;
-+
-+ if (*delim != '=')
-+ return -EINVAL;
-+
-+ value = delim + 1;
-+ delim = strchr(value, ',');
-+ if (!delim)
-+ delim = value + strlen(value);
-+
-+ if (!strncmp(value, "normal", delim - value))
-+ mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL;
-+ else if (!strncmp(value, "upside_down", delim - value))
-+ mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP;
-+ else if (!strncmp(value, "left_side_up", delim - value))
-+ mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP;
-+ else if (!strncmp(value, "right_side_up", delim - value))
-+ mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP;
-+ else
-+ return -EINVAL;
-+
-+ return 0;
-+}
-+
- static int drm_mode_parse_cmdline_options(const char *str,
- bool freestanding,
- const struct drm_connector *connector,
-@@ -1657,6 +1684,9 @@ static int drm_mode_parse_cmdline_option
- return -EINVAL;
-
- mode->tv_margins.bottom = margin;
-+ } else if (!strncmp(option, "panel_orientation", delim - option)) {
-+ if (drm_mode_parse_panel_orientation(delim, mode))
-+ return -EINVAL;
- } else {
- return -EINVAL;
- }
-@@ -1722,6 +1752,8 @@ bool drm_mode_parse_command_line_for_con
- char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
- int i, len, ret;
-
-+ mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
-+
- #ifdef CONFIG_FB
- if (!mode_option)
- mode_option = fb_mode_option;
---- a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
-+++ b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
-@@ -65,3 +65,4 @@ cmdline_test(drm_cmdline_test_bpp_extra_
- cmdline_test(drm_cmdline_test_extra_and_option)
- cmdline_test(drm_cmdline_test_freestanding_options)
- cmdline_test(drm_cmdline_test_freestanding_force_e_and_options)
-+cmdline_test(drm_cmdline_test_panel_orientation)
---- a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
-+++ b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
-@@ -1103,6 +1103,28 @@ static int drm_cmdline_test_freestanding
- return 0;
- }
-
-+static int drm_cmdline_test_panel_orientation(void *ignored)
-+{
-+ struct drm_cmdline_mode mode = { };
-+
-+ FAIL_ON(!drm_mode_parse_command_line_for_connector("panel_orientation=upside_down",
-+ &no_connector,
-+ &mode));
-+ FAIL_ON(mode.specified);
-+ FAIL_ON(mode.refresh_specified);
-+ FAIL_ON(mode.bpp_specified);
-+
-+ FAIL_ON(mode.panel_orientation != DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP);
-+
-+ FAIL_ON(mode.rb);
-+ FAIL_ON(mode.cvt);
-+ FAIL_ON(mode.interlace);
-+ FAIL_ON(mode.margins);
-+ FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
-+
-+ return 0;
-+}
-+
- #include "drm_selftest.c"
-
- static int __init test_drm_cmdline_init(void)
---- a/include/drm/drm_connector.h
-+++ b/include/drm/drm_connector.h
-@@ -1066,6 +1066,14 @@ struct drm_cmdline_mode {
- unsigned int rotation_reflection;
-
- /**
-+ * @panel_orientation:
-+ *
-+ * drm-connector "panel orientation" property override value,
-+ * DRM_MODE_PANEL_ORIENTATION_UNKNOWN if not set.
-+ */
-+ enum drm_panel_orientation panel_orientation;
-+
-+ /**
- * @tv_margins: TV margins to apply to the mode.
- */
- struct drm_connector_tv_margins tv_margins;
--- /dev/null
+From 339666068713986cfe1456175dd8a7514f6ed2ab Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:31 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Remove some unnecessary
+ code (v2)
+
+Commit 5b926617cdef41ce0696e09834991194b1759e28 upstream.
+
+fb_get_options() will return fb_mode_option if no video=<connector-name>
+argument is present on the kernel commandline, so there is no need to also
+do this in drm_mode_parse_command_line_for_connector() as our only caller
+uses fb_get_options() to get the mode_option argument.
+
+Changes in v2:
+-Split out the changes dealing with the initialization of the mode struct
+ into a separate patch
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-10-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c | 5 -----
+ 1 file changed, 5 deletions(-)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1754,11 +1754,6 @@ bool drm_mode_parse_command_line_for_con
+
+ mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
+
+-#ifdef CONFIG_FB
+- if (!mode_option)
+- mode_option = fb_mode_option;
+-#endif
+-
+ if (!mode_option) {
+ mode->specified = false;
+ return false;
--- /dev/null
+From d89b3f22cf7b6bba8081f6d16c9087019fdcf586 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:32 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Explicitly memset the
+ passed in drm_cmdline_mode struct
+
+Commit d1fe276b5115f0d581c3cfe6154633b3547e8aab upstream.
+
+Instead of only setting mode->specified on false on an early exit and
+leaving e.g. mode->bpp_specified and mode->refresh_specified as is,
+lets be consistent and just zero out the entire passed in struct at
+the top of drm_mode_parse_command_line_for_connector()
+
+Changes in v3:
+-Drop "mode->specified = false;" line instead of the "return false;" (oops)
+ This crasher was reported-by: kernel test robot <lkp@intel.com>
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-11-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1752,12 +1752,11 @@ bool drm_mode_parse_command_line_for_con
+ char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
+ int i, len, ret;
+
++ memset(mode, 0, sizeof(*mode));
+ mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
+
+- if (!mode_option) {
+- mode->specified = false;
++ if (!mode_option)
+ return false;
+- }
+
+ name = mode_option;
+
+++ /dev/null
-From 339666068713986cfe1456175dd8a7514f6ed2ab Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:31 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Remove some unnecessary
- code (v2)
-
-Commit 5b926617cdef41ce0696e09834991194b1759e28 upstream.
-
-fb_get_options() will return fb_mode_option if no video=<connector-name>
-argument is present on the kernel commandline, so there is no need to also
-do this in drm_mode_parse_command_line_for_connector() as our only caller
-uses fb_get_options() to get the mode_option argument.
-
-Changes in v2:
--Split out the changes dealing with the initialization of the mode struct
- into a separate patch
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-10-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c | 5 -----
- 1 file changed, 5 deletions(-)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1754,11 +1754,6 @@ bool drm_mode_parse_command_line_for_con
-
- mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
-
--#ifdef CONFIG_FB
-- if (!mode_option)
-- mode_option = fb_mode_option;
--#endif
--
- if (!mode_option) {
- mode->specified = false;
- return false;
+++ /dev/null
-From d89b3f22cf7b6bba8081f6d16c9087019fdcf586 Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:32 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Explicitly memset the
- passed in drm_cmdline_mode struct
-
-Commit d1fe276b5115f0d581c3cfe6154633b3547e8aab upstream.
-
-Instead of only setting mode->specified on false on an early exit and
-leaving e.g. mode->bpp_specified and mode->refresh_specified as is,
-lets be consistent and just zero out the entire passed in struct at
-the top of drm_mode_parse_command_line_for_connector()
-
-Changes in v3:
--Drop "mode->specified = false;" line instead of the "return false;" (oops)
- This crasher was reported-by: kernel test robot <lkp@intel.com>
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-11-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c | 5 ++---
- 1 file changed, 2 insertions(+), 3 deletions(-)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1752,12 +1752,11 @@ bool drm_mode_parse_command_line_for_con
- char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
- int i, len, ret;
-
-+ memset(mode, 0, sizeof(*mode));
- mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
-
-- if (!mode_option) {
-- mode->specified = false;
-+ if (!mode_option)
- return false;
-- }
-
- name = mode_option;
-
--- /dev/null
+From 12b60ef71cc005ee7290f692169d46a7e78df01a Mon Sep 17 00:00:00 2001
+From: Yukimasa Sugizaki <4298265+Terminus-IMRC@users.noreply.github.com>
+Date: Fri, 20 Mar 2020 19:01:23 +0900
+Subject: [PATCH] drm/v3d: Replace wait_for macros to remove use of
+ msleep (#3510)
+
+commit 9daee6141cc9c75b09659b02b1cb9eeb2f5e16cc upstream.
+
+The wait_for macro's for Broadcom V3D driver used msleep, which is
+inappropriate due to its inaccuracy at low values (minimum wait time
+is about 30ms on the Raspberry Pi). This sleep was triggering in
+v3d_clean_caches(), causing us to only be able to dispatch ~33 compute
+jobs per second.
+
+This patch replaces the macro with the one from the Intel i915 version
+which uses usleep_range to provide more accurate waits.
+
+v2: Split from the vc4 patch so that we can confidently apply to
+ stable (by anholt)
+
+Signed-off-by: James Hughes <james.hughes@raspberrypi.com>
+Signed-off-by: Eric Anholt <eric@anholt.net>
+Link: https://patchwork.freedesktop.org/patch/msgid/20200217153145.13780-1-james.hughes@raspberrypi.com
+Link: https://github.com/raspberrypi/linux/issues/3460
+Fixes: 57692c94dcbe ("drm/v3d: Introduce a new DRM driver for Broadcom V3D V3.x+")
+
+Co-authored-by: James Hughes <james.hughes@raspberrypi.com>
+---
+ drivers/gpu/drm/v3d/v3d_drv.h | 41 ++++++++++++++++++++++++-----------
+ 1 file changed, 28 insertions(+), 13 deletions(-)
+
+--- a/drivers/gpu/drm/v3d/v3d_drv.h
++++ b/drivers/gpu/drm/v3d/v3d_drv.h
+@@ -260,27 +260,42 @@ struct v3d_csd_job {
+ };
+
+ /**
+- * _wait_for - magic (register) wait macro
++ * __wait_for - magic wait macro
+ *
+- * Does the right thing for modeset paths when run under kdgb or similar atomic
+- * contexts. Note that it's important that we check the condition again after
+- * having timed out, since the timeout could be due to preemption or similar and
+- * we've never had a chance to check the condition before the timeout.
++ * Macro to help avoid open coding check/wait/timeout patterns. Note that it's
++ * important that we check the condition again after having timed out, since the
++ * timeout could be due to preemption or similar and we've never had a chance to
++ * check the condition before the timeout.
+ */
+-#define wait_for(COND, MS) ({ \
+- unsigned long timeout__ = jiffies + msecs_to_jiffies(MS) + 1; \
+- int ret__ = 0; \
+- while (!(COND)) { \
+- if (time_after(jiffies, timeout__)) { \
+- if (!(COND)) \
+- ret__ = -ETIMEDOUT; \
++#define __wait_for(OP, COND, US, Wmin, Wmax) ({ \
++ const ktime_t end__ = ktime_add_ns(ktime_get_raw(), 1000ll * (US)); \
++ long wait__ = (Wmin); /* recommended min for usleep is 10 us */ \
++ int ret__; \
++ might_sleep(); \
++ for (;;) { \
++ const bool expired__ = ktime_after(ktime_get_raw(), end__); \
++ OP; \
++ /* Guarantee COND check prior to timeout */ \
++ barrier(); \
++ if (COND) { \
++ ret__ = 0; \
+ break; \
+ } \
+- msleep(1); \
++ if (expired__) { \
++ ret__ = -ETIMEDOUT; \
++ break; \
++ } \
++ usleep_range(wait__, wait__ * 2); \
++ if (wait__ < (Wmax)) \
++ wait__ <<= 1; \
+ } \
+ ret__; \
+ })
+
++#define _wait_for(COND, US, Wmin, Wmax) __wait_for(, (COND), (US), (Wmin), \
++ (Wmax))
++#define wait_for(COND, MS) _wait_for((COND), (MS) * 1000, 10, 1000)
++
+ static inline unsigned long nsecs_to_jiffies_timeout(const u64 n)
+ {
+ /* nsecs_to_jiffies64() does not guard against overflow */
--- /dev/null
+From 863dace20e48954a7e013a2e88e27c692ce165b0 Mon Sep 17 00:00:00 2001
+From: Nick B <nick@pelagiris.org>
+Date: Mon, 9 Mar 2020 09:05:39 -0400
+Subject: [PATCH] Reduce noise from rpi poe hat fan
+
+This adds 2 extra states, at 40c and 45c, with PWM of 31 and 63 (out
+of 255) for the rpi poe hat fan. This significantly improves user
+experience by providing a smoother ramp up of the fan, from a pwm 0
+to 31 to 63 then finally to 150, and additionally makes it very easy
+for users to further tweak the values as needed for their specific
+application.
+
+The possible concerns I have are that a hysteresis of 2000 (2c) could
+be too narrow, and that running the fan more at a reduced temperature
+(40000 - 40c) could cause problems.
+
+Signed-off-by: Nick B <nick@pelagiris.org>
+---
+ .../arm/boot/dts/overlays/rpi-poe-overlay.dts | 35 ++++++++++++++++---
+ 1 file changed, 30 insertions(+), 5 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/rpi-poe-overlay.dts
++++ b/arch/arm/boot/dts/overlays/rpi-poe-overlay.dts
+@@ -14,9 +14,9 @@
+ compatible = "raspberrypi,rpi-poe-fan";
+ firmware = <&firmware>;
+ cooling-min-state = <0>;
+- cooling-max-state = <2>;
++ cooling-max-state = <4>;
+ #cooling-cells = <2>;
+- cooling-levels = <0 150 255>;
++ cooling-levels = <0 31 63 150 255>;
+ status = "okay";
+ };
+ };
+@@ -27,12 +27,21 @@
+ __overlay__ {
+ trips {
+ trip0: trip0 {
+- temperature = <50000>;
+- hysteresis = <5000>;
++ temperature = <40000>;
++ hysteresis = <2000>;
+ type = "active";
+ };
+ trip1: trip1 {
+-
++ temperature = <45000>;
++ hysteresis = <2000>;
++ type = "active";
++ };
++ trip2: trip2 {
++ temperature = <50000>;
++ hysteresis = <2000>;
++ type = "active";
++ };
++ trip3: trip3 {
+ temperature = <55000>;
+ hysteresis = <5000>;
+ type = "active";
+@@ -47,6 +56,14 @@
+ trip = <&trip1>;
+ cooling-device = <&fan0 1 2>;
+ };
++ map2 {
++ trip = <&trip2>;
++ cooling-device = <&fan0 2 3>;
++ };
++ map3 {
++ trip = <&trip3>;
++ cooling-device = <&fan0 3 4>;
++ };
+ };
+ };
+ };
+@@ -58,6 +75,10 @@
+ poe_fan_temp0_hyst = <&trip0>,"hysteresis:0";
+ poe_fan_temp1 = <&trip1>,"temperature:0";
+ poe_fan_temp1_hyst = <&trip1>,"hysteresis:0";
++ poe_fan_temp2 = <&trip2>,"temperature:0";
++ poe_fan_temp2_hyst = <&trip2>,"hysteresis:0";
++ poe_fan_temp3 = <&trip3>,"temperature:0";
++ poe_fan_temp3_hyst = <&trip3>,"hysteresis:0";
+ };
+ };
+
+@@ -66,5 +87,9 @@
+ poe_fan_temp0_hyst = <&trip0>,"hysteresis:0";
+ poe_fan_temp1 = <&trip1>,"temperature:0";
+ poe_fan_temp1_hyst = <&trip1>,"hysteresis:0";
++ poe_fan_temp2 = <&trip2>,"temperature:0";
++ poe_fan_temp2_hyst = <&trip2>,"hysteresis:0";
++ poe_fan_temp3 = <&trip3>,"temperature:0";
++ poe_fan_temp3_hyst = <&trip3>,"hysteresis:0";
+ };
+ };
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
-Date: Sat, 7 Mar 2020 22:37:52 +0100
-Subject: [PATCH] add Sensirion SPS30 to i2c-sensor overlay
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add support for Sensirion SPS30 particulate matter sensor with fixed
-address 0x69.
-
-Signed-off-by: Petr Štetiar <ynezz@true.cz>
-
-diff --git a/arch/arm/boot/dts/overlays/README b/arch/arm/boot/dts/overlays/README
-index 62ad35f78bad..0d7d00ac92c4 100644
---- a/arch/arm/boot/dts/overlays/README
-+++ b/arch/arm/boot/dts/overlays/README
-@@ -1261,6 +1261,9 @@ Params: addr Set the address for the BME280, BME680, BMP280,
- si7020 Select the Silicon Labs Si7013/20/21 humidity/
- temperature sensor
-
-+ sps30 Select the Sensirion SPS30 particulate matter
-+ sensor. Fixed address 0x69.
-+
- tmp102 Select the Texas Instruments TMP102 temp sensor
- Valid addresses 0x48-0x4b, default 0x48
-
-diff --git a/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts b/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts
-index 40881d72a157..ce97837b0db5 100644
---- a/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts
-@@ -231,6 +231,20 @@
- };
- };
-
-+ fragment@15 {
-+ target = <&i2c_arm>;
-+ __dormant__ {
-+ #address-cells = <1>;
-+ #size-cells = <0>;
-+ status = "okay";
-+
-+ sps30: sps30@69 {
-+ compatible = "sensirion,sps30";
-+ reg = <0x69>;
-+ status = "okay";
-+ };
-+ };
-+ };
-
- __overrides__ {
- addr = <&bme280>,"reg:0", <&bmp280>,"reg:0", <&tmp102>,"reg:0",
-@@ -252,5 +266,6 @@
- ds1621 = <0>,"+12";
- max17040 = <0>,"+13";
- bme680 = <0>,"+14";
-+ sps30 = <0>,"+15";
- };
- };
--- /dev/null
+From 60f3874207c50db6f6d9dbac40977843cb77acd5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
+Date: Sat, 7 Mar 2020 22:37:52 +0100
+Subject: [PATCH] add Sensirion SPS30 to i2c-sensor overlay
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add support for Sensirion SPS30 particulate matter sensor with fixed
+address 0x69.
+
+Signed-off-by: Petr Štetiar <ynezz@true.cz>
+---
+ arch/arm/boot/dts/overlays/README | 3 +++
+ arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts | 15 +++++++++++++++
+ 2 files changed, 18 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -1261,6 +1261,9 @@ Params: addr Set the
+ si7020 Select the Silicon Labs Si7013/20/21 humidity/
+ temperature sensor
+
++ sps30 Select the Sensirion SPS30 particulate matter
++ sensor. Fixed address 0x69.
++
+ tmp102 Select the Texas Instruments TMP102 temp sensor
+ Valid addresses 0x48-0x4b, default 0x48
+
+--- a/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts
++++ b/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts
+@@ -231,6 +231,20 @@
+ };
+ };
+
++ fragment@15 {
++ target = <&i2c_arm>;
++ __dormant__ {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "okay";
++
++ sps30: sps30@69 {
++ compatible = "sensirion,sps30";
++ reg = <0x69>;
++ status = "okay";
++ };
++ };
++ };
+
+ __overrides__ {
+ addr = <&bme280>,"reg:0", <&bmp280>,"reg:0", <&tmp102>,"reg:0",
+@@ -252,5 +266,6 @@
+ ds1621 = <0>,"+12";
+ max17040 = <0>,"+13";
+ bme680 = <0>,"+14";
++ sps30 = <0>,"+15";
+ };
+ };
--- /dev/null
+From 4af6218f1d01e5ae54dc43e4bd2421617c777570 Mon Sep 17 00:00:00 2001
+From: Ricardo Ribalda Delgado <ribalda@kernel.org>
+Date: Mon, 7 Oct 2019 12:06:31 -0300
+Subject: [PATCH] media: add V4L2_CTRL_TYPE_AREA control type
+
+Commit d1dc49370f8371b00e682ac409aa1987ce641e93 upstream.
+
+This type contains the width and the height of a rectangular area.
+
+Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
+Signed-off-by: Ricardo Ribalda Delgado <ribalda@kernel.org>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ drivers/media/v4l2-core/v4l2-ctrls.c | 21 ++++++++++++++
+ include/media/v4l2-ctrls.h | 42 ++++++++++++++++++++++++++++
+ include/uapi/linux/videodev2.h | 6 ++++
+ 3 files changed, 69 insertions(+)
+
+--- a/drivers/media/v4l2-core/v4l2-ctrls.c
++++ b/drivers/media/v4l2-core/v4l2-ctrls.c
+@@ -1673,6 +1673,7 @@ static int std_validate_compound(const s
+ {
+ struct v4l2_ctrl_mpeg2_slice_params *p_mpeg2_slice_params;
+ struct v4l2_ctrl_vp8_frame_header *p_vp8_frame_header;
++ struct v4l2_area *area;
+ void *p = ptr.p + idx * ctrl->elem_size;
+
+ switch ((u32)ctrl->type) {
+@@ -1749,6 +1750,11 @@ static int std_validate_compound(const s
+ zero_padding(p_vp8_frame_header->entropy_header);
+ zero_padding(p_vp8_frame_header->coder_state);
+ break;
++ case V4L2_CTRL_TYPE_AREA:
++ area = p;
++ if (!area->width || !area->height)
++ return -EINVAL;
++ break;
+ default:
+ return -EINVAL;
+ }
+@@ -2422,6 +2428,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s
+ case V4L2_CTRL_TYPE_VP8_FRAME_HEADER:
+ elem_size = sizeof(struct v4l2_ctrl_vp8_frame_header);
+ break;
++ case V4L2_CTRL_TYPE_AREA:
++ elem_size = sizeof(struct v4l2_area);
++ break;
+ default:
+ if (type < V4L2_CTRL_COMPOUND_TYPES)
+ elem_size = sizeof(s32);
+@@ -4086,6 +4095,18 @@ int __v4l2_ctrl_s_ctrl_string(struct v4l
+ }
+ EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_string);
+
++int __v4l2_ctrl_s_ctrl_area(struct v4l2_ctrl *ctrl,
++ const struct v4l2_area *area)
++{
++ lockdep_assert_held(ctrl->handler->lock);
++
++ /* It's a driver bug if this happens. */
++ WARN_ON(ctrl->type != V4L2_CTRL_TYPE_AREA);
++ *ctrl->p_new.p_area = *area;
++ return set_ctrl(NULL, ctrl, 0);
++}
++EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_area);
++
+ void v4l2_ctrl_request_complete(struct media_request *req,
+ struct v4l2_ctrl_handler *main_hdl)
+ {
+--- a/include/media/v4l2-ctrls.h
++++ b/include/media/v4l2-ctrls.h
+@@ -50,6 +50,7 @@ struct poll_table_struct;
+ * @p_h264_slice_params: Pointer to a struct v4l2_ctrl_h264_slice_params.
+ * @p_h264_decode_params: Pointer to a struct v4l2_ctrl_h264_decode_params.
+ * @p_vp8_frame_header: Pointer to a VP8 frame header structure.
++ * @p_area: Pointer to an area.
+ * @p: Pointer to a compound value.
+ */
+ union v4l2_ctrl_ptr {
+@@ -68,6 +69,7 @@ union v4l2_ctrl_ptr {
+ struct v4l2_ctrl_h264_slice_params *p_h264_slice_params;
+ struct v4l2_ctrl_h264_decode_params *p_h264_decode_params;
+ struct v4l2_ctrl_vp8_frame_header *p_vp8_frame_header;
++ struct v4l2_area *p_area;
+ void *p;
+ };
+
+@@ -1063,6 +1065,46 @@ static inline int v4l2_ctrl_s_ctrl_strin
+ v4l2_ctrl_unlock(ctrl);
+
+ return rval;
++}
++
++/**
++ * __v4l2_ctrl_s_ctrl_area() - Unlocked variant of v4l2_ctrl_s_ctrl_area().
++ *
++ * @ctrl: The control.
++ * @area: The new area.
++ *
++ * This sets the control's new area safely by going through the control
++ * framework. This function assumes the control's handler is already locked,
++ * allowing it to be used from within the &v4l2_ctrl_ops functions.
++ *
++ * This function is for area type controls only.
++ */
++int __v4l2_ctrl_s_ctrl_area(struct v4l2_ctrl *ctrl,
++ const struct v4l2_area *area);
++
++/**
++ * v4l2_ctrl_s_ctrl_area() - Helper function to set a control's area value
++ * from within a driver.
++ *
++ * @ctrl: The control.
++ * @area: The new area.
++ *
++ * This sets the control's new area safely by going through the control
++ * framework. This function will lock the control's handler, so it cannot be
++ * used from within the &v4l2_ctrl_ops functions.
++ *
++ * This function is for area type controls only.
++ */
++static inline int v4l2_ctrl_s_ctrl_area(struct v4l2_ctrl *ctrl,
++ const struct v4l2_area *area)
++{
++ int rval;
++
++ v4l2_ctrl_lock(ctrl);
++ rval = __v4l2_ctrl_s_ctrl_area(ctrl, area);
++ v4l2_ctrl_unlock(ctrl);
++
++ return rval;
+ }
+
+ /* Internal helper functions that deal with control events. */
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -427,6 +427,11 @@ struct v4l2_fract {
+ __u32 denominator;
+ };
+
++struct v4l2_area {
++ __u32 width;
++ __u32 height;
++};
++
+ /**
+ * struct v4l2_capability - Describes V4L2 device caps returned by VIDIOC_QUERYCAP
+ *
+@@ -1725,6 +1730,7 @@ enum v4l2_ctrl_type {
+ V4L2_CTRL_TYPE_U8 = 0x0100,
+ V4L2_CTRL_TYPE_U16 = 0x0101,
+ V4L2_CTRL_TYPE_U32 = 0x0102,
++ V4L2_CTRL_TYPE_AREA = 0x0106,
+ };
+
+ /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
--- /dev/null
+From 12eba72027d415bb3dfd4c8124813a322b27c793 Mon Sep 17 00:00:00 2001
+From: Ricardo Ribalda Delgado <ribalda@kernel.org>
+Date: Mon, 7 Oct 2019 12:06:33 -0300
+Subject: [PATCH] media: add V4L2_CID_UNIT_CELL_SIZE control
+
+Commit 61fd036d01111679b01e4b92e6bd0cdd33809aea upstream.
+
+This control returns the unit cell size in nanometres. The struct provides
+the width and the height in separated fields to take into consideration
+asymmetric pixels and/or hardware binning.
+This control is required for automatic calibration of sensors/cameras.
+
+Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de>
+Signed-off-by: Ricardo Ribalda Delgado <ribalda@kernel.org>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ drivers/media/v4l2-core/v4l2-ctrls.c | 5 +++++
+ include/uapi/linux/v4l2-controls.h | 1 +
+ 2 files changed, 6 insertions(+)
+
+--- a/drivers/media/v4l2-core/v4l2-ctrls.c
++++ b/drivers/media/v4l2-core/v4l2-ctrls.c
+@@ -995,6 +995,7 @@ const char *v4l2_ctrl_get_name(u32 id)
+ case V4L2_CID_AUTO_FOCUS_RANGE: return "Auto Focus, Range";
+ case V4L2_CID_PAN_SPEED: return "Pan, Speed";
+ case V4L2_CID_TILT_SPEED: return "Tilt, Speed";
++ case V4L2_CID_UNIT_CELL_SIZE: return "Unit Cell Size";
+
+ /* FM Radio Modulator controls */
+ /* Keep the order of the 'case's the same as in v4l2-controls.h! */
+@@ -1376,6 +1377,10 @@ void v4l2_ctrl_fill(u32 id, const char *
+ case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HEADER:
+ *type = V4L2_CTRL_TYPE_VP8_FRAME_HEADER;
+ break;
++ case V4L2_CID_UNIT_CELL_SIZE:
++ *type = V4L2_CTRL_TYPE_AREA;
++ *flags |= V4L2_CTRL_FLAG_READ_ONLY;
++ break;
+ default:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ break;
+--- a/include/uapi/linux/v4l2-controls.h
++++ b/include/uapi/linux/v4l2-controls.h
+@@ -1035,6 +1035,7 @@ enum v4l2_jpeg_chroma_subsampling {
+ #define V4L2_CID_TEST_PATTERN_GREENR (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 5)
+ #define V4L2_CID_TEST_PATTERN_BLUE (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 6)
+ #define V4L2_CID_TEST_PATTERN_GREENB (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 7)
++#define V4L2_CID_UNIT_CELL_SIZE (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 8)
+
+
+ /* Image processing controls */
--- /dev/null
+From c63ea6a840ad87e32239eb6b771ac8bbc3279b54 Mon Sep 17 00:00:00 2001
+From: Benoit Parrot <bparrot@ti.com>
+Date: Mon, 7 Oct 2019 12:10:07 -0300
+Subject: [PATCH] media: v4l2-common: add pixel encoding support
+
+Commit d5a897c8428b38053df4b427a4277b1a0722bfa0 upstream.
+
+It is often useful to figure out if a pixel_format is either YUV or RGB
+especially for driver who can perform the pixel encoding conversion.
+
+Instead of having each driver implement its own "is_this_yuv/rgb"
+function based on a restricted set of pixel value, it is better to do
+this in centralized manner.
+
+We therefore add a pixel_enc member to the v4l2_format_info structure to
+quickly identify the related pixel encoding.
+And add helper functions to check pixel encoding.
+
+Signed-off-by: Benoit Parrot <bparrot@ti.com>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ drivers/media/v4l2-core/v4l2-common.c | 126 +++++++++++++-------------
+ include/media/v4l2-common.h | 33 ++++++-
+ 2 files changed, 95 insertions(+), 64 deletions(-)
+
+--- a/drivers/media/v4l2-core/v4l2-common.c
++++ b/drivers/media/v4l2-core/v4l2-common.c
+@@ -236,77 +236,77 @@ const struct v4l2_format_info *v4l2_form
+ {
+ static const struct v4l2_format_info formats[] = {
+ /* RGB formats */
+- { .format = V4L2_PIX_FMT_BGR24, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_RGB24, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_HSV24, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_BGR32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_XBGR32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_BGRX32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_RGB32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_XRGB32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_RGBX32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_HSV32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_ARGB32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_RGBA32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_ABGR32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_BGRA32, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_GREY, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_BGR24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_RGB24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_HSV24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_BGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_XBGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_BGRX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_RGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_XRGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_RGBX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_HSV32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_GREY, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+
+ /* YUV packed formats */
+- { .format = V4L2_PIX_FMT_YUYV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_YVYU, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_UYVY, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_VYUY, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_YUYV, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_YVYU, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_UYVY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_VYUY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+
+ /* YUV planar formats */
+- { .format = V4L2_PIX_FMT_NV12, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
+- { .format = V4L2_PIX_FMT_NV21, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
+- { .format = V4L2_PIX_FMT_NV16, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_NV61, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_NV24, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_NV42, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-
+- { .format = V4L2_PIX_FMT_YUV410, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 },
+- { .format = V4L2_PIX_FMT_YVU410, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 },
+- { .format = V4L2_PIX_FMT_YUV411P, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_YUV420, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
+- { .format = V4L2_PIX_FMT_YVU420, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
+- { .format = V4L2_PIX_FMT_YUV422P, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_NV12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
++ { .format = V4L2_PIX_FMT_NV21, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
++ { .format = V4L2_PIX_FMT_NV16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_NV61, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_NV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_NV42, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++
++ { .format = V4L2_PIX_FMT_YUV410, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 },
++ { .format = V4L2_PIX_FMT_YVU410, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 },
++ { .format = V4L2_PIX_FMT_YUV411P, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_YUV420, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
++ { .format = V4L2_PIX_FMT_YVU420, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
++ { .format = V4L2_PIX_FMT_YUV422P, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
+
+ /* YUV planar formats, non contiguous variant */
+- { .format = V4L2_PIX_FMT_YUV420M, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
+- { .format = V4L2_PIX_FMT_YVU420M, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
+- { .format = V4L2_PIX_FMT_YUV422M, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_YVU422M, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_YUV444M, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_YVU444M, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1 },
+-
+- { .format = V4L2_PIX_FMT_NV12M, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
+- { .format = V4L2_PIX_FMT_NV21M, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
+- { .format = V4L2_PIX_FMT_NV16M, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_NV61M, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_YUV420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
++ { .format = V4L2_PIX_FMT_YVU420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
++ { .format = V4L2_PIX_FMT_YUV422M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_YVU422M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_YUV444M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_YVU444M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1 },
++
++ { .format = V4L2_PIX_FMT_NV12M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
++ { .format = V4L2_PIX_FMT_NV21M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
++ { .format = V4L2_PIX_FMT_NV16M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_NV61M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+
+ /* Bayer RGB formats */
+- { .format = V4L2_PIX_FMT_SBGGR8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SGBRG8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SGRBG8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SRGGB8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SBGGR10, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SGBRG10, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SGRBG10, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SRGGB10, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SBGGR10ALAW8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SGBRG10ALAW8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SGRBG10ALAW8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SRGGB10ALAW8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SBGGR10DPCM8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SGBRG10DPCM8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SGRBG10DPCM8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SRGGB10DPCM8, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SBGGR12, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SGBRG12, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SGRBG12, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+- { .format = V4L2_PIX_FMT_SRGGB12, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SBGGR8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SGBRG8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SGRBG8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SRGGB8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SBGGR10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SGBRG10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SGRBG10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SRGGB10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SBGGR10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SGBRG10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SGRBG10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SRGGB10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SBGGR10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SGBRG10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SGRBG10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SRGGB10DPCM8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SBGGR12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SGBRG12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SGRBG12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_SRGGB12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+ };
+ unsigned int i;
+
+--- a/include/media/v4l2-common.h
++++ b/include/media/v4l2-common.h
+@@ -457,8 +457,24 @@ int v4l2_s_parm_cap(struct video_device
+ /* Pixel format and FourCC helpers */
+
+ /**
++ * enum v4l2_pixel_encoding - specifies the pixel encoding value
++ *
++ * @V4L2_PIXEL_ENC_UNKNOWN: Pixel encoding is unknown/un-initialized
++ * @V4L2_PIXEL_ENC_YUV: Pixel encoding is YUV
++ * @V4L2_PIXEL_ENC_RGB: Pixel encoding is RGB
++ * @V4L2_PIXEL_ENC_BAYER: Pixel encoding is Bayer
++ */
++enum v4l2_pixel_encoding {
++ V4L2_PIXEL_ENC_UNKNOWN = 0,
++ V4L2_PIXEL_ENC_YUV = 1,
++ V4L2_PIXEL_ENC_RGB = 2,
++ V4L2_PIXEL_ENC_BAYER = 3,
++};
++
++/**
+ * struct v4l2_format_info - information about a V4L2 format
+ * @format: 4CC format identifier (V4L2_PIX_FMT_*)
++ * @pixel_enc: Pixel encoding (see enum v4l2_pixel_encoding above)
+ * @mem_planes: Number of memory planes, which includes the alpha plane (1 to 4).
+ * @comp_planes: Number of component planes, which includes the alpha plane (1 to 4).
+ * @bpp: Array of per-plane bytes per pixel
+@@ -469,6 +485,7 @@ int v4l2_s_parm_cap(struct video_device
+ */
+ struct v4l2_format_info {
+ u32 format;
++ u8 pixel_enc;
+ u8 mem_planes;
+ u8 comp_planes;
+ u8 bpp[4];
+@@ -478,8 +495,22 @@ struct v4l2_format_info {
+ u8 block_h[4];
+ };
+
+-const struct v4l2_format_info *v4l2_format_info(u32 format);
++static inline bool v4l2_is_format_rgb(const struct v4l2_format_info *f)
++{
++ return f && f->pixel_enc == V4L2_PIXEL_ENC_RGB;
++}
++
++static inline bool v4l2_is_format_yuv(const struct v4l2_format_info *f)
++{
++ return f && f->pixel_enc == V4L2_PIXEL_ENC_YUV;
++}
+
++static inline bool v4l2_is_format_bayer(const struct v4l2_format_info *f)
++{
++ return f && f->pixel_enc == V4L2_PIXEL_ENC_BAYER;
++}
++
++const struct v4l2_format_info *v4l2_format_info(u32 format);
+ void v4l2_apply_frmsize_constraints(u32 *width, u32 *height,
+ const struct v4l2_frmsize_stepwise *frmsize);
+ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat,
--- /dev/null
+From 560f3a9051578499e72ce4b1beaedd007ff46f96 Mon Sep 17 00:00:00 2001
+From: Benoit Parrot <bparrot@ti.com>
+Date: Mon, 7 Oct 2019 12:10:08 -0300
+Subject: [PATCH] media: v4l2-common: add RGB565 and RGB55 to
+ v4l2_format_info
+
+Commit b373f84d77e1c409aacb4ff5bb5726c45fc8b166 upstream.
+
+Add RGB565 and RGB555 to the v4l2_format_info table.
+
+Signed-off-by: Benoit Parrot <bparrot@ti.com>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ drivers/media/v4l2-core/v4l2-common.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/media/v4l2-core/v4l2-common.c
++++ b/drivers/media/v4l2-core/v4l2-common.c
+@@ -251,6 +251,8 @@ const struct v4l2_format_info *v4l2_form
+ { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_GREY, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++ { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+
+ /* YUV packed formats */
+ { .format = V4L2_PIX_FMT_YUYV, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
--- /dev/null
+From dfcdc4ed9a514cd5d77dd18c6527f257f8aaf378 Mon Sep 17 00:00:00 2001
+From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Date: Fri, 11 Oct 2019 06:32:40 -0300
+Subject: [PATCH] media: vb2: add V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF
+
+This patch adds support for the V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF
+flag.
+
+It also adds a new V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF
+capability.
+
+Drivers should set vb2_queue->subsystem_flags to
+VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF to indicate support
+for this flag.
+
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ Documentation/media/uapi/v4l/buffer.rst | 13 +++++++++++++
+ Documentation/media/uapi/v4l/vidioc-reqbufs.rst | 6 ++++++
+ drivers/media/common/videobuf2/videobuf2-v4l2.c | 12 ++++++++++--
+ include/media/videobuf2-core.h | 3 +++
+ include/media/videobuf2-v4l2.h | 5 +++++
+ include/uapi/linux/videodev2.h | 13 ++++++++-----
+ 6 files changed, 45 insertions(+), 7 deletions(-)
+
+--- a/Documentation/media/uapi/v4l/buffer.rst
++++ b/Documentation/media/uapi/v4l/buffer.rst
+@@ -607,6 +607,19 @@ Buffer Flags
+ applications shall use this flag for output buffers if the data in
+ this buffer has not been created by the CPU but by some
+ DMA-capable unit, in which case caches have not been used.
++ * .. _`V4L2-BUF-FLAG-M2M-HOLD-CAPTURE-BUF`:
++
++ - ``V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF``
++ - 0x00000200
++ - Only valid if ``V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF`` is
++ set. It is typically used with stateless decoders where multiple
++ output buffers each decode to a slice of the decoded frame.
++ Applications can set this flag when queueing the output buffer
++ to prevent the driver from dequeueing the capture buffer after
++ the output buffer has been decoded (i.e. the capture buffer is
++ 'held'). If the timestamp of this output buffer differs from that
++ of the previous output buffer, then that indicates the start of a
++ new frame and the previously held capture buffer is dequeued.
+ * .. _`V4L2-BUF-FLAG-LAST`:
+
+ - ``V4L2_BUF_FLAG_LAST``
+--- a/Documentation/media/uapi/v4l/vidioc-reqbufs.rst
++++ b/Documentation/media/uapi/v4l/vidioc-reqbufs.rst
+@@ -125,6 +125,7 @@ aborting or finishing any DMA in progres
+ .. _V4L2-BUF-CAP-SUPPORTS-DMABUF:
+ .. _V4L2-BUF-CAP-SUPPORTS-REQUESTS:
+ .. _V4L2-BUF-CAP-SUPPORTS-ORPHANED-BUFS:
++.. _V4L2-BUF-CAP-SUPPORTS-M2M-HOLD-CAPTURE-BUF:
+
+ .. cssclass:: longtable
+
+@@ -150,6 +151,11 @@ aborting or finishing any DMA in progres
+ - The kernel allows calling :ref:`VIDIOC_REQBUFS` while buffers are still
+ mapped or exported via DMABUF. These orphaned buffers will be freed
+ when they are unmapped or when the exported DMABUF fds are closed.
++ * - ``V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF``
++ - 0x00000020
++ - Only valid for stateless decoders. If set, then userspace can set the
++ ``V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF`` flag to hold off on returning the
++ capture buffer until the OUTPUT timestamp changes.
+
+ Return Value
+ ============
+--- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
++++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
+@@ -49,8 +49,11 @@ module_param(debug, int, 0644);
+ V4L2_BUF_FLAG_REQUEST_FD | \
+ V4L2_BUF_FLAG_TIMESTAMP_MASK)
+ /* Output buffer flags that should be passed on to the driver */
+-#define V4L2_BUFFER_OUT_FLAGS (V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME | \
+- V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_TIMECODE)
++#define V4L2_BUFFER_OUT_FLAGS (V4L2_BUF_FLAG_PFRAME | \
++ V4L2_BUF_FLAG_BFRAME | \
++ V4L2_BUF_FLAG_KEYFRAME | \
++ V4L2_BUF_FLAG_TIMECODE | \
++ V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF)
+
+ /*
+ * __verify_planes_array() - verify that the planes array passed in struct
+@@ -194,6 +197,7 @@ static int vb2_fill_vb2_v4l2_buffer(stru
+ }
+ vbuf->sequence = 0;
+ vbuf->request_fd = -1;
++ vbuf->is_held = false;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
+ switch (b->memory) {
+@@ -321,6 +325,8 @@ static int vb2_fill_vb2_v4l2_buffer(stru
+ */
+ vbuf->flags &= ~V4L2_BUF_FLAG_TIMECODE;
+ vbuf->field = b->field;
++ if (!(q->subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF))
++ vbuf->flags &= ~V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF;
+ } else {
+ /* Zero any output buffer flags as this is a capture buffer */
+ vbuf->flags &= ~V4L2_BUFFER_OUT_FLAGS;
+@@ -654,6 +660,8 @@ static void fill_buf_caps(struct vb2_que
+ *caps |= V4L2_BUF_CAP_SUPPORTS_USERPTR;
+ if (q->io_modes & VB2_DMABUF)
+ *caps |= V4L2_BUF_CAP_SUPPORTS_DMABUF;
++ if (q->subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF)
++ *caps |= V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF;
+ #ifdef CONFIG_MEDIA_CONTROLLER_REQUEST_API
+ if (q->supports_requests)
+ *caps |= V4L2_BUF_CAP_SUPPORTS_REQUESTS;
+--- a/include/media/videobuf2-core.h
++++ b/include/media/videobuf2-core.h
+@@ -505,6 +505,8 @@ struct vb2_buf_ops {
+ * @buf_ops: callbacks to deliver buffer information.
+ * between user-space and kernel-space.
+ * @drv_priv: driver private data.
++ * @subsystem_flags: Flags specific to the subsystem (V4L2/DVB/etc.). Not used
++ * by the vb2 core.
+ * @buf_struct_size: size of the driver-specific buffer structure;
+ * "0" indicates the driver doesn't want to use a custom buffer
+ * structure type. for example, ``sizeof(struct vb2_v4l2_buffer)``
+@@ -571,6 +573,7 @@ struct vb2_queue {
+ const struct vb2_buf_ops *buf_ops;
+
+ void *drv_priv;
++ u32 subsystem_flags;
+ unsigned int buf_struct_size;
+ u32 timestamp_flags;
+ gfp_t gfp_flags;
+--- a/include/media/videobuf2-v4l2.h
++++ b/include/media/videobuf2-v4l2.h
+@@ -33,6 +33,7 @@
+ * @timecode: frame timecode.
+ * @sequence: sequence count of this frame.
+ * @request_fd: the request_fd associated with this buffer
++ * @is_held: if true, then this capture buffer was held
+ * @planes: plane information (userptr/fd, length, bytesused, data_offset).
+ *
+ * Should contain enough information to be able to cover all the fields
+@@ -46,9 +47,13 @@ struct vb2_v4l2_buffer {
+ struct v4l2_timecode timecode;
+ __u32 sequence;
+ __s32 request_fd;
++ bool is_held;
+ struct vb2_plane planes[VB2_MAX_PLANES];
+ };
+
++/* VB2 V4L2 flags as set in vb2_queue.subsystem_flags */
++#define VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF (1 << 0)
++
+ /*
+ * to_vb2_v4l2_buffer() - cast struct vb2_buffer * to struct vb2_v4l2_buffer *
+ */
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -925,11 +925,12 @@ struct v4l2_requestbuffers {
+ };
+
+ /* capabilities for struct v4l2_requestbuffers and v4l2_create_buffers */
+-#define V4L2_BUF_CAP_SUPPORTS_MMAP (1 << 0)
+-#define V4L2_BUF_CAP_SUPPORTS_USERPTR (1 << 1)
+-#define V4L2_BUF_CAP_SUPPORTS_DMABUF (1 << 2)
+-#define V4L2_BUF_CAP_SUPPORTS_REQUESTS (1 << 3)
+-#define V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS (1 << 4)
++#define V4L2_BUF_CAP_SUPPORTS_MMAP (1 << 0)
++#define V4L2_BUF_CAP_SUPPORTS_USERPTR (1 << 1)
++#define V4L2_BUF_CAP_SUPPORTS_DMABUF (1 << 2)
++#define V4L2_BUF_CAP_SUPPORTS_REQUESTS (1 << 3)
++#define V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS (1 << 4)
++#define V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF (1 << 5)
+
+ /**
+ * struct v4l2_plane - plane info for multi-planar buffers
+@@ -1051,6 +1052,8 @@ static inline __u64 v4l2_timeval_to_ns(c
+ #define V4L2_BUF_FLAG_IN_REQUEST 0x00000080
+ /* timecode field is valid */
+ #define V4L2_BUF_FLAG_TIMECODE 0x00000100
++/* Don't return the capture buffer until OUTPUT timestamp changes */
++#define V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF 0x00000200
+ /* Buffer is prepared for queuing */
+ #define V4L2_BUF_FLAG_PREPARED 0x00000400
+ /* Cache handling flags */
--- /dev/null
+From dc9b786e4b9a1262b536b3c9d0fa88e34a2b3f8f Mon Sep 17 00:00:00 2001
+From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Date: Fri, 11 Oct 2019 06:32:41 -0300
+Subject: [PATCH] media: v4l2-mem2mem: support held capture buffers
+
+Commit f8cca8c97a63d77f48334cde81d15014f43530ef upstream.
+
+Check for held buffers that are ready to be returned to vb2 in
+__v4l2_m2m_try_queue(). This avoids drivers having to handle this
+case.
+
+Add v4l2_m2m_buf_done_and_job_finish() to correctly return source
+and destination buffers and mark the job as finished while taking
+a held destination buffer into account (i.e. that buffer won't be
+returned). This has to be done while job_spinlock is held to avoid
+race conditions.
+
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ drivers/media/v4l2-core/v4l2-mem2mem.c | 130 ++++++++++++++++++-------
+ include/media/v4l2-mem2mem.h | 33 ++++++-
+ 2 files changed, 128 insertions(+), 35 deletions(-)
+
+--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
++++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
+@@ -284,7 +284,8 @@ static void v4l2_m2m_try_run(struct v4l2
+ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev,
+ struct v4l2_m2m_ctx *m2m_ctx)
+ {
+- unsigned long flags_job, flags_out, flags_cap;
++ unsigned long flags_job;
++ struct vb2_v4l2_buffer *dst, *src;
+
+ dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx);
+
+@@ -307,20 +308,30 @@ static void __v4l2_m2m_try_queue(struct
+ goto job_unlock;
+ }
+
+- spin_lock_irqsave(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out);
+- if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue)
+- && !m2m_ctx->out_q_ctx.buffered) {
++ src = v4l2_m2m_next_src_buf(m2m_ctx);
++ dst = v4l2_m2m_next_dst_buf(m2m_ctx);
++ if (!src && !m2m_ctx->out_q_ctx.buffered) {
+ dprintk("No input buffers available\n");
+- goto out_unlock;
++ goto job_unlock;
+ }
+- spin_lock_irqsave(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap);
+- if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue)
+- && !m2m_ctx->cap_q_ctx.buffered) {
++ if (!dst && !m2m_ctx->cap_q_ctx.buffered) {
+ dprintk("No output buffers available\n");
+- goto cap_unlock;
++ goto job_unlock;
++ }
++
++ if (src && dst &&
++ dst->is_held && dst->vb2_buf.copied_timestamp &&
++ dst->vb2_buf.timestamp != src->vb2_buf.timestamp) {
++ dst->is_held = false;
++ v4l2_m2m_dst_buf_remove(m2m_ctx);
++ v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
++ dst = v4l2_m2m_next_dst_buf(m2m_ctx);
++
++ if (!dst && !m2m_ctx->cap_q_ctx.buffered) {
++ dprintk("No output buffers available after returning held buffer\n");
++ goto job_unlock;
++ }
+ }
+- spin_unlock_irqrestore(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap);
+- spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out);
+
+ if (m2m_dev->m2m_ops->job_ready
+ && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) {
+@@ -331,13 +342,6 @@ static void __v4l2_m2m_try_queue(struct
+ list_add_tail(&m2m_ctx->queue, &m2m_dev->job_queue);
+ m2m_ctx->job_flags |= TRANS_QUEUED;
+
+- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+- return;
+-
+-cap_unlock:
+- spin_unlock_irqrestore(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap);
+-out_unlock:
+- spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out);
+ job_unlock:
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+ }
+@@ -412,37 +416,97 @@ static void v4l2_m2m_cancel_job(struct v
+ }
+ }
+
+-void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
+- struct v4l2_m2m_ctx *m2m_ctx)
++/*
++ * Schedule the next job, called from v4l2_m2m_job_finish() or
++ * v4l2_m2m_buf_done_and_job_finish().
++ */
++static void v4l2_m2m_schedule_next_job(struct v4l2_m2m_dev *m2m_dev,
++ struct v4l2_m2m_ctx *m2m_ctx)
+ {
+- unsigned long flags;
++ /*
++ * This instance might have more buffers ready, but since we do not
++ * allow more than one job on the job_queue per instance, each has
++ * to be scheduled separately after the previous one finishes.
++ */
++ __v4l2_m2m_try_queue(m2m_dev, m2m_ctx);
+
+- spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
++ /*
++ * We might be running in atomic context,
++ * but the job must be run in non-atomic context.
++ */
++ schedule_work(&m2m_dev->job_work);
++}
++
++/*
++ * Assumes job_spinlock is held, called from v4l2_m2m_job_finish() or
++ * v4l2_m2m_buf_done_and_job_finish().
++ */
++static bool _v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
++ struct v4l2_m2m_ctx *m2m_ctx)
++{
+ if (!m2m_dev->curr_ctx || m2m_dev->curr_ctx != m2m_ctx) {
+- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+ dprintk("Called by an instance not currently running\n");
+- return;
++ return false;
+ }
+
+ list_del(&m2m_dev->curr_ctx->queue);
+ m2m_dev->curr_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING);
+ wake_up(&m2m_dev->curr_ctx->finished);
+ m2m_dev->curr_ctx = NULL;
++ return true;
++}
+
+- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+-
+- /* This instance might have more buffers ready, but since we do not
+- * allow more than one job on the job_queue per instance, each has
+- * to be scheduled separately after the previous one finishes. */
+- __v4l2_m2m_try_queue(m2m_dev, m2m_ctx);
++void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
++ struct v4l2_m2m_ctx *m2m_ctx)
++{
++ unsigned long flags;
++ bool schedule_next;
+
+- /* We might be running in atomic context,
+- * but the job must be run in non-atomic context.
++ /*
++ * This function should not be used for drivers that support
++ * holding capture buffers. Those should use
++ * v4l2_m2m_buf_done_and_job_finish() instead.
+ */
+- schedule_work(&m2m_dev->job_work);
++ WARN_ON(m2m_ctx->cap_q_ctx.q.subsystem_flags &
++ VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF);
++ spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
++ schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx);
++ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
++
++ if (schedule_next)
++ v4l2_m2m_schedule_next_job(m2m_dev, m2m_ctx);
+ }
+ EXPORT_SYMBOL(v4l2_m2m_job_finish);
+
++void v4l2_m2m_buf_done_and_job_finish(struct v4l2_m2m_dev *m2m_dev,
++ struct v4l2_m2m_ctx *m2m_ctx,
++ enum vb2_buffer_state state)
++{
++ struct vb2_v4l2_buffer *src_buf, *dst_buf;
++ bool schedule_next = false;
++ unsigned long flags;
++
++ spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
++ src_buf = v4l2_m2m_src_buf_remove(m2m_ctx);
++ dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx);
++
++ if (WARN_ON(!src_buf || !dst_buf))
++ goto unlock;
++ v4l2_m2m_buf_done(src_buf, state);
++ dst_buf->is_held = src_buf->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF;
++ if (!dst_buf->is_held) {
++ v4l2_m2m_dst_buf_remove(m2m_ctx);
++ v4l2_m2m_buf_done(dst_buf, state);
++ }
++ schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx);
++unlock:
++ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
++
++ if (schedule_next)
++ v4l2_m2m_schedule_next_job(m2m_dev, m2m_ctx);
++}
++EXPORT_SYMBOL(v4l2_m2m_buf_done_and_job_finish);
++
+ int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ struct v4l2_requestbuffers *reqbufs)
+ {
+--- a/include/media/v4l2-mem2mem.h
++++ b/include/media/v4l2-mem2mem.h
+@@ -21,7 +21,8 @@
+ * callback.
+ * The job does NOT have to end before this callback returns
+ * (and it will be the usual case). When the job finishes,
+- * v4l2_m2m_job_finish() has to be called.
++ * v4l2_m2m_job_finish() or v4l2_m2m_buf_done_and_job_finish()
++ * has to be called.
+ * @job_ready: optional. Should return 0 if the driver does not have a job
+ * fully prepared to run yet (i.e. it will not be able to finish a
+ * transaction without sleeping). If not provided, it will be
+@@ -33,7 +34,8 @@
+ * stop the device safely; e.g. in the next interrupt handler),
+ * even if the transaction would not have been finished by then.
+ * After the driver performs the necessary steps, it has to call
+- * v4l2_m2m_job_finish() (as if the transaction ended normally).
++ * v4l2_m2m_job_finish() or v4l2_m2m_buf_done_and_job_finish() as
++ * if the transaction ended normally.
+ * This function does not have to (and will usually not) wait
+ * until the device enters a state when it can be stopped.
+ */
+@@ -173,6 +175,33 @@ void v4l2_m2m_try_schedule(struct v4l2_m
+ void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
+ struct v4l2_m2m_ctx *m2m_ctx);
+
++/**
++ * v4l2_m2m_buf_done_and_job_finish() - return source/destination buffers with
++ * state and inform the framework that a job has been finished and have it
++ * clean up
++ *
++ * @m2m_dev: opaque pointer to the internal data to handle M2M context
++ * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx
++ * @state: vb2 buffer state passed to v4l2_m2m_buf_done().
++ *
++ * Drivers that set V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF must use this
++ * function instead of job_finish() to take held buffers into account. It is
++ * optional for other drivers.
++ *
++ * This function removes the source buffer from the ready list and returns
++ * it with the given state. The same is done for the destination buffer, unless
++ * it is marked 'held'. In that case the buffer is kept on the ready list.
++ *
++ * After that the job is finished (see job_finish()).
++ *
++ * This allows for multiple output buffers to be used to fill in a single
++ * capture buffer. This is typically used by stateless decoders where
++ * multiple e.g. H.264 slices contribute to a single decoded frame.
++ */
++void v4l2_m2m_buf_done_and_job_finish(struct v4l2_m2m_dev *m2m_dev,
++ struct v4l2_m2m_ctx *m2m_ctx,
++ enum vb2_buffer_state state);
++
+ static inline void
+ v4l2_m2m_buf_done(struct vb2_v4l2_buffer *buf, enum vb2_buffer_state state)
+ {
--- /dev/null
+From b2ea711d2c21ec021de4ff09a0a2b5b4224f9749 Mon Sep 17 00:00:00 2001
+From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Date: Fri, 11 Oct 2019 06:32:42 -0300
+Subject: [PATCH] media: videodev2.h: add V4L2_DEC_CMD_FLUSH
+
+Add this new V4L2_DEC_CMD_FLUSH decoder command and document it.
+
+Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
+Reviewed-by: Alexandre Courbot <acourbot@chromium.org>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst | 10 +++++++++-
+ Documentation/media/videodev2.h.rst.exceptions | 1 +
+ include/uapi/linux/videodev2.h | 1 +
+ 3 files changed, 11 insertions(+), 1 deletion(-)
+
+--- a/Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst
++++ b/Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst
+@@ -208,7 +208,15 @@ introduced in Linux 3.3. They are, howev
+ been started yet, the driver will return an ``EPERM`` error code. When
+ the decoder is already running, this command does nothing. No
+ flags are defined for this command.
+-
++ * - ``V4L2_DEC_CMD_FLUSH``
++ - 4
++ - Flush any held capture buffers. Only valid for stateless decoders.
++ This command is typically used when the application reached the
++ end of the stream and the last output buffer had the
++ ``V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF`` flag set. This would prevent
++ dequeueing the capture buffer containing the last decoded frame.
++ So this command can be used to explicitly flush that final decoded
++ frame. This command does nothing if there are no held capture buffers.
+
+ Return Value
+ ============
+--- a/Documentation/media/videodev2.h.rst.exceptions
++++ b/Documentation/media/videodev2.h.rst.exceptions
+@@ -434,6 +434,7 @@ replace define V4L2_DEC_CMD_START decode
+ replace define V4L2_DEC_CMD_STOP decoder-cmds
+ replace define V4L2_DEC_CMD_PAUSE decoder-cmds
+ replace define V4L2_DEC_CMD_RESUME decoder-cmds
++replace define V4L2_DEC_CMD_FLUSH decoder-cmds
+
+ replace define V4L2_DEC_CMD_START_MUTE_AUDIO decoder-cmds
+ replace define V4L2_DEC_CMD_PAUSE_TO_BLACK decoder-cmds
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -1989,6 +1989,7 @@ struct v4l2_encoder_cmd {
+ #define V4L2_DEC_CMD_STOP (1)
+ #define V4L2_DEC_CMD_PAUSE (2)
+ #define V4L2_DEC_CMD_RESUME (3)
++#define V4L2_DEC_CMD_FLUSH (4)
+
+ /* Flags for V4L2_DEC_CMD_START */
+ #define V4L2_DEC_CMD_START_MUTE_AUDIO (1 << 0)
--- /dev/null
+From 1decb017f990ea61ab421e316bf1af3a5199b73a Mon Sep 17 00:00:00 2001
+From: Jernej Skrabec <jernej.skrabec@siol.net>
+Date: Fri, 11 Oct 2019 06:32:43 -0300
+Subject: [PATCH] media: v4l2-mem2mem: add stateless_(try_)decoder_cmd
+ ioctl helpers
+
+Commit bef41d93aac64b54c3008ca6170bec54f85784f5 upstream.
+
+These helpers are used by stateless codecs when they support multiple
+slices per frame and hold capture buffer flag is set. It's expected that
+all such codecs will use this code.
+
+Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
+Co-developed-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ drivers/media/v4l2-core/v4l2-mem2mem.c | 53 ++++++++++++++++++++++++++
+ include/media/v4l2-mem2mem.h | 4 ++
+ 2 files changed, 57 insertions(+)
+
+--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
++++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
+@@ -1218,6 +1218,59 @@ int v4l2_m2m_ioctl_try_decoder_cmd(struc
+ }
+ EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_try_decoder_cmd);
+
++int v4l2_m2m_ioctl_stateless_try_decoder_cmd(struct file *file, void *fh,
++ struct v4l2_decoder_cmd *dc)
++{
++ if (dc->cmd != V4L2_DEC_CMD_FLUSH)
++ return -EINVAL;
++
++ dc->flags = 0;
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_stateless_try_decoder_cmd);
++
++int v4l2_m2m_ioctl_stateless_decoder_cmd(struct file *file, void *priv,
++ struct v4l2_decoder_cmd *dc)
++{
++ struct v4l2_fh *fh = file->private_data;
++ struct vb2_v4l2_buffer *out_vb, *cap_vb;
++ struct v4l2_m2m_dev *m2m_dev = fh->m2m_ctx->m2m_dev;
++ unsigned long flags;
++ int ret;
++
++ ret = v4l2_m2m_ioctl_stateless_try_decoder_cmd(file, priv, dc);
++ if (ret < 0)
++ return ret;
++
++ spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
++ out_vb = v4l2_m2m_last_src_buf(fh->m2m_ctx);
++ cap_vb = v4l2_m2m_last_dst_buf(fh->m2m_ctx);
++
++ /*
++ * If there is an out buffer pending, then clear any HOLD flag.
++ *
++ * By clearing this flag we ensure that when this output
++ * buffer is processed any held capture buffer will be released.
++ */
++ if (out_vb) {
++ out_vb->flags &= ~V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF;
++ } else if (cap_vb && cap_vb->is_held) {
++ /*
++ * If there were no output buffers, but there is a
++ * capture buffer that is held, then release that
++ * buffer.
++ */
++ cap_vb->is_held = false;
++ v4l2_m2m_dst_buf_remove(fh->m2m_ctx);
++ v4l2_m2m_buf_done(cap_vb, VB2_BUF_STATE_DONE);
++ }
++ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_stateless_decoder_cmd);
++
+ /*
+ * v4l2_file_operations helpers. It is assumed here same lock is used
+ * for the output and the capture buffer queue.
+--- a/include/media/v4l2-mem2mem.h
++++ b/include/media/v4l2-mem2mem.h
+@@ -701,6 +701,10 @@ int v4l2_m2m_ioctl_try_encoder_cmd(struc
+ struct v4l2_encoder_cmd *ec);
+ int v4l2_m2m_ioctl_try_decoder_cmd(struct file *file, void *fh,
+ struct v4l2_decoder_cmd *dc);
++int v4l2_m2m_ioctl_stateless_try_decoder_cmd(struct file *file, void *fh,
++ struct v4l2_decoder_cmd *dc);
++int v4l2_m2m_ioctl_stateless_decoder_cmd(struct file *file, void *priv,
++ struct v4l2_decoder_cmd *dc);
+ int v4l2_m2m_fop_mmap(struct file *file, struct vm_area_struct *vma);
+ __poll_t v4l2_m2m_fop_poll(struct file *file, poll_table *wait);
+
--- /dev/null
+From 1d55acac432983ad8301f5430c42ac549b4b4c6f Mon Sep 17 00:00:00 2001
+From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Date: Fri, 11 Oct 2019 06:32:44 -0300
+Subject: [PATCH] media: v4l2-mem2mem: add new_frame detection
+
+Commit f07602ac388723233e9e3c5a05b54baf34e0a3e9 upstream.
+
+Drivers that support VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF
+typically want to know if a new frame is started (i.e. the first
+slice is about to be processed). Add a new_frame bool to v4l2_m2m_ctx
+and set it accordingly.
+
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ drivers/media/v4l2-core/v4l2-mem2mem.c | 11 +++++++++--
+ include/media/v4l2-mem2mem.h | 7 +++++++
+ 2 files changed, 16 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
++++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
+@@ -319,8 +319,10 @@ static void __v4l2_m2m_try_queue(struct
+ goto job_unlock;
+ }
+
+- if (src && dst &&
+- dst->is_held && dst->vb2_buf.copied_timestamp &&
++ m2m_ctx->new_frame = true;
++
++ if (src && dst && dst->is_held &&
++ dst->vb2_buf.copied_timestamp &&
+ dst->vb2_buf.timestamp != src->vb2_buf.timestamp) {
+ dst->is_held = false;
+ v4l2_m2m_dst_buf_remove(m2m_ctx);
+@@ -333,6 +335,11 @@ static void __v4l2_m2m_try_queue(struct
+ }
+ }
+
++ if (src && dst && (m2m_ctx->cap_q_ctx.q.subsystem_flags &
++ VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF))
++ m2m_ctx->new_frame = !dst->vb2_buf.copied_timestamp ||
++ dst->vb2_buf.timestamp != src->vb2_buf.timestamp;
++
+ if (m2m_dev->m2m_ops->job_ready
+ && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) {
+ dprintk("Driver not ready\n");
+--- a/include/media/v4l2-mem2mem.h
++++ b/include/media/v4l2-mem2mem.h
+@@ -75,6 +75,11 @@ struct v4l2_m2m_queue_ctx {
+ * struct v4l2_m2m_ctx - Memory to memory context structure
+ *
+ * @q_lock: struct &mutex lock
++ * @new_frame: valid in the device_run callback: if true, then this
++ * starts a new frame; if false, then this is a new slice
++ * for an existing frame. This is always true unless
++ * V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF is set, which
++ * indicates slicing support.
+ * @m2m_dev: opaque pointer to the internal data to handle M2M context
+ * @cap_q_ctx: Capture (output to memory) queue context
+ * @out_q_ctx: Output (input from memory) queue context
+@@ -91,6 +96,8 @@ struct v4l2_m2m_ctx {
+ /* optional cap/out vb2 queues lock */
+ struct mutex *q_lock;
+
++ bool new_frame;
++
+ /* internal use only */
+ struct v4l2_m2m_dev *m2m_dev;
+
--- /dev/null
+From 20076d276d045c03f809bb16f0e1fafcfe63a81f Mon Sep 17 00:00:00 2001
+From: Ricardo Ribalda Delgado <ribalda@kernel.org>
+Date: Mon, 7 Oct 2019 12:06:32 -0300
+Subject: [PATCH] media: Documentation: media: Document
+ V4L2_CTRL_TYPE_AREA
+
+Commit 8ae3a0862993c09a8ef0f9abb379553370c517e3 upstream.
+
+A struct v4l2_area containing the width and the height of a rectangular
+area.
+
+Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
+Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de>
+Signed-off-by: Ricardo Ribalda Delgado <ribalda@kernel.org>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ Documentation/media/uapi/v4l/vidioc-queryctrl.rst | 6 ++++++
+ Documentation/media/videodev2.h.rst.exceptions | 1 +
+ 2 files changed, 7 insertions(+)
+
+--- a/Documentation/media/uapi/v4l/vidioc-queryctrl.rst
++++ b/Documentation/media/uapi/v4l/vidioc-queryctrl.rst
+@@ -443,6 +443,12 @@ See also the examples in :ref:`control`.
+ - n/a
+ - A struct :c:type:`v4l2_ctrl_mpeg2_quantization`, containing MPEG-2
+ quantization matrices for stateless video decoders.
++ * - ``V4L2_CTRL_TYPE_AREA``
++ - n/a
++ - n/a
++ - n/a
++ - A struct :c:type:`v4l2_area`, containing the width and the height
++ of a rectangular area. Units depend on the use case.
+ * - ``V4L2_CTRL_TYPE_H264_SPS``
+ - n/a
+ - n/a
+--- a/Documentation/media/videodev2.h.rst.exceptions
++++ b/Documentation/media/videodev2.h.rst.exceptions
+@@ -141,6 +141,7 @@ replace symbol V4L2_CTRL_TYPE_H264_PPS :
+ replace symbol V4L2_CTRL_TYPE_H264_SCALING_MATRIX :c:type:`v4l2_ctrl_type`
+ replace symbol V4L2_CTRL_TYPE_H264_SLICE_PARAMS :c:type:`v4l2_ctrl_type`
+ replace symbol V4L2_CTRL_TYPE_H264_DECODE_PARAMS :c:type:`v4l2_ctrl_type`
++replace symbol V4L2_CTRL_TYPE_AREA :c:type:`v4l2_ctrl_type`
+
+ # V4L2 capability defines
+ replace define V4L2_CAP_VIDEO_CAPTURE device-capabilities
--- /dev/null
+From 5f6c08984a6578201fe3a2394ccb0d3a30fdf027 Mon Sep 17 00:00:00 2001
+From: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+Date: Tue, 22 Oct 2019 12:26:52 -0300
+Subject: [PATCH] media: v4l: Add definitions for HEVC stateless
+ decoding
+
+This introduces the required definitions for HEVC decoding support with
+stateless VPUs. The controls associated to the HEVC slice format provide
+the required meta-data for decoding slices extracted from the bitstream.
+
+They are not exported to the public V4L2 API since reworking this API
+will likely be needed for covering various use-cases and new hardware.
+
+Multi-slice decoding is exposed as a valid decoding mode to match current
+H.264 support but it is not yet implemented.
+
+The interface comes with the following limitations:
+* No custom quantization matrices (scaling lists);
+* Support for a single temporal layer only;
+* No slice entry point offsets support;
+* No conformance window support;
+* No VUI parameters support;
+* No support for SPS extensions: range, multilayer, 3d, scc, 4 bits;
+* No support for PPS extensions: range, multilayer, 3d, scc, 4 bits.
+
+Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+[hverkuil-cisco@xs4all.nl: use 1ULL in flags defines in hevc-ctrls.h]
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ Documentation/media/uapi/v4l/biblio.rst | 9 +
+ .../media/uapi/v4l/ext-ctrls-codec.rst | 553 +++++++++++++++++-
+ .../media/uapi/v4l/vidioc-queryctrl.rst | 18 +
+ .../media/videodev2.h.rst.exceptions | 3 +
+ drivers/media/v4l2-core/v4l2-ctrls.c | 109 +++-
+ drivers/media/v4l2-core/v4l2-ioctl.c | 1 +
+ include/media/hevc-ctrls.h | 212 +++++++
+ include/media/v4l2-ctrls.h | 7 +
+ 8 files changed, 908 insertions(+), 4 deletions(-)
+ create mode 100644 include/media/hevc-ctrls.h
+
+--- a/Documentation/media/uapi/v4l/biblio.rst
++++ b/Documentation/media/uapi/v4l/biblio.rst
+@@ -131,6 +131,15 @@ ITU-T Rec. H.264 Specification (04/2017
+
+ :author: International Telecommunication Union (http://www.itu.ch)
+
++.. _hevc:
++
++ITU H.265/HEVC
++==============
++
++:title: ITU-T Rec. H.265 | ISO/IEC 23008-2 "High Efficiency Video Coding"
++
++:author: International Telecommunication Union (http://www.itu.ch), International Organisation for Standardisation (http://www.iso.ch)
++
+ .. _jfif:
+
+ JFIF
+--- a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst
++++ b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst
+@@ -1983,9 +1983,9 @@ enum v4l2_mpeg_video_h264_hierarchical_c
+ - ``reference_ts``
+ - Timestamp of the V4L2 capture buffer to use as reference, used
+ with B-coded and P-coded frames. The timestamp refers to the
+- ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the
+- :c:func:`v4l2_timeval_to_ns()` function to convert the struct
+- :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64.
++ ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the
++ :c:func:`v4l2_timeval_to_ns()` function to convert the struct
++ :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64.
+ * - __u16
+ - ``frame_num``
+ -
+@@ -3693,3 +3693,550 @@ enum v4l2_mpeg_video_hevc_size_of_length
+ Indicates whether to generate SPS and PPS at every IDR. Setting it to 0
+ disables generating SPS and PPS at every IDR. Setting it to one enables
+ generating SPS and PPS at every IDR.
++
++.. _v4l2-mpeg-hevc:
++
++``V4L2_CID_MPEG_VIDEO_HEVC_SPS (struct)``
++ Specifies the Sequence Parameter Set fields (as extracted from the
++ bitstream) for the associated HEVC slice data.
++ These bitstream parameters are defined according to :ref:`hevc`.
++ They are described in section 7.4.3.2 "Sequence parameter set RBSP
++ semantics" of the specification.
++
++.. c:type:: v4l2_ctrl_hevc_sps
++
++.. cssclass:: longtable
++
++.. flat-table:: struct v4l2_ctrl_hevc_sps
++ :header-rows: 0
++ :stub-columns: 0
++ :widths: 1 1 2
++
++ * - __u16
++ - ``pic_width_in_luma_samples``
++ -
++ * - __u16
++ - ``pic_height_in_luma_samples``
++ -
++ * - __u8
++ - ``bit_depth_luma_minus8``
++ -
++ * - __u8
++ - ``bit_depth_chroma_minus8``
++ -
++ * - __u8
++ - ``log2_max_pic_order_cnt_lsb_minus4``
++ -
++ * - __u8
++ - ``sps_max_dec_pic_buffering_minus1``
++ -
++ * - __u8
++ - ``sps_max_num_reorder_pics``
++ -
++ * - __u8
++ - ``sps_max_latency_increase_plus1``
++ -
++ * - __u8
++ - ``log2_min_luma_coding_block_size_minus3``
++ -
++ * - __u8
++ - ``log2_diff_max_min_luma_coding_block_size``
++ -
++ * - __u8
++ - ``log2_min_luma_transform_block_size_minus2``
++ -
++ * - __u8
++ - ``log2_diff_max_min_luma_transform_block_size``
++ -
++ * - __u8
++ - ``max_transform_hierarchy_depth_inter``
++ -
++ * - __u8
++ - ``max_transform_hierarchy_depth_intra``
++ -
++ * - __u8
++ - ``pcm_sample_bit_depth_luma_minus1``
++ -
++ * - __u8
++ - ``pcm_sample_bit_depth_chroma_minus1``
++ -
++ * - __u8
++ - ``log2_min_pcm_luma_coding_block_size_minus3``
++ -
++ * - __u8
++ - ``log2_diff_max_min_pcm_luma_coding_block_size``
++ -
++ * - __u8
++ - ``num_short_term_ref_pic_sets``
++ -
++ * - __u8
++ - ``num_long_term_ref_pics_sps``
++ -
++ * - __u8
++ - ``chroma_format_idc``
++ -
++ * - __u64
++ - ``flags``
++ - See :ref:`Sequence Parameter Set Flags <hevc_sps_flags>`
++
++.. _hevc_sps_flags:
++
++``Sequence Parameter Set Flags``
++
++.. cssclass:: longtable
++
++.. flat-table::
++ :header-rows: 0
++ :stub-columns: 0
++ :widths: 1 1 2
++
++ * - ``V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE``
++ - 0x00000001
++ -
++ * - ``V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED``
++ - 0x00000002
++ -
++ * - ``V4L2_HEVC_SPS_FLAG_AMP_ENABLED``
++ - 0x00000004
++ -
++ * - ``V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET``
++ - 0x00000008
++ -
++ * - ``V4L2_HEVC_SPS_FLAG_PCM_ENABLED``
++ - 0x00000010
++ -
++ * - ``V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED``
++ - 0x00000020
++ -
++ * - ``V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT``
++ - 0x00000040
++ -
++ * - ``V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED``
++ - 0x00000080
++ -
++ * - ``V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED``
++ - 0x00000100
++ -
++
++``V4L2_CID_MPEG_VIDEO_HEVC_PPS (struct)``
++ Specifies the Picture Parameter Set fields (as extracted from the
++ bitstream) for the associated HEVC slice data.
++ These bitstream parameters are defined according to :ref:`hevc`.
++ They are described in section 7.4.3.3 "Picture parameter set RBSP
++ semantics" of the specification.
++
++.. c:type:: v4l2_ctrl_hevc_pps
++
++.. cssclass:: longtable
++
++.. flat-table:: struct v4l2_ctrl_hevc_pps
++ :header-rows: 0
++ :stub-columns: 0
++ :widths: 1 1 2
++
++ * - __u8
++ - ``num_extra_slice_header_bits``
++ -
++ * - __s8
++ - ``init_qp_minus26``
++ -
++ * - __u8
++ - ``diff_cu_qp_delta_depth``
++ -
++ * - __s8
++ - ``pps_cb_qp_offset``
++ -
++ * - __s8
++ - ``pps_cr_qp_offset``
++ -
++ * - __u8
++ - ``num_tile_columns_minus1``
++ -
++ * - __u8
++ - ``num_tile_rows_minus1``
++ -
++ * - __u8
++ - ``column_width_minus1[20]``
++ -
++ * - __u8
++ - ``row_height_minus1[22]``
++ -
++ * - __s8
++ - ``pps_beta_offset_div2``
++ -
++ * - __s8
++ - ``pps_tc_offset_div2``
++ -
++ * - __u8
++ - ``log2_parallel_merge_level_minus2``
++ -
++ * - __u8
++ - ``padding[4]``
++ - Applications and drivers must set this to zero.
++ * - __u64
++ - ``flags``
++ - See :ref:`Picture Parameter Set Flags <hevc_pps_flags>`
++
++.. _hevc_pps_flags:
++
++``Picture Parameter Set Flags``
++
++.. cssclass:: longtable
++
++.. flat-table::
++ :header-rows: 0
++ :stub-columns: 0
++ :widths: 1 1 2
++
++ * - ``V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT``
++ - 0x00000001
++ -
++ * - ``V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT``
++ - 0x00000002
++ -
++ * - ``V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED``
++ - 0x00000004
++ -
++ * - ``V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT``
++ - 0x00000008
++ -
++ * - ``V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED``
++ - 0x00000010
++ -
++ * - ``V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED``
++ - 0x00000020
++ -
++ * - ``V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED``
++ - 0x00000040
++ -
++ * - ``V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT``
++ - 0x00000080
++ -
++ * - ``V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED``
++ - 0x00000100
++ -
++ * - ``V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED``
++ - 0x00000200
++ -
++ * - ``V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED``
++ - 0x00000400
++ -
++ * - ``V4L2_HEVC_PPS_FLAG_TILES_ENABLED``
++ - 0x00000800
++ -
++ * - ``V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED``
++ - 0x00001000
++ -
++ * - ``V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED``
++ - 0x00002000
++ -
++ * - ``V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED``
++ - 0x00004000
++ -
++ * - ``V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED``
++ - 0x00008000
++ -
++ * - ``V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER``
++ - 0x00010000
++ -
++ * - ``V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT``
++ - 0x00020000
++ -
++ * - ``V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT``
++ - 0x00040000
++ -
++
++``V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS (struct)``
++ Specifies various slice-specific parameters, especially from the NAL unit
++ header, general slice segment header and weighted prediction parameter
++ parts of the bitstream.
++ These bitstream parameters are defined according to :ref:`hevc`.
++ They are described in section 7.4.7 "General slice segment header
++ semantics" of the specification.
++
++.. c:type:: v4l2_ctrl_hevc_slice_params
++
++.. cssclass:: longtable
++
++.. flat-table:: struct v4l2_ctrl_hevc_slice_params
++ :header-rows: 0
++ :stub-columns: 0
++ :widths: 1 1 2
++
++ * - __u32
++ - ``bit_size``
++ - Size (in bits) of the current slice data.
++ * - __u32
++ - ``data_bit_offset``
++ - Offset (in bits) to the video data in the current slice data.
++ * - __u8
++ - ``nal_unit_type``
++ -
++ * - __u8
++ - ``nuh_temporal_id_plus1``
++ -
++ * - __u8
++ - ``slice_type``
++ -
++ (V4L2_HEVC_SLICE_TYPE_I, V4L2_HEVC_SLICE_TYPE_P or
++ V4L2_HEVC_SLICE_TYPE_B).
++ * - __u8
++ - ``colour_plane_id``
++ -
++ * - __u16
++ - ``slice_pic_order_cnt``
++ -
++ * - __u8
++ - ``num_ref_idx_l0_active_minus1``
++ -
++ * - __u8
++ - ``num_ref_idx_l1_active_minus1``
++ -
++ * - __u8
++ - ``collocated_ref_idx``
++ -
++ * - __u8
++ - ``five_minus_max_num_merge_cand``
++ -
++ * - __s8
++ - ``slice_qp_delta``
++ -
++ * - __s8
++ - ``slice_cb_qp_offset``
++ -
++ * - __s8
++ - ``slice_cr_qp_offset``
++ -
++ * - __s8
++ - ``slice_act_y_qp_offset``
++ -
++ * - __s8
++ - ``slice_act_cb_qp_offset``
++ -
++ * - __s8
++ - ``slice_act_cr_qp_offset``
++ -
++ * - __s8
++ - ``slice_beta_offset_div2``
++ -
++ * - __s8
++ - ``slice_tc_offset_div2``
++ -
++ * - __u8
++ - ``pic_struct``
++ -
++ * - __u8
++ - ``num_active_dpb_entries``
++ - The number of entries in ``dpb``.
++ * - __u8
++ - ``ref_idx_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]``
++ - The list of L0 reference elements as indices in the DPB.
++ * - __u8
++ - ``ref_idx_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]``
++ - The list of L1 reference elements as indices in the DPB.
++ * - __u8
++ - ``num_rps_poc_st_curr_before``
++ - The number of reference pictures in the short-term set that come before
++ the current frame.
++ * - __u8
++ - ``num_rps_poc_st_curr_after``
++ - The number of reference pictures in the short-term set that come after
++ the current frame.
++ * - __u8
++ - ``num_rps_poc_lt_curr``
++ - The number of reference pictures in the long-term set.
++ * - __u8
++ - ``padding[7]``
++ - Applications and drivers must set this to zero.
++ * - struct :c:type:`v4l2_hevc_dpb_entry`
++ - ``dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]``
++ - The decoded picture buffer, for meta-data about reference frames.
++ * - struct :c:type:`v4l2_hevc_pred_weight_table`
++ - ``pred_weight_table``
++ - The prediction weight coefficients for inter-picture prediction.
++ * - __u64
++ - ``flags``
++ - See :ref:`Slice Parameters Flags <hevc_slice_params_flags>`
++
++.. _hevc_slice_params_flags:
++
++``Slice Parameters Flags``
++
++.. cssclass:: longtable
++
++.. flat-table::
++ :header-rows: 0
++ :stub-columns: 0
++ :widths: 1 1 2
++
++ * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_LUMA``
++ - 0x00000001
++ -
++ * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_CHROMA``
++ - 0x00000002
++ -
++ * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED``
++ - 0x00000004
++ -
++ * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_MVD_L1_ZERO``
++ - 0x00000008
++ -
++ * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT``
++ - 0x00000010
++ -
++ * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_COLLOCATED_FROM_L0``
++ - 0x00000020
++ -
++ * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_USE_INTEGER_MV``
++ - 0x00000040
++ -
++ * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED``
++ - 0x00000080
++ -
++ * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED``
++ - 0x00000100
++ -
++
++.. c:type:: v4l2_hevc_dpb_entry
++
++.. cssclass:: longtable
++
++.. flat-table:: struct v4l2_hevc_dpb_entry
++ :header-rows: 0
++ :stub-columns: 0
++ :widths: 1 1 2
++
++ * - __u64
++ - ``timestamp``
++ - Timestamp of the V4L2 capture buffer to use as reference, used
++ with B-coded and P-coded frames. The timestamp refers to the
++ ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the
++ :c:func:`v4l2_timeval_to_ns()` function to convert the struct
++ :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64.
++ * - __u8
++ - ``rps``
++ - The reference set for the reference frame
++ (V4L2_HEVC_DPB_ENTRY_RPS_ST_CURR_BEFORE,
++ V4L2_HEVC_DPB_ENTRY_RPS_ST_CURR_AFTER or
++ V4L2_HEVC_DPB_ENTRY_RPS_LT_CURR)
++ * - __u8
++ - ``field_pic``
++ - Whether the reference is a field picture or a frame.
++ * - __u16
++ - ``pic_order_cnt[2]``
++ - The picture order count of the reference. Only the first element of the
++ array is used for frame pictures, while the first element identifies the
++ top field and the second the bottom field in field-coded pictures.
++ * - __u8
++ - ``padding[2]``
++ - Applications and drivers must set this to zero.
++
++.. c:type:: v4l2_hevc_pred_weight_table
++
++.. cssclass:: longtable
++
++.. flat-table:: struct v4l2_hevc_pred_weight_table
++ :header-rows: 0
++ :stub-columns: 0
++ :widths: 1 1 2
++
++ * - __u8
++ - ``luma_log2_weight_denom``
++ -
++ * - __s8
++ - ``delta_chroma_log2_weight_denom``
++ -
++ * - __s8
++ - ``delta_luma_weight_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]``
++ -
++ * - __s8
++ - ``luma_offset_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]``
++ -
++ * - __s8
++ - ``delta_chroma_weight_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2]``
++ -
++ * - __s8
++ - ``chroma_offset_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2]``
++ -
++ * - __s8
++ - ``delta_luma_weight_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]``
++ -
++ * - __s8
++ - ``luma_offset_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]``
++ -
++ * - __s8
++ - ``delta_chroma_weight_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2]``
++ -
++ * - __s8
++ - ``chroma_offset_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2]``
++ -
++ * - __u8
++ - ``padding[6]``
++ - Applications and drivers must set this to zero.
++
++``V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE (enum)``
++ Specifies the decoding mode to use. Currently exposes slice-based and
++ frame-based decoding but new modes might be added later on.
++ This control is used as a modifier for V4L2_PIX_FMT_HEVC_SLICE
++ pixel format. Applications that support V4L2_PIX_FMT_HEVC_SLICE
++ are required to set this control in order to specify the decoding mode
++ that is expected for the buffer.
++ Drivers may expose a single or multiple decoding modes, depending
++ on what they can support.
++
++ .. note::
++
++ This menu control is not yet part of the public kernel API and
++ it is expected to change.
++
++.. c:type:: v4l2_mpeg_video_hevc_decode_mode
++
++.. cssclass:: longtable
++
++.. flat-table::
++ :header-rows: 0
++ :stub-columns: 0
++ :widths: 1 1 2
++
++ * - ``V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED``
++ - 0
++ - Decoding is done at the slice granularity.
++ The OUTPUT buffer must contain a single slice.
++ * - ``V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_FRAME_BASED``
++ - 1
++ - Decoding is done at the frame granularity.
++ The OUTPUT buffer must contain all slices needed to decode the
++ frame. The OUTPUT buffer must also contain both fields.
++
++``V4L2_CID_MPEG_VIDEO_HEVC_START_CODE (enum)``
++ Specifies the HEVC slice start code expected for each slice.
++ This control is used as a modifier for V4L2_PIX_FMT_HEVC_SLICE
++ pixel format. Applications that support V4L2_PIX_FMT_HEVC_SLICE
++ are required to set this control in order to specify the start code
++ that is expected for the buffer.
++ Drivers may expose a single or multiple start codes, depending
++ on what they can support.
++
++ .. note::
++
++ This menu control is not yet part of the public kernel API and
++ it is expected to change.
++
++.. c:type:: v4l2_mpeg_video_hevc_start_code
++
++.. cssclass:: longtable
++
++.. flat-table::
++ :header-rows: 0
++ :stub-columns: 0
++ :widths: 1 1 2
++
++ * - ``V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE``
++ - 0
++ - Selecting this value specifies that HEVC slices are passed
++ to the driver without any start code.
++ * - ``V4L2_MPEG_VIDEO_HEVC_START_CODE_ANNEX_B``
++ - 1
++ - Selecting this value specifies that HEVC slices are expected
++ to be prefixed by Annex B start codes. According to :ref:`hevc`
++ valid start codes can be 3-bytes 0x000001 or 4-bytes 0x00000001.
+--- a/Documentation/media/uapi/v4l/vidioc-queryctrl.rst
++++ b/Documentation/media/uapi/v4l/vidioc-queryctrl.rst
+@@ -479,6 +479,24 @@ See also the examples in :ref:`control`.
+ - n/a
+ - A struct :c:type:`v4l2_ctrl_h264_decode_params`, containing H264
+ decode parameters for stateless video decoders.
++ * - ``V4L2_CTRL_TYPE_HEVC_SPS``
++ - n/a
++ - n/a
++ - n/a
++ - A struct :c:type:`v4l2_ctrl_hevc_sps`, containing HEVC Sequence
++ Parameter Set for stateless video decoders.
++ * - ``V4L2_CTRL_TYPE_HEVC_PPS``
++ - n/a
++ - n/a
++ - n/a
++ - A struct :c:type:`v4l2_ctrl_hevc_pps`, containing HEVC Picture
++ Parameter Set for stateless video decoders.
++ * - ``V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS``
++ - n/a
++ - n/a
++ - n/a
++ - A struct :c:type:`v4l2_ctrl_hevc_slice_params`, containing HEVC
++ slice parameters for stateless video decoders.
+
+ .. tabularcolumns:: |p{6.6cm}|p{2.2cm}|p{8.7cm}|
+
+--- a/Documentation/media/videodev2.h.rst.exceptions
++++ b/Documentation/media/videodev2.h.rst.exceptions
+@@ -141,6 +141,9 @@ replace symbol V4L2_CTRL_TYPE_H264_PPS :
+ replace symbol V4L2_CTRL_TYPE_H264_SCALING_MATRIX :c:type:`v4l2_ctrl_type`
+ replace symbol V4L2_CTRL_TYPE_H264_SLICE_PARAMS :c:type:`v4l2_ctrl_type`
+ replace symbol V4L2_CTRL_TYPE_H264_DECODE_PARAMS :c:type:`v4l2_ctrl_type`
++replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`v4l2_ctrl_type`
++replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`v4l2_ctrl_type`
++replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`v4l2_ctrl_type`
+ replace symbol V4L2_CTRL_TYPE_AREA :c:type:`v4l2_ctrl_type`
+
+ # V4L2 capability defines
+--- a/drivers/media/v4l2-core/v4l2-ctrls.c
++++ b/drivers/media/v4l2-core/v4l2-ctrls.c
+@@ -567,6 +567,16 @@ const char * const *v4l2_ctrl_get_menu(u
+ "Disabled at slice boundary",
+ "NULL",
+ };
++ static const char * const hevc_decode_mode[] = {
++ "Slice-Based",
++ "Frame-Based",
++ NULL,
++ };
++ static const char * const hevc_start_code[] = {
++ "No Start Code",
++ "Annex B Start Code",
++ NULL,
++ };
+
+ switch (id) {
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+@@ -688,7 +698,10 @@ const char * const *v4l2_ctrl_get_menu(u
+ return hevc_tier;
+ case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE:
+ return hevc_loop_filter_mode;
+-
++ case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE:
++ return hevc_decode_mode;
++ case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE:
++ return hevc_start_code;
+ default:
+ return NULL;
+ }
+@@ -958,6 +971,11 @@ const char *v4l2_ctrl_get_name(u32 id)
+ case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD: return "HEVC Size of Length Field";
+ case V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES: return "Reference Frames for a P-Frame";
+ case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR: return "Prepend SPS and PPS to IDR";
++ case V4L2_CID_MPEG_VIDEO_HEVC_SPS: return "HEVC Sequence Parameter Set";
++ case V4L2_CID_MPEG_VIDEO_HEVC_PPS: return "HEVC Picture Parameter Set";
++ case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS: return "HEVC Slice Parameters";
++ case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE: return "HEVC Decode Mode";
++ case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE: return "HEVC Start Code";
+
+ /* CAMERA controls */
+ /* Keep the order of the 'case's the same as in v4l2-controls.h! */
+@@ -1267,6 +1285,8 @@ void v4l2_ctrl_fill(u32 id, const char *
+ case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD:
+ case V4L2_CID_MPEG_VIDEO_HEVC_TIER:
+ case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE:
++ case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE:
++ case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE:
+ *type = V4L2_CTRL_TYPE_MENU;
+ break;
+ case V4L2_CID_LINK_FREQ:
+@@ -1377,6 +1397,15 @@ void v4l2_ctrl_fill(u32 id, const char *
+ case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HEADER:
+ *type = V4L2_CTRL_TYPE_VP8_FRAME_HEADER;
+ break;
++ case V4L2_CID_MPEG_VIDEO_HEVC_SPS:
++ *type = V4L2_CTRL_TYPE_HEVC_SPS;
++ break;
++ case V4L2_CID_MPEG_VIDEO_HEVC_PPS:
++ *type = V4L2_CTRL_TYPE_HEVC_PPS;
++ break;
++ case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS:
++ *type = V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS;
++ break;
+ case V4L2_CID_UNIT_CELL_SIZE:
+ *type = V4L2_CTRL_TYPE_AREA;
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+@@ -1678,8 +1707,12 @@ static int std_validate_compound(const s
+ {
+ struct v4l2_ctrl_mpeg2_slice_params *p_mpeg2_slice_params;
+ struct v4l2_ctrl_vp8_frame_header *p_vp8_frame_header;
++ struct v4l2_ctrl_hevc_sps *p_hevc_sps;
++ struct v4l2_ctrl_hevc_pps *p_hevc_pps;
++ struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params;
+ struct v4l2_area *area;
+ void *p = ptr.p + idx * ctrl->elem_size;
++ unsigned int i;
+
+ switch ((u32)ctrl->type) {
+ case V4L2_CTRL_TYPE_MPEG2_SLICE_PARAMS:
+@@ -1755,11 +1788,76 @@ static int std_validate_compound(const s
+ zero_padding(p_vp8_frame_header->entropy_header);
+ zero_padding(p_vp8_frame_header->coder_state);
+ break;
++
++ case V4L2_CTRL_TYPE_HEVC_SPS:
++ p_hevc_sps = p;
++
++ if (!(p_hevc_sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED)) {
++ p_hevc_sps->pcm_sample_bit_depth_luma_minus1 = 0;
++ p_hevc_sps->pcm_sample_bit_depth_chroma_minus1 = 0;
++ p_hevc_sps->log2_min_pcm_luma_coding_block_size_minus3 = 0;
++ p_hevc_sps->log2_diff_max_min_pcm_luma_coding_block_size = 0;
++ }
++
++ if (!(p_hevc_sps->flags &
++ V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT))
++ p_hevc_sps->num_long_term_ref_pics_sps = 0;
++ break;
++
++ case V4L2_CTRL_TYPE_HEVC_PPS:
++ p_hevc_pps = p;
++
++ if (!(p_hevc_pps->flags &
++ V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED))
++ p_hevc_pps->diff_cu_qp_delta_depth = 0;
++
++ if (!(p_hevc_pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED)) {
++ p_hevc_pps->num_tile_columns_minus1 = 0;
++ p_hevc_pps->num_tile_rows_minus1 = 0;
++ memset(&p_hevc_pps->column_width_minus1, 0,
++ sizeof(p_hevc_pps->column_width_minus1));
++ memset(&p_hevc_pps->row_height_minus1, 0,
++ sizeof(p_hevc_pps->row_height_minus1));
++
++ p_hevc_pps->flags &=
++ ~V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED;
++ }
++
++ if (p_hevc_pps->flags &
++ V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER) {
++ p_hevc_pps->pps_beta_offset_div2 = 0;
++ p_hevc_pps->pps_tc_offset_div2 = 0;
++ }
++
++ zero_padding(*p_hevc_pps);
++ break;
++
++ case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
++ p_hevc_slice_params = p;
++
++ if (p_hevc_slice_params->num_active_dpb_entries >
++ V4L2_HEVC_DPB_ENTRIES_NUM_MAX)
++ return -EINVAL;
++
++ zero_padding(p_hevc_slice_params->pred_weight_table);
++
++ for (i = 0; i < p_hevc_slice_params->num_active_dpb_entries;
++ i++) {
++ struct v4l2_hevc_dpb_entry *dpb_entry =
++ &p_hevc_slice_params->dpb[i];
++
++ zero_padding(*dpb_entry);
++ }
++
++ zero_padding(*p_hevc_slice_params);
++ break;
++
+ case V4L2_CTRL_TYPE_AREA:
+ area = p;
+ if (!area->width || !area->height)
+ return -EINVAL;
+ break;
++
+ default:
+ return -EINVAL;
+ }
+@@ -2433,6 +2531,15 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s
+ case V4L2_CTRL_TYPE_VP8_FRAME_HEADER:
+ elem_size = sizeof(struct v4l2_ctrl_vp8_frame_header);
+ break;
++ case V4L2_CTRL_TYPE_HEVC_SPS:
++ elem_size = sizeof(struct v4l2_ctrl_hevc_sps);
++ break;
++ case V4L2_CTRL_TYPE_HEVC_PPS:
++ elem_size = sizeof(struct v4l2_ctrl_hevc_pps);
++ break;
++ case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
++ elem_size = sizeof(struct v4l2_ctrl_hevc_slice_params);
++ break;
+ case V4L2_CTRL_TYPE_AREA:
+ elem_size = sizeof(struct v4l2_area);
+ break;
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1356,6 +1356,7 @@ static void v4l_fill_fmtdesc(struct v4l2
+ case V4L2_PIX_FMT_VP8_FRAME: descr = "VP8 Frame"; break;
+ case V4L2_PIX_FMT_VP9: descr = "VP9"; break;
+ case V4L2_PIX_FMT_HEVC: descr = "HEVC"; break; /* aka H.265 */
++ case V4L2_PIX_FMT_HEVC_SLICE: descr = "HEVC Parsed Slice Data"; break;
+ case V4L2_PIX_FMT_FWHT: descr = "FWHT"; break; /* used in vicodec */
+ case V4L2_PIX_FMT_FWHT_STATELESS: descr = "FWHT Stateless"; break; /* used in vicodec */
+ case V4L2_PIX_FMT_CPIA1: descr = "GSPCA CPiA YUV"; break;
+--- /dev/null
++++ b/include/media/hevc-ctrls.h
+@@ -0,0 +1,212 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * These are the HEVC state controls for use with stateless HEVC
++ * codec drivers.
++ *
++ * It turns out that these structs are not stable yet and will undergo
++ * more changes. So keep them private until they are stable and ready to
++ * become part of the official public API.
++ */
++
++#ifndef _HEVC_CTRLS_H_
++#define _HEVC_CTRLS_H_
++
++#include <linux/videodev2.h>
++
++/* The pixel format isn't stable at the moment and will likely be renamed. */
++#define V4L2_PIX_FMT_HEVC_SLICE v4l2_fourcc('S', '2', '6', '5') /* HEVC parsed slices */
++
++#define V4L2_CID_MPEG_VIDEO_HEVC_SPS (V4L2_CID_MPEG_BASE + 1008)
++#define V4L2_CID_MPEG_VIDEO_HEVC_PPS (V4L2_CID_MPEG_BASE + 1009)
++#define V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS (V4L2_CID_MPEG_BASE + 1010)
++#define V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE (V4L2_CID_MPEG_BASE + 1015)
++#define V4L2_CID_MPEG_VIDEO_HEVC_START_CODE (V4L2_CID_MPEG_BASE + 1016)
++
++/* enum v4l2_ctrl_type type values */
++#define V4L2_CTRL_TYPE_HEVC_SPS 0x0120
++#define V4L2_CTRL_TYPE_HEVC_PPS 0x0121
++#define V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS 0x0122
++
++enum v4l2_mpeg_video_hevc_decode_mode {
++ V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED,
++ V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_FRAME_BASED,
++};
++
++enum v4l2_mpeg_video_hevc_start_code {
++ V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE,
++ V4L2_MPEG_VIDEO_HEVC_START_CODE_ANNEX_B,
++};
++
++#define V4L2_HEVC_SLICE_TYPE_B 0
++#define V4L2_HEVC_SLICE_TYPE_P 1
++#define V4L2_HEVC_SLICE_TYPE_I 2
++
++#define V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE (1ULL << 0)
++#define V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED (1ULL << 1)
++#define V4L2_HEVC_SPS_FLAG_AMP_ENABLED (1ULL << 2)
++#define V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET (1ULL << 3)
++#define V4L2_HEVC_SPS_FLAG_PCM_ENABLED (1ULL << 4)
++#define V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED (1ULL << 5)
++#define V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT (1ULL << 6)
++#define V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED (1ULL << 7)
++#define V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED (1ULL << 8)
++
++/* The controls are not stable at the moment and will likely be reworked. */
++struct v4l2_ctrl_hevc_sps {
++ /* ISO/IEC 23008-2, ITU-T Rec. H.265: Sequence parameter set */
++ __u16 pic_width_in_luma_samples;
++ __u16 pic_height_in_luma_samples;
++ __u8 bit_depth_luma_minus8;
++ __u8 bit_depth_chroma_minus8;
++ __u8 log2_max_pic_order_cnt_lsb_minus4;
++ __u8 sps_max_dec_pic_buffering_minus1;
++ __u8 sps_max_num_reorder_pics;
++ __u8 sps_max_latency_increase_plus1;
++ __u8 log2_min_luma_coding_block_size_minus3;
++ __u8 log2_diff_max_min_luma_coding_block_size;
++ __u8 log2_min_luma_transform_block_size_minus2;
++ __u8 log2_diff_max_min_luma_transform_block_size;
++ __u8 max_transform_hierarchy_depth_inter;
++ __u8 max_transform_hierarchy_depth_intra;
++ __u8 pcm_sample_bit_depth_luma_minus1;
++ __u8 pcm_sample_bit_depth_chroma_minus1;
++ __u8 log2_min_pcm_luma_coding_block_size_minus3;
++ __u8 log2_diff_max_min_pcm_luma_coding_block_size;
++ __u8 num_short_term_ref_pic_sets;
++ __u8 num_long_term_ref_pics_sps;
++ __u8 chroma_format_idc;
++
++ __u8 padding;
++
++ __u64 flags;
++};
++
++#define V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT (1ULL << 0)
++#define V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT (1ULL << 1)
++#define V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED (1ULL << 2)
++#define V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT (1ULL << 3)
++#define V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED (1ULL << 4)
++#define V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED (1ULL << 5)
++#define V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED (1ULL << 6)
++#define V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT (1ULL << 7)
++#define V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED (1ULL << 8)
++#define V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED (1ULL << 9)
++#define V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED (1ULL << 10)
++#define V4L2_HEVC_PPS_FLAG_TILES_ENABLED (1ULL << 11)
++#define V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED (1ULL << 12)
++#define V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED (1ULL << 13)
++#define V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED (1ULL << 14)
++#define V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED (1ULL << 15)
++#define V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER (1ULL << 16)
++#define V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT (1ULL << 17)
++#define V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT (1ULL << 18)
++
++struct v4l2_ctrl_hevc_pps {
++ /* ISO/IEC 23008-2, ITU-T Rec. H.265: Picture parameter set */
++ __u8 num_extra_slice_header_bits;
++ __s8 init_qp_minus26;
++ __u8 diff_cu_qp_delta_depth;
++ __s8 pps_cb_qp_offset;
++ __s8 pps_cr_qp_offset;
++ __u8 num_tile_columns_minus1;
++ __u8 num_tile_rows_minus1;
++ __u8 column_width_minus1[20];
++ __u8 row_height_minus1[22];
++ __s8 pps_beta_offset_div2;
++ __s8 pps_tc_offset_div2;
++ __u8 log2_parallel_merge_level_minus2;
++
++ __u8 padding[4];
++ __u64 flags;
++};
++
++#define V4L2_HEVC_DPB_ENTRY_RPS_ST_CURR_BEFORE 0x01
++#define V4L2_HEVC_DPB_ENTRY_RPS_ST_CURR_AFTER 0x02
++#define V4L2_HEVC_DPB_ENTRY_RPS_LT_CURR 0x03
++
++#define V4L2_HEVC_DPB_ENTRIES_NUM_MAX 16
++
++struct v4l2_hevc_dpb_entry {
++ __u64 timestamp;
++ __u8 rps;
++ __u8 field_pic;
++ __u16 pic_order_cnt[2];
++ __u8 padding[2];
++};
++
++struct v4l2_hevc_pred_weight_table {
++ __s8 delta_luma_weight_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++ __s8 luma_offset_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++ __s8 delta_chroma_weight_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
++ __s8 chroma_offset_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
++
++ __s8 delta_luma_weight_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++ __s8 luma_offset_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++ __s8 delta_chroma_weight_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
++ __s8 chroma_offset_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
++
++ __u8 padding[6];
++
++ __u8 luma_log2_weight_denom;
++ __s8 delta_chroma_log2_weight_denom;
++};
++
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_LUMA (1ULL << 0)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_CHROMA (1ULL << 1)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED (1ULL << 2)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_MVD_L1_ZERO (1ULL << 3)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT (1ULL << 4)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_COLLOCATED_FROM_L0 (1ULL << 5)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_USE_INTEGER_MV (1ULL << 6)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED (1ULL << 7)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED (1ULL << 8)
++
++struct v4l2_ctrl_hevc_slice_params {
++ __u32 bit_size;
++ __u32 data_bit_offset;
++
++ /* ISO/IEC 23008-2, ITU-T Rec. H.265: NAL unit header */
++ __u8 nal_unit_type;
++ __u8 nuh_temporal_id_plus1;
++
++ /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
++ __u8 slice_type;
++ __u8 colour_plane_id;
++ __u16 slice_pic_order_cnt;
++ __u8 num_ref_idx_l0_active_minus1;
++ __u8 num_ref_idx_l1_active_minus1;
++ __u8 collocated_ref_idx;
++ __u8 five_minus_max_num_merge_cand;
++ __s8 slice_qp_delta;
++ __s8 slice_cb_qp_offset;
++ __s8 slice_cr_qp_offset;
++ __s8 slice_act_y_qp_offset;
++ __s8 slice_act_cb_qp_offset;
++ __s8 slice_act_cr_qp_offset;
++ __s8 slice_beta_offset_div2;
++ __s8 slice_tc_offset_div2;
++
++ /* ISO/IEC 23008-2, ITU-T Rec. H.265: Picture timing SEI message */
++ __u8 pic_struct;
++
++ /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
++ __u8 num_active_dpb_entries;
++ __u8 ref_idx_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++ __u8 ref_idx_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++
++ __u8 num_rps_poc_st_curr_before;
++ __u8 num_rps_poc_st_curr_after;
++ __u8 num_rps_poc_lt_curr;
++
++ __u8 padding;
++
++ /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
++ struct v4l2_hevc_dpb_entry dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++
++ /* ISO/IEC 23008-2, ITU-T Rec. H.265: Weighted prediction parameter */
++ struct v4l2_hevc_pred_weight_table pred_weight_table;
++
++ __u64 flags;
++};
++
++#endif
+--- a/include/media/v4l2-ctrls.h
++++ b/include/media/v4l2-ctrls.h
+@@ -21,6 +21,7 @@
+ #include <media/fwht-ctrls.h>
+ #include <media/h264-ctrls.h>
+ #include <media/vp8-ctrls.h>
++#include <media/hevc-ctrls.h>
+
+ /* forward references */
+ struct file;
+@@ -50,6 +51,9 @@ struct poll_table_struct;
+ * @p_h264_slice_params: Pointer to a struct v4l2_ctrl_h264_slice_params.
+ * @p_h264_decode_params: Pointer to a struct v4l2_ctrl_h264_decode_params.
+ * @p_vp8_frame_header: Pointer to a VP8 frame header structure.
++ * @p_hevc_sps: Pointer to an HEVC sequence parameter set structure.
++ * @p_hevc_pps: Pointer to an HEVC picture parameter set structure.
++ * @p_hevc_slice_params: Pointer to an HEVC slice parameters structure.
+ * @p_area: Pointer to an area.
+ * @p: Pointer to a compound value.
+ */
+@@ -69,6 +73,9 @@ union v4l2_ctrl_ptr {
+ struct v4l2_ctrl_h264_slice_params *p_h264_slice_params;
+ struct v4l2_ctrl_h264_decode_params *p_h264_decode_params;
+ struct v4l2_ctrl_vp8_frame_header *p_vp8_frame_header;
++ struct v4l2_ctrl_hevc_sps *p_hevc_sps;
++ struct v4l2_ctrl_hevc_pps *p_hevc_pps;
++ struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params;
+ struct v4l2_area *p_area;
+ void *p;
+ };
--- /dev/null
+From 73d8a76ec5b5e1240af4142a9ccbd39179d779af Mon Sep 17 00:00:00 2001
+From: Jernej Skrabec <jernej.skrabec@siol.net>
+Date: Wed, 6 Nov 2019 08:02:53 +0100
+Subject: [PATCH] media: v4l2-mem2mem: Fix hold buf flag checks
+
+Commit 1076df3a77b490d33429560a9e0603b3673223e2 upstream.
+
+Hold buf flag is set on output queue, not capture. Fix that.
+
+Fixes: f07602ac3887 ("media: v4l2-mem2mem: add new_frame detection")
+Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
+---
+ drivers/media/v4l2-core/v4l2-mem2mem.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
++++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
+@@ -335,7 +335,7 @@ static void __v4l2_m2m_try_queue(struct
+ }
+ }
+
+- if (src && dst && (m2m_ctx->cap_q_ctx.q.subsystem_flags &
++ if (src && dst && (m2m_ctx->out_q_ctx.q.subsystem_flags &
+ VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF))
+ m2m_ctx->new_frame = !dst->vb2_buf.copied_timestamp ||
+ dst->vb2_buf.timestamp != src->vb2_buf.timestamp;
+@@ -474,7 +474,7 @@ void v4l2_m2m_job_finish(struct v4l2_m2m
+ * holding capture buffers. Those should use
+ * v4l2_m2m_buf_done_and_job_finish() instead.
+ */
+- WARN_ON(m2m_ctx->cap_q_ctx.q.subsystem_flags &
++ WARN_ON(m2m_ctx->out_q_ctx.q.subsystem_flags &
+ VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF);
+ spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
+ schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx);
--- /dev/null
+From 662256810630f6ac6d06ee0cdc5f4660b25f7e98 Mon Sep 17 00:00:00 2001
+From: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+Date: Tue, 22 Oct 2019 12:26:53 -0300
+Subject: [PATCH] media: pixfmt: Document the HEVC slice pixel format
+
+Commit de06f289283298e2938445019999cec46435375c upstream.
+
+Document the current state of the HEVC slice pixel format.
+The format will need to evolve in the future, which is why it is
+not part of the public API.
+
+Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ .../media/uapi/v4l/pixfmt-compressed.rst | 23 +++++++++++++++++++
+ 1 file changed, 23 insertions(+)
+
+--- a/Documentation/media/uapi/v4l/pixfmt-compressed.rst
++++ b/Documentation/media/uapi/v4l/pixfmt-compressed.rst
+@@ -188,6 +188,29 @@ Compressed Formats
+ If :ref:`VIDIOC_ENUM_FMT` reports ``V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM``
+ then the decoder has no requirements since it can parse all the
+ information from the raw bytestream.
++ * .. _V4L2-PIX-FMT-HEVC-SLICE:
++
++ - ``V4L2_PIX_FMT_HEVC_SLICE``
++ - 'S265'
++ - HEVC parsed slice data, as extracted from the HEVC bitstream.
++ This format is adapted for stateless video decoders that implement a
++ HEVC pipeline (using the :ref:`mem2mem` and :ref:`media-request-api`).
++ This pixelformat has two modifiers that must be set at least once
++ through the ``V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE``
++ and ``V4L2_CID_MPEG_VIDEO_HEVC_START_CODE`` controls.
++ Metadata associated with the frame to decode is required to be passed
++ through the following controls :
++ * ``V4L2_CID_MPEG_VIDEO_HEVC_SPS``
++ * ``V4L2_CID_MPEG_VIDEO_HEVC_PPS``
++ * ``V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS``
++ See the :ref:`associated Codec Control IDs <v4l2-mpeg-hevc>`.
++ Buffers associated with this pixel format must contain the appropriate
++ number of macroblocks to decode a full corresponding frame.
++
++ .. note::
++
++ This format is not yet part of the public kernel API and it
++ is expected to change.
+ * .. _V4L2-PIX-FMT-FWHT:
+
+ - ``V4L2_PIX_FMT_FWHT``
--- /dev/null
+From 70b5a28786215c996503210abd3e44c200771640 Mon Sep 17 00:00:00 2001
+From: Jernej Skrabec <jernej.skrabec@siol.net>
+Date: Fri, 13 Dec 2019 17:04:25 +0100
+Subject: [PATCH] media: uapi: hevc: Add scaling matrix control
+
+Taken from https://patchwork.linuxtv.org/patch/60728/
+Changes (mainly documentation) have been requested.
+
+HEVC has a scaling matrix concept. Add support for it.
+
+Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
+---
+ .../media/uapi/v4l/ext-ctrls-codec.rst | 41 +++++++++++++++++++
+ .../media/uapi/v4l/pixfmt-compressed.rst | 1 +
+ drivers/media/v4l2-core/v4l2-ctrls.c | 10 +++++
+ include/media/hevc-ctrls.h | 11 +++++
+ 4 files changed, 63 insertions(+)
+
+--- a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst
++++ b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst
+@@ -4174,6 +4174,47 @@ enum v4l2_mpeg_video_hevc_size_of_length
+ - ``padding[6]``
+ - Applications and drivers must set this to zero.
+
++``V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX (struct)``
++ Specifies the scaling matrix (as extracted from the bitstream) for
++ the associated HEVC slice data. The bitstream parameters are
++ defined according to :ref:`hevc`, section 7.4.5 "Scaling list
++ data semantics". For further documentation, refer to the above
++ specification, unless there is an explicit comment stating
++ otherwise.
++
++ .. note::
++
++ This compound control is not yet part of the public kernel API and
++ it is expected to change.
++
++.. c:type:: v4l2_ctrl_hevc_scaling_matrix
++
++.. cssclass:: longtable
++
++.. flat-table:: struct v4l2_ctrl_hevc_scaling_matrix
++ :header-rows: 0
++ :stub-columns: 0
++ :widths: 1 1 2
++
++ * - __u8
++ - ``scaling_list_4x4[6][16]``
++ -
++ * - __u8
++ - ``scaling_list_8x8[6][64]``
++ -
++ * - __u8
++ - ``scaling_list_16x16[6][64]``
++ -
++ * - __u8
++ - ``scaling_list_32x32[2][64]``
++ -
++ * - __u8
++ - ``scaling_list_dc_coef_16x16[6]``
++ -
++ * - __u8
++ - ``scaling_list_dc_coef_32x32[2]``
++ -
++
+ ``V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE (enum)``
+ Specifies the decoding mode to use. Currently exposes slice-based and
+ frame-based decoding but new modes might be added later on.
+--- a/Documentation/media/uapi/v4l/pixfmt-compressed.rst
++++ b/Documentation/media/uapi/v4l/pixfmt-compressed.rst
+@@ -203,6 +203,7 @@ Compressed Formats
+ * ``V4L2_CID_MPEG_VIDEO_HEVC_SPS``
+ * ``V4L2_CID_MPEG_VIDEO_HEVC_PPS``
+ * ``V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS``
++ * ``V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX``
+ See the :ref:`associated Codec Control IDs <v4l2-mpeg-hevc>`.
+ Buffers associated with this pixel format must contain the appropriate
+ number of macroblocks to decode a full corresponding frame.
+--- a/drivers/media/v4l2-core/v4l2-ctrls.c
++++ b/drivers/media/v4l2-core/v4l2-ctrls.c
+@@ -974,6 +974,7 @@ const char *v4l2_ctrl_get_name(u32 id)
+ case V4L2_CID_MPEG_VIDEO_HEVC_SPS: return "HEVC Sequence Parameter Set";
+ case V4L2_CID_MPEG_VIDEO_HEVC_PPS: return "HEVC Picture Parameter Set";
+ case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS: return "HEVC Slice Parameters";
++ case V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX: return "HEVC Scaling Matrix";
+ case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE: return "HEVC Decode Mode";
+ case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE: return "HEVC Start Code";
+
+@@ -1406,6 +1407,9 @@ void v4l2_ctrl_fill(u32 id, const char *
+ case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS:
+ *type = V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS;
+ break;
++ case V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX:
++ *type = V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX;
++ break;
+ case V4L2_CID_UNIT_CELL_SIZE:
+ *type = V4L2_CTRL_TYPE_AREA;
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+@@ -1852,6 +1856,9 @@ static int std_validate_compound(const s
+ zero_padding(*p_hevc_slice_params);
+ break;
+
++ case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX:
++ break;
++
+ case V4L2_CTRL_TYPE_AREA:
+ area = p;
+ if (!area->width || !area->height)
+@@ -2540,6 +2547,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s
+ case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
+ elem_size = sizeof(struct v4l2_ctrl_hevc_slice_params);
+ break;
++ case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX:
++ elem_size = sizeof(struct v4l2_ctrl_hevc_scaling_matrix);
++ break;
+ case V4L2_CTRL_TYPE_AREA:
+ elem_size = sizeof(struct v4l2_area);
+ break;
+--- a/include/media/hevc-ctrls.h
++++ b/include/media/hevc-ctrls.h
+@@ -19,6 +19,7 @@
+ #define V4L2_CID_MPEG_VIDEO_HEVC_SPS (V4L2_CID_MPEG_BASE + 1008)
+ #define V4L2_CID_MPEG_VIDEO_HEVC_PPS (V4L2_CID_MPEG_BASE + 1009)
+ #define V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS (V4L2_CID_MPEG_BASE + 1010)
++#define V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX (V4L2_CID_MPEG_BASE + 1011)
+ #define V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE (V4L2_CID_MPEG_BASE + 1015)
+ #define V4L2_CID_MPEG_VIDEO_HEVC_START_CODE (V4L2_CID_MPEG_BASE + 1016)
+
+@@ -26,6 +27,7 @@
+ #define V4L2_CTRL_TYPE_HEVC_SPS 0x0120
+ #define V4L2_CTRL_TYPE_HEVC_PPS 0x0121
+ #define V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS 0x0122
++#define V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX 0x0123
+
+ enum v4l2_mpeg_video_hevc_decode_mode {
+ V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED,
+@@ -209,4 +211,13 @@ struct v4l2_ctrl_hevc_slice_params {
+ __u64 flags;
+ };
+
++struct v4l2_ctrl_hevc_scaling_matrix {
++ __u8 scaling_list_4x4[6][16];
++ __u8 scaling_list_8x8[6][64];
++ __u8 scaling_list_16x16[6][64];
++ __u8 scaling_list_32x32[2][64];
++ __u8 scaling_list_dc_coef_16x16[6];
++ __u8 scaling_list_dc_coef_32x32[2];
++};
++
+ #endif
--- /dev/null
+From 88eb3b015b6f61252fd214d39fc7fc0379ee0442 Mon Sep 17 00:00:00 2001
+From: Jernej Skrabec <jernej.skrabec@siol.net>
+Date: Fri, 13 Dec 2019 17:04:27 +0100
+Subject: [PATCH] media: uapi: hevc: Add segment address field
+
+From https://patchwork.linuxtv.org/patch/60725/
+Changes requested, but mainly docs.
+
+If HEVC frame consists of multiple slices, segment address has to be
+known in order to properly decode it.
+
+Add segment address field to slice parameters.
+
+Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
+---
+ Documentation/media/uapi/v4l/ext-ctrls-codec.rst | 5 ++++-
+ include/media/hevc-ctrls.h | 5 ++++-
+ 2 files changed, 8 insertions(+), 2 deletions(-)
+
+--- a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst
++++ b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst
+@@ -3969,6 +3969,9 @@ enum v4l2_mpeg_video_hevc_size_of_length
+ * - __u32
+ - ``data_bit_offset``
+ - Offset (in bits) to the video data in the current slice data.
++ * - __u32
++ - ``slice_segment_addr``
++ -
+ * - __u8
+ - ``nal_unit_type``
+ -
+@@ -4046,7 +4049,7 @@ enum v4l2_mpeg_video_hevc_size_of_length
+ - ``num_rps_poc_lt_curr``
+ - The number of reference pictures in the long-term set.
+ * - __u8
+- - ``padding[7]``
++ - ``padding[5]``
+ - Applications and drivers must set this to zero.
+ * - struct :c:type:`v4l2_hevc_dpb_entry`
+ - ``dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]``
+--- a/include/media/hevc-ctrls.h
++++ b/include/media/hevc-ctrls.h
+@@ -167,6 +167,9 @@ struct v4l2_ctrl_hevc_slice_params {
+ __u32 bit_size;
+ __u32 data_bit_offset;
+
++ /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
++ __u32 slice_segment_addr;
++
+ /* ISO/IEC 23008-2, ITU-T Rec. H.265: NAL unit header */
+ __u8 nal_unit_type;
+ __u8 nuh_temporal_id_plus1;
+@@ -200,7 +203,7 @@ struct v4l2_ctrl_hevc_slice_params {
+ __u8 num_rps_poc_st_curr_after;
+ __u8 num_rps_poc_lt_curr;
+
+- __u8 padding;
++ __u8 padding[5];
+
+ /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
+ struct v4l2_hevc_dpb_entry dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
--- /dev/null
+From e8355c6b60adb6704c9fb863f380f2d7b457d82c Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 23 Mar 2020 18:34:01 +0000
+Subject: [PATCH] media: hevc_ctrls: Add slice param dependent slice
+ segment
+
+Adds V4L2_HEVC_SLICE_PARAMS_FLAG_DEPENDENT_SLICE_SEGMENT define.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ include/media/hevc-ctrls.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/include/media/hevc-ctrls.h
++++ b/include/media/hevc-ctrls.h
+@@ -162,6 +162,7 @@ struct v4l2_hevc_pred_weight_table {
+ #define V4L2_HEVC_SLICE_PARAMS_FLAG_USE_INTEGER_MV (1ULL << 6)
+ #define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED (1ULL << 7)
+ #define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED (1ULL << 8)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_DEPENDENT_SLICE_SEGMENT (1ULL << 9)
+
+ struct v4l2_ctrl_hevc_slice_params {
+ __u32 bit_size;
--- /dev/null
+From 6a42d17668699234bfa2d459e29cc2732e59759b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 23 Mar 2020 19:00:17 +0000
+Subject: [PATCH] media: uapi: Add hevc ctrls for WPP decoding
+
+WPP can allow greater parallelism within the decode, but needs
+offset information to be passed in.
+
+Adds num_entry_point_offsets and entry_point_offset_minus1 to
+v4l2_ctrl_hevc_slice_params.
+
+This is based on Jernej Skrabec's patches for cedrus which
+implement the same feature.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ include/media/hevc-ctrls.h | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/include/media/hevc-ctrls.h
++++ b/include/media/hevc-ctrls.h
+@@ -170,6 +170,7 @@ struct v4l2_ctrl_hevc_slice_params {
+
+ /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
+ __u32 slice_segment_addr;
++ __u32 num_entry_point_offsets;
+
+ /* ISO/IEC 23008-2, ITU-T Rec. H.265: NAL unit header */
+ __u8 nal_unit_type;
+@@ -204,7 +205,9 @@ struct v4l2_ctrl_hevc_slice_params {
+ __u8 num_rps_poc_st_curr_after;
+ __u8 num_rps_poc_lt_curr;
+
+- __u8 padding[5];
++ __u8 padding;
++
++ __u32 entry_point_offset_minus1[256];
+
+ /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
+ struct v4l2_hevc_dpb_entry dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
--- /dev/null
+From a8f52dad0ed65192eb880a4a1ca90b236e99711e Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 24 Jan 2020 14:28:21 +0000
+Subject: [PATCH] media: videodev2.h: Add a format for column YUV4:2:0
+ modes
+
+Some of the Broadcom codec blocks use a column based YUV4:2:0 image
+format, so add the documentation and defines for both 8 and 10 bit
+versions.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ .../media/uapi/v4l/pixfmt-nv12-col128.rst | 215 ++++++++++++++++++
+ Documentation/media/uapi/v4l/pixfmt-nv12.rst | 14 +-
+ Documentation/media/uapi/v4l/yuv-formats.rst | 1 +
+ drivers/media/v4l2-core/v4l2-ioctl.c | 2 +
+ include/uapi/linux/videodev2.h | 4 +
+ 5 files changed, 233 insertions(+), 3 deletions(-)
+ create mode 100644 Documentation/media/uapi/v4l/pixfmt-nv12-col128.rst
+
+--- /dev/null
++++ b/Documentation/media/uapi/v4l/pixfmt-nv12-col128.rst
+@@ -0,0 +1,215 @@
++.. Permission is granted to copy, distribute and/or modify this
++.. document under the terms of the GNU Free Documentation License,
++.. Version 1.1 or any later version published by the Free Software
++.. Foundation, with no Invariant Sections, no Front-Cover Texts
++.. and no Back-Cover Texts. A copy of the license is included at
++.. Documentation/media/uapi/fdl-appendix.rst.
++..
++.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections
++
++.. _V4L2_PIX_FMT_NV12_COL128:
++.. _V4L2_PIX_FMT_NV12_10_COL128:
++
++********************************************************************************
++V4L2_PIX_FMT_NV12_COL128, V4L2_PIX_FMT_NV12_10_COL128
++********************************************************************************
++
++
++V4L2_PIX_FMT_NV21_COL128
++Formats with ½ horizontal and vertical chroma resolution. This format
++has two planes - one for luminance and one for chrominance. Chroma
++samples are interleaved. The difference to ``V4L2_PIX_FMT_NV12`` is the
++memory layout. The image is split into columns of 128 bytes wide rather than
++being in raster order.
++
++V4L2_PIX_FMT_NV12_10_COL128
++Follows the same pattern as ``V4L2_PIX_FMT_NV21_COL128`` with 128 byte, but is
++a 10bit format with 3 10-bit samples being packed into 4 bytes. Each 128 byte
++wide column therefore contains 96 samples.
++
++
++Description
++===========
++
++This is the two-plane versions of the YUV 4:2:0 format where data is
++grouped into 128 byte wide columns. The three components are separated into
++two sub-images or planes. The Y plane has one byte per pixel and pixels
++are grouped into 128 byte wide columns. The CbCr plane has the same width,
++in bytes, as the Y plane (and the image), but is half as tall in pixels.
++The chroma plane is also in 128 byte columns, reflecting 64 Cb and 64 Cr
++samples.
++
++The chroma samples for a column follow the luma samples. If there is any
++paddding, then that will be reflected via the selection API.
++The luma height must be a multiple of 2 lines.
++
++The normal bytesperline is effectively fixed at 128. However the format
++requires knowledge of the stride between columns, therefore the bytesperline
++value has been repurposed to denote the number of 128 byte long lines between
++the start of each column.
++
++**Byte Order.**
++
++
++.. flat-table::
++ :header-rows: 0
++ :stub-columns: 0
++ :widths: 12 12 12 12 12 4 12 12 12 12
++
++ * - start + 0:
++ - Y'\ :sub:`0,0`
++ - Y'\ :sub:`0,1`
++ - Y'\ :sub:`0,2`
++ - Y'\ :sub:`0,3`
++ - ...
++ - Y'\ :sub:`0,124`
++ - Y'\ :sub:`0,125`
++ - Y'\ :sub:`0,126`
++ - Y'\ :sub:`0,127`
++ * - start + 128:
++ - Y'\ :sub:`1,0`
++ - Y'\ :sub:`1,1`
++ - Y'\ :sub:`1,2`
++ - Y'\ :sub:`1,3`
++ - ...
++ - Y'\ :sub:`1,124`
++ - Y'\ :sub:`1,125`
++ - Y'\ :sub:`1,126`
++ - Y'\ :sub:`1,127`
++ * - start + 256:
++ - Y'\ :sub:`2,0`
++ - Y'\ :sub:`2,1`
++ - Y'\ :sub:`2,2`
++ - Y'\ :sub:`2,3`
++ - ...
++ - Y'\ :sub:`2,124`
++ - Y'\ :sub:`2,125`
++ - Y'\ :sub:`2,126`
++ - Y'\ :sub:`2,127`
++ * - ...
++ - ...
++ - ...
++ - ...
++ - ...
++ - ...
++ - ...
++ - ...
++ * - start + ((height-1) * 128):
++ - Y'\ :sub:`height-1,0`
++ - Y'\ :sub:`height-1,1`
++ - Y'\ :sub:`height-1,2`
++ - Y'\ :sub:`height-1,3`
++ - ...
++ - Y'\ :sub:`height-1,124`
++ - Y'\ :sub:`height-1,125`
++ - Y'\ :sub:`height-1,126`
++ - Y'\ :sub:`height-1,127`
++ * - start + ((height) * 128):
++ - Cb\ :sub:`0,0`
++ - Cr\ :sub:`0,0`
++ - Cb\ :sub:`0,1`
++ - Cr\ :sub:`0,1`
++ - ...
++ - Cb\ :sub:`0,62`
++ - Cr\ :sub:`0,62`
++ - Cb\ :sub:`0,63`
++ - Cr\ :sub:`0,63`
++ * - start + ((height+1) * 128):
++ - Cb\ :sub:`1,0`
++ - Cr\ :sub:`1,0`
++ - Cb\ :sub:`1,1`
++ - Cr\ :sub:`1,1`
++ - ...
++ - Cb\ :sub:`1,62`
++ - Cr\ :sub:`1,62`
++ - Cb\ :sub:`1,63`
++ - Cr\ :sub:`1,63`
++ * - ...
++ - ...
++ - ...
++ - ...
++ - ...
++ - ...
++ - ...
++ - ...
++ * - start + ((height+(height/2)-1) * 128):
++ - Cb\ :sub:`(height/2)-1,0`
++ - Cr\ :sub:`(height/2)-1,0`
++ - Cb\ :sub:`(height/2)-1,1`
++ - Cr\ :sub:`(height/2)-1,1`
++ - ...
++ - Cb\ :sub:`(height/2)-1,62`
++ - Cr\ :sub:`(height/2)-1,62`
++ - Cb\ :sub:`(height/2)-1,63`
++ - Cr\ :sub:`(height/2)-1,63`
++ * - start + (bytesperline * 128):
++ - Y'\ :sub:`0,128`
++ - Y'\ :sub:`0,129`
++ - Y'\ :sub:`0,130`
++ - Y'\ :sub:`0,131`
++ - ...
++ - Y'\ :sub:`0,252`
++ - Y'\ :sub:`0,253`
++ - Y'\ :sub:`0,254`
++ - Y'\ :sub:`0,255`
++ * - ...
++ - ...
++ - ...
++ - ...
++ - ...
++ - ...
++ - ...
++ - ...
++
++V4L2_PIX_FMT_NV12_10_COL128 uses the same 128 byte column structure, but
++encodes 10-bit YUV.
++3 10-bit values are packed into 4 bytes as bits 9:0, 19:10, and 29:20, with
++bits 30 & 31 unused. For the luma plane, bits 9:0 are Y0, 19:10 are Y1, and
++29:20 are Y2. For the chroma plane the samples always come in pairs of Cr
++and Cb, so it needs to be considered 6 values packed in 8 bytes.
++
++Bit-packed representation.
++
++.. raw:: latex
++
++ \small
++
++.. tabularcolumns:: |p{1.2cm}||p{1.2cm}||p{1.2cm}||p{1.2cm}|p{3.2cm}|p{3.2cm}|
++
++.. flat-table::
++ :header-rows: 0
++ :stub-columns: 0
++ :widths: 8 8 8 8
++
++ * - Y'\ :sub:`00[7:0]`
++ - Y'\ :sub:`01[5:0] (bits 7--2)` Y'\ :sub:`00[9:8]`\ (bits 1--0)
++ - Y'\ :sub:`02[3:0] (bits 7--4)` Y'\ :sub:`01[9:6]`\ (bits 3--0)
++ - unused (bits 7--6)` Y'\ :sub:`02[9:4]`\ (bits 5--0)
++
++.. raw:: latex
++
++ \small
++
++.. tabularcolumns:: |p{1.2cm}||p{1.2cm}||p{1.2cm}||p{1.2cm}|p{3.2cm}|p{3.2cm}|
++
++.. flat-table::
++ :header-rows: 0
++ :stub-columns: 0
++ :widths: 12 12 12 12 12 12 12 12
++
++ * - Cb\ :sub:`00[7:0]`
++ - Cr\ :sub:`00[5:0]`\ (bits 7--2) Cb\ :sub:`00[9:8]`\ (bits 1--0)
++ - Cb\ :sub:`01[3:0]`\ (bits 7--4) Cr\ :sub:`00[9:6]`\ (bits 3--0)
++ - unused (bits 7--6) Cb\ :sub:`02[9:4]`\ (bits 5--0)
++ - Cr\ :sub:`01[7:0]`
++ - Cb\ :sub:`02[5:0]`\ (bits 7--2) Cr\ :sub:`01[9:8]`\ (bits 1--0)
++ - Cr\ :sub:`02[3:0]`\ (bits 7--4) Cb\ :sub:`02[9:6]`\ (bits 3--0)
++ - unused (bits 7--6) Cr\ :sub:`02[9:4]`\ (bits 5--0)
++
++.. raw:: latex
++
++ \normalsize
++
++
++
++
+--- a/Documentation/media/uapi/v4l/pixfmt-nv12.rst
++++ b/Documentation/media/uapi/v4l/pixfmt-nv12.rst
+@@ -10,9 +10,9 @@
+ .. _V4L2-PIX-FMT-NV12:
+ .. _V4L2-PIX-FMT-NV21:
+
+-******************************************************
+-V4L2_PIX_FMT_NV12 ('NV12'), V4L2_PIX_FMT_NV21 ('NV21')
+-******************************************************
++********************************************************************************
++V4L2_PIX_FMT_NV12 ('NV12'), V4L2_PIX_FMT_NV21 ('NV21'), V4L2_PIX_FMT_NV12_COL128
++********************************************************************************
+
+
+ V4L2_PIX_FMT_NV21
+@@ -38,6 +38,14 @@ with a Cr byte.
+ If the Y plane has pad bytes after each row, then the CbCr plane has as
+ many pad bytes after its rows.
+
++``V4L2_PIX_FMT_NV12_COL128`` is the tiled version of
++``V4L2_PIX_FMT_NV12`` with the image broken down into 128 pixel wide columns of
++Y followed by the associated combined CbCr plane.
++The normal bytesperline is effectively fixed at 128. However the format
++requires knowledge of the stride between columns, therefore the bytesperline
++value has been repurposed to denote the number of 128 byte long lines between
++the start of each column.
++
+ **Byte Order.**
+ Each cell is one byte.
+
+--- a/Documentation/media/uapi/v4l/yuv-formats.rst
++++ b/Documentation/media/uapi/v4l/yuv-formats.rst
+@@ -57,6 +57,7 @@ to brightness information.
+ pixfmt-nv12
+ pixfmt-nv12m
+ pixfmt-nv12mt
++ pixfmt-nv12-col128
+ pixfmt-nv16
+ pixfmt-nv16m
+ pixfmt-nv24
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1258,6 +1258,8 @@ static void v4l_fill_fmtdesc(struct v4l2
+ case V4L2_PIX_FMT_NV61M: descr = "Y/CrCb 4:2:2 (N-C)"; break;
+ case V4L2_PIX_FMT_NV12MT: descr = "Y/CbCr 4:2:0 (64x32 MB, N-C)"; break;
+ case V4L2_PIX_FMT_NV12MT_16X16: descr = "Y/CbCr 4:2:0 (16x16 MB, N-C)"; break;
++ case V4L2_PIX_FMT_NV12_COL128: descr = "Y/CbCr 4:2:0 (128b cols)"; break;
++ case V4L2_PIX_FMT_NV12_10_COL128: descr = "10-bit Y/CbCr 4:2:0 (128b cols)"; break;
+ case V4L2_PIX_FMT_YUV420M: descr = "Planar YUV 4:2:0 (N-C)"; break;
+ case V4L2_PIX_FMT_YVU420M: descr = "Planar YVU 4:2:0 (N-C)"; break;
+ case V4L2_PIX_FMT_YUV422M: descr = "Planar YUV 4:2:2 (N-C)"; break;
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -737,6 +737,10 @@ struct v4l2_pix_format {
+ #define V4L2_PIX_FMT_INZI v4l2_fourcc('I', 'N', 'Z', 'I') /* Intel Planar Greyscale 10-bit and Depth 16-bit */
+ #define V4L2_PIX_FMT_SUNXI_TILED_NV12 v4l2_fourcc('S', 'T', '1', '2') /* Sunxi Tiled NV12 Format */
+ #define V4L2_PIX_FMT_CNF4 v4l2_fourcc('C', 'N', 'F', '4') /* Intel 4-bit packed depth confidence information */
++#define V4L2_PIX_FMT_NV12_COL128 v4l2_fourcc('N', 'C', '1', '2') /* 12 Y/CbCr 4:2:0 128 pixel wide column */
++#define V4L2_PIX_FMT_NV12_10_COL128 v4l2_fourcc('N', 'C', '3', '0')
++ /* Y/CbCr 4:2:0 10bpc, 3x10 packed as 4 bytes in
++ * a 128 bytes / 96 pixel wide column */
+
+ /* 10bit raw bayer packed, 32 bytes for every 25 pixels, last LSB 6 bits unused */
+ #define V4L2_PIX_FMT_IPU3_SBGGR10 v4l2_fourcc('i', 'p', '3', 'b') /* IPU3 packed 10-bit BGGR bayer */
--- /dev/null
+From b8ae9d55d468a9f55524296247dba93531c29c99 Mon Sep 17 00:00:00 2001
+From: John Cox <jc@kynesim.co.uk>
+Date: Thu, 5 Mar 2020 14:46:54 +0000
+Subject: [PATCH] media: v4l2-mem2mem: allow request job buffer
+ processing after job finish
+
+Allow the capture buffer to be detached from a v4l2 request job such
+that another job can start before the capture buffer is returned. This
+allows h/w codecs that can process multiple requests at the same time
+to operate more efficiently.
+
+Signed-off-by: John Cox <jc@kynesim.co.uk>
+---
+ drivers/media/v4l2-core/v4l2-mem2mem.c | 105 +++++++++++++++++++++++--
+ include/media/v4l2-mem2mem.h | 47 +++++++++++
+ include/media/videobuf2-v4l2.h | 3 +
+ 3 files changed, 149 insertions(+), 6 deletions(-)
+
+--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
++++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
+@@ -399,15 +399,18 @@ static void v4l2_m2m_cancel_job(struct v
+ {
+ struct v4l2_m2m_dev *m2m_dev;
+ unsigned long flags;
++ bool det_abort_req;
+
+ m2m_dev = m2m_ctx->m2m_dev;
+ spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
+
++ det_abort_req = !list_empty(&m2m_ctx->det_list);
+ m2m_ctx->job_flags |= TRANS_ABORT;
+ if (m2m_ctx->job_flags & TRANS_RUNNING) {
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+ if (m2m_dev->m2m_ops->job_abort)
+ m2m_dev->m2m_ops->job_abort(m2m_ctx->priv);
++ det_abort_req = false;
+ dprintk("m2m_ctx %p running, will wait to complete\n", m2m_ctx);
+ wait_event(m2m_ctx->finished,
+ !(m2m_ctx->job_flags & TRANS_RUNNING));
+@@ -421,6 +424,11 @@ static void v4l2_m2m_cancel_job(struct v
+ /* Do nothing, was not on queue/running */
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+ }
++
++ /* Wait for detached buffers to come back too */
++ if (det_abort_req && m2m_dev->m2m_ops->job_abort)
++ m2m_dev->m2m_ops->job_abort(m2m_ctx->priv);
++ wait_event(m2m_ctx->det_empty, list_empty(&m2m_ctx->det_list));
+ }
+
+ /*
+@@ -458,6 +466,7 @@ static bool _v4l2_m2m_job_finish(struct
+
+ list_del(&m2m_dev->curr_ctx->queue);
+ m2m_dev->curr_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING);
++ m2m_ctx->cap_detached = false;
+ wake_up(&m2m_dev->curr_ctx->finished);
+ m2m_dev->curr_ctx = NULL;
+ return true;
+@@ -485,6 +494,80 @@ void v4l2_m2m_job_finish(struct v4l2_m2m
+ }
+ EXPORT_SYMBOL(v4l2_m2m_job_finish);
+
++struct vb2_v4l2_buffer *_v4l2_m2m_cap_buf_detach(struct v4l2_m2m_ctx *m2m_ctx)
++{
++ struct vb2_v4l2_buffer *buf;
++
++ buf = v4l2_m2m_dst_buf_remove(m2m_ctx);
++ list_add_tail(&container_of(buf, struct v4l2_m2m_buffer, vb)->list,
++ &m2m_ctx->det_list);
++ m2m_ctx->cap_detached = true;
++ buf->is_held = true;
++ buf->det_state = VB2_BUF_STATE_ACTIVE;
++
++ return buf;
++}
++
++struct vb2_v4l2_buffer *v4l2_m2m_cap_buf_detach(struct v4l2_m2m_dev *m2m_dev,
++ struct v4l2_m2m_ctx *m2m_ctx)
++{
++ unsigned long flags;
++ struct vb2_v4l2_buffer *src_buf, *dst_buf;
++
++ spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
++
++ dst_buf = NULL;
++ src_buf = v4l2_m2m_next_src_buf(m2m_ctx);
++
++ if (!(src_buf->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF) &&
++ !m2m_ctx->cap_detached)
++ dst_buf = _v4l2_m2m_cap_buf_detach(m2m_ctx);
++
++ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
++ return dst_buf;
++}
++EXPORT_SYMBOL(v4l2_m2m_cap_buf_detach);
++
++static void _v4l2_m2m_cap_buf_return(struct v4l2_m2m_ctx *m2m_ctx,
++ struct vb2_v4l2_buffer *buf,
++ enum vb2_buffer_state state)
++{
++ buf->det_state = state;
++
++ /*
++ * Always signal done in the order we got stuff
++ * Stop if we find a buf that is still in use
++ */
++ while (!list_empty(&m2m_ctx->det_list)) {
++ buf = &list_first_entry(&m2m_ctx->det_list,
++ struct v4l2_m2m_buffer, list)->vb;
++ state = buf->det_state;
++ if (state != VB2_BUF_STATE_DONE &&
++ state != VB2_BUF_STATE_ERROR)
++ return;
++ list_del(&container_of(buf, struct v4l2_m2m_buffer, vb)->list);
++ buf->det_state = VB2_BUF_STATE_DEQUEUED;
++ v4l2_m2m_buf_done(buf, state);
++ }
++ wake_up(&m2m_ctx->det_empty);
++}
++
++void v4l2_m2m_cap_buf_return(struct v4l2_m2m_dev *m2m_dev,
++ struct v4l2_m2m_ctx *m2m_ctx,
++ struct vb2_v4l2_buffer *buf,
++ enum vb2_buffer_state state)
++{
++ unsigned long flags;
++
++ if (!buf)
++ return;
++
++ spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
++ _v4l2_m2m_cap_buf_return(m2m_ctx, buf, state);
++ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
++}
++EXPORT_SYMBOL(v4l2_m2m_cap_buf_return);
++
+ void v4l2_m2m_buf_done_and_job_finish(struct v4l2_m2m_dev *m2m_dev,
+ struct v4l2_m2m_ctx *m2m_ctx,
+ enum vb2_buffer_state state)
+@@ -495,15 +578,23 @@ void v4l2_m2m_buf_done_and_job_finish(st
+
+ spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
+ src_buf = v4l2_m2m_src_buf_remove(m2m_ctx);
+- dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx);
+
+- if (WARN_ON(!src_buf || !dst_buf))
++ if (WARN_ON(!src_buf))
+ goto unlock;
+ v4l2_m2m_buf_done(src_buf, state);
+- dst_buf->is_held = src_buf->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF;
+- if (!dst_buf->is_held) {
+- v4l2_m2m_dst_buf_remove(m2m_ctx);
+- v4l2_m2m_buf_done(dst_buf, state);
++
++ if (!m2m_ctx->cap_detached) {
++ dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx);
++ if (WARN_ON(!dst_buf))
++ goto unlock;
++
++ dst_buf->is_held = src_buf->flags
++ & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF;
++
++ if (!dst_buf->is_held) {
++ dst_buf = _v4l2_m2m_cap_buf_detach(m2m_ctx);
++ _v4l2_m2m_cap_buf_return(m2m_ctx, dst_buf, state);
++ }
+ }
+ schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx);
+ unlock:
+@@ -983,12 +1074,14 @@ struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(s
+ m2m_ctx->priv = drv_priv;
+ m2m_ctx->m2m_dev = m2m_dev;
+ init_waitqueue_head(&m2m_ctx->finished);
++ init_waitqueue_head(&m2m_ctx->det_empty);
+
+ out_q_ctx = &m2m_ctx->out_q_ctx;
+ cap_q_ctx = &m2m_ctx->cap_q_ctx;
+
+ INIT_LIST_HEAD(&out_q_ctx->rdy_queue);
+ INIT_LIST_HEAD(&cap_q_ctx->rdy_queue);
++ INIT_LIST_HEAD(&m2m_ctx->det_list);
+ spin_lock_init(&out_q_ctx->rdy_spinlock);
+ spin_lock_init(&cap_q_ctx->rdy_spinlock);
+
+--- a/include/media/v4l2-mem2mem.h
++++ b/include/media/v4l2-mem2mem.h
+@@ -88,6 +88,9 @@ struct v4l2_m2m_queue_ctx {
+ * %TRANS_QUEUED, %TRANS_RUNNING and %TRANS_ABORT.
+ * @finished: Wait queue used to signalize when a job queue finished.
+ * @priv: Instance private data
++ * @cap_detached: Current job's capture buffer has been detached
++ * @det_list: List of detached (post-job but still in flight) capture buffers
++ * @det_empty: Wait queue signalled when det_list goes empty
+ *
+ * The memory to memory context is specific to a file handle, NOT to e.g.
+ * a device.
+@@ -111,6 +114,11 @@ struct v4l2_m2m_ctx {
+ wait_queue_head_t finished;
+
+ void *priv;
++
++ /* Detached buffer handling */
++ bool cap_detached;
++ struct list_head det_list;
++ wait_queue_head_t det_empty;
+ };
+
+ /**
+@@ -216,6 +224,45 @@ v4l2_m2m_buf_done(struct vb2_v4l2_buffer
+ }
+
+ /**
++ * v4l2_m2m_cap_buf_detach() - detach the capture buffer from the job and
++ * return it.
++ *
++ * @m2m_dev: opaque pointer to the internal data to handle M2M context
++ * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx
++ *
++ * This function is designed to be used in conjunction with
++ * v4l2_m2m_buf_done_and_job_finish(). It allows the next job to start
++ * execution before the capture buffer is returned to the user which can be
++ * important if the underlying processing has multiple phases that are more
++ * efficiently executed in parallel.
++ *
++ * If used then it must be called before v4l2_m2m_buf_done_and_job_finish()
++ * as otherwise the buffer will have already gone.
++ *
++ * It is the callers reponsibilty to ensure that all detached buffers are
++ * returned.
++ */
++struct vb2_v4l2_buffer *v4l2_m2m_cap_buf_detach(struct v4l2_m2m_dev *m2m_dev,
++ struct v4l2_m2m_ctx *m2m_ctx);
++
++/**
++ * v4l2_m2m_cap_buf_return() - return a capture buffer, previously detached
++ * with v4l2_m2m_cap_buf_detach() to the user.
++ *
++ * @m2m_dev: opaque pointer to the internal data to handle M2M context
++ * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx
++ * @buf: the buffer to return
++ * @state: vb2 buffer state passed to v4l2_m2m_buf_done().
++ *
++ * Buffers returned by this function will be returned to the user in the order
++ * of the original jobs rather than the order in which this function is called.
++ */
++void v4l2_m2m_cap_buf_return(struct v4l2_m2m_dev *m2m_dev,
++ struct v4l2_m2m_ctx *m2m_ctx,
++ struct vb2_v4l2_buffer *buf,
++ enum vb2_buffer_state state);
++
++/**
+ * v4l2_m2m_reqbufs() - multi-queue-aware REQBUFS multiplexer
+ *
+ * @file: pointer to struct &file
+--- a/include/media/videobuf2-v4l2.h
++++ b/include/media/videobuf2-v4l2.h
+@@ -35,6 +35,8 @@
+ * @request_fd: the request_fd associated with this buffer
+ * @is_held: if true, then this capture buffer was held
+ * @planes: plane information (userptr/fd, length, bytesused, data_offset).
++ * @det_state: if a detached request capture buffer then this contains its
++ * current state
+ *
+ * Should contain enough information to be able to cover all the fields
+ * of &struct v4l2_buffer at ``videodev2.h``.
+@@ -49,6 +51,7 @@ struct vb2_v4l2_buffer {
+ __s32 request_fd;
+ bool is_held;
+ struct vb2_plane planes[VB2_MAX_PLANES];
++ enum vb2_buffer_state det_state;
+ };
+
+ /* VB2 V4L2 flags as set in vb2_queue.subsystem_flags */
--- /dev/null
+From 15b4e8fa2d5101b989856c42cdae6ec764c99db0 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 17 Mar 2020 10:53:16 +0000
+Subject: [PATCH] media: dt-bindings: media: Add binding for the
+ Raspberry PI HEVC decoder
+
+Adds a binding for the HEVC decoder found on the BCM2711 / Raspberry Pi 4.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ .../bindings/media/rpivid_hevc.yaml | 72 +++++++++++++++++++
+ MAINTAINERS | 7 ++
+ 2 files changed, 79 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/media/rpivid_hevc.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/media/rpivid_hevc.yaml
+@@ -0,0 +1,72 @@
++# SPDX-License-Identifier: GPL-2.0-only
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/media/rpivid_hevc.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Raspberry Pi HEVC Decoder
++
++maintainers:
++ - Raspberry Pi <kernel-list@raspberrypi.com>
++
++description: |-
++ The Camera Adaptation Layer (CAL) is a key component for image capture
++ applications. The capture module provides the system interface and the
++ processing capability to connect CSI2 image-sensor modules to the
++ DRA72x device.
++
++properties:
++ compatible:
++ enum:
++ - raspberrypi,rpivid-vid-decoder
++
++ reg:
++ minItems: 2
++ items:
++ - description: The HEVC main register region
++ - description: The Interrupt controller register region
++
++ reg-names:
++ minItems: 2
++ items:
++ - const: hevc
++ - const: intc
++
++ interrupts:
++ maxItems: 1
++
++ clocks:
++ items:
++ - description: The HEVC block clock
++
++ clock-names:
++ items:
++ - const: hevc
++
++required:
++ - compatible
++ - reg
++ - reg-names
++ - interrupts
++ - clocks
++
++additionalProperties: false
++
++examples:
++ - |
++ #include <dt-bindings/interrupt-controller/arm-gic.h>
++
++ video-codec@7eb10000 {
++ compatible = "raspberrypi,rpivid-vid-decoder";
++ reg = <0x0 0x7eb10000 0x1000>, /* INTC */
++ <0x0 0x7eb00000 0x10000>; /* HEVC */
++ reg-names = "intc",
++ "hevc";
++
++ interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
++
++ clocks = <&clk 0>;
++ clock-names = "hevc";
++ };
++
++...
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -3198,6 +3198,13 @@ N: bcm2711
+ N: bcm2835
+ F: drivers/staging/vc04_services
+
++BROADCOM BCM2711 HEVC DECODER
++M: Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
++L: linux-media@vger.kernel.org
++S: Maintained
++F: Documentation/devicetree/bindings/media/rpivid_hevc.jaml
++F: drivers/staging/media/rpivid
++
+ BROADCOM BCM2835 CAMERA DRIVER
+ M: Dave Stevenson <dave.stevenson@raspberrypi.org>
+ L: linux-media@vger.kernel.org
--- /dev/null
+From 82bbd353e2dc364bf37e6f0b91890cb432b1a72f Mon Sep 17 00:00:00 2001
+From: John Cox <jc@kynesim.co.uk>
+Date: Thu, 5 Mar 2020 18:30:41 +0000
+Subject: [PATCH] staging: media: Add Raspberry Pi V4L2 H265 decoder
+
+This driver is for the HEVC/H265 decoder block on the Raspberry
+Pi 4, and conforms to the V4L2 stateless decoder API.
+
+Signed-off-by: John Cox <jc@kynesim.co.uk>
+---
+ drivers/staging/media/Kconfig | 2 +
+ drivers/staging/media/Makefile | 1 +
+ drivers/staging/media/rpivid/Kconfig | 16 +
+ drivers/staging/media/rpivid/Makefile | 5 +
+ drivers/staging/media/rpivid/rpivid.c | 432 ++++
+ drivers/staging/media/rpivid/rpivid.h | 181 ++
+ drivers/staging/media/rpivid/rpivid_dec.c | 79 +
+ drivers/staging/media/rpivid/rpivid_dec.h | 19 +
+ drivers/staging/media/rpivid/rpivid_h265.c | 2275 +++++++++++++++++++
+ drivers/staging/media/rpivid/rpivid_hw.c | 321 +++
+ drivers/staging/media/rpivid/rpivid_hw.h | 300 +++
+ drivers/staging/media/rpivid/rpivid_video.c | 593 +++++
+ drivers/staging/media/rpivid/rpivid_video.h | 30 +
+ 14 files changed, 4256 insertions(+)
+ create mode 100644 drivers/staging/media/rpivid/Kconfig
+ create mode 100644 drivers/staging/media/rpivid/Makefile
+ create mode 100644 drivers/staging/media/rpivid/rpivid.c
+ create mode 100644 drivers/staging/media/rpivid/rpivid.h
+ create mode 100644 drivers/staging/media/rpivid/rpivid_dec.c
+ create mode 100644 drivers/staging/media/rpivid/rpivid_dec.h
+ create mode 100644 drivers/staging/media/rpivid/rpivid_h265.c
+ create mode 100644 drivers/staging/media/rpivid/rpivid_hw.c
+ create mode 100644 drivers/staging/media/rpivid/rpivid_hw.h
+ create mode 100644 drivers/staging/media/rpivid/rpivid_video.c
+ create mode 100644 drivers/staging/media/rpivid/rpivid_video.h
+
+--- a/drivers/staging/media/Kconfig
++++ b/drivers/staging/media/Kconfig
+@@ -30,6 +30,8 @@ source "drivers/staging/media/meson/vdec
+
+ source "drivers/staging/media/omap4iss/Kconfig"
+
++source "drivers/staging/media/rpivid/Kconfig"
++
+ source "drivers/staging/media/sunxi/Kconfig"
+
+ source "drivers/staging/media/tegra-vde/Kconfig"
+--- a/drivers/staging/media/Makefile
++++ b/drivers/staging/media/Makefile
+@@ -3,6 +3,7 @@ obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += alleg
+ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/
+ obj-$(CONFIG_VIDEO_MESON_VDEC) += meson/vdec/
+ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/
++obj-$(CONFIG_VIDEO_RPIVID) += rpivid/
+ obj-$(CONFIG_VIDEO_SUNXI) += sunxi/
+ obj-$(CONFIG_TEGRA_VDE) += tegra-vde/
+ obj-$(CONFIG_VIDEO_HANTRO) += hantro/
+--- /dev/null
++++ b/drivers/staging/media/rpivid/Kconfig
+@@ -0,0 +1,16 @@
++# SPDX-License-Identifier: GPL-2.0
++
++config VIDEO_RPIVID
++ tristate "Rpi H265 driver"
++ depends on VIDEO_DEV && VIDEO_V4L2
++ depends on MEDIA_CONTROLLER
++ depends on OF
++ depends on MEDIA_CONTROLLER_REQUEST_API
++ select VIDEOBUF2_DMA_CONTIG
++ select V4L2_MEM2MEM_DEV
++ help
++ Support for the Rpi H265 h/w decoder.
++
++ To compile this driver as a module, choose M here: the module
++ will be called rpivid-hevc.
++
+--- /dev/null
++++ b/drivers/staging/media/rpivid/Makefile
+@@ -0,0 +1,5 @@
++# SPDX-License-Identifier: GPL-2.0
++obj-$(CONFIG_VIDEO_RPIVID) += rpivid-hevc.o
++
++rpivid-hevc-y = rpivid.o rpivid_video.o rpivid_dec.o \
++ rpivid_hw.o rpivid_h265.o
+--- /dev/null
++++ b/drivers/staging/media/rpivid/rpivid.c
+@@ -0,0 +1,432 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Raspberry Pi HEVC driver
++ *
++ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
++ *
++ * Based on the Cedrus VPU driver, that is:
++ *
++ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
++ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
++ * Copyright (C) 2018 Bootlin
++ */
++
++#include <linux/platform_device.h>
++#include <linux/module.h>
++#include <linux/of.h>
++
++#include <media/v4l2-device.h>
++#include <media/v4l2-ioctl.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-mem2mem.h>
++
++#include "rpivid.h"
++#include "rpivid_video.h"
++#include "rpivid_hw.h"
++#include "rpivid_dec.h"
++
++/*
++ * Default /dev/videoN node number.
++ * Deliberately avoid the very low numbers as these are often taken by webcams
++ * etc, and simple apps tend to only go for /dev/video0.
++ */
++static int video_nr = 19;
++module_param(video_nr, int, 0644);
++MODULE_PARM_DESC(video_nr, "decoder video device number");
++
++static const struct rpivid_control rpivid_ctrls[] = {
++ {
++ .cfg = {
++ .id = V4L2_CID_MPEG_VIDEO_HEVC_SPS,
++ },
++ .required = true,
++ },
++ {
++ .cfg = {
++ .id = V4L2_CID_MPEG_VIDEO_HEVC_PPS,
++ },
++ .required = true,
++ },
++ {
++ .cfg = {
++ .id = V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX,
++ },
++ .required = false,
++ },
++ {
++ .cfg = {
++ .id = V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS,
++ },
++ .required = true,
++ },
++ {
++ .cfg = {
++ .id = V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE,
++ .max = V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED,
++ .def = V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED,
++ },
++ .required = false,
++ },
++ {
++ .cfg = {
++ .id = V4L2_CID_MPEG_VIDEO_HEVC_START_CODE,
++ .max = V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE,
++ .def = V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE,
++ },
++ .required = false,
++ },
++};
++
++#define rpivid_ctrls_COUNT ARRAY_SIZE(rpivid_ctrls)
++
++void *rpivid_find_control_data(struct rpivid_ctx *ctx, u32 id)
++{
++ unsigned int i;
++
++ for (i = 0; ctx->ctrls[i]; i++)
++ if (ctx->ctrls[i]->id == id)
++ return ctx->ctrls[i]->p_cur.p;
++
++ return NULL;
++}
++
++static int rpivid_init_ctrls(struct rpivid_dev *dev, struct rpivid_ctx *ctx)
++{
++ struct v4l2_ctrl_handler *hdl = &ctx->hdl;
++ struct v4l2_ctrl *ctrl;
++ unsigned int ctrl_size;
++ unsigned int i;
++
++ v4l2_ctrl_handler_init(hdl, rpivid_ctrls_COUNT);
++ if (hdl->error) {
++ v4l2_err(&dev->v4l2_dev,
++ "Failed to initialize control handler\n");
++ return hdl->error;
++ }
++
++ ctrl_size = sizeof(ctrl) * rpivid_ctrls_COUNT + 1;
++
++ ctx->ctrls = kzalloc(ctrl_size, GFP_KERNEL);
++ if (!ctx->ctrls)
++ return -ENOMEM;
++
++ for (i = 0; i < rpivid_ctrls_COUNT; i++) {
++ ctrl = v4l2_ctrl_new_custom(hdl, &rpivid_ctrls[i].cfg,
++ NULL);
++ if (hdl->error) {
++ v4l2_err(&dev->v4l2_dev,
++ "Failed to create new custom control id=%#x\n",
++ rpivid_ctrls[i].cfg.id);
++
++ v4l2_ctrl_handler_free(hdl);
++ kfree(ctx->ctrls);
++ return hdl->error;
++ }
++
++ ctx->ctrls[i] = ctrl;
++ }
++
++ ctx->fh.ctrl_handler = hdl;
++ v4l2_ctrl_handler_setup(hdl);
++
++ return 0;
++}
++
++static int rpivid_request_validate(struct media_request *req)
++{
++ struct media_request_object *obj;
++ struct v4l2_ctrl_handler *parent_hdl, *hdl;
++ struct rpivid_ctx *ctx = NULL;
++ struct v4l2_ctrl *ctrl_test;
++ unsigned int count;
++ unsigned int i;
++
++ list_for_each_entry(obj, &req->objects, list) {
++ struct vb2_buffer *vb;
++
++ if (vb2_request_object_is_buffer(obj)) {
++ vb = container_of(obj, struct vb2_buffer, req_obj);
++ ctx = vb2_get_drv_priv(vb->vb2_queue);
++
++ break;
++ }
++ }
++
++ if (!ctx)
++ return -ENOENT;
++
++ count = vb2_request_buffer_cnt(req);
++ if (!count) {
++ v4l2_info(&ctx->dev->v4l2_dev,
++ "No buffer was provided with the request\n");
++ return -ENOENT;
++ } else if (count > 1) {
++ v4l2_info(&ctx->dev->v4l2_dev,
++ "More than one buffer was provided with the request\n");
++ return -EINVAL;
++ }
++
++ parent_hdl = &ctx->hdl;
++
++ hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl);
++ if (!hdl) {
++ v4l2_info(&ctx->dev->v4l2_dev, "Missing codec control(s)\n");
++ return -ENOENT;
++ }
++
++ for (i = 0; i < rpivid_ctrls_COUNT; i++) {
++ if (!rpivid_ctrls[i].required)
++ continue;
++
++ ctrl_test =
++ v4l2_ctrl_request_hdl_ctrl_find(hdl,
++ rpivid_ctrls[i].cfg.id);
++ if (!ctrl_test) {
++ v4l2_info(&ctx->dev->v4l2_dev,
++ "Missing required codec control\n");
++ return -ENOENT;
++ }
++ }
++
++ v4l2_ctrl_request_hdl_put(hdl);
++
++ return vb2_request_validate(req);
++}
++
++static int rpivid_open(struct file *file)
++{
++ struct rpivid_dev *dev = video_drvdata(file);
++ struct rpivid_ctx *ctx = NULL;
++ int ret;
++
++ if (mutex_lock_interruptible(&dev->dev_mutex))
++ return -ERESTARTSYS;
++
++ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
++ if (!ctx) {
++ mutex_unlock(&dev->dev_mutex);
++ return -ENOMEM;
++ }
++
++ v4l2_fh_init(&ctx->fh, video_devdata(file));
++ file->private_data = &ctx->fh;
++ ctx->dev = dev;
++
++ ret = rpivid_init_ctrls(dev, ctx);
++ if (ret)
++ goto err_free;
++
++ ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
++ &rpivid_queue_init);
++ if (IS_ERR(ctx->fh.m2m_ctx)) {
++ ret = PTR_ERR(ctx->fh.m2m_ctx);
++ goto err_ctrls;
++ }
++
++ /* The only bit of format info that we can guess now is H265 src
++ * Everything else we need more info for
++ */
++ ctx->src_fmt.pixelformat = RPIVID_SRC_PIXELFORMAT_DEFAULT;
++ rpivid_prepare_src_format(&ctx->src_fmt);
++
++ v4l2_fh_add(&ctx->fh);
++
++ mutex_unlock(&dev->dev_mutex);
++
++ return 0;
++
++err_ctrls:
++ v4l2_ctrl_handler_free(&ctx->hdl);
++err_free:
++ kfree(ctx);
++ mutex_unlock(&dev->dev_mutex);
++
++ return ret;
++}
++
++static int rpivid_release(struct file *file)
++{
++ struct rpivid_dev *dev = video_drvdata(file);
++ struct rpivid_ctx *ctx = container_of(file->private_data,
++ struct rpivid_ctx, fh);
++
++ mutex_lock(&dev->dev_mutex);
++
++ v4l2_fh_del(&ctx->fh);
++ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
++
++ v4l2_ctrl_handler_free(&ctx->hdl);
++ kfree(ctx->ctrls);
++
++ v4l2_fh_exit(&ctx->fh);
++
++ kfree(ctx);
++
++ mutex_unlock(&dev->dev_mutex);
++
++ return 0;
++}
++
++static const struct v4l2_file_operations rpivid_fops = {
++ .owner = THIS_MODULE,
++ .open = rpivid_open,
++ .release = rpivid_release,
++ .poll = v4l2_m2m_fop_poll,
++ .unlocked_ioctl = video_ioctl2,
++ .mmap = v4l2_m2m_fop_mmap,
++};
++
++static const struct video_device rpivid_video_device = {
++ .name = RPIVID_NAME,
++ .vfl_dir = VFL_DIR_M2M,
++ .fops = &rpivid_fops,
++ .ioctl_ops = &rpivid_ioctl_ops,
++ .minor = -1,
++ .release = video_device_release_empty,
++ .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
++};
++
++static const struct v4l2_m2m_ops rpivid_m2m_ops = {
++ .device_run = rpivid_device_run,
++};
++
++static const struct media_device_ops rpivid_m2m_media_ops = {
++ .req_validate = rpivid_request_validate,
++ .req_queue = v4l2_m2m_request_queue,
++};
++
++static int rpivid_probe(struct platform_device *pdev)
++{
++ struct rpivid_dev *dev;
++ struct video_device *vfd;
++ int ret;
++
++ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
++ if (!dev)
++ return -ENOMEM;
++
++ dev->vfd = rpivid_video_device;
++ dev->dev = &pdev->dev;
++ dev->pdev = pdev;
++
++ ret = 0;
++ ret = rpivid_hw_probe(dev);
++ if (ret) {
++ dev_err(&pdev->dev, "Failed to probe hardware\n");
++ return ret;
++ }
++
++ dev->dec_ops = &rpivid_dec_ops_h265;
++
++ mutex_init(&dev->dev_mutex);
++
++ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
++ if (ret) {
++ dev_err(&pdev->dev, "Failed to register V4L2 device\n");
++ return ret;
++ }
++
++ vfd = &dev->vfd;
++ vfd->lock = &dev->dev_mutex;
++ vfd->v4l2_dev = &dev->v4l2_dev;
++
++ snprintf(vfd->name, sizeof(vfd->name), "%s", rpivid_video_device.name);
++ video_set_drvdata(vfd, dev);
++
++ dev->m2m_dev = v4l2_m2m_init(&rpivid_m2m_ops);
++ if (IS_ERR(dev->m2m_dev)) {
++ v4l2_err(&dev->v4l2_dev,
++ "Failed to initialize V4L2 M2M device\n");
++ ret = PTR_ERR(dev->m2m_dev);
++
++ goto err_v4l2;
++ }
++
++ dev->mdev.dev = &pdev->dev;
++ strscpy(dev->mdev.model, RPIVID_NAME, sizeof(dev->mdev.model));
++ strscpy(dev->mdev.bus_info, "platform:" RPIVID_NAME,
++ sizeof(dev->mdev.bus_info));
++
++ media_device_init(&dev->mdev);
++ dev->mdev.ops = &rpivid_m2m_media_ops;
++ dev->v4l2_dev.mdev = &dev->mdev;
++
++ ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
++ if (ret) {
++ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
++ goto err_m2m;
++ }
++
++ v4l2_info(&dev->v4l2_dev,
++ "Device registered as /dev/video%d\n", vfd->num);
++
++ ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd,
++ MEDIA_ENT_F_PROC_VIDEO_DECODER);
++ if (ret) {
++ v4l2_err(&dev->v4l2_dev,
++ "Failed to initialize V4L2 M2M media controller\n");
++ goto err_video;
++ }
++
++ ret = media_device_register(&dev->mdev);
++ if (ret) {
++ v4l2_err(&dev->v4l2_dev, "Failed to register media device\n");
++ goto err_m2m_mc;
++ }
++
++ platform_set_drvdata(pdev, dev);
++
++ return 0;
++
++err_m2m_mc:
++ v4l2_m2m_unregister_media_controller(dev->m2m_dev);
++err_video:
++ video_unregister_device(&dev->vfd);
++err_m2m:
++ v4l2_m2m_release(dev->m2m_dev);
++err_v4l2:
++ v4l2_device_unregister(&dev->v4l2_dev);
++
++ return ret;
++}
++
++static int rpivid_remove(struct platform_device *pdev)
++{
++ struct rpivid_dev *dev = platform_get_drvdata(pdev);
++
++ if (media_devnode_is_registered(dev->mdev.devnode)) {
++ media_device_unregister(&dev->mdev);
++ v4l2_m2m_unregister_media_controller(dev->m2m_dev);
++ media_device_cleanup(&dev->mdev);
++ }
++
++ v4l2_m2m_release(dev->m2m_dev);
++ video_unregister_device(&dev->vfd);
++ v4l2_device_unregister(&dev->v4l2_dev);
++
++ rpivid_hw_remove(dev);
++
++ return 0;
++}
++
++static const struct of_device_id rpivid_dt_match[] = {
++ {
++ .compatible = "raspberrypi,rpivid-vid-decoder",
++ },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, rpivid_dt_match);
++
++static struct platform_driver rpivid_driver = {
++ .probe = rpivid_probe,
++ .remove = rpivid_remove,
++ .driver = {
++ .name = RPIVID_NAME,
++ .of_match_table = of_match_ptr(rpivid_dt_match),
++ },
++};
++module_platform_driver(rpivid_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("John Cox <jc@kynesim.co.uk>");
++MODULE_DESCRIPTION("Raspberry Pi HEVC V4L2 driver");
+--- /dev/null
++++ b/drivers/staging/media/rpivid/rpivid.h
+@@ -0,0 +1,181 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Raspberry Pi HEVC driver
++ *
++ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
++ *
++ * Based on the Cedrus VPU driver, that is:
++ *
++ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
++ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
++ * Copyright (C) 2018 Bootlin
++ */
++
++#ifndef _RPIVID_H_
++#define _RPIVID_H_
++
++#include <linux/clk.h>
++#include <linux/platform_device.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-mem2mem.h>
++#include <media/videobuf2-v4l2.h>
++#include <media/videobuf2-dma-contig.h>
++
++#define OPT_DEBUG_POLL_IRQ 0
++
++#define RPIVID_NAME "rpivid"
++
++#define RPIVID_CAPABILITY_UNTILED BIT(0)
++#define RPIVID_CAPABILITY_H265_DEC BIT(1)
++
++#define RPIVID_QUIRK_NO_DMA_OFFSET BIT(0)
++
++#define RPIVID_SRC_PIXELFORMAT_DEFAULT V4L2_PIX_FMT_HEVC_SLICE
++
++enum rpivid_irq_status {
++ RPIVID_IRQ_NONE,
++ RPIVID_IRQ_ERROR,
++ RPIVID_IRQ_OK,
++};
++
++struct rpivid_control {
++ struct v4l2_ctrl_config cfg;
++ unsigned char required:1;
++};
++
++struct rpivid_h265_run {
++ const struct v4l2_ctrl_hevc_sps *sps;
++ const struct v4l2_ctrl_hevc_pps *pps;
++ const struct v4l2_ctrl_hevc_slice_params *slice_params;
++ const struct v4l2_ctrl_hevc_scaling_matrix *scaling_matrix;
++};
++
++struct rpivid_run {
++ struct vb2_v4l2_buffer *src;
++ struct vb2_v4l2_buffer *dst;
++
++ struct rpivid_h265_run h265;
++};
++
++struct rpivid_buffer {
++ struct v4l2_m2m_buffer m2m_buf;
++};
++
++struct rpivid_dec_state;
++struct rpivid_dec_env;
++#define RPIVID_DEC_ENV_COUNT 3
++
++struct rpivid_gptr {
++ size_t size;
++ __u8 *ptr;
++ dma_addr_t addr;
++ unsigned long attrs;
++};
++
++struct rpivid_dev;
++typedef void (*rpivid_irq_callback)(struct rpivid_dev *dev, void *ctx);
++
++struct rpivid_q_aux;
++#define RPIVID_AUX_ENT_COUNT VB2_MAX_FRAME
++
++#define RPIVID_P2BUF_COUNT 2
++
++struct rpivid_ctx {
++ struct v4l2_fh fh;
++ struct rpivid_dev *dev;
++
++ struct v4l2_pix_format src_fmt;
++ struct v4l2_pix_format dst_fmt;
++ int dst_fmt_set;
++
++ struct v4l2_ctrl_handler hdl;
++ struct v4l2_ctrl **ctrls;
++
++ /* Decode state - stateless decoder my *** */
++ /* state contains stuff that is only needed in phase0
++ * it could be held in dec_env but that would be wasteful
++ */
++ struct rpivid_dec_state *state;
++ struct rpivid_dec_env *dec0;
++
++ /* Spinlock protecting dec_free */
++ spinlock_t dec_lock;
++ struct rpivid_dec_env *dec_free;
++
++ struct rpivid_dec_env *dec_pool;
++
++ /* Some of these should be in dev */
++ struct rpivid_gptr bitbufs[1]; /* Will be 2 */
++ struct rpivid_gptr cmdbufs[1]; /* Will be 2 */
++ unsigned int p2idx;
++ atomic_t p2out;
++ struct rpivid_gptr pu_bufs[RPIVID_P2BUF_COUNT];
++ struct rpivid_gptr coeff_bufs[RPIVID_P2BUF_COUNT];
++
++ /* Spinlock protecting aux_free */
++ spinlock_t aux_lock;
++ struct rpivid_q_aux *aux_free;
++
++ struct rpivid_q_aux *aux_ents[RPIVID_AUX_ENT_COUNT];
++
++ unsigned int colmv_stride;
++ unsigned int colmv_picsize;
++};
++
++struct rpivid_dec_ops {
++ void (*setup)(struct rpivid_ctx *ctx, struct rpivid_run *run);
++ int (*start)(struct rpivid_ctx *ctx);
++ void (*stop)(struct rpivid_ctx *ctx);
++ void (*trigger)(struct rpivid_ctx *ctx);
++};
++
++struct rpivid_variant {
++ unsigned int capabilities;
++ unsigned int quirks;
++ unsigned int mod_rate;
++};
++
++struct rpivid_hw_irq_ent;
++
++struct rpivid_hw_irq_ctrl {
++ /* Spinlock protecting claim and tail */
++ spinlock_t lock;
++ struct rpivid_hw_irq_ent *claim;
++ struct rpivid_hw_irq_ent *tail;
++
++ /* Ent for pending irq - also prevents sched */
++ struct rpivid_hw_irq_ent *irq;
++ /* Non-zero => do not start a new job - outer layer sched pending */
++ int no_sched;
++ /* Thread CB requested */
++ bool thread_reqed;
++};
++
++struct rpivid_dev {
++ struct v4l2_device v4l2_dev;
++ struct video_device vfd;
++ struct media_device mdev;
++ struct media_pad pad[2];
++ struct platform_device *pdev;
++ struct device *dev;
++ struct v4l2_m2m_dev *m2m_dev;
++ struct rpivid_dec_ops *dec_ops;
++
++ /* Device file mutex */
++ struct mutex dev_mutex;
++
++ void __iomem *base_irq;
++ void __iomem *base_h265;
++
++ struct clk *clock;
++
++ struct rpivid_hw_irq_ctrl ic_active1;
++ struct rpivid_hw_irq_ctrl ic_active2;
++};
++
++extern struct rpivid_dec_ops rpivid_dec_ops_h265;
++
++void *rpivid_find_control_data(struct rpivid_ctx *ctx, u32 id);
++
++#endif
+--- /dev/null
++++ b/drivers/staging/media/rpivid/rpivid_dec.c
+@@ -0,0 +1,79 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Raspberry Pi HEVC driver
++ *
++ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
++ *
++ * Based on the Cedrus VPU driver, that is:
++ *
++ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
++ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
++ * Copyright (C) 2018 Bootlin
++ */
++
++#include <media/v4l2-device.h>
++#include <media/v4l2-ioctl.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-mem2mem.h>
++
++#include "rpivid.h"
++#include "rpivid_dec.h"
++
++void rpivid_device_run(void *priv)
++{
++ struct rpivid_ctx *ctx = priv;
++ struct rpivid_dev *dev = ctx->dev;
++ struct rpivid_run run = {};
++ struct media_request *src_req;
++
++ run.src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
++ run.dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
++
++ if (!run.src || !run.dst) {
++ v4l2_err(&dev->v4l2_dev, "%s: Missing buffer: src=%p, dst=%p\n",
++ __func__, run.src, run.dst);
++ /* We are stuffed - this probably won't dig us out of our
++ * current situation but it is better than nothing
++ */
++ v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx,
++ VB2_BUF_STATE_ERROR);
++ return;
++ }
++
++ /* Apply request(s) controls if needed. */
++ src_req = run.src->vb2_buf.req_obj.req;
++
++ if (src_req)
++ v4l2_ctrl_request_setup(src_req, &ctx->hdl);
++
++ switch (ctx->src_fmt.pixelformat) {
++ case V4L2_PIX_FMT_HEVC_SLICE:
++ run.h265.sps =
++ rpivid_find_control_data(ctx,
++ V4L2_CID_MPEG_VIDEO_HEVC_SPS);
++ run.h265.pps =
++ rpivid_find_control_data(ctx,
++ V4L2_CID_MPEG_VIDEO_HEVC_PPS);
++ run.h265.slice_params =
++ rpivid_find_control_data(ctx,
++ V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS);
++ run.h265.scaling_matrix =
++ rpivid_find_control_data(ctx,
++ V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX);
++ break;
++
++ default:
++ break;
++ }
++
++ v4l2_m2m_buf_copy_metadata(run.src, run.dst, true);
++
++ dev->dec_ops->setup(ctx, &run);
++
++ /* Complete request(s) controls if needed. */
++
++ if (src_req)
++ v4l2_ctrl_request_complete(src_req, &ctx->hdl);
++
++ dev->dec_ops->trigger(ctx);
++}
+--- /dev/null
++++ b/drivers/staging/media/rpivid/rpivid_dec.h
+@@ -0,0 +1,19 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Raspberry Pi HEVC driver
++ *
++ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
++ *
++ * Based on the Cedrus VPU driver, that is:
++ *
++ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
++ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
++ * Copyright (C) 2018 Bootlin
++ */
++
++#ifndef _RPIVID_DEC_H_
++#define _RPIVID_DEC_H_
++
++void rpivid_device_run(void *priv);
++
++#endif
+--- /dev/null
++++ b/drivers/staging/media/rpivid/rpivid_h265.c
+@@ -0,0 +1,2275 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Raspberry Pi HEVC driver
++ *
++ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
++ *
++ * Based on the Cedrus VPU driver, that is:
++ *
++ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
++ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
++ * Copyright (C) 2018 Bootlin
++ */
++
++#include <linux/delay.h>
++#include <linux/types.h>
++
++#include <media/videobuf2-dma-contig.h>
++
++#include "rpivid.h"
++#include "rpivid_hw.h"
++
++#define DEBUG_TRACE_P1_CMD 0
++#define DEBUG_TRACE_EXECUTION 0
++
++#if DEBUG_TRACE_EXECUTION
++#define xtrace_in(dev_, de_)\
++ v4l2_info(&(dev_)->v4l2_dev, "%s[%d]: in\n", __func__,\
++ (de_) == NULL ? -1 : (de_)->decode_order)
++#define xtrace_ok(dev_, de_)\
++ v4l2_info(&(dev_)->v4l2_dev, "%s[%d]: ok\n", __func__,\
++ (de_) == NULL ? -1 : (de_)->decode_order)
++#define xtrace_fin(dev_, de_)\
++ v4l2_info(&(dev_)->v4l2_dev, "%s[%d]: finish\n", __func__,\
++ (de_) == NULL ? -1 : (de_)->decode_order)
++#define xtrace_fail(dev_, de_)\
++ v4l2_info(&(dev_)->v4l2_dev, "%s[%d]: FAIL\n", __func__,\
++ (de_) == NULL ? -1 : (de_)->decode_order)
++#else
++#define xtrace_in(dev_, de_)
++#define xtrace_ok(dev_, de_)
++#define xtrace_fin(dev_, de_)
++#define xtrace_fail(dev_, de_)
++#endif
++
++enum hevc_slice_type {
++ HEVC_SLICE_B = 0,
++ HEVC_SLICE_P = 1,
++ HEVC_SLICE_I = 2,
++};
++
++enum hevc_layer { L0 = 0, L1 = 1 };
++
++static int gptr_alloc(struct rpivid_dev *const dev, struct rpivid_gptr *gptr,
++ size_t size, unsigned long attrs)
++{
++ gptr->size = size;
++ gptr->attrs = attrs;
++ gptr->addr = 0;
++ gptr->ptr = dma_alloc_attrs(dev->dev, gptr->size, &gptr->addr,
++ GFP_KERNEL, gptr->attrs);
++ return !gptr->ptr ? -ENOMEM : 0;
++}
++
++static void gptr_free(struct rpivid_dev *const dev,
++ struct rpivid_gptr *const gptr)
++{
++ if (gptr->ptr)
++ dma_free_attrs(dev->dev, gptr->size, gptr->ptr, gptr->addr,
++ gptr->attrs);
++ gptr->size = 0;
++ gptr->ptr = NULL;
++ gptr->addr = 0;
++ gptr->attrs = 0;
++}
++
++/* Realloc but do not copy */
++static int gptr_realloc_new(struct rpivid_dev * const dev,
++ struct rpivid_gptr * const gptr, size_t size)
++{
++ if (size == gptr->size)
++ return 0;
++
++ if (gptr->ptr)
++ dma_free_attrs(dev->dev, gptr->size, gptr->ptr,
++ gptr->addr, gptr->attrs);
++
++ gptr->addr = 0;
++ gptr->size = size;
++ gptr->ptr = dma_alloc_attrs(dev->dev, gptr->size,
++ &gptr->addr, GFP_KERNEL, gptr->attrs);
++ return gptr->ptr ? 0 : -ENOMEM;
++}
++
++/* floor(log2(x)) */
++static unsigned int log2_size(size_t x)
++{
++ unsigned int n = 0;
++
++ if (x & ~0xffff) {
++ n += 16;
++ x >>= 16;
++ }
++ if (x & ~0xff) {
++ n += 8;
++ x >>= 8;
++ }
++ if (x & ~0xf) {
++ n += 4;
++ x >>= 4;
++ }
++ if (x & ~3) {
++ n += 2;
++ x >>= 2;
++ }
++ return (x & ~1) ? n + 1 : n;
++}
++
++static size_t round_up_size(const size_t x)
++{
++ /* Admit no size < 256 */
++ const unsigned int n = x < 256 ? 8 : log2_size(x) - 1;
++
++ return x >= (3 << n) ? 4 << n : (3 << n);
++}
++
++static size_t next_size(const size_t x)
++{
++ return round_up_size(x + 1);
++}
++
++#define NUM_SCALING_FACTORS 4064 /* Not a typo = 0xbe0 + 0x400 */
++
++#define AXI_BASE64 0
++
++#define PROB_BACKUP ((20 << 12) + (20 << 6) + (0 << 0))
++#define PROB_RELOAD ((20 << 12) + (20 << 0) + (0 << 6))
++
++#define HEVC_MAX_REFS V4L2_HEVC_DPB_ENTRIES_NUM_MAX
++
++//////////////////////////////////////////////////////////////////////////////
++
++struct rpi_cmd {
++ u32 addr;
++ u32 data;
++} __packed;
++
++struct rpivid_q_aux {
++ unsigned int refcount;
++ unsigned int q_index;
++ struct rpivid_q_aux *next;
++ struct rpivid_gptr col;
++};
++
++//////////////////////////////////////////////////////////////////////////////
++
++enum rpivid_decode_state {
++ RPIVID_DECODE_SLICE_START,
++ RPIVID_DECODE_SLICE_CONTINUE,
++ RPIVID_DECODE_ERROR_CONTINUE,
++ RPIVID_DECODE_ERROR_DONE,
++ RPIVID_DECODE_PHASE1,
++ RPIVID_DECODE_END,
++};
++
++struct rpivid_dec_env {
++ struct rpivid_ctx *ctx;
++ struct rpivid_dec_env *next;
++
++ enum rpivid_decode_state state;
++ unsigned int decode_order;
++ int p1_status; /* P1 status - what to realloc */
++
++ struct rpivid_dec_env *phase_wait_q_next;
++
++ struct rpi_cmd *cmd_fifo;
++ unsigned int cmd_len, cmd_max;
++ unsigned int num_slice_msgs;
++ unsigned int pic_width_in_ctbs_y;
++ unsigned int pic_height_in_ctbs_y;
++ unsigned int dpbno_col;
++ u32 reg_slicestart;
++ int collocated_from_l0_flag;
++ unsigned int wpp_entry_x;
++ unsigned int wpp_entry_y;
++
++ u32 rpi_config2;
++ u32 rpi_framesize;
++ u32 rpi_currpoc;
++
++ struct vb2_v4l2_buffer *frame_buf; // Detached dest buffer
++ unsigned int frame_c_offset;
++ unsigned int frame_stride;
++ dma_addr_t frame_addr;
++ dma_addr_t ref_addrs[16];
++ struct rpivid_q_aux *frame_aux;
++ struct rpivid_q_aux *col_aux;
++
++ dma_addr_t pu_base_vc;
++ dma_addr_t coeff_base_vc;
++ u32 pu_stride;
++ u32 coeff_stride;
++
++ struct rpivid_gptr *bit_copy_gptr;
++ size_t bit_copy_len;
++ struct rpivid_gptr *cmd_copy_gptr;
++
++ u16 slice_msgs[2 * HEVC_MAX_REFS * 8 + 3];
++ u8 scaling_factors[NUM_SCALING_FACTORS];
++
++ struct rpivid_hw_irq_ent irq_ent;
++};
++
++#define member_size(type, member) sizeof(((type *)0)->member)
++
++struct rpivid_dec_state {
++ struct v4l2_ctrl_hevc_sps sps;
++ struct v4l2_ctrl_hevc_pps pps;
++
++ // Helper vars & tables derived from sps/pps
++ unsigned int log2_ctb_size; /* log2 width of a CTB */
++ unsigned int ctb_width; /* Width in CTBs */
++ unsigned int ctb_height; /* Height in CTBs */
++ unsigned int ctb_size; /* Pic area in CTBs */
++ unsigned int num_tile_columns;
++ unsigned int num_tile_rows;
++ u8 column_width[member_size(struct v4l2_ctrl_hevc_pps,
++ column_width_minus1)];
++ u8 row_height[member_size(struct v4l2_ctrl_hevc_pps,
++ row_height_minus1)];
++
++ int *col_bd;
++ int *row_bd;
++ int *ctb_addr_rs_to_ts;
++ int *ctb_addr_ts_to_rs;
++ int *tile_id;
++
++ // Aux starage for DPB
++ // Hold refs
++ struct rpivid_q_aux *ref_aux[HEVC_MAX_REFS];
++ struct rpivid_q_aux *frame_aux;
++
++ // Slice vars
++ unsigned int slice_idx;
++ bool frame_end;
++ bool slice_temporal_mvp; /* Slice flag but constant for frame */
++
++ // Temp vars per run - don't actually need to persist
++ u8 *src_buf;
++ dma_addr_t src_addr;
++ const struct v4l2_ctrl_hevc_slice_params *sh;
++ unsigned int nb_refs[2];
++ unsigned int slice_qp;
++ unsigned int max_num_merge_cand; // 0 if I-slice
++ bool dependent_slice_segment_flag;
++};
++
++static inline int clip_int(const int x, const int lo, const int hi)
++{
++ return x < lo ? lo : x > hi ? hi : x;
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Phase 1 command and bit FIFOs
++
++#if DEBUG_TRACE_P1_CMD
++static int p1_z;
++#endif
++
++// ???? u16 addr - put in u32
++static int p1_apb_write(struct rpivid_dec_env *const de, const u16 addr,
++ const u32 data)
++{
++ if (de->cmd_len == de->cmd_max)
++ de->cmd_fifo =
++ krealloc(de->cmd_fifo,
++ (de->cmd_max *= 2) * sizeof(struct rpi_cmd),
++ GFP_KERNEL);
++ de->cmd_fifo[de->cmd_len].addr = addr;
++ de->cmd_fifo[de->cmd_len].data = data;
++
++#if DEBUG_TRACE_P1_CMD
++ if (++p1_z < 256) {
++ v4l2_info(&de->ctx->dev->v4l2_dev, "[%02x] %x %x\n",
++ de->cmd_len, addr, data);
++ }
++#endif
++
++ return de->cmd_len++;
++}
++
++static int ctb_to_tile(unsigned int ctb, unsigned int *bd, int num)
++{
++ int i;
++
++ for (i = 1; ctb >= bd[i]; i++)
++ ; // bd[] has num+1 elements; bd[0]=0;
++ return i - 1;
++}
++
++static int ctb_to_slice_w_h(unsigned int ctb, int ctb_size, int width,
++ unsigned int *bd, int num)
++{
++ if (ctb < bd[num - 1])
++ return ctb_size;
++ else if (width % ctb_size)
++ return width % ctb_size;
++ else
++ return ctb_size;
++}
++
++static void aux_q_free(struct rpivid_ctx *const ctx,
++ struct rpivid_q_aux *const aq)
++{
++ struct rpivid_dev *const dev = ctx->dev;
++
++ gptr_free(dev, &aq->col);
++ kfree(aq);
++}
++
++static struct rpivid_q_aux *aux_q_alloc(struct rpivid_ctx *const ctx)
++{
++ struct rpivid_dev *const dev = ctx->dev;
++ struct rpivid_q_aux *const aq = kzalloc(sizeof(*aq), GFP_KERNEL);
++
++ if (!aq)
++ return NULL;
++
++ aq->refcount = 1;
++ if (gptr_alloc(dev, &aq->col, ctx->colmv_picsize,
++ DMA_ATTR_FORCE_CONTIGUOUS | DMA_ATTR_NO_KERNEL_MAPPING))
++ goto fail;
++
++ return aq;
++
++fail:
++ kfree(aq);
++ return NULL;
++}
++
++static struct rpivid_q_aux *aux_q_new(struct rpivid_ctx *const ctx,
++ const unsigned int q_index)
++{
++ struct rpivid_q_aux *aq;
++ unsigned long lockflags;
++
++ spin_lock_irqsave(&ctx->aux_lock, lockflags);
++ aq = ctx->aux_free;
++ if (aq) {
++ ctx->aux_free = aq->next;
++ aq->next = NULL;
++ aq->refcount = 1;
++ }
++ spin_unlock_irqrestore(&ctx->aux_lock, lockflags);
++
++ if (!aq) {
++ aq = aux_q_alloc(ctx);
++ if (!aq)
++ return NULL;
++ }
++
++ aq->q_index = q_index;
++ ctx->aux_ents[q_index] = aq;
++ return aq;
++}
++
++static struct rpivid_q_aux *aux_q_ref(struct rpivid_ctx *const ctx,
++ struct rpivid_q_aux *const aq)
++{
++ if (aq) {
++ unsigned long lockflags;
++
++ spin_lock_irqsave(&ctx->aux_lock, lockflags);
++
++ ++aq->refcount;
++
++ spin_unlock_irqrestore(&ctx->aux_lock, lockflags);
++ }
++ return aq;
++}
++
++static void aux_q_release(struct rpivid_ctx *const ctx,
++ struct rpivid_q_aux **const paq)
++{
++ struct rpivid_q_aux *const aq = *paq;
++ *paq = NULL;
++
++ if (aq) {
++ unsigned long lockflags;
++
++ spin_lock_irqsave(&ctx->aux_lock, lockflags);
++
++ if (--aq->refcount == 0) {
++ aq->next = ctx->aux_free;
++ ctx->aux_free = aq;
++ ctx->aux_ents[aq->q_index] = NULL;
++ }
++
++ spin_unlock_irqrestore(&ctx->aux_lock, lockflags);
++ }
++}
++
++static void aux_q_init(struct rpivid_ctx *const ctx)
++{
++ spin_lock_init(&ctx->aux_lock);
++ ctx->aux_free = NULL;
++}
++
++static void aux_q_uninit(struct rpivid_ctx *const ctx)
++{
++ struct rpivid_q_aux *aq;
++
++ ctx->colmv_picsize = 0;
++ ctx->colmv_stride = 0;
++ while ((aq = ctx->aux_free) != NULL) {
++ ctx->aux_free = aq->next;
++ aux_q_free(ctx, aq);
++ }
++}
++
++//////////////////////////////////////////////////////////////////////////////
++
++/*
++ * Initialisation process for context variables (CABAC init)
++ * see H.265 9.3.2.2
++ *
++ * N.B. If comparing with FFmpeg note that this h/w uses slightly different
++ * offsets to FFmpegs array
++ */
++
++/* Actual number of values */
++#define RPI_PROB_VALS 154U
++/* Rounded up as we copy words */
++#define RPI_PROB_ARRAY_SIZE ((154 + 3) & ~3)
++
++/* Initialiser values - see tables H.265 9-4 through 9-42 */
++static const u8 prob_init[3][156] = {
++ {
++ 153, 200, 139, 141, 157, 154, 154, 154, 154, 154, 184, 154, 154,
++ 154, 184, 63, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154,
++ 154, 154, 154, 153, 138, 138, 111, 141, 94, 138, 182, 154, 154,
++ 154, 140, 92, 137, 138, 140, 152, 138, 139, 153, 74, 149, 92,
++ 139, 107, 122, 152, 140, 179, 166, 182, 140, 227, 122, 197, 110,
++ 110, 124, 125, 140, 153, 125, 127, 140, 109, 111, 143, 127, 111,
++ 79, 108, 123, 63, 110, 110, 124, 125, 140, 153, 125, 127, 140,
++ 109, 111, 143, 127, 111, 79, 108, 123, 63, 91, 171, 134, 141,
++ 138, 153, 136, 167, 152, 152, 139, 139, 111, 111, 125, 110, 110,
++ 94, 124, 108, 124, 107, 125, 141, 179, 153, 125, 107, 125, 141,
++ 179, 153, 125, 107, 125, 141, 179, 153, 125, 140, 139, 182, 182,
++ 152, 136, 152, 136, 153, 136, 139, 111, 136, 139, 111, 0, 0,
++ },
++ {
++ 153, 185, 107, 139, 126, 197, 185, 201, 154, 149, 154, 139, 154,
++ 154, 154, 152, 110, 122, 95, 79, 63, 31, 31, 153, 153, 168,
++ 140, 198, 79, 124, 138, 94, 153, 111, 149, 107, 167, 154, 154,
++ 154, 154, 196, 196, 167, 154, 152, 167, 182, 182, 134, 149, 136,
++ 153, 121, 136, 137, 169, 194, 166, 167, 154, 167, 137, 182, 125,
++ 110, 94, 110, 95, 79, 125, 111, 110, 78, 110, 111, 111, 95,
++ 94, 108, 123, 108, 125, 110, 94, 110, 95, 79, 125, 111, 110,
++ 78, 110, 111, 111, 95, 94, 108, 123, 108, 121, 140, 61, 154,
++ 107, 167, 91, 122, 107, 167, 139, 139, 155, 154, 139, 153, 139,
++ 123, 123, 63, 153, 166, 183, 140, 136, 153, 154, 166, 183, 140,
++ 136, 153, 154, 166, 183, 140, 136, 153, 154, 170, 153, 123, 123,
++ 107, 121, 107, 121, 167, 151, 183, 140, 151, 183, 140, 0, 0,
++ },
++ {
++ 153, 160, 107, 139, 126, 197, 185, 201, 154, 134, 154, 139, 154,
++ 154, 183, 152, 154, 137, 95, 79, 63, 31, 31, 153, 153, 168,
++ 169, 198, 79, 224, 167, 122, 153, 111, 149, 92, 167, 154, 154,
++ 154, 154, 196, 167, 167, 154, 152, 167, 182, 182, 134, 149, 136,
++ 153, 121, 136, 122, 169, 208, 166, 167, 154, 152, 167, 182, 125,
++ 110, 124, 110, 95, 94, 125, 111, 111, 79, 125, 126, 111, 111,
++ 79, 108, 123, 93, 125, 110, 124, 110, 95, 94, 125, 111, 111,
++ 79, 125, 126, 111, 111, 79, 108, 123, 93, 121, 140, 61, 154,
++ 107, 167, 91, 107, 107, 167, 139, 139, 170, 154, 139, 153, 139,
++ 123, 123, 63, 124, 166, 183, 140, 136, 153, 154, 166, 183, 140,
++ 136, 153, 154, 166, 183, 140, 136, 153, 154, 170, 153, 138, 138,
++ 122, 121, 122, 121, 167, 151, 183, 140, 151, 183, 140, 0, 0,
++ },
++};
++
++static void write_prob(struct rpivid_dec_env *const de,
++ const struct rpivid_dec_state *const s)
++{
++ u8 dst[RPI_PROB_ARRAY_SIZE];
++
++ const unsigned int init_type =
++ ((s->sh->flags & V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT) != 0 &&
++ s->sh->slice_type != HEVC_SLICE_I) ?
++ s->sh->slice_type + 1 :
++ 2 - s->sh->slice_type;
++ const u8 *p = prob_init[init_type];
++ const int q = clip_int(s->slice_qp, 0, 51);
++ unsigned int i;
++
++ for (i = 0; i < RPI_PROB_VALS; i++) {
++ int init_value = p[i];
++ int m = (init_value >> 4) * 5 - 45;
++ int n = ((init_value & 15) << 3) - 16;
++ int pre = 2 * (((m * q) >> 4) + n) - 127;
++
++ pre ^= pre >> 31;
++ if (pre > 124)
++ pre = 124 + (pre & 1);
++ dst[i] = pre;
++ }
++ for (i = RPI_PROB_VALS; i != RPI_PROB_ARRAY_SIZE; ++i)
++ dst[i] = 0;
++
++ for (i = 0; i < RPI_PROB_ARRAY_SIZE; i += 4)
++ p1_apb_write(de, 0x1000 + i,
++ dst[i] + (dst[i + 1] << 8) + (dst[i + 2] << 16) +
++ (dst[i + 3] << 24));
++}
++
++static void write_scaling_factors(struct rpivid_dec_env *const de)
++{
++ int i;
++ const u8 *p = (u8 *)de->scaling_factors;
++
++ for (i = 0; i < NUM_SCALING_FACTORS; i += 4, p += 4)
++ p1_apb_write(de, 0x2000 + i,
++ p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24));
++}
++
++static inline __u32 dma_to_axi_addr(dma_addr_t a)
++{
++ return (__u32)(a >> 6);
++}
++
++static void write_bitstream(struct rpivid_dec_env *const de,
++ const struct rpivid_dec_state *const s)
++{
++ // Note that FFmpeg removes emulation prevention bytes, so this is
++ // matched in the configuration here.
++ // Whether that is the correct behaviour or not is not clear in the
++ // spec.
++ const int rpi_use_emu = 1;
++ unsigned int offset = s->sh->data_bit_offset / 8 + 1;
++ const unsigned int len = (s->sh->bit_size + 7) / 8 - offset;
++ dma_addr_t addr;
++
++ if (s->src_addr != 0) {
++ addr = s->src_addr + offset;
++ } else {
++ memcpy(de->bit_copy_gptr->ptr + de->bit_copy_len,
++ s->src_buf + offset, len);
++ addr = de->bit_copy_gptr->addr + de->bit_copy_len;
++ de->bit_copy_len += (len + 63) & ~63;
++ }
++ offset = addr & 63;
++
++ p1_apb_write(de, RPI_BFBASE, dma_to_axi_addr(addr));
++ p1_apb_write(de, RPI_BFNUM, len);
++ p1_apb_write(de, RPI_BFCONTROL, offset + (1 << 7)); // Stop
++ p1_apb_write(de, RPI_BFCONTROL, offset + (rpi_use_emu << 6));
++}
++
++//////////////////////////////////////////////////////////////////////////////
++
++static void write_slice(struct rpivid_dec_env *const de,
++ const struct rpivid_dec_state *const s,
++ const unsigned int slice_w,
++ const unsigned int slice_h)
++{
++ u32 u32 = (s->sh->slice_type << 12) +
++ (((s->sh->flags &
++ V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_LUMA) != 0)
++ << 14) +
++ (((s->sh->flags &
++ V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_CHROMA) != 0)
++ << 15) +
++ (slice_w << 17) + (slice_h << 24);
++
++ u32 |= (s->max_num_merge_cand << 0) + (s->nb_refs[L0] << 4) +
++ (s->nb_refs[L1] << 8);
++
++ if (s->sh->slice_type == HEVC_SLICE_B)
++ u32 |= ((s->sh->flags &
++ V4L2_HEVC_SLICE_PARAMS_FLAG_MVD_L1_ZERO) != 0)
++ << 16;
++ p1_apb_write(de, RPI_SLICE, u32);
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Tiles mode
++
++static void new_entry_point(struct rpivid_dec_env *const de,
++ const struct rpivid_dec_state *const s,
++ const int do_bte,
++ const int reset_qp_y, const int ctb_addr_ts)
++{
++ int ctb_col = s->ctb_addr_ts_to_rs[ctb_addr_ts] %
++ de->pic_width_in_ctbs_y;
++ int ctb_row = s->ctb_addr_ts_to_rs[ctb_addr_ts] /
++ de->pic_width_in_ctbs_y;
++
++ int tile_x = ctb_to_tile(ctb_col, s->col_bd, s->num_tile_columns);
++ int tile_y = ctb_to_tile(ctb_row, s->row_bd, s->num_tile_rows);
++
++ int endx = s->col_bd[tile_x + 1] - 1;
++ int endy = s->row_bd[tile_y + 1] - 1;
++
++ u8 slice_w = ctb_to_slice_w_h(ctb_col, 1 << s->log2_ctb_size,
++ s->sps.pic_width_in_luma_samples,
++ s->col_bd, s->num_tile_columns);
++ u8 slice_h = ctb_to_slice_w_h(ctb_row, 1 << s->log2_ctb_size,
++ s->sps.pic_height_in_luma_samples,
++ s->row_bd, s->num_tile_rows);
++
++ p1_apb_write(de, RPI_TILESTART,
++ s->col_bd[tile_x] + (s->row_bd[tile_y] << 16));
++ p1_apb_write(de, RPI_TILEEND, endx + (endy << 16));
++
++ if (do_bte)
++ p1_apb_write(de, RPI_BEGINTILEEND, endx + (endy << 16));
++
++ write_slice(de, s, slice_w, slice_h);
++
++ if (reset_qp_y) {
++ unsigned int sps_qp_bd_offset =
++ 6 * s->sps.bit_depth_luma_minus8;
++
++ p1_apb_write(de, RPI_QP, sps_qp_bd_offset + s->slice_qp);
++ }
++
++ p1_apb_write(de, RPI_MODE,
++ (0xFFFF << 0) + (0x0 << 16) +
++ ((tile_x == s->num_tile_columns - 1) << 17) +
++ ((tile_y == s->num_tile_rows - 1) << 18));
++
++ p1_apb_write(de, RPI_CONTROL, (ctb_col << 0) + (ctb_row << 16));
++}
++
++//////////////////////////////////////////////////////////////////////////////
++
++static void new_slice_segment(struct rpivid_dec_env *const de,
++ const struct rpivid_dec_state *const s)
++{
++ const struct v4l2_ctrl_hevc_sps *const sps = &s->sps;
++ const struct v4l2_ctrl_hevc_pps *const pps = &s->pps;
++
++ p1_apb_write(de,
++ RPI_SPS0,
++ ((sps->log2_min_luma_coding_block_size_minus3 + 3) << 0) |
++ (s->log2_ctb_size << 4) |
++ ((sps->log2_min_luma_transform_block_size_minus2 + 2)
++ << 8) |
++ ((sps->log2_min_luma_transform_block_size_minus2 + 2 +
++ sps->log2_diff_max_min_luma_transform_block_size)
++ << 12) |
++ ((sps->bit_depth_luma_minus8 + 8) << 16) |
++ ((sps->bit_depth_chroma_minus8 + 8) << 20) |
++ (sps->max_transform_hierarchy_depth_intra << 24) |
++ (sps->max_transform_hierarchy_depth_inter << 28));
++
++ p1_apb_write(de,
++ RPI_SPS1,
++ ((sps->pcm_sample_bit_depth_luma_minus1 + 1) << 0) |
++ ((sps->pcm_sample_bit_depth_chroma_minus1 + 1) << 4) |
++ ((sps->log2_min_pcm_luma_coding_block_size_minus3 + 3)
++ << 8) |
++ ((sps->log2_min_pcm_luma_coding_block_size_minus3 + 3 +
++ sps->log2_diff_max_min_pcm_luma_coding_block_size)
++ << 12) |
++ (((sps->flags & V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE) ?
++ 0 : sps->chroma_format_idc) << 16) |
++ ((!!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED)) << 18) |
++ ((!!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED)) << 19) |
++ ((!!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED))
++ << 20) |
++ ((!!(sps->flags &
++ V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED))
++ << 21));
++
++ p1_apb_write(de,
++ RPI_PPS,
++ ((s->log2_ctb_size - pps->diff_cu_qp_delta_depth) << 0) |
++ ((!!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED))
++ << 4) |
++ ((!!(pps->flags &
++ V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED))
++ << 5) |
++ ((!!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED))
++ << 6) |
++ ((!!(pps->flags &
++ V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED))
++ << 7) |
++ (((pps->pps_cb_qp_offset + s->sh->slice_cb_qp_offset) & 255)
++ << 8) |
++ (((pps->pps_cr_qp_offset + s->sh->slice_cr_qp_offset) & 255)
++ << 16) |
++ ((!!(pps->flags &
++ V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED))
++ << 24));
++
++ if ((sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED) != 0)
++ write_scaling_factors(de);
++
++ if (!s->dependent_slice_segment_flag) {
++ int ctb_col = s->sh->slice_segment_addr %
++ de->pic_width_in_ctbs_y;
++ int ctb_row = s->sh->slice_segment_addr /
++ de->pic_width_in_ctbs_y;
++
++ de->reg_slicestart = (ctb_col << 0) + (ctb_row << 16);
++ }
++
++ p1_apb_write(de, RPI_SLICESTART, de->reg_slicestart);
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Slice messages
++
++static void msg_slice(struct rpivid_dec_env *const de, const u16 msg)
++{
++ de->slice_msgs[de->num_slice_msgs++] = msg;
++}
++
++static void program_slicecmds(struct rpivid_dec_env *const de,
++ const int sliceid)
++{
++ int i;
++
++ p1_apb_write(de, RPI_SLICECMDS, de->num_slice_msgs + (sliceid << 8));
++
++ for (i = 0; i < de->num_slice_msgs; i++)
++ p1_apb_write(de, 0x4000 + 4 * i, de->slice_msgs[i] & 0xffff);
++}
++
++// NoBackwardPredictionFlag 8.3.5
++// Simply checks POCs
++static int has_backward(const struct v4l2_hevc_dpb_entry *const dpb,
++ const __u8 *const idx, const unsigned int n,
++ const unsigned int cur_poc)
++{
++ unsigned int i;
++
++ for (i = 0; i < n; ++i) {
++ // Compare mod 2^16
++ // We only get u16 pocs & 8.3.1 says
++ // "The bitstream shall not contain data that result in values
++ // of DiffPicOrderCnt( picA, picB ) used in the decoding
++ // process that are not in the range of −2^15 to 2^15 − 1,
++ // inclusive."
++ if (((cur_poc - dpb[idx[i]].pic_order_cnt[0]) & 0x8000) != 0)
++ return 0;
++ }
++ return 1;
++}
++
++static void pre_slice_decode(struct rpivid_dec_env *const de,
++ const struct rpivid_dec_state *const s)
++{
++ const struct v4l2_ctrl_hevc_slice_params *const sh = s->sh;
++ int weighted_pred_flag, idx;
++ u16 cmd_slice;
++ unsigned int collocated_from_l0_flag;
++
++ de->num_slice_msgs = 0;
++
++ cmd_slice = 0;
++ if (sh->slice_type == HEVC_SLICE_I)
++ cmd_slice = 1;
++ if (sh->slice_type == HEVC_SLICE_P)
++ cmd_slice = 2;
++ if (sh->slice_type == HEVC_SLICE_B)
++ cmd_slice = 3;
++
++ cmd_slice |= (s->nb_refs[L0] << 2) | (s->nb_refs[L1] << 6) |
++ (s->max_num_merge_cand << 11);
++
++ collocated_from_l0_flag =
++ !s->slice_temporal_mvp ||
++ sh->slice_type != HEVC_SLICE_B ||
++ (sh->flags & V4L2_HEVC_SLICE_PARAMS_FLAG_COLLOCATED_FROM_L0);
++ cmd_slice |= collocated_from_l0_flag << 14;
++
++ if (sh->slice_type == HEVC_SLICE_P || sh->slice_type == HEVC_SLICE_B) {
++ // Flag to say all reference pictures are from the past
++ const int no_backward_pred_flag =
++ has_backward(sh->dpb, sh->ref_idx_l0, s->nb_refs[L0],
++ sh->slice_pic_order_cnt) &&
++ has_backward(sh->dpb, sh->ref_idx_l1, s->nb_refs[L1],
++ sh->slice_pic_order_cnt);
++ cmd_slice |= no_backward_pred_flag << 10;
++ msg_slice(de, cmd_slice);
++
++ if (s->slice_temporal_mvp) {
++ const __u8 *const rpl = collocated_from_l0_flag ?
++ sh->ref_idx_l0 : sh->ref_idx_l1;
++ de->dpbno_col = rpl[sh->collocated_ref_idx];
++ //v4l2_info(&de->ctx->dev->v4l2_dev,
++ // "L0=%d col_ref_idx=%d,
++ // dpb_no=%d\n", collocated_from_l0_flag,
++ // sh->collocated_ref_idx, de->dpbno_col);
++ }
++
++ // Write reference picture descriptions
++ weighted_pred_flag =
++ sh->slice_type == HEVC_SLICE_P ?
++ !!(s->pps.flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED) :
++ !!(s->pps.flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED);
++
++ for (idx = 0; idx < s->nb_refs[L0]; ++idx) {
++ unsigned int dpb_no = sh->ref_idx_l0[idx];
++ //v4l2_info(&de->ctx->dev->v4l2_dev,
++ // "L0[%d]=dpb[%d]\n", idx, dpb_no);
++
++ msg_slice(de,
++ dpb_no |
++ (sh->dpb[dpb_no].rps ==
++ V4L2_HEVC_DPB_ENTRY_RPS_LT_CURR ?
++ (1 << 4) : 0) |
++ (weighted_pred_flag ? (3 << 5) : 0));
++ msg_slice(de, sh->dpb[dpb_no].pic_order_cnt[0]);
++
++ if (weighted_pred_flag) {
++ const struct v4l2_hevc_pred_weight_table
++ *const w = &sh->pred_weight_table;
++ const int luma_weight_denom =
++ (1 << w->luma_log2_weight_denom);
++ const unsigned int chroma_log2_weight_denom =
++ (w->luma_log2_weight_denom +
++ w->delta_chroma_log2_weight_denom);
++ const int chroma_weight_denom =
++ (1 << chroma_log2_weight_denom);
++
++ msg_slice(de,
++ w->luma_log2_weight_denom |
++ (((w->delta_luma_weight_l0[idx] +
++ luma_weight_denom) & 0x1ff)
++ << 3));
++ msg_slice(de, w->luma_offset_l0[idx] & 0xff);
++ msg_slice(de,
++ chroma_log2_weight_denom |
++ (((w->delta_chroma_weight_l0[idx][0] +
++ chroma_weight_denom) & 0x1ff)
++ << 3));
++ msg_slice(de,
++ w->chroma_offset_l0[idx][0] & 0xff);
++ msg_slice(de,
++ chroma_log2_weight_denom |
++ (((w->delta_chroma_weight_l0[idx][1] +
++ chroma_weight_denom) & 0x1ff)
++ << 3));
++ msg_slice(de,
++ w->chroma_offset_l0[idx][1] & 0xff);
++ }
++ }
++
++ for (idx = 0; idx < s->nb_refs[L1]; ++idx) {
++ unsigned int dpb_no = sh->ref_idx_l1[idx];
++ //v4l2_info(&de->ctx->dev->v4l2_dev,
++ // "L1[%d]=dpb[%d]\n", idx, dpb_no);
++ msg_slice(de,
++ dpb_no |
++ (sh->dpb[dpb_no].rps ==
++ V4L2_HEVC_DPB_ENTRY_RPS_LT_CURR ?
++ (1 << 4) : 0) |
++ (weighted_pred_flag ? (3 << 5) : 0));
++ msg_slice(de, sh->dpb[dpb_no].pic_order_cnt[0]);
++ if (weighted_pred_flag) {
++ const struct v4l2_hevc_pred_weight_table
++ *const w = &sh->pred_weight_table;
++ const int luma_weight_denom =
++ (1 << w->luma_log2_weight_denom);
++ const unsigned int chroma_log2_weight_denom =
++ (w->luma_log2_weight_denom +
++ w->delta_chroma_log2_weight_denom);
++ const int chroma_weight_denom =
++ (1 << chroma_log2_weight_denom);
++
++ msg_slice(de,
++ w->luma_log2_weight_denom |
++ (((w->delta_luma_weight_l1[idx] +
++ luma_weight_denom) & 0x1ff) << 3));
++ msg_slice(de, w->luma_offset_l1[idx] & 0xff);
++ msg_slice(de,
++ chroma_log2_weight_denom |
++ (((w->delta_chroma_weight_l1[idx][0] +
++ chroma_weight_denom) & 0x1ff)
++ << 3));
++ msg_slice(de,
++ w->chroma_offset_l1[idx][0] & 0xff);
++ msg_slice(de,
++ chroma_log2_weight_denom |
++ (((w->delta_chroma_weight_l1[idx][1] +
++ chroma_weight_denom) & 0x1ff)
++ << 3));
++ msg_slice(de,
++ w->chroma_offset_l1[idx][1] & 0xff);
++ }
++ }
++ } else {
++ msg_slice(de, cmd_slice);
++ }
++
++ msg_slice(de,
++ (sh->slice_beta_offset_div2 & 15) |
++ ((sh->slice_tc_offset_div2 & 15) << 4) |
++ ((sh->flags &
++ V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED) ?
++ 1 << 8 : 0) |
++ ((sh->flags &
++ V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED) ?
++ 1 << 9 : 0) |
++ ((s->pps.flags &
++ V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED) ?
++ 1 << 10 : 0));
++
++ msg_slice(de, ((sh->slice_cr_qp_offset & 31) << 5) +
++ (sh->slice_cb_qp_offset & 31)); // CMD_QPOFF
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Write STATUS register with expected end CTU address of previous slice
++
++static void end_previous_slice(struct rpivid_dec_env *const de,
++ const struct rpivid_dec_state *const s,
++ const int ctb_addr_ts)
++{
++ int last_x =
++ s->ctb_addr_ts_to_rs[ctb_addr_ts - 1] % de->pic_width_in_ctbs_y;
++ int last_y =
++ s->ctb_addr_ts_to_rs[ctb_addr_ts - 1] / de->pic_width_in_ctbs_y;
++
++ p1_apb_write(de, RPI_STATUS, 1 + (last_x << 5) + (last_y << 18));
++}
++
++static void wpp_pause(struct rpivid_dec_env *const de, int ctb_row)
++{
++ p1_apb_write(de, RPI_STATUS, (ctb_row << 18) + 0x25);
++ p1_apb_write(de, RPI_TRANSFER, PROB_BACKUP);
++ p1_apb_write(de, RPI_MODE,
++ ctb_row == de->pic_height_in_ctbs_y - 1 ?
++ 0x70000 : 0x30000);
++ p1_apb_write(de, RPI_CONTROL, (ctb_row << 16) + 2);
++}
++
++static void wpp_end_previous_slice(struct rpivid_dec_env *const de,
++ const struct rpivid_dec_state *const s,
++ int ctb_addr_ts)
++{
++ int new_x = s->sh->slice_segment_addr % de->pic_width_in_ctbs_y;
++ int new_y = s->sh->slice_segment_addr / de->pic_width_in_ctbs_y;
++ int last_x =
++ s->ctb_addr_ts_to_rs[ctb_addr_ts - 1] % de->pic_width_in_ctbs_y;
++ int last_y =
++ s->ctb_addr_ts_to_rs[ctb_addr_ts - 1] / de->pic_width_in_ctbs_y;
++
++ if (de->wpp_entry_x < 2 && (de->wpp_entry_y < new_y || new_x > 2) &&
++ de->pic_width_in_ctbs_y > 2)
++ wpp_pause(de, last_y);
++ p1_apb_write(de, RPI_STATUS, 1 + (last_x << 5) + (last_y << 18));
++ if (new_x == 2 || (de->pic_width_in_ctbs_y == 2 &&
++ de->wpp_entry_y < new_y))
++ p1_apb_write(de, RPI_TRANSFER, PROB_BACKUP);
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Wavefront mode
++
++static void wpp_entry_point(struct rpivid_dec_env *const de,
++ const struct rpivid_dec_state *const s,
++ const int do_bte,
++ const int reset_qp_y, const int ctb_addr_ts)
++{
++ int ctb_size = 1 << s->log2_ctb_size;
++ int ctb_addr_rs = s->ctb_addr_ts_to_rs[ctb_addr_ts];
++
++ int ctb_col = de->wpp_entry_x = ctb_addr_rs % de->pic_width_in_ctbs_y;
++ int ctb_row = de->wpp_entry_y = ctb_addr_rs / de->pic_width_in_ctbs_y;
++
++ int endx = de->pic_width_in_ctbs_y - 1;
++ int endy = ctb_row;
++
++ u8 slice_w = ctb_to_slice_w_h(ctb_col, ctb_size,
++ s->sps.pic_width_in_luma_samples,
++ s->col_bd, s->num_tile_columns);
++ u8 slice_h = ctb_to_slice_w_h(ctb_row, ctb_size,
++ s->sps.pic_height_in_luma_samples,
++ s->row_bd, s->num_tile_rows);
++
++ p1_apb_write(de, RPI_TILESTART, 0);
++ p1_apb_write(de, RPI_TILEEND, endx + (endy << 16));
++
++ if (do_bte)
++ p1_apb_write(de, RPI_BEGINTILEEND, endx + (endy << 16));
++
++ write_slice(de, s, slice_w,
++ ctb_row == de->pic_height_in_ctbs_y - 1 ?
++ slice_h : ctb_size);
++
++ if (reset_qp_y) {
++ unsigned int sps_qp_bd_offset =
++ 6 * s->sps.bit_depth_luma_minus8;
++
++ p1_apb_write(de, RPI_QP, sps_qp_bd_offset + s->slice_qp);
++ }
++
++ p1_apb_write(de, RPI_MODE,
++ ctb_row == de->pic_height_in_ctbs_y - 1 ?
++ 0x60001 : 0x20001);
++ p1_apb_write(de, RPI_CONTROL, (ctb_col << 0) + (ctb_row << 16));
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Wavefront mode
++
++static void wpp_decode_slice(struct rpivid_dec_env *const de,
++ const struct rpivid_dec_state *const s,
++ const struct v4l2_ctrl_hevc_slice_params *sh,
++ int ctb_addr_ts)
++{
++ int i, reset_qp_y = 1;
++ int indep = !s->dependent_slice_segment_flag;
++ int ctb_col = s->sh->slice_segment_addr % de->pic_width_in_ctbs_y;
++
++ if (ctb_addr_ts)
++ wpp_end_previous_slice(de, s, ctb_addr_ts);
++ pre_slice_decode(de, s);
++ write_bitstream(de, s);
++ if (ctb_addr_ts == 0 || indep || de->pic_width_in_ctbs_y == 1)
++ write_prob(de, s);
++ else if (ctb_col == 0)
++ p1_apb_write(de, RPI_TRANSFER, PROB_RELOAD);
++ else
++ reset_qp_y = 0;
++ program_slicecmds(de, s->slice_idx);
++ new_slice_segment(de, s);
++ wpp_entry_point(de, s, indep, reset_qp_y, ctb_addr_ts);
++
++ for (i = 0; i < s->sh->num_entry_point_offsets; i++) {
++ int ctb_addr_rs = s->ctb_addr_ts_to_rs[ctb_addr_ts];
++ int ctb_row = ctb_addr_rs / de->pic_width_in_ctbs_y;
++ int last_x = de->pic_width_in_ctbs_y - 1;
++
++ if (de->pic_width_in_ctbs_y > 2)
++ wpp_pause(de, ctb_row);
++ p1_apb_write(de, RPI_STATUS,
++ (ctb_row << 18) + (last_x << 5) + 2);
++ if (de->pic_width_in_ctbs_y == 2)
++ p1_apb_write(de, RPI_TRANSFER, PROB_BACKUP);
++ if (de->pic_width_in_ctbs_y == 1)
++ write_prob(de, s);
++ else
++ p1_apb_write(de, RPI_TRANSFER, PROB_RELOAD);
++ ctb_addr_ts += s->column_width[0];
++ wpp_entry_point(de, s, 0, 1, ctb_addr_ts);
++ }
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Tiles mode
++
++static void decode_slice(struct rpivid_dec_env *const de,
++ const struct rpivid_dec_state *const s,
++ const struct v4l2_ctrl_hevc_slice_params *const sh,
++ int ctb_addr_ts)
++{
++ int i, reset_qp_y;
++
++ if (ctb_addr_ts)
++ end_previous_slice(de, s, ctb_addr_ts);
++
++ pre_slice_decode(de, s);
++ write_bitstream(de, s);
++
++#if DEBUG_TRACE_P1_CMD
++ if (p1_z < 256) {
++ v4l2_info(&de->ctx->dev->v4l2_dev,
++ "TS=%d, tile=%d/%d, dss=%d, flags=%#llx\n",
++ ctb_addr_ts, s->tile_id[ctb_addr_ts],
++ s->tile_id[ctb_addr_ts - 1],
++ s->dependent_slice_segment_flag, sh->flags);
++ }
++#endif
++
++ reset_qp_y = ctb_addr_ts == 0 ||
++ s->tile_id[ctb_addr_ts] != s->tile_id[ctb_addr_ts - 1] ||
++ !s->dependent_slice_segment_flag;
++ if (reset_qp_y)
++ write_prob(de, s);
++
++ program_slicecmds(de, s->slice_idx);
++ new_slice_segment(de, s);
++ new_entry_point(de, s, !s->dependent_slice_segment_flag, reset_qp_y,
++ ctb_addr_ts);
++
++ for (i = 0; i < s->sh->num_entry_point_offsets; i++) {
++ int ctb_addr_rs = s->ctb_addr_ts_to_rs[ctb_addr_ts];
++ int ctb_col = ctb_addr_rs % de->pic_width_in_ctbs_y;
++ int ctb_row = ctb_addr_rs / de->pic_width_in_ctbs_y;
++ int tile_x = ctb_to_tile(ctb_col, s->col_bd,
++ s->num_tile_columns - 1);
++ int tile_y =
++ ctb_to_tile(ctb_row, s->row_bd, s->num_tile_rows - 1);
++ int last_x = s->col_bd[tile_x + 1] - 1;
++ int last_y = s->row_bd[tile_y + 1] - 1;
++
++ p1_apb_write(de, RPI_STATUS,
++ 2 + (last_x << 5) + (last_y << 18));
++ write_prob(de, s);
++ ctb_addr_ts += s->column_width[tile_x] * s->row_height[tile_y];
++ new_entry_point(de, s, 0, 1, ctb_addr_ts);
++ }
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Scaling factors
++
++static void expand_scaling_list(const unsigned int size_id,
++ const unsigned int matrix_id, u8 *const dst0,
++ const u8 *const src0, uint8_t dc)
++{
++ u8 *d;
++ unsigned int x, y;
++
++ // FIXME: matrix_id is unused ?
++ switch (size_id) {
++ case 0:
++ memcpy(dst0, src0, 16);
++ break;
++ case 1:
++ memcpy(dst0, src0, 64);
++ break;
++ case 2:
++ d = dst0;
++
++ for (y = 0; y != 16; y++) {
++ const u8 *s = src0 + (y >> 1) * 8;
++
++ for (x = 0; x != 8; ++x) {
++ *d++ = *s;
++ *d++ = *s++;
++ }
++ }
++ dst0[0] = dc;
++ break;
++ default:
++ d = dst0;
++
++ for (y = 0; y != 32; y++) {
++ const u8 *s = src0 + (y >> 2) * 8;
++
++ for (x = 0; x != 8; ++x) {
++ *d++ = *s;
++ *d++ = *s;
++ *d++ = *s;
++ *d++ = *s++;
++ }
++ }
++ dst0[0] = dc;
++ break;
++ }
++}
++
++static void populate_scaling_factors(const struct rpivid_run *const run,
++ struct rpivid_dec_env *const de,
++ const struct rpivid_dec_state *const s)
++{
++ const struct v4l2_ctrl_hevc_scaling_matrix *const sl =
++ run->h265.scaling_matrix;
++ // Array of constants for scaling factors
++ static const u32 scaling_factor_offsets[4][6] = {
++ // MID0 MID1 MID2 MID3 MID4 MID5
++ // SID0 (4x4)
++ { 0x0000, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050 },
++ // SID1 (8x8)
++ { 0x0060, 0x00A0, 0x00E0, 0x0120, 0x0160, 0x01A0 },
++ // SID2 (16x16)
++ { 0x01E0, 0x02E0, 0x03E0, 0x04E0, 0x05E0, 0x06E0 },
++ // SID3 (32x32)
++ { 0x07E0, 0x0BE0, 0x0000, 0x0000, 0x0000, 0x0000 }
++ };
++
++ unsigned int mid;
++
++ for (mid = 0; mid < 6; mid++)
++ expand_scaling_list(0, mid,
++ de->scaling_factors +
++ scaling_factor_offsets[0][mid],
++ sl->scaling_list_4x4[mid], 0);
++ for (mid = 0; mid < 6; mid++)
++ expand_scaling_list(1, mid,
++ de->scaling_factors +
++ scaling_factor_offsets[1][mid],
++ sl->scaling_list_8x8[mid], 0);
++ for (mid = 0; mid < 6; mid++)
++ expand_scaling_list(2, mid,
++ de->scaling_factors +
++ scaling_factor_offsets[2][mid],
++ sl->scaling_list_16x16[mid],
++ sl->scaling_list_dc_coef_16x16[mid]);
++ for (mid = 0; mid < 2; mid += 1)
++ expand_scaling_list(3, mid,
++ de->scaling_factors +
++ scaling_factor_offsets[3][mid],
++ sl->scaling_list_32x32[mid],
++ sl->scaling_list_dc_coef_32x32[mid]);
++}
++
++static void free_ps_info(struct rpivid_dec_state *const s)
++{
++ kfree(s->ctb_addr_rs_to_ts);
++ s->ctb_addr_rs_to_ts = NULL;
++ kfree(s->ctb_addr_ts_to_rs);
++ s->ctb_addr_ts_to_rs = NULL;
++ kfree(s->tile_id);
++ s->tile_id = NULL;
++
++ kfree(s->col_bd);
++ s->col_bd = NULL;
++ kfree(s->row_bd);
++ s->row_bd = NULL;
++}
++
++static int updated_ps(struct rpivid_dec_state *const s)
++{
++ unsigned int ctb_addr_rs;
++ int j, x, y, tile_id;
++ unsigned int i;
++
++ free_ps_info(s);
++
++ // Inferred parameters
++ s->log2_ctb_size = s->sps.log2_min_luma_coding_block_size_minus3 + 3 +
++ s->sps.log2_diff_max_min_luma_coding_block_size;
++
++ s->ctb_width = (s->sps.pic_width_in_luma_samples +
++ (1 << s->log2_ctb_size) - 1) >>
++ s->log2_ctb_size;
++ s->ctb_height = (s->sps.pic_height_in_luma_samples +
++ (1 << s->log2_ctb_size) - 1) >>
++ s->log2_ctb_size;
++ s->ctb_size = s->ctb_width * s->ctb_height;
++
++ // Inferred parameters
++
++ if (!(s->pps.flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED)) {
++ s->num_tile_columns = 1;
++ s->num_tile_rows = 1;
++ s->column_width[0] = s->ctb_width;
++ s->row_height[0] = s->ctb_height;
++ } else {
++ s->num_tile_columns = s->pps.num_tile_columns_minus1 + 1;
++ s->num_tile_rows = s->pps.num_tile_rows_minus1 + 1;
++ for (i = 0; i < s->num_tile_columns; ++i)
++ s->column_width[i] = s->pps.column_width_minus1[i] + 1;
++ for (i = 0; i < s->num_tile_rows; ++i)
++ s->row_height[i] = s->pps.row_height_minus1[i] + 1;
++ }
++
++ s->col_bd = kmalloc((s->num_tile_columns + 1) * sizeof(*s->col_bd),
++ GFP_KERNEL);
++ s->row_bd = kmalloc((s->num_tile_rows + 1) * sizeof(*s->row_bd),
++ GFP_KERNEL);
++
++ s->col_bd[0] = 0;
++ for (i = 0; i < s->num_tile_columns; i++)
++ s->col_bd[i + 1] = s->col_bd[i] + s->column_width[i];
++
++ s->row_bd[0] = 0;
++ for (i = 0; i < s->num_tile_rows; i++)
++ s->row_bd[i + 1] = s->row_bd[i] + s->row_height[i];
++
++ s->ctb_addr_rs_to_ts = kmalloc_array(s->ctb_size,
++ sizeof(*s->ctb_addr_rs_to_ts),
++ GFP_KERNEL);
++ s->ctb_addr_ts_to_rs = kmalloc_array(s->ctb_size,
++ sizeof(*s->ctb_addr_ts_to_rs),
++ GFP_KERNEL);
++ s->tile_id = kmalloc_array(s->ctb_size, sizeof(*s->tile_id),
++ GFP_KERNEL);
++
++ for (ctb_addr_rs = 0; ctb_addr_rs < s->ctb_size; ctb_addr_rs++) {
++ int tb_x = ctb_addr_rs % s->ctb_width;
++ int tb_y = ctb_addr_rs / s->ctb_width;
++ int tile_x = 0;
++ int tile_y = 0;
++ int val = 0;
++
++ for (i = 0; i < s->num_tile_columns; i++) {
++ if (tb_x < s->col_bd[i + 1]) {
++ tile_x = i;
++ break;
++ }
++ }
++
++ for (i = 0; i < s->num_tile_rows; i++) {
++ if (tb_y < s->row_bd[i + 1]) {
++ tile_y = i;
++ break;
++ }
++ }
++
++ for (i = 0; i < tile_x; i++)
++ val += s->row_height[tile_y] * s->column_width[i];
++ for (i = 0; i < tile_y; i++)
++ val += s->ctb_width * s->row_height[i];
++
++ val += (tb_y - s->row_bd[tile_y]) * s->column_width[tile_x] +
++ tb_x - s->col_bd[tile_x];
++
++ s->ctb_addr_rs_to_ts[ctb_addr_rs] = val;
++ s->ctb_addr_ts_to_rs[val] = ctb_addr_rs;
++ }
++
++ for (j = 0, tile_id = 0; j < s->num_tile_rows; j++)
++ for (i = 0; i < s->num_tile_columns; i++, tile_id++)
++ for (y = s->row_bd[j]; y < s->row_bd[j + 1]; y++)
++ for (x = s->col_bd[i];
++ x < s->col_bd[i + 1];
++ x++)
++ s->tile_id[s->ctb_addr_rs_to_ts
++ [y * s->ctb_width +
++ x]] = tile_id;
++
++ return 0;
++}
++
++static int frame_end(struct rpivid_dev *const dev,
++ struct rpivid_dec_env *const de,
++ const struct rpivid_dec_state *const s)
++{
++ const unsigned int last_x = s->col_bd[s->num_tile_columns] - 1;
++ const unsigned int last_y = s->row_bd[s->num_tile_rows] - 1;
++ size_t cmd_size;
++
++ if (s->pps.flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED) {
++ if (de->wpp_entry_x < 2 && de->pic_width_in_ctbs_y > 2)
++ wpp_pause(de, last_y);
++ }
++ p1_apb_write(de, RPI_STATUS, 1 + (last_x << 5) + (last_y << 18));
++
++ // Copy commands out to dma buf
++ cmd_size = de->cmd_len * sizeof(de->cmd_fifo[0]);
++
++ if (!de->cmd_copy_gptr->ptr || cmd_size > de->cmd_copy_gptr->size) {
++ size_t cmd_alloc = round_up_size(cmd_size);
++
++ if (gptr_realloc_new(dev, de->cmd_copy_gptr, cmd_alloc)) {
++ v4l2_err(&dev->v4l2_dev,
++ "Alloc cmd buffer (%d): FAILED\n", cmd_alloc);
++ return -ENOMEM;
++ }
++ v4l2_info(&dev->v4l2_dev, "Alloc cmd buffer (%d): OK\n",
++ cmd_alloc);
++ }
++
++ memcpy(de->cmd_copy_gptr->ptr, de->cmd_fifo, cmd_size);
++ return 0;
++}
++
++static void setup_colmv(struct rpivid_ctx *const ctx, struct rpivid_run *run,
++ struct rpivid_dec_state *const s)
++{
++ ctx->colmv_stride = ALIGN(s->sps.pic_width_in_luma_samples, 64);
++ ctx->colmv_picsize = ctx->colmv_stride *
++ (ALIGN(s->sps.pic_height_in_luma_samples, 64) >> 4);
++}
++
++// Can be called from irq context
++static struct rpivid_dec_env *dec_env_new(struct rpivid_ctx *const ctx)
++{
++ struct rpivid_dec_env *de;
++ unsigned long lock_flags;
++
++ spin_lock_irqsave(&ctx->dec_lock, lock_flags);
++
++ de = ctx->dec_free;
++ if (de) {
++ ctx->dec_free = de->next;
++ de->next = NULL;
++ de->state = RPIVID_DECODE_SLICE_START;
++ }
++
++ spin_unlock_irqrestore(&ctx->dec_lock, lock_flags);
++ return de;
++}
++
++// Can be called from irq context
++static void dec_env_delete(struct rpivid_dec_env *const de)
++{
++ struct rpivid_ctx * const ctx = de->ctx;
++ unsigned long lock_flags;
++
++ aux_q_release(ctx, &de->frame_aux);
++ aux_q_release(ctx, &de->col_aux);
++
++ spin_lock_irqsave(&ctx->dec_lock, lock_flags);
++
++ de->state = RPIVID_DECODE_END;
++ de->next = ctx->dec_free;
++ ctx->dec_free = de;
++
++ spin_unlock_irqrestore(&ctx->dec_lock, lock_flags);
++}
++
++static void dec_env_uninit(struct rpivid_ctx *const ctx)
++{
++ unsigned int i;
++
++ if (ctx->dec_pool) {
++ for (i = 0; i != RPIVID_DEC_ENV_COUNT; ++i) {
++ struct rpivid_dec_env *const de = ctx->dec_pool + i;
++
++ kfree(de->cmd_fifo);
++ }
++
++ kfree(ctx->dec_pool);
++ }
++
++ ctx->dec_pool = NULL;
++ ctx->dec_free = NULL;
++}
++
++static int dec_env_init(struct rpivid_ctx *const ctx)
++{
++ unsigned int i;
++
++ ctx->dec_pool = kzalloc(sizeof(*ctx->dec_pool) * RPIVID_DEC_ENV_COUNT,
++ GFP_KERNEL);
++ if (!ctx->dec_pool)
++ return -1;
++
++ spin_lock_init(&ctx->dec_lock);
++
++ // Build free chain
++ ctx->dec_free = ctx->dec_pool;
++ for (i = 0; i != RPIVID_DEC_ENV_COUNT - 1; ++i)
++ ctx->dec_pool[i].next = ctx->dec_pool + i + 1;
++
++ // Fill in other bits
++ for (i = 0; i != RPIVID_DEC_ENV_COUNT; ++i) {
++ struct rpivid_dec_env *const de = ctx->dec_pool + i;
++
++ de->ctx = ctx;
++ de->decode_order = i;
++ de->cmd_max = 1024;
++ de->cmd_fifo = kmalloc_array(de->cmd_max,
++ sizeof(struct rpi_cmd),
++ GFP_KERNEL);
++ if (!de->cmd_fifo)
++ goto fail;
++ }
++
++ return 0;
++
++fail:
++ dec_env_uninit(ctx);
++ return -1;
++}
++
++// Assume that we get exactly the same DPB for every slice
++// it makes no real sense otherwise
++#if V4L2_HEVC_DPB_ENTRIES_NUM_MAX > 16
++#error HEVC_DPB_ENTRIES > h/w slots
++#endif
++
++static u32 mk_config2(const struct rpivid_dec_state *const s)
++{
++ const struct v4l2_ctrl_hevc_sps *const sps = &s->sps;
++ const struct v4l2_ctrl_hevc_pps *const pps = &s->pps;
++ u32 c;
++ // BitDepthY
++ c = (sps->bit_depth_luma_minus8 + 8) << 0;
++ // BitDepthC
++ c |= (sps->bit_depth_chroma_minus8 + 8) << 4;
++ // BitDepthY
++ if (sps->bit_depth_luma_minus8)
++ c |= BIT(8);
++ // BitDepthC
++ if (sps->bit_depth_chroma_minus8)
++ c |= BIT(9);
++ c |= s->log2_ctb_size << 10;
++ if (pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED)
++ c |= BIT(13);
++ if (sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED)
++ c |= BIT(14);
++ if (sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED)
++ c |= BIT(15); /* Write motion vectors to external memory */
++ c |= (pps->log2_parallel_merge_level_minus2 + 2) << 16;
++ if (s->slice_temporal_mvp)
++ c |= BIT(19);
++ if (sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED)
++ c |= BIT(20);
++ c |= (pps->pps_cb_qp_offset & 31) << 21;
++ c |= (pps->pps_cr_qp_offset & 31) << 26;
++ return c;
++}
++
++static void rpivid_h265_setup(struct rpivid_ctx *ctx, struct rpivid_run *run)
++{
++ struct rpivid_dev *const dev = ctx->dev;
++ const struct v4l2_ctrl_hevc_slice_params *const sh =
++ run->h265.slice_params;
++ const struct v4l2_hevc_pred_weight_table *pred_weight_table;
++ struct rpivid_q_aux *dpb_q_aux[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++ struct rpivid_dec_state *const s = ctx->state;
++ struct vb2_queue *vq;
++ struct rpivid_dec_env *de;
++ int ctb_addr_ts;
++ unsigned int i;
++ int use_aux;
++ bool slice_temporal_mvp;
++
++ pred_weight_table = &sh->pred_weight_table;
++
++ s->frame_end =
++ ((run->src->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF) == 0);
++
++ de = ctx->dec0;
++ slice_temporal_mvp = (sh->flags &
++ V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED);
++
++ if (de && de->state != RPIVID_DECODE_END) {
++ ++s->slice_idx;
++
++ switch (de->state) {
++ case RPIVID_DECODE_SLICE_CONTINUE:
++ // Expected state
++ break;
++ default:
++ v4l2_err(&dev->v4l2_dev, "%s: Unexpected state: %d\n",
++ __func__, de->state);
++ /* FALLTHRU */
++ case RPIVID_DECODE_ERROR_CONTINUE:
++ // Uncleared error - fail now
++ goto fail;
++ }
++
++ if (s->slice_temporal_mvp != slice_temporal_mvp) {
++ v4l2_warn(&dev->v4l2_dev,
++ "Slice Temporal MVP non-constant\n");
++ goto fail;
++ }
++ } else {
++ /* Frame start */
++ unsigned int ctb_size_y;
++ bool sps_changed = false;
++
++ if (memcmp(&s->sps, run->h265.sps, sizeof(s->sps)) != 0) {
++ /* SPS changed */
++ v4l2_info(&dev->v4l2_dev, "SPS changed\n");
++ memcpy(&s->sps, run->h265.sps, sizeof(s->sps));
++ sps_changed = true;
++ }
++ if (sps_changed ||
++ memcmp(&s->pps, run->h265.pps, sizeof(s->pps)) != 0) {
++ /* SPS changed */
++ v4l2_info(&dev->v4l2_dev, "PPS changed\n");
++ memcpy(&s->pps, run->h265.pps, sizeof(s->pps));
++
++ /* Recalc stuff as required */
++ updated_ps(s);
++ }
++
++ de = dec_env_new(ctx);
++ if (!de) {
++ v4l2_err(&dev->v4l2_dev,
++ "Failed to find free decode env\n");
++ goto fail;
++ }
++ ctx->dec0 = de;
++
++ ctb_size_y =
++ 1U << (s->sps.log2_min_luma_coding_block_size_minus3 +
++ 3 +
++ s->sps.log2_diff_max_min_luma_coding_block_size);
++
++ de->pic_width_in_ctbs_y =
++ (s->sps.pic_width_in_luma_samples + ctb_size_y - 1) /
++ ctb_size_y; // 7-15
++ de->pic_height_in_ctbs_y =
++ (s->sps.pic_height_in_luma_samples + ctb_size_y - 1) /
++ ctb_size_y; // 7-17
++ de->cmd_len = 0;
++ de->dpbno_col = ~0U;
++
++ de->bit_copy_gptr = ctx->bitbufs + 0;
++ de->bit_copy_len = 0;
++ de->cmd_copy_gptr = ctx->cmdbufs + 0;
++
++ de->frame_c_offset = ctx->dst_fmt.height * 128;
++ de->frame_stride = ctx->dst_fmt.bytesperline * 128;
++ de->frame_addr =
++ vb2_dma_contig_plane_dma_addr(&run->dst->vb2_buf, 0);
++ de->frame_aux = NULL;
++
++ if (s->sps.bit_depth_luma_minus8 !=
++ s->sps.bit_depth_chroma_minus8) {
++ v4l2_warn(&dev->v4l2_dev,
++ "Chroma depth (%d) != Luma depth (%d)\n",
++ s->sps.bit_depth_chroma_minus8 + 8,
++ s->sps.bit_depth_luma_minus8 + 8);
++ goto fail;
++ }
++ if (s->sps.bit_depth_luma_minus8 == 0) {
++ if (ctx->dst_fmt.pixelformat !=
++ V4L2_PIX_FMT_NV12_COL128) {
++ v4l2_err(&dev->v4l2_dev,
++ "Pixel format %#x != NV12_COL128 for 8-bit output",
++ ctx->dst_fmt.pixelformat);
++ goto fail;
++ }
++ } else if (s->sps.bit_depth_luma_minus8 == 2) {
++ if (ctx->dst_fmt.pixelformat !=
++ V4L2_PIX_FMT_NV12_10_COL128) {
++ v4l2_err(&dev->v4l2_dev,
++ "Pixel format %#x != NV12_10_COL128 for 10-bit output",
++ ctx->dst_fmt.pixelformat);
++ goto fail;
++ }
++ } else {
++ v4l2_warn(&dev->v4l2_dev,
++ "Luma depth (%d) unsupported\n",
++ s->sps.bit_depth_luma_minus8 + 8);
++ goto fail;
++ }
++ if (run->dst->vb2_buf.num_planes != 1) {
++ v4l2_warn(&dev->v4l2_dev, "Capture planes (%d) != 1\n",
++ run->dst->vb2_buf.num_planes);
++ goto fail;
++ }
++ if (run->dst->planes[0].length <
++ ctx->dst_fmt.sizeimage) {
++ v4l2_warn(&dev->v4l2_dev,
++ "Capture plane[0] length (%d) < sizeimage (%d)\n",
++ run->dst->planes[0].length,
++ ctx->dst_fmt.sizeimage);
++ goto fail;
++ }
++
++ if (s->sps.pic_width_in_luma_samples > 4096 ||
++ s->sps.pic_height_in_luma_samples > 4096) {
++ v4l2_warn(&dev->v4l2_dev,
++ "Pic dimension (%dx%d) exeeds 4096\n",
++ s->sps.pic_width_in_luma_samples,
++ s->sps.pic_height_in_luma_samples);
++ goto fail;
++ }
++
++ // Fill in ref planes with our address s.t. if we mess
++ // up refs somehow then we still have a valid address
++ // entry
++ for (i = 0; i != 16; ++i)
++ de->ref_addrs[i] = de->frame_addr;
++
++ /*
++ * Stash initial temporal_mvp flag
++ * This must be the same for all pic slices (7.4.7.1)
++ */
++ s->slice_temporal_mvp = slice_temporal_mvp;
++
++ // Phase 2 reg pre-calc
++ de->rpi_config2 = mk_config2(s);
++ de->rpi_framesize = (s->sps.pic_height_in_luma_samples << 16) |
++ s->sps.pic_width_in_luma_samples;
++ de->rpi_currpoc = sh->slice_pic_order_cnt;
++
++ if (s->sps.flags &
++ V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED) {
++ setup_colmv(ctx, run, s);
++ }
++
++ s->slice_idx = 0;
++
++ if (sh->slice_segment_addr != 0) {
++ v4l2_warn(&dev->v4l2_dev,
++ "New frame but segment_addr=%d\n",
++ sh->slice_segment_addr);
++ goto fail;
++ }
++
++ /* Allocate a bitbuf if we need one - don't need one if single
++ * slice as we can use the src buf directly
++ */
++ if (!s->frame_end && !de->bit_copy_gptr->ptr) {
++ const size_t wxh = s->sps.pic_width_in_luma_samples *
++ s->sps.pic_height_in_luma_samples;
++ size_t bits_alloc;
++
++ /* Annex A gives a min compression of 2 @ lvl 3.1
++ * (wxh <= 983040) and min 4 thereafter but avoid
++ * the odity of 983041 having a lower limit than
++ * 983040.
++ * Multiply by 3/2 for 4:2:0
++ */
++ bits_alloc = wxh < 983040 ? wxh * 3 / 4 :
++ wxh < 983040 * 2 ? 983040 * 3 / 4 :
++ wxh * 3 / 8;
++ bits_alloc = round_up_size(bits_alloc);
++
++ if (gptr_alloc(dev, de->bit_copy_gptr,
++ bits_alloc,
++ DMA_ATTR_FORCE_CONTIGUOUS) != 0) {
++ v4l2_err(&dev->v4l2_dev,
++ "Unable to alloc buf (%d) for bit copy\n",
++ bits_alloc);
++ goto fail;
++ }
++ v4l2_info(&dev->v4l2_dev,
++ "Alloc buf (%d) for bit copy OK\n",
++ bits_alloc);
++ }
++ }
++
++ // Pre calc a few things
++ s->src_addr =
++ !s->frame_end ?
++ 0 :
++ vb2_dma_contig_plane_dma_addr(&run->src->vb2_buf, 0);
++ s->src_buf = s->src_addr != 0 ? NULL :
++ vb2_plane_vaddr(&run->src->vb2_buf, 0);
++ if (!s->src_addr && !s->src_buf) {
++ v4l2_err(&dev->v4l2_dev, "Failed to map src buffer\n");
++ goto fail;
++ }
++
++ s->sh = sh;
++ s->slice_qp = 26 + s->pps.init_qp_minus26 + s->sh->slice_qp_delta;
++ s->max_num_merge_cand = sh->slice_type == HEVC_SLICE_I ?
++ 0 :
++ (5 - sh->five_minus_max_num_merge_cand);
++ // * SH DSS flag invented by me - but clearly needed
++ s->dependent_slice_segment_flag =
++ ((sh->flags &
++ V4L2_HEVC_SLICE_PARAMS_FLAG_DEPENDENT_SLICE_SEGMENT) != 0);
++
++ s->nb_refs[0] = (sh->slice_type == HEVC_SLICE_I) ?
++ 0 :
++ sh->num_ref_idx_l0_active_minus1 + 1;
++ s->nb_refs[1] = (sh->slice_type != HEVC_SLICE_B) ?
++ 0 :
++ sh->num_ref_idx_l1_active_minus1 + 1;
++
++ if (s->sps.flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED)
++ populate_scaling_factors(run, de, s);
++
++ ctb_addr_ts = s->ctb_addr_rs_to_ts[sh->slice_segment_addr];
++
++ if ((s->pps.flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED))
++ wpp_decode_slice(de, s, sh, ctb_addr_ts);
++ else
++ decode_slice(de, s, sh, ctb_addr_ts);
++
++ if (!s->frame_end)
++ return;
++
++ // Frame end
++ memset(dpb_q_aux, 0,
++ sizeof(*dpb_q_aux) * V4L2_HEVC_DPB_ENTRIES_NUM_MAX);
++ /*
++ * Need Aux ents for all (ref) DPB ents if temporal MV could
++ * be enabled for any pic
++ * ** At the moment we have aux ents for all pics whether or not
++ * they are ref
++ */
++ use_aux = ((s->sps.flags &
++ V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED) != 0);
++
++ // Locate ref frames
++ // At least in the current implementation this is constant across all
++ // slices. If this changes we will need idx mapping code.
++ // Uses sh so here rather than trigger
++
++ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
++
++ if (!vq) {
++ v4l2_err(&dev->v4l2_dev, "VQ gone!\n");
++ goto fail;
++ }
++
++ // v4l2_info(&dev->v4l2_dev, "rpivid_h265_end of frame\n");
++ if (frame_end(dev, de, s))
++ goto fail;
++
++ for (i = 0; i < sh->num_active_dpb_entries; ++i) {
++ int buffer_index =
++ vb2_find_timestamp(vq, sh->dpb[i].timestamp, 0);
++ struct vb2_buffer *buf = buffer_index < 0 ?
++ NULL :
++ vb2_get_buffer(vq, buffer_index);
++
++ if (!buf) {
++ v4l2_warn(&dev->v4l2_dev,
++ "Missing DPB ent %d, timestamp=%lld, index=%d\n",
++ i, (long long)sh->dpb[i].timestamp,
++ buffer_index);
++ continue;
++ }
++
++ if (use_aux) {
++ dpb_q_aux[i] = aux_q_ref(ctx,
++ ctx->aux_ents[buffer_index]);
++ if (!dpb_q_aux[i])
++ v4l2_warn(&dev->v4l2_dev,
++ "Missing DPB AUX ent %d index=%d\n",
++ i, buffer_index);
++ }
++
++ de->ref_addrs[i] =
++ vb2_dma_contig_plane_dma_addr(buf, 0);
++ }
++
++ // Move DPB from temp
++ for (i = 0; i != V4L2_HEVC_DPB_ENTRIES_NUM_MAX; ++i) {
++ aux_q_release(ctx, &s->ref_aux[i]);
++ s->ref_aux[i] = dpb_q_aux[i];
++ }
++ // Unref the old frame aux too - it is either in the DPB or not
++ // now
++ aux_q_release(ctx, &s->frame_aux);
++
++ if (use_aux) {
++ // New frame so new aux ent
++ // ??? Do we need this if non-ref ??? can we tell
++ s->frame_aux = aux_q_new(ctx, run->dst->vb2_buf.index);
++
++ if (!s->frame_aux) {
++ v4l2_err(&dev->v4l2_dev,
++ "Failed to obtain aux storage for frame\n");
++ goto fail;
++ }
++
++ de->frame_aux = aux_q_ref(ctx, s->frame_aux);
++ }
++
++ if (de->dpbno_col != ~0U) {
++ if (de->dpbno_col >= sh->num_active_dpb_entries) {
++ v4l2_err(&dev->v4l2_dev,
++ "Col ref index %d >= %d\n",
++ de->dpbno_col,
++ sh->num_active_dpb_entries);
++ } else {
++ // Standard requires that the col pic is
++ // constant for the duration of the pic
++ // (text of collocated_ref_idx in H265-2 2018
++ // 7.4.7.1)
++
++ // Spot the collocated ref in passing
++ de->col_aux = aux_q_ref(ctx,
++ dpb_q_aux[de->dpbno_col]);
++
++ if (!de->col_aux) {
++ v4l2_warn(&dev->v4l2_dev,
++ "Missing DPB ent for col\n");
++ // Probably need to abort if this fails
++ // as P2 may explode on bad data
++ goto fail;
++ }
++ }
++ }
++
++ de->state = RPIVID_DECODE_PHASE1;
++ return;
++
++fail:
++ if (de)
++ // Actual error reporting happens in Trigger
++ de->state = s->frame_end ? RPIVID_DECODE_ERROR_DONE :
++ RPIVID_DECODE_ERROR_CONTINUE;
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Handle PU and COEFF stream overflow
++
++// Returns:
++// -1 Phase 1 decode error
++// 0 OK
++// >0 Out of space (bitmask)
++
++#define STATUS_COEFF_EXHAUSTED 8
++#define STATUS_PU_EXHAUSTED 16
++
++static int check_status(const struct rpivid_dev *const dev)
++{
++ const u32 cfstatus = apb_read(dev, RPI_CFSTATUS);
++ const u32 cfnum = apb_read(dev, RPI_CFNUM);
++ u32 status = apb_read(dev, RPI_STATUS);
++
++ // Handle PU and COEFF stream overflow
++
++ // this is the definition of successful completion of phase 1
++ // it assures that status register is zero and all blocks in each tile
++ // have completed
++ if (cfstatus == cfnum)
++ return 0; //No error
++
++ status &= (STATUS_PU_EXHAUSTED | STATUS_COEFF_EXHAUSTED);
++ if (status)
++ return status;
++
++ return -1;
++}
++
++static void cb_phase2(struct rpivid_dev *const dev, void *v)
++{
++ struct rpivid_dec_env *const de = v;
++ struct rpivid_ctx *const ctx = de->ctx;
++
++ xtrace_in(dev, de);
++
++ v4l2_m2m_cap_buf_return(dev->m2m_dev, ctx->fh.m2m_ctx, de->frame_buf,
++ VB2_BUF_STATE_DONE);
++ de->frame_buf = NULL;
++
++ /* Delete de before finish as finish might immediately trigger a reuse
++ * of de
++ */
++ dec_env_delete(de);
++
++ if (atomic_add_return(-1, &ctx->p2out) >= RPIVID_P2BUF_COUNT - 1) {
++ xtrace_fin(dev, de);
++ v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx,
++ VB2_BUF_STATE_DONE);
++ }
++
++ xtrace_ok(dev, de);
++}
++
++static void phase2_claimed(struct rpivid_dev *const dev, void *v)
++{
++ struct rpivid_dec_env *const de = v;
++ unsigned int i;
++
++ xtrace_in(dev, de);
++
++ apb_write_vc_addr(dev, RPI_PURBASE, de->pu_base_vc);
++ apb_write_vc_len(dev, RPI_PURSTRIDE, de->pu_stride);
++ apb_write_vc_addr(dev, RPI_COEFFRBASE, de->coeff_base_vc);
++ apb_write_vc_len(dev, RPI_COEFFRSTRIDE, de->coeff_stride);
++
++ apb_write_vc_addr(dev, RPI_OUTYBASE, de->frame_addr);
++ apb_write_vc_addr(dev, RPI_OUTCBASE,
++ de->frame_addr + de->frame_c_offset);
++ apb_write_vc_len(dev, RPI_OUTYSTRIDE, de->frame_stride);
++ apb_write_vc_len(dev, RPI_OUTCSTRIDE, de->frame_stride);
++
++ // v4l2_info(&dev->v4l2_dev, "Frame: Y=%llx, C=%llx, Stride=%x\n",
++ // de->frame_addr, de->frame_addr + de->frame_c_offset,
++ // de->frame_stride);
++
++ for (i = 0; i < 16; i++) {
++ // Strides are in fact unused but fill in anyway
++ apb_write_vc_addr(dev, 0x9000 + 16 * i, de->ref_addrs[i]);
++ apb_write_vc_len(dev, 0x9004 + 16 * i, de->frame_stride);
++ apb_write_vc_addr(dev, 0x9008 + 16 * i,
++ de->ref_addrs[i] + de->frame_c_offset);
++ apb_write_vc_len(dev, 0x900C + 16 * i, de->frame_stride);
++ }
++
++ apb_write(dev, RPI_CONFIG2, de->rpi_config2);
++ apb_write(dev, RPI_FRAMESIZE, de->rpi_framesize);
++ apb_write(dev, RPI_CURRPOC, de->rpi_currpoc);
++ // v4l2_info(&dev->v4l2_dev, "Config2=%#x, FrameSize=%#x, POC=%#x\n",
++ // de->rpi_config2, de->rpi_framesize, de->rpi_currpoc);
++
++ // collocated reads/writes
++ apb_write_vc_len(dev, RPI_COLSTRIDE,
++ de->ctx->colmv_stride); // Read vals
++ apb_write_vc_len(dev, RPI_MVSTRIDE,
++ de->ctx->colmv_stride); // Write vals
++ apb_write_vc_addr(dev, RPI_MVBASE,
++ !de->frame_aux ? 0 : de->frame_aux->col.addr);
++ apb_write_vc_addr(dev, RPI_COLBASE,
++ !de->col_aux ? 0 : de->col_aux->col.addr);
++
++ //v4l2_info(&dev->v4l2_dev,
++ // "Mv=%llx, Col=%llx, Stride=%x, Buf=%llx->%llx\n",
++ // de->rpi_mvbase, de->rpi_colbase, de->ctx->colmv_stride,
++ // de->ctx->colmvbuf.addr, de->ctx->colmvbuf.addr +
++ // de->ctx->colmvbuf.size);
++
++ rpivid_hw_irq_active2_irq(dev, &de->irq_ent, cb_phase2, de);
++
++ apb_write_final(dev, RPI_NUMROWS, de->pic_height_in_ctbs_y);
++
++ xtrace_ok(dev, de);
++}
++
++static void phase1_claimed(struct rpivid_dev *const dev, void *v);
++
++static void phase1_thread(struct rpivid_dev *const dev, void *v)
++{
++ struct rpivid_dec_env *const de = v;
++ struct rpivid_ctx *const ctx = de->ctx;
++
++ struct rpivid_gptr *const pu_gptr = ctx->pu_bufs + ctx->p2idx;
++ struct rpivid_gptr *const coeff_gptr = ctx->coeff_bufs + ctx->p2idx;
++
++ xtrace_in(dev, de);
++
++ if (de->p1_status & STATUS_PU_EXHAUSTED) {
++ if (gptr_realloc_new(dev, pu_gptr, next_size(pu_gptr->size))) {
++ v4l2_err(&dev->v4l2_dev,
++ "%s: PU realloc (%#x) failed\n",
++ __func__, pu_gptr->size);
++ goto fail;
++ }
++ v4l2_info(&dev->v4l2_dev, "%s: PU realloc (%#x) OK\n",
++ __func__, pu_gptr->size);
++ }
++
++ if (de->p1_status & STATUS_COEFF_EXHAUSTED) {
++ if (gptr_realloc_new(dev, coeff_gptr,
++ next_size(coeff_gptr->size))) {
++ v4l2_err(&dev->v4l2_dev,
++ "%s: Coeff realloc (%#x) failed\n",
++ __func__, coeff_gptr->size);
++ goto fail;
++ }
++ v4l2_info(&dev->v4l2_dev, "%s: Coeff realloc (%#x) OK\n",
++ __func__, coeff_gptr->size);
++ }
++
++ phase1_claimed(dev, de);
++ xtrace_ok(dev, de);
++ return;
++
++fail:
++ dec_env_delete(de);
++ xtrace_fin(dev, de);
++ v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx,
++ VB2_BUF_STATE_ERROR);
++ xtrace_fail(dev, de);
++}
++
++/* Always called in irq context (this is good) */
++static void cb_phase1(struct rpivid_dev *const dev, void *v)
++{
++ struct rpivid_dec_env *const de = v;
++ struct rpivid_ctx *const ctx = de->ctx;
++
++ xtrace_in(dev, de);
++
++ de->p1_status = check_status(dev);
++ if (de->p1_status != 0) {
++ v4l2_info(&dev->v4l2_dev, "%s: Post wait: %#x\n",
++ __func__, de->p1_status);
++
++ if (de->p1_status < 0)
++ goto fail;
++
++ /* Need to realloc - push onto a thread rather than IRQ */
++ rpivid_hw_irq_active1_thread(dev, &de->irq_ent,
++ phase1_thread, de);
++ return;
++ }
++
++ /* After the frame-buf is detached it must be returned but from
++ * this point onward (phase2_claimed, cb_phase2) there are no error
++ * paths so the return at the end of cb_phase2 is all that is needed
++ */
++ de->frame_buf = v4l2_m2m_cap_buf_detach(dev->m2m_dev, ctx->fh.m2m_ctx);
++ if (!de->frame_buf) {
++ v4l2_err(&dev->v4l2_dev, "%s: No detached buffer\n", __func__);
++ goto fail;
++ }
++
++ ctx->p2idx =
++ (ctx->p2idx + 1 >= RPIVID_P2BUF_COUNT) ? 0 : ctx->p2idx + 1;
++
++ // Enable the next setup if our Q isn't too big
++ if (atomic_add_return(1, &ctx->p2out) < RPIVID_P2BUF_COUNT) {
++ xtrace_fin(dev, de);
++ v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx,
++ VB2_BUF_STATE_DONE);
++ }
++
++ rpivid_hw_irq_active2_claim(dev, &de->irq_ent, phase2_claimed, de);
++
++ xtrace_ok(dev, de);
++ return;
++
++fail:
++ dec_env_delete(de);
++ xtrace_fin(dev, de);
++ v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx,
++ VB2_BUF_STATE_ERROR);
++ xtrace_fail(dev, de);
++}
++
++static void phase1_claimed(struct rpivid_dev *const dev, void *v)
++{
++ struct rpivid_dec_env *const de = v;
++ struct rpivid_ctx *const ctx = de->ctx;
++
++ const struct rpivid_gptr * const pu_gptr = ctx->pu_bufs + ctx->p2idx;
++ const struct rpivid_gptr * const coeff_gptr = ctx->coeff_bufs +
++ ctx->p2idx;
++
++ xtrace_in(dev, de);
++
++ de->pu_base_vc = pu_gptr->addr;
++ de->pu_stride =
++ ALIGN_DOWN(pu_gptr->size / de->pic_height_in_ctbs_y, 64);
++
++ de->coeff_base_vc = coeff_gptr->addr;
++ de->coeff_stride =
++ ALIGN_DOWN(coeff_gptr->size / de->pic_height_in_ctbs_y, 64);
++
++ apb_write_vc_addr(dev, RPI_PUWBASE, de->pu_base_vc);
++ apb_write_vc_len(dev, RPI_PUWSTRIDE, de->pu_stride);
++ apb_write_vc_addr(dev, RPI_COEFFWBASE, de->coeff_base_vc);
++ apb_write_vc_len(dev, RPI_COEFFWSTRIDE, de->coeff_stride);
++
++ // Trigger command FIFO
++ apb_write(dev, RPI_CFNUM, de->cmd_len);
++
++ // Claim irq
++ rpivid_hw_irq_active1_irq(dev, &de->irq_ent, cb_phase1, de);
++
++ // And start the h/w
++ apb_write_vc_addr_final(dev, RPI_CFBASE, de->cmd_copy_gptr->addr);
++
++ xtrace_ok(dev, de);
++}
++
++static void dec_state_delete(struct rpivid_ctx *const ctx)
++{
++ unsigned int i;
++ struct rpivid_dec_state *const s = ctx->state;
++
++ if (!s)
++ return;
++ ctx->state = NULL;
++
++ free_ps_info(s);
++
++ for (i = 0; i != HEVC_MAX_REFS; ++i)
++ aux_q_release(ctx, &s->ref_aux[i]);
++ aux_q_release(ctx, &s->frame_aux);
++
++ kfree(s);
++}
++
++static void rpivid_h265_stop(struct rpivid_ctx *ctx)
++{
++ struct rpivid_dev *const dev = ctx->dev;
++ unsigned int i;
++
++ v4l2_info(&dev->v4l2_dev, "%s\n", __func__);
++
++ dec_env_uninit(ctx);
++ dec_state_delete(ctx);
++
++ // dec_env & state must be killed before this to release the buffer to
++ // the free pool
++ aux_q_uninit(ctx);
++
++ for (i = 0; i != ARRAY_SIZE(ctx->bitbufs); ++i)
++ gptr_free(dev, ctx->bitbufs + i);
++ for (i = 0; i != ARRAY_SIZE(ctx->cmdbufs); ++i)
++ gptr_free(dev, ctx->cmdbufs + i);
++ for (i = 0; i != ARRAY_SIZE(ctx->pu_bufs); ++i)
++ gptr_free(dev, ctx->pu_bufs + i);
++ for (i = 0; i != ARRAY_SIZE(ctx->coeff_bufs); ++i)
++ gptr_free(dev, ctx->coeff_bufs + i);
++}
++
++static int rpivid_h265_start(struct rpivid_ctx *ctx)
++{
++ struct rpivid_dev *const dev = ctx->dev;
++ unsigned int i;
++
++ unsigned int w = ctx->dst_fmt.width;
++ unsigned int h = ctx->dst_fmt.height;
++ unsigned int wxh;
++ size_t pu_alloc;
++ size_t coeff_alloc;
++
++ // Generate a sanitised WxH for memory alloc
++ // Assume HD if unset
++ if (w == 0)
++ w = 1920;
++ if (w > 4096)
++ w = 4096;
++ if (h == 0)
++ w = 1088;
++ if (h > 4096)
++ h = 4096;
++ wxh = w * h;
++
++ v4l2_info(&dev->v4l2_dev, "%s: (%dx%d)\n", __func__,
++ ctx->dst_fmt.width, ctx->dst_fmt.height);
++
++ ctx->dec0 = NULL;
++ ctx->state = kzalloc(sizeof(*ctx->state), GFP_KERNEL);
++ if (!ctx->state) {
++ v4l2_err(&dev->v4l2_dev, "Failed to allocate decode state\n");
++ goto fail;
++ }
++
++ if (dec_env_init(ctx) != 0) {
++ v4l2_err(&dev->v4l2_dev, "Failed to allocate decode envs\n");
++ goto fail;
++ }
++
++ // 16k is plenty for most purposes but we will realloc if needed
++ for (i = 0; i != ARRAY_SIZE(ctx->cmdbufs); ++i) {
++ if (gptr_alloc(dev, ctx->cmdbufs + i, 0x4000,
++ DMA_ATTR_FORCE_CONTIGUOUS))
++ goto fail;
++ }
++
++ // Finger in the air PU & Coeff alloc
++ // Will be realloced if too small
++ coeff_alloc = round_up_size(wxh);
++ pu_alloc = round_up_size(wxh / 4);
++ for (i = 0; i != ARRAY_SIZE(ctx->pu_bufs); ++i) {
++ // Don't actually need a kernel mapping here
++ if (gptr_alloc(dev, ctx->pu_bufs + i, pu_alloc,
++ DMA_ATTR_FORCE_CONTIGUOUS |
++ DMA_ATTR_NO_KERNEL_MAPPING))
++ goto fail;
++ if (gptr_alloc(dev, ctx->coeff_bufs + i, coeff_alloc,
++ DMA_ATTR_FORCE_CONTIGUOUS |
++ DMA_ATTR_NO_KERNEL_MAPPING))
++ goto fail;
++ }
++ aux_q_init(ctx);
++
++ return 0;
++
++fail:
++ rpivid_h265_stop(ctx);
++ return -ENOMEM;
++}
++
++static void rpivid_h265_trigger(struct rpivid_ctx *ctx)
++{
++ struct rpivid_dev *const dev = ctx->dev;
++ struct rpivid_dec_env *const de = ctx->dec0;
++
++ xtrace_in(dev, de);
++
++ switch (!de ? RPIVID_DECODE_ERROR_CONTINUE : de->state) {
++ case RPIVID_DECODE_SLICE_START:
++ de->state = RPIVID_DECODE_SLICE_CONTINUE;
++ /* FALLTHRU */
++ case RPIVID_DECODE_SLICE_CONTINUE:
++ v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx,
++ VB2_BUF_STATE_DONE);
++ break;
++ default:
++ v4l2_err(&dev->v4l2_dev, "%s: Unexpected state: %d\n", __func__,
++ de->state);
++ /* FALLTHRU */
++ case RPIVID_DECODE_ERROR_DONE:
++ ctx->dec0 = NULL;
++ dec_env_delete(de);
++ /* FALLTHRU */
++ case RPIVID_DECODE_ERROR_CONTINUE:
++ xtrace_fin(dev, de);
++ v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx,
++ VB2_BUF_STATE_ERROR);
++ break;
++ case RPIVID_DECODE_PHASE1:
++ ctx->dec0 = NULL;
++ rpivid_hw_irq_active1_claim(dev, &de->irq_ent, phase1_claimed,
++ de);
++ break;
++ }
++
++ xtrace_ok(dev, de);
++}
++
++struct rpivid_dec_ops rpivid_dec_ops_h265 = {
++ .setup = rpivid_h265_setup,
++ .start = rpivid_h265_start,
++ .stop = rpivid_h265_stop,
++ .trigger = rpivid_h265_trigger,
++};
+--- /dev/null
++++ b/drivers/staging/media/rpivid/rpivid_hw.c
+@@ -0,0 +1,321 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Raspberry Pi HEVC driver
++ *
++ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
++ *
++ * Based on the Cedrus VPU driver, that is:
++ *
++ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
++ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
++ * Copyright (C) 2018 Bootlin
++ */
++#include <linux/clk.h>
++#include <linux/component.h>
++#include <linux/dma-mapping.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/of_reserved_mem.h>
++#include <linux/of_device.h>
++#include <linux/of_platform.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/reset.h>
++
++#include <media/videobuf2-core.h>
++#include <media/v4l2-mem2mem.h>
++
++#include "rpivid.h"
++#include "rpivid_hw.h"
++
++static void pre_irq(struct rpivid_dev *dev, struct rpivid_hw_irq_ent *ient,
++ rpivid_irq_callback cb, void *v,
++ struct rpivid_hw_irq_ctrl *ictl)
++{
++ unsigned long flags;
++
++ if (ictl->irq) {
++ v4l2_err(&dev->v4l2_dev, "Attempt to claim IRQ when already claimed\n");
++ return;
++ }
++
++ ient->cb = cb;
++ ient->v = v;
++
++ // Not sure this lock is actually required
++ spin_lock_irqsave(&ictl->lock, flags);
++ ictl->irq = ient;
++ spin_unlock_irqrestore(&ictl->lock, flags);
++}
++
++static void sched_claim(struct rpivid_dev * const dev,
++ struct rpivid_hw_irq_ctrl * const ictl)
++{
++ for (;;) {
++ struct rpivid_hw_irq_ent *ient = NULL;
++ unsigned long flags;
++
++ spin_lock_irqsave(&ictl->lock, flags);
++
++ if (--ictl->no_sched <= 0) {
++ ient = ictl->claim;
++ if (!ictl->irq && ient) {
++ ictl->claim = ient->next;
++ ictl->no_sched = 1;
++ }
++ }
++
++ spin_unlock_irqrestore(&ictl->lock, flags);
++
++ if (!ient)
++ break;
++
++ ient->cb(dev, ient->v);
++ }
++}
++
++/* Should only ever be called from its own IRQ cb so no lock required */
++static void pre_thread(struct rpivid_dev *dev,
++ struct rpivid_hw_irq_ent *ient,
++ rpivid_irq_callback cb, void *v,
++ struct rpivid_hw_irq_ctrl *ictl)
++{
++ ient->cb = cb;
++ ient->v = v;
++ ictl->irq = ient;
++ ictl->thread_reqed = true;
++ ictl->no_sched++;
++}
++
++// Called in irq context
++static void do_irq(struct rpivid_dev * const dev,
++ struct rpivid_hw_irq_ctrl * const ictl)
++{
++ struct rpivid_hw_irq_ent *ient;
++ unsigned long flags;
++
++ spin_lock_irqsave(&ictl->lock, flags);
++ ient = ictl->irq;
++ if (ient) {
++ ictl->no_sched++;
++ ictl->irq = NULL;
++ }
++ spin_unlock_irqrestore(&ictl->lock, flags);
++
++ if (ient) {
++ ient->cb(dev, ient->v);
++
++ sched_claim(dev, ictl);
++ }
++}
++
++static void do_claim(struct rpivid_dev * const dev,
++ struct rpivid_hw_irq_ent *ient,
++ const rpivid_irq_callback cb, void * const v,
++ struct rpivid_hw_irq_ctrl * const ictl)
++{
++ unsigned long flags;
++
++ ient->next = NULL;
++ ient->cb = cb;
++ ient->v = v;
++
++ spin_lock_irqsave(&ictl->lock, flags);
++
++ if (ictl->claim) {
++ // If we have a Q then add to end
++ ictl->tail->next = ient;
++ ictl->tail = ient;
++ ient = NULL;
++ } else if (ictl->no_sched || ictl->irq) {
++ // Empty Q but other activity in progress so Q
++ ictl->claim = ient;
++ ictl->tail = ient;
++ ient = NULL;
++ } else {
++ // Nothing else going on - schedule immediately and
++ // prevent anything else scheduling claims
++ ictl->no_sched = 1;
++ }
++
++ spin_unlock_irqrestore(&ictl->lock, flags);
++
++ if (ient) {
++ ient->cb(dev, ient->v);
++
++ sched_claim(dev, ictl);
++ }
++}
++
++static void ictl_init(struct rpivid_hw_irq_ctrl * const ictl)
++{
++ spin_lock_init(&ictl->lock);
++ ictl->claim = NULL;
++ ictl->tail = NULL;
++ ictl->irq = NULL;
++ ictl->no_sched = 0;
++}
++
++static void ictl_uninit(struct rpivid_hw_irq_ctrl * const ictl)
++{
++ // Nothing to do
++}
++
++#if !OPT_DEBUG_POLL_IRQ
++static irqreturn_t rpivid_irq_irq(int irq, void *data)
++{
++ struct rpivid_dev * const dev = data;
++ __u32 ictrl;
++
++ ictrl = irq_read(dev, ARG_IC_ICTRL);
++ if (!(ictrl & ARG_IC_ICTRL_ALL_IRQ_MASK)) {
++ v4l2_warn(&dev->v4l2_dev, "IRQ but no IRQ bits set\n");
++ return IRQ_NONE;
++ }
++
++ // Cancel any/all irqs
++ irq_write(dev, ARG_IC_ICTRL, ictrl & ~ARG_IC_ICTRL_SET_ZERO_MASK);
++
++ // Service Active2 before Active1 so Phase 1 can transition to Phase 2
++ // without delay
++ if (ictrl & ARG_IC_ICTRL_ACTIVE2_INT_SET)
++ do_irq(dev, &dev->ic_active2);
++ if (ictrl & ARG_IC_ICTRL_ACTIVE1_INT_SET)
++ do_irq(dev, &dev->ic_active1);
++
++ return dev->ic_active1.thread_reqed || dev->ic_active2.thread_reqed ?
++ IRQ_WAKE_THREAD : IRQ_HANDLED;
++}
++
++static void do_thread(struct rpivid_dev * const dev,
++ struct rpivid_hw_irq_ctrl *const ictl)
++{
++ unsigned long flags;
++ struct rpivid_hw_irq_ent *ient = NULL;
++
++ spin_lock_irqsave(&ictl->lock, flags);
++
++ if (ictl->thread_reqed) {
++ ient = ictl->irq;
++ ictl->thread_reqed = false;
++ ictl->irq = NULL;
++ }
++
++ spin_unlock_irqrestore(&ictl->lock, flags);
++
++ if (ient) {
++ ient->cb(dev, ient->v);
++
++ sched_claim(dev, ictl);
++ }
++}
++
++static irqreturn_t rpivid_irq_thread(int irq, void *data)
++{
++ struct rpivid_dev * const dev = data;
++
++ do_thread(dev, &dev->ic_active1);
++ do_thread(dev, &dev->ic_active2);
++
++ return IRQ_HANDLED;
++}
++#endif
++
++/* May only be called from Active1 CB
++ * IRQs should not be expected until execution continues in the cb
++ */
++void rpivid_hw_irq_active1_thread(struct rpivid_dev *dev,
++ struct rpivid_hw_irq_ent *ient,
++ rpivid_irq_callback thread_cb, void *ctx)
++{
++ pre_thread(dev, ient, thread_cb, ctx, &dev->ic_active1);
++}
++
++void rpivid_hw_irq_active1_claim(struct rpivid_dev *dev,
++ struct rpivid_hw_irq_ent *ient,
++ rpivid_irq_callback ready_cb, void *ctx)
++{
++ do_claim(dev, ient, ready_cb, ctx, &dev->ic_active1);
++}
++
++void rpivid_hw_irq_active1_irq(struct rpivid_dev *dev,
++ struct rpivid_hw_irq_ent *ient,
++ rpivid_irq_callback irq_cb, void *ctx)
++{
++ pre_irq(dev, ient, irq_cb, ctx, &dev->ic_active1);
++}
++
++void rpivid_hw_irq_active2_claim(struct rpivid_dev *dev,
++ struct rpivid_hw_irq_ent *ient,
++ rpivid_irq_callback ready_cb, void *ctx)
++{
++ do_claim(dev, ient, ready_cb, ctx, &dev->ic_active2);
++}
++
++void rpivid_hw_irq_active2_irq(struct rpivid_dev *dev,
++ struct rpivid_hw_irq_ent *ient,
++ rpivid_irq_callback irq_cb, void *ctx)
++{
++ pre_irq(dev, ient, irq_cb, ctx, &dev->ic_active2);
++}
++
++int rpivid_hw_probe(struct rpivid_dev *dev)
++{
++ struct resource *res;
++ __u32 irq_stat;
++ int irq_dec;
++ int ret = 0;
++
++ ictl_init(&dev->ic_active1);
++ ictl_init(&dev->ic_active2);
++
++ res = platform_get_resource_byname(dev->pdev, IORESOURCE_MEM, "intc");
++ if (!res)
++ return -ENODEV;
++
++ dev->base_irq = devm_ioremap(dev->dev, res->start, resource_size(res));
++ if (IS_ERR(dev->base_irq))
++ return PTR_ERR(dev->base_irq);
++
++ res = platform_get_resource_byname(dev->pdev, IORESOURCE_MEM, "hevc");
++ if (!res)
++ return -ENODEV;
++
++ dev->base_h265 = devm_ioremap(dev->dev, res->start, resource_size(res));
++ if (IS_ERR(dev->base_h265))
++ return PTR_ERR(dev->base_h265);
++
++ dev->clock = devm_clk_get(&dev->pdev->dev, "hevc");
++ if (IS_ERR(dev->clock))
++ return PTR_ERR(dev->clock);
++
++ // Disable IRQs & reset anything pending
++ irq_write(dev, 0,
++ ARG_IC_ICTRL_ACTIVE1_EN_SET | ARG_IC_ICTRL_ACTIVE2_EN_SET);
++ irq_stat = irq_read(dev, 0);
++ irq_write(dev, 0, irq_stat);
++
++#if !OPT_DEBUG_POLL_IRQ
++ irq_dec = platform_get_irq(dev->pdev, 0);
++ if (irq_dec <= 0)
++ return irq_dec;
++ ret = devm_request_threaded_irq(dev->dev, irq_dec,
++ rpivid_irq_irq,
++ rpivid_irq_thread,
++ 0, dev_name(dev->dev), dev);
++ if (ret) {
++ dev_err(dev->dev, "Failed to request IRQ - %d\n", ret);
++
++ return ret;
++ }
++#endif
++ return ret;
++}
++
++void rpivid_hw_remove(struct rpivid_dev *dev)
++{
++ // IRQ auto freed on unload so no need to do it here
++ ictl_uninit(&dev->ic_active1);
++ ictl_uninit(&dev->ic_active2);
++}
++
+--- /dev/null
++++ b/drivers/staging/media/rpivid/rpivid_hw.h
+@@ -0,0 +1,300 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Raspberry Pi HEVC driver
++ *
++ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
++ *
++ * Based on the Cedrus VPU driver, that is:
++ *
++ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
++ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
++ * Copyright (C) 2018 Bootlin
++ */
++
++#ifndef _RPIVID_HW_H_
++#define _RPIVID_HW_H_
++
++struct rpivid_hw_irq_ent {
++ struct rpivid_hw_irq_ent *next;
++ rpivid_irq_callback cb;
++ void *v;
++};
++
++/* Phase 1 Register offsets */
++
++#define RPI_SPS0 0
++#define RPI_SPS1 4
++#define RPI_PPS 8
++#define RPI_SLICE 12
++#define RPI_TILESTART 16
++#define RPI_TILEEND 20
++#define RPI_SLICESTART 24
++#define RPI_MODE 28
++#define RPI_LEFT0 32
++#define RPI_LEFT1 36
++#define RPI_LEFT2 40
++#define RPI_LEFT3 44
++#define RPI_QP 48
++#define RPI_CONTROL 52
++#define RPI_STATUS 56
++#define RPI_VERSION 60
++#define RPI_BFBASE 64
++#define RPI_BFNUM 68
++#define RPI_BFCONTROL 72
++#define RPI_BFSTATUS 76
++#define RPI_PUWBASE 80
++#define RPI_PUWSTRIDE 84
++#define RPI_COEFFWBASE 88
++#define RPI_COEFFWSTRIDE 92
++#define RPI_SLICECMDS 96
++#define RPI_BEGINTILEEND 100
++#define RPI_TRANSFER 104
++#define RPI_CFBASE 108
++#define RPI_CFNUM 112
++#define RPI_CFSTATUS 116
++
++/* Phase 2 Register offsets */
++
++#define RPI_PURBASE 0x8000
++#define RPI_PURSTRIDE 0x8004
++#define RPI_COEFFRBASE 0x8008
++#define RPI_COEFFRSTRIDE 0x800C
++#define RPI_NUMROWS 0x8010
++#define RPI_CONFIG2 0x8014
++#define RPI_OUTYBASE 0x8018
++#define RPI_OUTYSTRIDE 0x801C
++#define RPI_OUTCBASE 0x8020
++#define RPI_OUTCSTRIDE 0x8024
++#define RPI_STATUS2 0x8028
++#define RPI_FRAMESIZE 0x802C
++#define RPI_MVBASE 0x8030
++#define RPI_MVSTRIDE 0x8034
++#define RPI_COLBASE 0x8038
++#define RPI_COLSTRIDE 0x803C
++#define RPI_CURRPOC 0x8040
++
++/*
++ * Write a general register value
++ * Order is unimportant
++ */
++static inline void apb_write(const struct rpivid_dev * const dev,
++ const unsigned int offset, const u32 val)
++{
++ writel_relaxed(val, dev->base_h265 + offset);
++}
++
++/* Write the final register value that actually starts the phase */
++static inline void apb_write_final(const struct rpivid_dev * const dev,
++ const unsigned int offset, const u32 val)
++{
++ writel(val, dev->base_h265 + offset);
++}
++
++static inline u32 apb_read(const struct rpivid_dev * const dev,
++ const unsigned int offset)
++{
++ return readl(dev->base_h265 + offset);
++}
++
++static inline void irq_write(const struct rpivid_dev * const dev,
++ const unsigned int offset, const u32 val)
++{
++ writel(val, dev->base_irq + offset);
++}
++
++static inline u32 irq_read(const struct rpivid_dev * const dev,
++ const unsigned int offset)
++{
++ return readl(dev->base_irq + offset);
++}
++
++static inline void apb_write_vc_addr(const struct rpivid_dev * const dev,
++ const unsigned int offset,
++ const dma_addr_t a)
++{
++ apb_write(dev, offset, (u32)(a >> 6));
++}
++
++static inline void apb_write_vc_addr_final(const struct rpivid_dev * const dev,
++ const unsigned int offset,
++ const dma_addr_t a)
++{
++ apb_write_final(dev, offset, (u32)(a >> 6));
++}
++
++static inline void apb_write_vc_len(const struct rpivid_dev * const dev,
++ const unsigned int offset,
++ const unsigned int x)
++{
++ apb_write(dev, offset, (x + 63) >> 6);
++}
++
++/* *ARG_IC_ICTRL - Interrupt control for ARGON Core*
++ * Offset (byte space) = 40'h2b10000
++ * Physical Address (byte space) = 40'h7eb10000
++ * Verilog Macro Address = `ARG_IC_REG_START + `ARGON_INTCTRL_ICTRL
++ * Reset Value = 32'b100x100x_100xxxxx_xxxxxxx0_x100x100
++ * Access = RW (32-bit only)
++ * Interrupt control logic for ARGON Core.
++ */
++#define ARG_IC_ICTRL 0
++
++/* acc=LWC ACTIVE1_INT FIELD ACCESS: LWC
++ *
++ * Interrupt 1
++ * This is set and held when an hevc_active1 interrupt edge is detected
++ * The polarity of the edge is set by the ACTIVE1_EDGE field
++ * Write a 1 to this bit to clear down the latched interrupt
++ * The latched interrupt is only enabled out onto the interrupt line if
++ * ACTIVE1_EN is set
++ * Reset value is *0* decimal.
++ */
++#define ARG_IC_ICTRL_ACTIVE1_INT_SET BIT(0)
++
++/* ACTIVE1_EDGE Sets the polarity of the interrupt edge detection logic
++ * This logic detects edges of the hevc_active1 line from the argon core
++ * 0 = negedge, 1 = posedge
++ * Reset value is *0* decimal.
++ */
++#define ARG_IC_ICTRL_ACTIVE1_EDGE_SET BIT(1)
++
++/* ACTIVE1_EN Enables ACTIVE1_INT out onto the argon interrupt line.
++ * If this isn't set, the interrupt logic will work but no interrupt will be
++ * set to the interrupt controller
++ * Reset value is *1* decimal.
++ *
++ * [JC] The above appears to be a lie - if unset then b0 is never set
++ */
++#define ARG_IC_ICTRL_ACTIVE1_EN_SET BIT(2)
++
++/* acc=RO ACTIVE1_STATUS FIELD ACCESS: RO
++ *
++ * The current status of the hevc_active1 signal
++ */
++#define ARG_IC_ICTRL_ACTIVE1_STATUS_SET BIT(3)
++
++/* acc=LWC ACTIVE2_INT FIELD ACCESS: LWC
++ *
++ * Interrupt 2
++ * This is set and held when an hevc_active2 interrupt edge is detected
++ * The polarity of the edge is set by the ACTIVE2_EDGE field
++ * Write a 1 to this bit to clear down the latched interrupt
++ * The latched interrupt is only enabled out onto the interrupt line if
++ * ACTIVE2_EN is set
++ * Reset value is *0* decimal.
++ */
++#define ARG_IC_ICTRL_ACTIVE2_INT_SET BIT(4)
++
++/* ACTIVE2_EDGE Sets the polarity of the interrupt edge detection logic
++ * This logic detects edges of the hevc_active2 line from the argon core
++ * 0 = negedge, 1 = posedge
++ * Reset value is *0* decimal.
++ */
++#define ARG_IC_ICTRL_ACTIVE2_EDGE_SET BIT(5)
++
++/* ACTIVE2_EN Enables ACTIVE2_INT out onto the argon interrupt line.
++ * If this isn't set, the interrupt logic will work but no interrupt will be
++ * set to the interrupt controller
++ * Reset value is *1* decimal.
++ */
++#define ARG_IC_ICTRL_ACTIVE2_EN_SET BIT(6)
++
++/* acc=RO ACTIVE2_STATUS FIELD ACCESS: RO
++ *
++ * The current status of the hevc_active2 signal
++ */
++#define ARG_IC_ICTRL_ACTIVE2_STATUS_SET BIT(7)
++
++/* TEST_INT Forces the argon int high for test purposes.
++ * Reset value is *0* decimal.
++ */
++#define ARG_IC_ICTRL_TEST_INT BIT(8)
++#define ARG_IC_ICTRL_SPARE BIT(9)
++
++/* acc=RO VP9_INTERRUPT_STATUS FIELD ACCESS: RO
++ *
++ * The current status of the vp9_interrupt signal
++ */
++#define ARG_IC_ICTRL_VP9_INTERRUPT_STATUS BIT(10)
++
++/* AIO_INT_ENABLE 1 = Or the AIO int in with the Argon int so the VPU can see
++ * it
++ * 0 = the AIO int is masked. (It should still be connected to the GIC though).
++ */
++#define ARG_IC_ICTRL_AIO_INT_ENABLE BIT(20)
++#define ARG_IC_ICTRL_H264_ACTIVE_INT BIT(21)
++#define ARG_IC_ICTRL_H264_ACTIVE_EDGE BIT(22)
++#define ARG_IC_ICTRL_H264_ACTIVE_EN BIT(23)
++#define ARG_IC_ICTRL_H264_ACTIVE_STATUS BIT(24)
++#define ARG_IC_ICTRL_H264_INTERRUPT_INT BIT(25)
++#define ARG_IC_ICTRL_H264_INTERRUPT_EDGE BIT(26)
++#define ARG_IC_ICTRL_H264_INTERRUPT_EN BIT(27)
++
++/* acc=RO H264_INTERRUPT_STATUS FIELD ACCESS: RO
++ *
++ * The current status of the h264_interrupt signal
++ */
++#define ARG_IC_ICTRL_H264_INTERRUPT_STATUS BIT(28)
++
++/* acc=LWC VP9_INTERRUPT_INT FIELD ACCESS: LWC
++ *
++ * Interrupt 1
++ * This is set and held when an vp9_interrupt interrupt edge is detected
++ * The polarity of the edge is set by the VP9_INTERRUPT_EDGE field
++ * Write a 1 to this bit to clear down the latched interrupt
++ * The latched interrupt is only enabled out onto the interrupt line if
++ * VP9_INTERRUPT_EN is set
++ * Reset value is *0* decimal.
++ */
++#define ARG_IC_ICTRL_VP9_INTERRUPT_INT BIT(29)
++
++/* VP9_INTERRUPT_EDGE Sets the polarity of the interrupt edge detection logic
++ * This logic detects edges of the vp9_interrupt line from the argon h264 core
++ * 0 = negedge, 1 = posedge
++ * Reset value is *0* decimal.
++ */
++#define ARG_IC_ICTRL_VP9_INTERRUPT_EDGE BIT(30)
++
++/* VP9_INTERRUPT_EN Enables VP9_INTERRUPT_INT out onto the argon interrupt line.
++ * If this isn't set, the interrupt logic will work but no interrupt will be
++ * set to the interrupt controller
++ * Reset value is *1* decimal.
++ */
++#define ARG_IC_ICTRL_VP9_INTERRUPT_EN BIT(31)
++
++/* Bits 19:12, 11 reserved - read ?, write 0 */
++#define ARG_IC_ICTRL_SET_ZERO_MASK ((0xff << 12) | BIT(11))
++
++/* All IRQ bits */
++#define ARG_IC_ICTRL_ALL_IRQ_MASK (\
++ ARG_IC_ICTRL_VP9_INTERRUPT_INT |\
++ ARG_IC_ICTRL_H264_INTERRUPT_INT |\
++ ARG_IC_ICTRL_ACTIVE1_INT_SET |\
++ ARG_IC_ICTRL_ACTIVE2_INT_SET)
++
++/* Auto release once all CBs called */
++void rpivid_hw_irq_active1_claim(struct rpivid_dev *dev,
++ struct rpivid_hw_irq_ent *ient,
++ rpivid_irq_callback ready_cb, void *ctx);
++/* May only be called in claim cb */
++void rpivid_hw_irq_active1_irq(struct rpivid_dev *dev,
++ struct rpivid_hw_irq_ent *ient,
++ rpivid_irq_callback irq_cb, void *ctx);
++/* May only be called in irq cb */
++void rpivid_hw_irq_active1_thread(struct rpivid_dev *dev,
++ struct rpivid_hw_irq_ent *ient,
++ rpivid_irq_callback thread_cb, void *ctx);
++
++/* Auto release once all CBs called */
++void rpivid_hw_irq_active2_claim(struct rpivid_dev *dev,
++ struct rpivid_hw_irq_ent *ient,
++ rpivid_irq_callback ready_cb, void *ctx);
++/* May only be called in claim cb */
++void rpivid_hw_irq_active2_irq(struct rpivid_dev *dev,
++ struct rpivid_hw_irq_ent *ient,
++ rpivid_irq_callback irq_cb, void *ctx);
++
++int rpivid_hw_probe(struct rpivid_dev *dev);
++void rpivid_hw_remove(struct rpivid_dev *dev);
++
++#endif
+--- /dev/null
++++ b/drivers/staging/media/rpivid/rpivid_video.c
+@@ -0,0 +1,593 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Raspberry Pi HEVC driver
++ *
++ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
++ *
++ * Based on the Cedrus VPU driver, that is:
++ *
++ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
++ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
++ * Copyright (C) 2018 Bootlin
++ */
++
++#include <media/videobuf2-dma-contig.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-ioctl.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-mem2mem.h>
++
++#include "rpivid.h"
++#include "rpivid_video.h"
++#include "rpivid_dec.h"
++
++#define RPIVID_DECODE_SRC BIT(0)
++#define RPIVID_DECODE_DST BIT(1)
++
++#define RPIVID_MIN_WIDTH 16U
++#define RPIVID_MIN_HEIGHT 16U
++#define RPIVID_MAX_WIDTH 4096U
++#define RPIVID_MAX_HEIGHT 4096U
++
++static inline struct rpivid_ctx *rpivid_file2ctx(struct file *file)
++{
++ return container_of(file->private_data, struct rpivid_ctx, fh);
++}
++
++/* constrain x to y,y*2 */
++static inline unsigned int constrain2x(unsigned int x, unsigned int y)
++{
++ return (x < y) ?
++ y :
++ (x > y * 2) ? y : x;
++}
++
++int rpivid_prepare_src_format(struct v4l2_pix_format *pix_fmt)
++{
++ if (pix_fmt->pixelformat != V4L2_PIX_FMT_HEVC_SLICE)
++ return -EINVAL;
++
++ /* Zero bytes per line for encoded source. */
++ pix_fmt->bytesperline = 0;
++ /* Choose some minimum size since this can't be 0 */
++ pix_fmt->sizeimage = max_t(u32, SZ_1K, pix_fmt->sizeimage);
++ pix_fmt->field = V4L2_FIELD_NONE;
++ return 0;
++}
++
++int rpivid_prepare_dst_format(struct v4l2_pix_format *pix_fmt)
++{
++ unsigned int width = pix_fmt->width;
++ unsigned int height = pix_fmt->height;
++ unsigned int sizeimage = pix_fmt->sizeimage;
++ unsigned int bytesperline = pix_fmt->bytesperline;
++
++ switch (pix_fmt->pixelformat) {
++ /* For column formats set bytesperline to column height (stride2) */
++ case V4L2_PIX_FMT_NV12_COL128:
++ /* Width rounds up to columns */
++ width = ALIGN(min(width, RPIVID_MAX_WIDTH), 128);
++
++ /* 16 aligned height - not sure we even need that */
++ height = ALIGN(height, 16);
++ /* column height
++ * Accept suggested shape if at least min & < 2 * min
++ */
++ bytesperline = constrain2x(bytesperline, height * 3 / 2);
++
++ /* image size
++ * Again allow plausible variation in case added padding is
++ * required
++ */
++ sizeimage = constrain2x(sizeimage, bytesperline * width);
++ break;
++
++ case V4L2_PIX_FMT_NV12_10_COL128:
++ /* width in pixels (3 pels = 4 bytes) rounded to 128 byte
++ * columns
++ */
++ width = ALIGN(((min(width, RPIVID_MAX_WIDTH) + 2) / 3), 32) * 3;
++
++ /* 16-aligned height. */
++ height = ALIGN(height, 16);
++
++ /* column height
++ * Accept suggested shape if at least min & < 2 * min
++ */
++ bytesperline = constrain2x(bytesperline, height * 3 / 2);
++
++ /* image size
++ * Again allow plausible variation in case added padding is
++ * required
++ */
++ sizeimage = constrain2x(sizeimage,
++ bytesperline * width * 4 / 3);
++ break;
++
++ default:
++ return -EINVAL;
++ }
++
++ pix_fmt->width = width;
++ pix_fmt->height = height;
++
++ pix_fmt->field = V4L2_FIELD_NONE;
++ pix_fmt->bytesperline = bytesperline;
++ pix_fmt->sizeimage = sizeimage;
++ return 0;
++}
++
++static int rpivid_querycap(struct file *file, void *priv,
++ struct v4l2_capability *cap)
++{
++ strscpy(cap->driver, RPIVID_NAME, sizeof(cap->driver));
++ strscpy(cap->card, RPIVID_NAME, sizeof(cap->card));
++ snprintf(cap->bus_info, sizeof(cap->bus_info),
++ "platform:%s", RPIVID_NAME);
++
++ return 0;
++}
++
++static int rpivid_enum_fmt_vid_out(struct file *file, void *priv,
++ struct v4l2_fmtdesc *f)
++{
++ // Input formats
++
++ // H.265 Slice only currently
++ if (f->index == 0) {
++ f->pixelformat = V4L2_PIX_FMT_HEVC_SLICE;
++ return 0;
++ }
++
++ return -EINVAL;
++}
++
++static int rpivid_hevc_validate_sps(const struct v4l2_ctrl_hevc_sps * const sps)
++{
++ const unsigned int ctb_log2_size_y =
++ sps->log2_min_luma_coding_block_size_minus3 + 3 +
++ sps->log2_diff_max_min_luma_coding_block_size;
++ const unsigned int min_tb_log2_size_y =
++ sps->log2_min_luma_transform_block_size_minus2 + 2;
++ const unsigned int max_tb_log2_size_y = min_tb_log2_size_y +
++ sps->log2_diff_max_min_luma_transform_block_size;
++
++ /* Local limitations */
++ if (sps->pic_width_in_luma_samples < 32 ||
++ sps->pic_width_in_luma_samples > 4096)
++ return 0;
++ if (sps->pic_height_in_luma_samples < 32 ||
++ sps->pic_height_in_luma_samples > 4096)
++ return 0;
++ if (!(sps->bit_depth_luma_minus8 == 0 ||
++ sps->bit_depth_luma_minus8 == 2))
++ return 0;
++ if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
++ return 0;
++ if (sps->chroma_format_idc != 1)
++ return 0;
++
++ /* Limits from H.265 7.4.3.2.1 */
++ if (sps->log2_max_pic_order_cnt_lsb_minus4 > 12)
++ return 0;
++ if (sps->sps_max_dec_pic_buffering_minus1 > 15)
++ return 0;
++ if (sps->sps_max_num_reorder_pics >
++ sps->sps_max_dec_pic_buffering_minus1)
++ return 0;
++ if (ctb_log2_size_y > 6)
++ return 0;
++ if (max_tb_log2_size_y > 5)
++ return 0;
++ if (max_tb_log2_size_y > ctb_log2_size_y)
++ return 0;
++ if (sps->max_transform_hierarchy_depth_inter >
++ (ctb_log2_size_y - min_tb_log2_size_y))
++ return 0;
++ if (sps->max_transform_hierarchy_depth_intra >
++ (ctb_log2_size_y - min_tb_log2_size_y))
++ return 0;
++ /* Check pcm stuff */
++ if (sps->num_short_term_ref_pic_sets > 64)
++ return 0;
++ if (sps->num_long_term_ref_pics_sps > 32)
++ return 0;
++ return 1;
++}
++
++static inline int is_sps_set(const struct v4l2_ctrl_hevc_sps * const sps)
++{
++ return sps && sps->pic_width_in_luma_samples != 0;
++}
++
++static u32 pixelformat_from_sps(const struct v4l2_ctrl_hevc_sps * const sps,
++ const int index)
++{
++ u32 pf = 0;
++
++ // Use width 0 as a signifier of unsetness
++ if (!is_sps_set(sps)) {
++ /* Treat this as an error? For now return both */
++ if (index == 0)
++ pf = V4L2_PIX_FMT_NV12_COL128;
++ else if (index == 1)
++ pf = V4L2_PIX_FMT_NV12_10_COL128;
++ } else if (index == 0 && rpivid_hevc_validate_sps(sps)) {
++ if (sps->bit_depth_luma_minus8 == 0)
++ pf = V4L2_PIX_FMT_NV12_COL128;
++ else if (sps->bit_depth_luma_minus8 == 2)
++ pf = V4L2_PIX_FMT_NV12_10_COL128;
++ }
++
++ return pf;
++}
++
++static struct v4l2_pix_format
++rpivid_hevc_default_dst_fmt(struct rpivid_ctx * const ctx)
++{
++ const struct v4l2_ctrl_hevc_sps * const sps =
++ rpivid_find_control_data(ctx, V4L2_CID_MPEG_VIDEO_HEVC_SPS);
++ struct v4l2_pix_format pix_fmt = {
++ .width = sps->pic_width_in_luma_samples,
++ .height = sps->pic_height_in_luma_samples,
++ .pixelformat = pixelformat_from_sps(sps, 0)
++ };
++
++ rpivid_prepare_dst_format(&pix_fmt);
++ return pix_fmt;
++}
++
++static u32 rpivid_hevc_get_dst_pixelformat(struct rpivid_ctx * const ctx,
++ const int index)
++{
++ const struct v4l2_ctrl_hevc_sps * const sps =
++ rpivid_find_control_data(ctx, V4L2_CID_MPEG_VIDEO_HEVC_SPS);
++
++ return pixelformat_from_sps(sps, index);
++}
++
++static int rpivid_enum_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_fmtdesc *f)
++{
++ struct rpivid_ctx * const ctx = rpivid_file2ctx(file);
++
++ const u32 pf = rpivid_hevc_get_dst_pixelformat(ctx, f->index);
++
++ if (pf == 0)
++ return -EINVAL;
++
++ f->pixelformat = pf;
++ return 0;
++}
++
++static int rpivid_g_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct rpivid_ctx *ctx = rpivid_file2ctx(file);
++
++ if (!ctx->dst_fmt_set)
++ ctx->dst_fmt = rpivid_hevc_default_dst_fmt(ctx);
++ f->fmt.pix = ctx->dst_fmt;
++ return 0;
++}
++
++static int rpivid_g_fmt_vid_out(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct rpivid_ctx *ctx = rpivid_file2ctx(file);
++
++ f->fmt.pix = ctx->src_fmt;
++ return 0;
++}
++
++static inline void copy_color(struct v4l2_pix_format *d,
++ const struct v4l2_pix_format *s)
++{
++ d->colorspace = s->colorspace;
++ d->xfer_func = s->xfer_func;
++ d->ycbcr_enc = s->ycbcr_enc;
++ d->quantization = s->quantization;
++}
++
++static int rpivid_try_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct rpivid_ctx *ctx = rpivid_file2ctx(file);
++ const struct v4l2_ctrl_hevc_sps * const sps =
++ rpivid_find_control_data(ctx, V4L2_CID_MPEG_VIDEO_HEVC_SPS);
++ u32 pixelformat;
++ int i;
++
++ /* Reject format types we don't support */
++ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
++ return -EINVAL;
++
++ for (i = 0; (pixelformat = pixelformat_from_sps(sps, i)) != 0; i++) {
++ if (f->fmt.pix.pixelformat == pixelformat)
++ break;
++ }
++
++ // If we can't use requested fmt then set to default
++ if (pixelformat == 0) {
++ pixelformat = pixelformat_from_sps(sps, 0);
++ // If we don't have a default then give up
++ if (pixelformat == 0)
++ return -EINVAL;
++ }
++
++ // We don't have any way of finding out colourspace so believe
++ // anything we are told - take anything set in src as a default
++ if (f->fmt.pix.colorspace == V4L2_COLORSPACE_DEFAULT)
++ copy_color(&f->fmt.pix, &ctx->src_fmt);
++
++ f->fmt.pix.pixelformat = pixelformat;
++ return rpivid_prepare_dst_format(&f->fmt.pix);
++}
++
++static int rpivid_try_fmt_vid_out(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
++ return -EINVAL;
++
++ if (rpivid_prepare_src_format(&f->fmt.pix)) {
++ // Set default src format
++ f->fmt.pix.pixelformat = RPIVID_SRC_PIXELFORMAT_DEFAULT;
++ rpivid_prepare_src_format(&f->fmt.pix);
++ }
++ return 0;
++}
++
++static int rpivid_s_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct rpivid_ctx *ctx = rpivid_file2ctx(file);
++ struct vb2_queue *vq;
++ int ret;
++
++ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
++ if (vb2_is_busy(vq))
++ return -EBUSY;
++
++ ret = rpivid_try_fmt_vid_cap(file, priv, f);
++ if (ret)
++ return ret;
++
++ ctx->dst_fmt = f->fmt.pix;
++ ctx->dst_fmt_set = 1;
++
++ return 0;
++}
++
++static int rpivid_s_fmt_vid_out(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct rpivid_ctx *ctx = rpivid_file2ctx(file);
++ struct vb2_queue *vq;
++ int ret;
++
++ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
++ if (vb2_is_busy(vq))
++ return -EBUSY;
++
++ ret = rpivid_try_fmt_vid_out(file, priv, f);
++ if (ret)
++ return ret;
++
++ ctx->src_fmt = f->fmt.pix;
++ ctx->dst_fmt_set = 0; // Setting src invalidates dst
++
++ vq->subsystem_flags |=
++ VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF;
++
++ /* Propagate colorspace information to capture. */
++ copy_color(&ctx->dst_fmt, &f->fmt.pix);
++ return 0;
++}
++
++const struct v4l2_ioctl_ops rpivid_ioctl_ops = {
++ .vidioc_querycap = rpivid_querycap,
++
++ .vidioc_enum_fmt_vid_cap = rpivid_enum_fmt_vid_cap,
++ .vidioc_g_fmt_vid_cap = rpivid_g_fmt_vid_cap,
++ .vidioc_try_fmt_vid_cap = rpivid_try_fmt_vid_cap,
++ .vidioc_s_fmt_vid_cap = rpivid_s_fmt_vid_cap,
++
++ .vidioc_enum_fmt_vid_out = rpivid_enum_fmt_vid_out,
++ .vidioc_g_fmt_vid_out = rpivid_g_fmt_vid_out,
++ .vidioc_try_fmt_vid_out = rpivid_try_fmt_vid_out,
++ .vidioc_s_fmt_vid_out = rpivid_s_fmt_vid_out,
++
++ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
++ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
++ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
++ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
++ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
++ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
++ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
++
++ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
++ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
++
++ .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_stateless_try_decoder_cmd,
++ .vidioc_decoder_cmd = v4l2_m2m_ioctl_stateless_decoder_cmd,
++
++ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
++};
++
++static int rpivid_queue_setup(struct vb2_queue *vq, unsigned int *nbufs,
++ unsigned int *nplanes, unsigned int sizes[],
++ struct device *alloc_devs[])
++{
++ struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
++ struct v4l2_pix_format *pix_fmt;
++
++ if (V4L2_TYPE_IS_OUTPUT(vq->type))
++ pix_fmt = &ctx->src_fmt;
++ else
++ pix_fmt = &ctx->dst_fmt;
++
++ if (*nplanes) {
++ if (sizes[0] < pix_fmt->sizeimage)
++ return -EINVAL;
++ } else {
++ sizes[0] = pix_fmt->sizeimage;
++ *nplanes = 1;
++ }
++
++ return 0;
++}
++
++static void rpivid_queue_cleanup(struct vb2_queue *vq, u32 state)
++{
++ struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
++ struct vb2_v4l2_buffer *vbuf;
++
++ for (;;) {
++ if (V4L2_TYPE_IS_OUTPUT(vq->type))
++ vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
++ else
++ vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
++
++ if (!vbuf)
++ return;
++
++ v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
++ &ctx->hdl);
++ v4l2_m2m_buf_done(vbuf, state);
++ }
++}
++
++static int rpivid_buf_out_validate(struct vb2_buffer *vb)
++{
++ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
++
++ vbuf->field = V4L2_FIELD_NONE;
++ return 0;
++}
++
++static int rpivid_buf_prepare(struct vb2_buffer *vb)
++{
++ struct vb2_queue *vq = vb->vb2_queue;
++ struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
++ struct v4l2_pix_format *pix_fmt;
++
++ if (V4L2_TYPE_IS_OUTPUT(vq->type))
++ pix_fmt = &ctx->src_fmt;
++ else
++ pix_fmt = &ctx->dst_fmt;
++
++ if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage)
++ return -EINVAL;
++
++ vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage);
++
++ return 0;
++}
++
++static int rpivid_start_streaming(struct vb2_queue *vq, unsigned int count)
++{
++ struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
++ struct rpivid_dev *dev = ctx->dev;
++ int ret = 0;
++
++ if (ctx->src_fmt.pixelformat != V4L2_PIX_FMT_HEVC_SLICE)
++ return -EINVAL;
++
++ if (V4L2_TYPE_IS_OUTPUT(vq->type) && dev->dec_ops->start)
++ ret = dev->dec_ops->start(ctx);
++
++ ret = clk_set_rate(dev->clock, 500 * 1000 * 1000);
++ if (ret) {
++ dev_err(dev->dev, "Failed to set clock rate\n");
++ goto out;
++ }
++
++ ret = clk_prepare_enable(dev->clock);
++ if (ret)
++ dev_err(dev->dev, "Failed to enable clock\n");
++
++out:
++ if (ret)
++ rpivid_queue_cleanup(vq, VB2_BUF_STATE_QUEUED);
++
++ return ret;
++}
++
++static void rpivid_stop_streaming(struct vb2_queue *vq)
++{
++ struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
++ struct rpivid_dev *dev = ctx->dev;
++
++ if (V4L2_TYPE_IS_OUTPUT(vq->type) && dev->dec_ops->stop)
++ dev->dec_ops->stop(ctx);
++
++ rpivid_queue_cleanup(vq, VB2_BUF_STATE_ERROR);
++
++ clk_disable_unprepare(dev->clock);
++}
++
++static void rpivid_buf_queue(struct vb2_buffer *vb)
++{
++ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
++ struct rpivid_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
++
++ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
++}
++
++static void rpivid_buf_request_complete(struct vb2_buffer *vb)
++{
++ struct rpivid_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
++
++ v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
++}
++
++static struct vb2_ops rpivid_qops = {
++ .queue_setup = rpivid_queue_setup,
++ .buf_prepare = rpivid_buf_prepare,
++ .buf_queue = rpivid_buf_queue,
++ .buf_out_validate = rpivid_buf_out_validate,
++ .buf_request_complete = rpivid_buf_request_complete,
++ .start_streaming = rpivid_start_streaming,
++ .stop_streaming = rpivid_stop_streaming,
++ .wait_prepare = vb2_ops_wait_prepare,
++ .wait_finish = vb2_ops_wait_finish,
++};
++
++int rpivid_queue_init(void *priv, struct vb2_queue *src_vq,
++ struct vb2_queue *dst_vq)
++{
++ struct rpivid_ctx *ctx = priv;
++ int ret;
++
++ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
++ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
++ src_vq->drv_priv = ctx;
++ src_vq->buf_struct_size = sizeof(struct rpivid_buffer);
++ src_vq->min_buffers_needed = 1;
++ src_vq->ops = &rpivid_qops;
++ src_vq->mem_ops = &vb2_dma_contig_memops;
++ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
++ src_vq->lock = &ctx->dev->dev_mutex;
++ src_vq->dev = ctx->dev->dev;
++ src_vq->supports_requests = true;
++ src_vq->requires_requests = true;
++
++ ret = vb2_queue_init(src_vq);
++ if (ret)
++ return ret;
++
++ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
++ dst_vq->drv_priv = ctx;
++ dst_vq->buf_struct_size = sizeof(struct rpivid_buffer);
++ dst_vq->min_buffers_needed = 1;
++ dst_vq->ops = &rpivid_qops;
++ dst_vq->mem_ops = &vb2_dma_contig_memops;
++ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
++ dst_vq->lock = &ctx->dev->dev_mutex;
++ dst_vq->dev = ctx->dev->dev;
++
++ return vb2_queue_init(dst_vq);
++}
+--- /dev/null
++++ b/drivers/staging/media/rpivid/rpivid_video.h
+@@ -0,0 +1,30 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Raspberry Pi HEVC driver
++ *
++ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
++ *
++ * Based on the Cedrus VPU driver, that is:
++ *
++ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
++ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
++ * Copyright (C) 2018 Bootlin
++ */
++
++#ifndef _RPIVID_VIDEO_H_
++#define _RPIVID_VIDEO_H_
++
++struct rpivid_format {
++ u32 pixelformat;
++ u32 directions;
++ unsigned int capabilities;
++};
++
++extern const struct v4l2_ioctl_ops rpivid_ioctl_ops;
++
++int rpivid_queue_init(void *priv, struct vb2_queue *src_vq,
++ struct vb2_queue *dst_vq);
++int rpivid_prepare_src_format(struct v4l2_pix_format *pix_fmt);
++int rpivid_prepare_dst_format(struct v4l2_pix_format *pix_fmt);
++
++#endif
--- /dev/null
+From b1d6499e00b6061ecc7061335199acf86f54d31a Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 13 Mar 2020 16:52:55 +0000
+Subject: [PATCH] dtoverlays: Add overlay to enable the HEVC V4L2
+ driver
+
+This replaces the rpivid_mem register mapping driver.
+When the driver is complete, these DT changes should be
+merged into the base DT instead of being an overlay.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 7 +++
+ .../boot/dts/overlays/rpivid-v4l2-overlay.dts | 55 +++++++++++++++++++
+ 4 files changed, 63 insertions(+), 2 deletions(-)
+ create mode 100644 arch/arm/boot/dts/overlays/rpivid-v4l2-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -140,6 +140,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ rpi-proto.dtbo \
+ rpi-sense.dtbo \
+ rpi-tv.dtbo \
++ rpivid-v4l2.dtbo \
+ rra-digidac1-wm8741-audio.dtbo \
+ sc16is750-i2c.dtbo \
+ sc16is752-i2c.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2064,6 +2064,13 @@ Load: dtoverlay=rpi-tv
+ Params: <None>
+
+
++Name: rpivid-v4l2
++Info: Load the V4L2 stateless video decoder driver for the HEVC block,
++ disabling the memory mapped devices in the process.
++Load: dtoverlay=rpivid-v4l2
++Params: <None>
++
++
+ Name: rra-digidac1-wm8741-audio
+ Info: Configures the Red Rocks Audio DigiDAC1 soundcard
+ Load: dtoverlay=rra-digidac1-wm8741-audio
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/rpivid-v4l2-overlay.dts
+@@ -0,0 +1,55 @@
++// SPDX-License-Identifier: GPL-2.0-only
++// Definitions for Raspberry Pi video decode engine
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/interrupt-controller/arm-gic.h>
++
++/{
++ compatible = "brcm,bcm2711";
++
++ fragment@0 {
++ target = <&scb>;
++ __overlay__ {
++ /* needed to avoid dtc warning */
++ #address-cells = <2>;
++ #size-cells = <1>;
++ codec@7eb10000 {
++ compatible = "raspberrypi,rpivid-vid-decoder";
++ reg = <0x0 0x7eb10000 0x1000>, /* INTC */
++ <0x0 0x7eb00000 0x10000>; /* HEVC */
++ reg-names = "intc",
++ "hevc";
++
++ interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
++
++ clocks = <&hevc_clk>;
++ clock-names = "hevc";
++
++ hevc_clk: hevc_clk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <500000000>;
++ };
++ };
++ };
++ };
++
++ fragment@1 {
++ target = <&scb>;
++ __overlay__ {
++ hevc-decoder@7eb00000 {
++ status = "disabled";
++ };
++ rpivid-local-intc@7eb10000 {
++ status = "disabled";
++ };
++ h264-decoder@7eb20000 {
++ status = "disabled";
++ };
++ vp9-decoder@7eb30000 {
++ status = "disabled";
++ };
++ };
++ };
++};