diff options
author | Álvaro Fernández Rojas <noltari@gmail.com> | 2020-05-27 15:12:04 +0200 |
---|---|---|
committer | Álvaro Fernández Rojas <noltari@gmail.com> | 2020-05-28 10:36:27 +0200 |
commit | 584d4bf1d3c2265a810e1494eb5c8ef0a72ee934 (patch) | |
tree | 20b3364aee884d3f65f246c16ee7f8bd4a465ea2 | |
parent | 9e467a764b4e30a04dd0431ea277f6acd26babe0 (diff) | |
download | openwrt-584d4bf1d3c2265a810e1494eb5c8ef0a72ee934.tar.gz openwrt-584d4bf1d3c2265a810e1494eb5c8ef0a72ee934.tar.bz2 openwrt-584d4bf1d3c2265a810e1494eb5c8ef0a72ee934.zip |
bcm27xx: update patches from RPi foundation
bcm2708: boot tested on RPi B+ v1.2
bcm2709: boot tested on RPi 3B v1.2
bcm2710: boot tested on RPi 3B v1.2
bcm2711: boot tested on RPi 4B v1.1 4G
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
416 files changed, 37487 insertions, 676 deletions
diff --git a/target/linux/bcm27xx/bcm2708/config-5.4 b/target/linux/bcm27xx/bcm2708/config-5.4 index 1f12a36ee7..c4802bef7d 100644 --- a/target/linux/bcm27xx/bcm2708/config-5.4 +++ b/target/linux/bcm27xx/bcm2708/config-5.4 @@ -43,6 +43,7 @@ CONFIG_ARM_ERRATA_411920=y CONFIG_ARM_HAS_SG_CHAIN=y CONFIG_ARM_L1_CACHE_SHIFT=5 CONFIG_ARM_PATCH_PHYS_VIRT=y +# CONFIG_ARM_RASPBERRYPI_CPUFREQ is not set # CONFIG_ARM_SCMI_PROTOCOL is not set CONFIG_ARM_THUMB=y CONFIG_ARM_TIMER_SP804=y @@ -79,7 +80,7 @@ CONFIG_CC_HAS_KASAN_GENERIC=y CONFIG_CLKDEV_LOOKUP=y CONFIG_CLKSRC_MMIO=y CONFIG_CLK_BCM2835=y -# CONFIG_CLK_RASPBERRYPI is not set +CONFIG_CLK_RASPBERRYPI=y CONFIG_CLONE_BACKWARDS=y CONFIG_CMA=y CONFIG_CMA_ALIGNMENT=8 @@ -136,6 +137,9 @@ CONFIG_DCACHE_WORD_ACCESS=y CONFIG_DEBUG_BUGVERBOSE=y CONFIG_DEBUG_INFO=y CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" +CONFIG_DMABUF_HEAPS=y +CONFIG_DMABUF_HEAPS_CMA=y +CONFIG_DMABUF_HEAPS_SYSTEM=y CONFIG_DMADEVICES=y CONFIG_DMA_BCM2708=y CONFIG_DMA_BCM2835=y @@ -249,6 +253,7 @@ CONFIG_HZ_FIXED=0 CONFIG_I2C=y # CONFIG_I2C_BCM2708 is not set CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_BRCMSTB is not set CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_INPUT=y @@ -342,6 +347,7 @@ CONFIG_REGMAP_MMIO=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_RESET_CONTROLLER=y +CONFIG_RESET_SIMPLE=y # CONFIG_RPIVID_MEM is not set CONFIG_SCSI=y # CONFIG_SCSI_LOWLEVEL is not set diff --git a/target/linux/bcm27xx/bcm2709/config-5.4 b/target/linux/bcm27xx/bcm2709/config-5.4 index c1630c599d..67019438e6 100644 --- a/target/linux/bcm27xx/bcm2709/config-5.4 +++ b/target/linux/bcm27xx/bcm2709/config-5.4 @@ -183,6 +183,9 @@ CONFIG_DEBUG_BUGVERBOSE=y CONFIG_DEBUG_INFO=y CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" CONFIG_DIMLIB=y +CONFIG_DMABUF_HEAPS=y +CONFIG_DMABUF_HEAPS_CMA=y +CONFIG_DMABUF_HEAPS_SYSTEM=y CONFIG_DMADEVICES=y CONFIG_DMA_BCM2708=y CONFIG_DMA_BCM2835=y @@ -317,6 +320,7 @@ CONFIG_HZ_FIXED=0 CONFIG_I2C=y # CONFIG_I2C_BCM2708 is not set CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_BRCMSTB is not set CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_INPUT=y @@ -441,6 +445,7 @@ CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_GPIO=y CONFIG_RESET_CONTROLLER=y +CONFIG_RESET_SIMPLE=y CONFIG_RFS_ACCEL=y # CONFIG_RPIVID_MEM is not set CONFIG_RPS=y diff --git a/target/linux/bcm27xx/bcm2710/config-5.4 b/target/linux/bcm27xx/bcm2710/config-5.4 index 4a5b491b0f..c83149212d 100644 --- a/target/linux/bcm27xx/bcm2710/config-5.4 +++ b/target/linux/bcm27xx/bcm2710/config-5.4 @@ -224,6 +224,9 @@ CONFIG_CRYPTO_XTS=y CONFIG_DCACHE_WORD_ACCESS=y CONFIG_DEBUG_BUGVERBOSE=y CONFIG_DEBUG_INFO=y +CONFIG_DMABUF_HEAPS=y +CONFIG_DMABUF_HEAPS_CMA=y +CONFIG_DMABUF_HEAPS_SYSTEM=y CONFIG_DMADEVICES=y CONFIG_DMA_BCM2708=y CONFIG_DMA_BCM2835=y @@ -232,6 +235,7 @@ CONFIG_DMA_DIRECT_REMAP=y CONFIG_DMA_ENGINE=y CONFIG_DMA_OF=y CONFIG_DMA_REMAP=y +CONFIG_DMA_SHARED_BUFFER=y CONFIG_DMA_VIRTUAL_CHANNELS=y CONFIG_DNOTIFY=y CONFIG_DRM_RCAR_WRITEBACK=y @@ -372,6 +376,7 @@ CONFIG_HZ_250=y CONFIG_I2C=y # CONFIG_I2C_BCM2708 is not set CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_BRCMSTB is not set CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 @@ -513,6 +518,7 @@ CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_GPIO=y CONFIG_RESET_CONTROLLER=y +CONFIG_RESET_SIMPLE=y CONFIG_RFS_ACCEL=y CONFIG_RODATA_FULL_DEFAULT_ENABLED=y # CONFIG_RPIVID_MEM is not set diff --git a/target/linux/bcm27xx/bcm2711/config-5.4 b/target/linux/bcm27xx/bcm2711/config-5.4 index abf6e8844a..edc04e6e62 100644 --- a/target/linux/bcm27xx/bcm2711/config-5.4 +++ b/target/linux/bcm27xx/bcm2711/config-5.4 @@ -229,6 +229,9 @@ CONFIG_DCACHE_WORD_ACCESS=y CONFIG_DEBUG_BUGVERBOSE=y CONFIG_DEBUG_INFO=y CONFIG_DIMLIB=y +CONFIG_DMABUF_HEAPS=y +CONFIG_DMABUF_HEAPS_CMA=y +CONFIG_DMABUF_HEAPS_SYSTEM=y CONFIG_DMADEVICES=y CONFIG_DMA_BCM2708=y CONFIG_DMA_BCM2835=y @@ -237,6 +240,7 @@ CONFIG_DMA_DIRECT_REMAP=y CONFIG_DMA_ENGINE=y CONFIG_DMA_OF=y CONFIG_DMA_REMAP=y +CONFIG_DMA_SHARED_BUFFER=y CONFIG_DMA_VIRTUAL_CHANNELS=y CONFIG_DNOTIFY=y CONFIG_DRM_RCAR_WRITEBACK=y @@ -378,6 +382,7 @@ CONFIG_HZ_250=y CONFIG_I2C=y # CONFIG_I2C_BCM2708 is not set CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_BRCMSTB is not set CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 @@ -523,6 +528,7 @@ CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_GPIO=y CONFIG_RESET_CONTROLLER=y +CONFIG_RESET_SIMPLE=y CONFIG_RFS_ACCEL=y CONFIG_RODATA_FULL_DEFAULT_ENABLED=y # CONFIG_RPIVID_MEM is not set diff --git a/target/linux/bcm27xx/modules/sound.mk b/target/linux/bcm27xx/modules/sound.mk index 8d449a51fa..a4aeb629c3 100644 --- a/target/linux/bcm27xx/modules/sound.mk +++ b/target/linux/bcm27xx/modules/sound.mk @@ -268,6 +268,33 @@ endef $(eval $(call KernelPackage,sound-soc-allo-katana-codec)) +define KernelPackage/sound-soc-audioinjector-isolated-soundcard + TITLE:=Support for AudioInjector Isolated soundcard + KCONFIG:= \ + CONFIG_SND_AUDIOINJECTOR_ISOLATED_SOUNDCARD \ + CONFIG_SND_SOC_CS4271 \ + CONFIG_SND_SOC_CS4271_I2C + FILES:= \ + $(LINUX_DIR)/sound/soc/bcm/snd-soc-audioinjector-isolated-soundcard.ko \ + $(LINUX_DIR)/sound/soc/codecs/snd-soc-wm8731.ko + AUTOLOAD:=$(call AutoLoad,68,snd-soc-cs4271.o \ + snd-soc-cs4271-i2c \ + snd-soc-audioinjector-isolated-soundcard) + DEPENDS:= \ + kmod-sound-soc-bcm2835-i2s \ + +kmod-i2c-bcm2835 \ + +kmod-regmap-i2c \ + +kmod-regmap-spi + $(call AddDepends/sound) +endef + +define KernelPackage/sound-soc-audioinjector-isolated-soundcard/description + This package contains support for AudioInjector Isolated soundcard +endef + +$(eval $(call KernelPackage,sound-soc-audioinjector-isolated-soundcard)) + + define KernelPackage/sound-soc-audioinjector-octo-soundcard TITLE:=Support for AudioInjector Octo soundcard KCONFIG:= \ @@ -884,6 +911,28 @@ endef $(eval $(call KernelPackage,sound-soc-rpi-dac)) +define KernelPackage/sound-soc-merus-amp + TITLE:=Support for Infineon Merus Amp + KCONFIG:= \ + CONFIG_SND_SOC_MA120X0P + FILES:= \ + $(LINUX_DIR)/sound/soc/codecs/snd-soc-ma120x0p.ko + AUTOLOAD:=$(call AutoLoad,68,snd-soc-ma120x0p) + DEPENDS:= \ + kmod-sound-soc-bcm2835-i2s \ + +kmod-sound-soc-rpi-simple-soundcard \ + +kmod-i2c-bcm2835 \ + +kmod-regmap-i2c + $(call AddDepends/sound) +endef + +define KernelPackage/sound-soc-merus-amp/description + This package contains support for Infineon Merus Amp +endef + +$(eval $(call KernelPackage,sound-soc-merus-amp)) + + define KernelPackage/sound-soc-rpi-proto TITLE:=Support for RPi-PROTO KCONFIG:= \ diff --git a/target/linux/bcm27xx/modules/video.mk b/target/linux/bcm27xx/modules/video.mk index f1da7d8321..f7286cee3c 100644 --- a/target/linux/bcm27xx/modules/video.mk +++ b/target/linux/bcm27xx/modules/video.mk @@ -33,12 +33,13 @@ define KernelPackage/drm-vc4 +kmod-sound-soc-core KCONFIG:= \ CONFIG_DRM_VC4 \ - CONFIG_DRM_VC4_HDMI_CEC=n \ + CONFIG_DRM_VC4_HDMI_CEC=y \ CONFIG_DRM_V3D=n \ CONFIG_DRM_TVE200=n FILES:= \ $(LINUX_DIR)/drivers/gpu/drm/vc4/vc4.ko \ - $(LINUX_DIR)/drivers/gpu/drm/drm_kms_helper.ko + $(LINUX_DIR)/drivers/gpu/drm/drm_kms_helper.ko \ + $(LINUX_DIR)/drivers/media/cec/cec.ko AUTOLOAD:=$(call AutoProbe,vc4) endef diff --git a/target/linux/bcm27xx/patches-5.4/950-0351-v3d_drv-Allow-clock-retrieval-by-name.patch b/target/linux/bcm27xx/patches-5.4/950-0351-v3d_drv-Allow-clock-retrieval-by-name.patch deleted file mode 100644 index 4529471b6c..0000000000 --- a/target/linux/bcm27xx/patches-5.4/950-0351-v3d_drv-Allow-clock-retrieval-by-name.patch +++ /dev/null @@ -1,23 +0,0 @@ -From a19956ff2941b73204c96127a22edef71b5d0d34 Mon Sep 17 00:00:00 2001 -From: popcornmix <popcornmix@gmail.com> -Date: Mon, 9 Sep 2019 23:50:44 +0100 -Subject: [PATCH] v3d_drv: Allow clock retrieval by name - -Signed-off-by: Phil Elwell <phil@raspberrypi.org> ---- - drivers/gpu/drm/v3d/v3d_drv.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - ---- a/drivers/gpu/drm/v3d/v3d_drv.c -+++ b/drivers/gpu/drm/v3d/v3d_drv.c -@@ -285,7 +285,9 @@ static int v3d_platform_drm_probe(struct - } - } - -- v3d->clk = devm_clk_get(dev, NULL); -+ v3d->clk = devm_clk_get(dev, "v3d"); -+ if (!v3d->clk) -+ v3d->clk = devm_clk_get(dev, NULL); - if (IS_ERR_OR_NULL(v3d->clk)) { - if (PTR_ERR(v3d->clk) != -EPROBE_DEFER) - dev_err(dev, "Failed to get clock (%ld)\n", PTR_ERR(v3d->clk)); diff --git a/target/linux/bcm27xx/patches-5.4/950-0352-v3d_gem-Kick-the-clock-so-firmware-knows-we-are-usin.patch b/target/linux/bcm27xx/patches-5.4/950-0351-v3d_gem-Kick-the-clock-so-firmware-knows-we-are-usin.patch index 1892a145fa..1892a145fa 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0352-v3d_gem-Kick-the-clock-so-firmware-knows-we-are-usin.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0351-v3d_gem-Kick-the-clock-so-firmware-knows-we-are-usin.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0355-clk-bcm2835-Disable-v3d-clock.patch b/target/linux/bcm27xx/patches-5.4/950-0352-clk-bcm2835-Disable-v3d-clock.patch index 160b39b9cd..160b39b9cd 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0355-clk-bcm2835-Disable-v3d-clock.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0352-clk-bcm2835-Disable-v3d-clock.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0356-raspberrypi-cpufreq-Only-report-integer-pll-divisor-.patch b/target/linux/bcm27xx/patches-5.4/950-0353-raspberrypi-cpufreq-Only-report-integer-pll-divisor-.patch index 192b13b69a..192b13b69a 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0356-raspberrypi-cpufreq-Only-report-integer-pll-divisor-.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0353-raspberrypi-cpufreq-Only-report-integer-pll-divisor-.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0357-arm-dts-Correct-Pi-4B-LED-values.patch b/target/linux/bcm27xx/patches-5.4/950-0354-arm-dts-Correct-Pi-4B-LED-values.patch index a3bae521e7..a3bae521e7 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0357-arm-dts-Correct-Pi-4B-LED-values.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0354-arm-dts-Correct-Pi-4B-LED-values.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0354-clk-raspberrypi-Also-support-v3d-clock.patch b/target/linux/bcm27xx/patches-5.4/950-0354-clk-raspberrypi-Also-support-v3d-clock.patch deleted file mode 100644 index b2a9363ad4..0000000000 --- a/target/linux/bcm27xx/patches-5.4/950-0354-clk-raspberrypi-Also-support-v3d-clock.patch +++ /dev/null @@ -1,647 +0,0 @@ -From e2262c8ab4755ab574580611d7da22509f07871c Mon Sep 17 00:00:00 2001 -From: popcornmix <popcornmix@gmail.com> -Date: Wed, 21 Aug 2019 14:55:56 +0100 -Subject: [PATCH] clk-raspberrypi: Also support v3d clock - -Signed-off-by: popcornmix <popcornmix@gmail.com> ---- - drivers/clk/bcm/clk-raspberrypi.c | 501 ++++++++++++++++++++++++------ - 1 file changed, 412 insertions(+), 89 deletions(-) - ---- a/drivers/clk/bcm/clk-raspberrypi.c -+++ b/drivers/clk/bcm/clk-raspberrypi.c -@@ -15,33 +15,103 @@ - #include <linux/io.h> - #include <linux/module.h> - #include <linux/platform_device.h> -- -+#include <dt-bindings/clock/bcm2835.h> - #include <soc/bcm2835/raspberrypi-firmware.h> - - #define RPI_FIRMWARE_ARM_CLK_ID 0x00000003 -+#define RPI_FIRMWARE_V3D_CLK_ID 0x00000005 - - #define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0) - #define RPI_FIRMWARE_STATE_WAIT_BIT BIT(1) - --/* -- * Even though the firmware interface alters 'pllb' the frequencies are -- * provided as per 'pllb_arm'. We need to scale before passing them trough. -- */ --#define RPI_FIRMWARE_PLLB_ARM_DIV_RATE 2 -- - #define A2W_PLL_FRAC_BITS 20 - -+#define SOC_BCM2835 BIT(0) -+#define SOC_BCM2711 BIT(1) -+#define SOC_ALL (SOC_BCM2835 | SOC_BCM2711) -+ - struct raspberrypi_clk { - struct device *dev; - struct rpi_firmware *firmware; - struct platform_device *cpufreq; -+}; -+ -+typedef int (*raspberrypi_clk_register)(struct raspberrypi_clk *rpi, -+ const void *data); -+ -+ -+/* assignment helper macros for different clock types */ -+#define _REGISTER(f, s, ...) { .clk_register = (raspberrypi_clk_register)f, \ -+ .supported = s, \ -+ .data = __VA_ARGS__ } -+#define REGISTER_PLL(s, ...) _REGISTER(&raspberrypi_register_pll, \ -+ s, \ -+ &(struct raspberrypi_pll_data) \ -+ {__VA_ARGS__}) -+#define REGISTER_PLL_DIV(s, ...) _REGISTER(&raspberrypi_register_pll_divider, \ -+ s, \ -+ &(struct raspberrypi_pll_divider_data) \ -+ {__VA_ARGS__}) -+#define REGISTER_CLK(s, ...) _REGISTER(&raspberrypi_register_clock, \ -+ s, \ -+ &(struct raspberrypi_clock_data) \ -+ {__VA_ARGS__}) -+ -+ -+struct raspberrypi_pll_data { -+ const char *name; -+ const char *const *parents; -+ int num_parents; -+ u32 clock_id; -+}; -+ -+struct raspberrypi_clock_data { -+ const char *name; -+ const char *const *parents; -+ int num_parents; -+ u32 flags; -+ u32 clock_id; -+}; -+ -+struct raspberrypi_pll_divider_data { -+ const char *name; -+ const char *divider_name; -+ const char *lookup; -+ const char *source_pll; -+ -+ u32 fixed_divider; -+ u32 flags; -+ u32 clock_id; -+}; - -- unsigned long min_rate; -- unsigned long max_rate; -+struct raspberrypi_clk_desc { -+ raspberrypi_clk_register clk_register; -+ unsigned int supported; -+ const void *data; -+}; - -- struct clk_hw pllb; -- struct clk_hw *pllb_arm; -- struct clk_lookup *pllb_arm_lookup; -+struct raspberrypi_clock { -+ struct clk_hw hw; -+ struct raspberrypi_clk *rpi; -+ u32 min_rate; -+ u32 max_rate; -+ const struct raspberrypi_clock_data *data; -+}; -+ -+struct raspberrypi_pll { -+ struct clk_hw hw; -+ struct raspberrypi_clk *rpi; -+ u32 min_rate; -+ u32 max_rate; -+ const struct raspberrypi_pll_data *data; -+}; -+ -+struct raspberrypi_pll_divider { -+ struct clk_divider div; -+ struct raspberrypi_clk *rpi; -+ u32 min_rate; -+ u32 max_rate; -+ const struct raspberrypi_pll_divider_data *data; - }; - - /* -@@ -83,56 +153,49 @@ static int raspberrypi_clock_property(st - return 0; - } - --static int raspberrypi_fw_pll_is_on(struct clk_hw *hw) -+static int raspberrypi_fw_is_on(struct raspberrypi_clk *rpi, u32 clock_id, const char *name) - { -- struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, -- pllb); - u32 val = 0; - int ret; - - ret = raspberrypi_clock_property(rpi->firmware, - RPI_FIRMWARE_GET_CLOCK_STATE, -- RPI_FIRMWARE_ARM_CLK_ID, &val); -+ clock_id, &val); - if (ret) - return 0; - - return !!(val & RPI_FIRMWARE_STATE_ENABLE_BIT); - } - -- --static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, -- unsigned long parent_rate) -+static unsigned long raspberrypi_fw_get_rate(struct raspberrypi_clk *rpi, -+ u32 clock_id, const char *name, unsigned long parent_rate) - { -- struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, -- pllb); - u32 val = 0; - int ret; - - ret = raspberrypi_clock_property(rpi->firmware, - RPI_FIRMWARE_GET_CLOCK_RATE, -- RPI_FIRMWARE_ARM_CLK_ID, -+ clock_id, - &val); - if (ret) -- return ret; -- -- return val * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; -+ dev_err_ratelimited(rpi->dev, "Failed to get %s frequency: %d", -+ name, ret); -+ return val; - } - --static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, -- unsigned long parent_rate) -+static int raspberrypi_fw_set_rate(struct raspberrypi_clk *rpi, -+ u32 clock_id, const char *name, u32 rate, -+ unsigned long parent_rate) - { -- struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, -- pllb); -- u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE; - int ret; - - ret = raspberrypi_clock_property(rpi->firmware, - RPI_FIRMWARE_SET_CLOCK_RATE, -- RPI_FIRMWARE_ARM_CLK_ID, -- &new_rate); -+ clock_id, -+ &rate); - if (ret) - dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d", -- clk_hw_get_name(hw), ret); -+ name, ret); - - return ret; - } -@@ -141,16 +204,18 @@ static int raspberrypi_fw_pll_set_rate(s - * Sadly there is no firmware rate rounding interface. We borrowed it from - * clk-bcm2835. - */ --static int raspberrypi_pll_determine_rate(struct clk_hw *hw, -+static int raspberrypi_determine_rate(struct raspberrypi_clk *rpi, -+ u32 clock_id, const char *name, unsigned long min_rate, unsigned long max_rate, - struct clk_rate_request *req) - { -- struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, -- pllb); -+#if 1 -+ req->rate = clamp(req->rate, min_rate, max_rate); -+#else - u64 div, final_rate; - u32 ndiv, fdiv; - - /* We can't use req->rate directly as it would overflow */ -- final_rate = clamp(req->rate, rpi->min_rate, rpi->max_rate); -+ final_rate = clamp(req->rate, min_rate, max_rate); - - div = (u64)final_rate << A2W_PLL_FRAC_BITS; - do_div(div, req->best_parent_rate); -@@ -163,9 +228,129 @@ static int raspberrypi_pll_determine_rat - - req->rate = final_rate >> A2W_PLL_FRAC_BITS; - -+#endif - return 0; - } - -+static int raspberrypi_fw_clock_is_on(struct clk_hw *hw) -+{ -+ struct raspberrypi_clock *pll = container_of(hw, struct raspberrypi_clock, hw); -+ struct raspberrypi_clk *rpi = pll->rpi; -+ const struct raspberrypi_clock_data *data = pll->data; -+ -+ return raspberrypi_fw_is_on(rpi, data->clock_id, data->name); -+} -+ -+static unsigned long raspberrypi_fw_clock_get_rate(struct clk_hw *hw, -+ unsigned long parent_rate) -+{ -+ struct raspberrypi_clock *pll = container_of(hw, struct raspberrypi_clock, hw); -+ struct raspberrypi_clk *rpi = pll->rpi; -+ const struct raspberrypi_clock_data *data = pll->data; -+ -+ return raspberrypi_fw_get_rate(rpi, data->clock_id, data->name, parent_rate); -+} -+ -+static int raspberrypi_fw_clock_set_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long parent_rate) -+{ -+ struct raspberrypi_clock *pll = container_of(hw, struct raspberrypi_clock, hw); -+ struct raspberrypi_clk *rpi = pll->rpi; -+ const struct raspberrypi_clock_data *data = pll->data; -+ -+ return raspberrypi_fw_set_rate(rpi, data->clock_id, data->name, rate, parent_rate); -+} -+ -+static int raspberrypi_clock_determine_rate(struct clk_hw *hw, -+ struct clk_rate_request *req) -+{ -+ struct raspberrypi_clock *pll = container_of(hw, struct raspberrypi_clock, hw); -+ struct raspberrypi_clk *rpi = pll->rpi; -+ const struct raspberrypi_clock_data *data = pll->data; -+ -+ return raspberrypi_determine_rate(rpi, data->clock_id, data->name, pll->min_rate, pll->max_rate, req); -+} -+ -+static int raspberrypi_fw_pll_is_on(struct clk_hw *hw) -+{ -+ struct raspberrypi_pll *pll = container_of(hw, struct raspberrypi_pll, hw); -+ struct raspberrypi_clk *rpi = pll->rpi; -+ const struct raspberrypi_pll_data *data = pll->data; -+ -+ return raspberrypi_fw_is_on(rpi, data->clock_id, data->name); -+} -+ -+static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, -+ unsigned long parent_rate) -+{ -+ struct raspberrypi_pll *pll = container_of(hw, struct raspberrypi_pll, hw); -+ struct raspberrypi_clk *rpi = pll->rpi; -+ const struct raspberrypi_pll_data *data = pll->data; -+ -+ return raspberrypi_fw_get_rate(rpi, data->clock_id, data->name, parent_rate); -+} -+ -+static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long parent_rate) -+{ -+ struct raspberrypi_pll *pll = container_of(hw, struct raspberrypi_pll, hw); -+ struct raspberrypi_clk *rpi = pll->rpi; -+ const struct raspberrypi_pll_data *data = pll->data; -+ -+ return raspberrypi_fw_set_rate(rpi, data->clock_id, data->name, rate, parent_rate); -+} -+ -+static int raspberrypi_pll_determine_rate(struct clk_hw *hw, -+ struct clk_rate_request *req) -+{ -+ struct raspberrypi_pll *pll = container_of(hw, struct raspberrypi_pll, hw); -+ struct raspberrypi_clk *rpi = pll->rpi; -+ const struct raspberrypi_pll_data *data = pll->data; -+ -+ return raspberrypi_determine_rate(rpi, data->clock_id, data->name, pll->min_rate, pll->max_rate, req); -+} -+ -+ -+static int raspberrypi_fw_pll_div_is_on(struct clk_hw *hw) -+{ -+ struct raspberrypi_pll_divider *pll = container_of(hw, struct raspberrypi_pll_divider, div.hw); -+ struct raspberrypi_clk *rpi = pll->rpi; -+ const struct raspberrypi_pll_divider_data *data = pll->data; -+ -+ return raspberrypi_fw_is_on(rpi, data->clock_id, data->name); -+} -+ -+static unsigned long raspberrypi_fw_pll_div_get_rate(struct clk_hw *hw, -+ unsigned long parent_rate) -+{ -+ struct raspberrypi_pll_divider *pll = container_of(hw, struct raspberrypi_pll_divider, div.hw); -+ struct raspberrypi_clk *rpi = pll->rpi; -+ const struct raspberrypi_pll_divider_data *data = pll->data; -+ -+ return raspberrypi_fw_get_rate(rpi, data->clock_id, data->name, parent_rate); -+} -+ -+static int raspberrypi_fw_pll_div_set_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long parent_rate) -+{ -+ struct raspberrypi_pll_divider *pll = container_of(hw, struct raspberrypi_pll_divider, div.hw); -+ struct raspberrypi_clk *rpi = pll->rpi; -+ const struct raspberrypi_pll_divider_data *data = pll->data; -+ -+ return raspberrypi_fw_set_rate(rpi, data->clock_id, data->name, rate, parent_rate); -+} -+ -+static int raspberrypi_pll_div_determine_rate(struct clk_hw *hw, -+ struct clk_rate_request *req) -+{ -+ struct raspberrypi_pll_divider *pll = container_of(hw, struct raspberrypi_pll_divider, div.hw); -+ struct raspberrypi_clk *rpi = pll->rpi; -+ const struct raspberrypi_pll_divider_data *data = pll->data; -+ -+ return raspberrypi_determine_rate(rpi, data->clock_id, data->name, pll->min_rate, pll->max_rate, req); -+} -+ -+ - static const struct clk_ops raspberrypi_firmware_pll_clk_ops = { - .is_prepared = raspberrypi_fw_pll_is_on, - .recalc_rate = raspberrypi_fw_pll_get_rate, -@@ -173,87 +358,225 @@ static const struct clk_ops raspberrypi_ - .determine_rate = raspberrypi_pll_determine_rate, - }; - --static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) -+static const struct clk_ops raspberrypi_firmware_pll_divider_clk_ops = { -+ .is_prepared = raspberrypi_fw_pll_div_is_on, -+ .recalc_rate = raspberrypi_fw_pll_div_get_rate, -+ .set_rate = raspberrypi_fw_pll_div_set_rate, -+ .determine_rate = raspberrypi_pll_div_determine_rate, -+}; -+ -+static const struct clk_ops raspberrypi_firmware_clk_ops = { -+ .is_prepared = raspberrypi_fw_clock_is_on, -+ .recalc_rate = raspberrypi_fw_clock_get_rate, -+ .set_rate = raspberrypi_fw_clock_set_rate, -+ .determine_rate = raspberrypi_clock_determine_rate, -+}; -+ -+ -+static int raspberrypi_get_clock_range(struct raspberrypi_clk *rpi, u32 clock_id, u32 *min_rate, u32 *max_rate) - { -- u32 min_rate = 0, max_rate = 0; -+ int ret; -+ -+ /* Get min & max rates set by the firmware */ -+ ret = raspberrypi_clock_property(rpi->firmware, -+ RPI_FIRMWARE_GET_MIN_CLOCK_RATE, -+ clock_id, -+ min_rate); -+ if (ret) { -+ dev_err(rpi->dev, "Failed to get clock %d min freq: %d (%d)\n", -+ clock_id, *min_rate, ret); -+ return ret; -+ } -+ -+ ret = raspberrypi_clock_property(rpi->firmware, -+ RPI_FIRMWARE_GET_MAX_CLOCK_RATE, -+ clock_id, -+ max_rate); -+ if (ret) { -+ dev_err(rpi->dev, "Failed to get clock %d max freq: %d (%d)\n", -+ clock_id, *max_rate, ret); -+ return ret; -+ } -+ return 0; -+} -+ -+ -+static int raspberrypi_register_pll(struct raspberrypi_clk *rpi, -+ const struct raspberrypi_pll_data *data) -+{ -+ struct raspberrypi_pll *pll; - struct clk_init_data init; - int ret; - - memset(&init, 0, sizeof(init)); - - /* All of the PLLs derive from the external oscillator. */ -- init.parent_names = (const char *[]){ "osc" }; -- init.num_parents = 1; -- init.name = "pllb"; -+ init.parent_names = data->parents; -+ init.num_parents = data->num_parents; -+ init.name = data->name; - init.ops = &raspberrypi_firmware_pll_clk_ops; - init.flags = CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED; - -- /* Get min & max rates set by the firmware */ -- ret = raspberrypi_clock_property(rpi->firmware, -- RPI_FIRMWARE_GET_MIN_CLOCK_RATE, -- RPI_FIRMWARE_ARM_CLK_ID, -- &min_rate); -+ pll = kzalloc(sizeof(*pll), GFP_KERNEL); -+ if (!pll) -+ return -ENOMEM; -+ pll->rpi = rpi; -+ pll->data = data; -+ pll->hw.init = &init; -+ -+ ret = raspberrypi_get_clock_range(rpi, data->clock_id, &pll->min_rate, &pll->max_rate); - if (ret) { -- dev_err(rpi->dev, "Failed to get %s min freq: %d\n", -- init.name, ret); -+ dev_err(rpi->dev, "%s: raspberrypi_get_clock_range(%s) failed: %d\n", __func__, init.name, ret); - return ret; - } - -- ret = raspberrypi_clock_property(rpi->firmware, -- RPI_FIRMWARE_GET_MAX_CLOCK_RATE, -- RPI_FIRMWARE_ARM_CLK_ID, -- &max_rate); -+ ret = devm_clk_hw_register(rpi->dev, &pll->hw); - if (ret) { -- dev_err(rpi->dev, "Failed to get %s max freq: %d\n", -- init.name, ret); -+ dev_err(rpi->dev, "%s: devm_clk_hw_register(%s) failed: %d\n", __func__, init.name, ret); - return ret; - } -+ return 0; -+} - -- if (!min_rate || !max_rate) { -- dev_err(rpi->dev, "Unexpected frequency range: min %u, max %u\n", -- min_rate, max_rate); -- return -EINVAL; -- } -+static int -+raspberrypi_register_pll_divider(struct raspberrypi_clk *rpi, -+ const struct raspberrypi_pll_divider_data *data) -+{ -+ struct raspberrypi_pll_divider *divider; -+ struct clk_init_data init; -+ int ret; -+ -+ memset(&init, 0, sizeof(init)); -+ -+ init.parent_names = &data->source_pll; -+ init.num_parents = 1; -+ init.name = data->name; -+ init.ops = &raspberrypi_firmware_pll_divider_clk_ops; -+ init.flags = data->flags | CLK_IGNORE_UNUSED; - -- dev_info(rpi->dev, "CPU frequency range: min %u, max %u\n", -- min_rate, max_rate); -+ divider = devm_kzalloc(rpi->dev, sizeof(*divider), GFP_KERNEL); -+ if (!divider) -+ return -ENOMEM; -+ -+ divider->div.hw.init = &init; -+ divider->rpi = rpi; -+ divider->data = data; -+ -+ ret = raspberrypi_get_clock_range(rpi, data->clock_id, ÷r->min_rate, ÷r->max_rate); -+ if (ret) { -+ dev_err(rpi->dev, "%s: raspberrypi_get_clock_range(%s) failed: %d\n", __func__, init.name, ret); -+ return ret; -+ } - -- rpi->min_rate = min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; -- rpi->max_rate = max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; -+ ret = devm_clk_hw_register(rpi->dev, ÷r->div.hw); -+ if (ret) { -+ dev_err(rpi->dev, "%s: devm_clk_hw_register(%s) failed: %d\n", __func__, init.name, ret); -+ return ret; -+ } - -- rpi->pllb.init = &init; -+ /* -+ * PLLH's channels have a fixed divide by 10 afterwards, which -+ * is what our consumers are actually using. -+ */ -+ if (data->fixed_divider != 0) { -+ struct clk_lookup *lookup; -+ struct clk_hw *clk = clk_hw_register_fixed_factor(rpi->dev, -+ data->divider_name, -+ data->name, -+ CLK_SET_RATE_PARENT, -+ 1, -+ data->fixed_divider); -+ if (IS_ERR(clk)) { -+ dev_err(rpi->dev, "%s: clk_hw_register_fixed_factor(%s) failed: %ld\n", __func__, init.name, PTR_ERR(clk)); -+ return PTR_ERR(clk); -+ } -+ if (data->lookup) { -+ lookup = clkdev_hw_create(clk, NULL, data->lookup); -+ if (IS_ERR(lookup)) { -+ dev_err(rpi->dev, "%s: clk_hw_register_fixed_factor(%s) failed: %ld\n", __func__, init.name, PTR_ERR(lookup)); -+ return PTR_ERR(lookup); -+ } -+ } -+ } - -- return devm_clk_hw_register(rpi->dev, &rpi->pllb); -+ return 0; - } - --static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) -+static int raspberrypi_register_clock(struct raspberrypi_clk *rpi, -+ const struct raspberrypi_clock_data *data) - { -- rpi->pllb_arm = clk_hw_register_fixed_factor(rpi->dev, -- "pllb_arm", "pllb", -- CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, -- 1, 2); -- if (IS_ERR(rpi->pllb_arm)) { -- dev_err(rpi->dev, "Failed to initialize pllb_arm\n"); -- return PTR_ERR(rpi->pllb_arm); -- } -+ struct raspberrypi_clock *clock; -+ struct clk_init_data init; -+ struct clk *clk; -+ int ret; -+ -+ memset(&init, 0, sizeof(init)); -+ init.parent_names = data->parents; -+ init.num_parents = data->num_parents; -+ init.name = data->name; -+ init.flags = data->flags | CLK_IGNORE_UNUSED; - -- rpi->pllb_arm_lookup = clkdev_hw_create(rpi->pllb_arm, NULL, "cpu0"); -- if (!rpi->pllb_arm_lookup) { -- dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n"); -- clk_hw_unregister_fixed_factor(rpi->pllb_arm); -+ init.ops = &raspberrypi_firmware_clk_ops; -+ -+ clock = devm_kzalloc(rpi->dev, sizeof(*clock), GFP_KERNEL); -+ if (!clock) - return -ENOMEM; -- } - -+ clock->rpi = rpi; -+ clock->data = data; -+ clock->hw.init = &init; -+ -+ ret = raspberrypi_get_clock_range(rpi, data->clock_id, &clock->min_rate, &clock->max_rate); -+ if (ret) { -+ dev_err(rpi->dev, "%s: raspberrypi_get_clock_range(%s) failed: %d\n", __func__, init.name, ret); -+ return ret; -+ } -+ clk = devm_clk_register(rpi->dev, &clock->hw); -+ if (IS_ERR(clk)) { -+ dev_err(rpi->dev, "%s: devm_clk_register(%s) failed: %ld\n", __func__, init.name, PTR_ERR(clk)); -+ return PTR_ERR(clk); -+ } -+ ret = clk_register_clkdev(clk, init.name, NULL); -+ if (ret) { -+ dev_err(rpi->dev, "%s: clk_register_clkdev(%s) failed: %d\n", __func__, init.name, ret); -+ return ret; -+ } - return 0; - } - -+ -+/* -+ * the real definition of all the pll, pll_dividers and clocks -+ * these make use of the above REGISTER_* macros -+ */ -+static const struct raspberrypi_clk_desc clk_desc_array[] = { -+ /* the PLL + PLL dividers */ -+ [BCM2835_CLOCK_V3D] = REGISTER_CLK( -+ SOC_ALL, -+ .name = "v3d", -+ .parents = (const char *[]){ "osc" }, -+ .num_parents = 1, -+ .clock_id = RPI_FIRMWARE_V3D_CLK_ID), -+ [BCM2835_PLLB_ARM] = REGISTER_PLL_DIV( -+ SOC_ALL, -+ .name = "pllb", -+ .source_pll = "osc", -+ .divider_name = "pllb_arm", -+ .lookup = "cpu0", -+ .fixed_divider = 1, -+ .clock_id = RPI_FIRMWARE_ARM_CLK_ID, -+ .flags = CLK_SET_RATE_PARENT), -+}; -+ - static int raspberrypi_clk_probe(struct platform_device *pdev) - { - struct device_node *firmware_node; - struct device *dev = &pdev->dev; - struct rpi_firmware *firmware; - struct raspberrypi_clk *rpi; -- int ret; -+ const struct raspberrypi_clk_desc *desc; -+ const size_t asize = ARRAY_SIZE(clk_desc_array); -+ int i; - - firmware_node = of_find_compatible_node(NULL, NULL, - "raspberrypi,bcm2835-firmware"); -@@ -275,16 +598,16 @@ static int raspberrypi_clk_probe(struct - rpi->firmware = firmware; - platform_set_drvdata(pdev, rpi); - -- ret = raspberrypi_register_pllb(rpi); -- if (ret) { -- dev_err(dev, "Failed to initialize pllb, %d\n", ret); -- return ret; -+ for (i = 0; i < asize; i++) { -+ desc = &clk_desc_array[i]; -+ if (desc->clk_register && desc->data /*&& -+ (desc->supported & pdata->soc)*/) { -+ int ret = desc->clk_register(rpi, desc->data); -+ if (ret) -+ return ret; -+ } - } - -- ret = raspberrypi_register_pllb_arm(rpi); -- if (ret) -- return ret; -- - rpi->cpufreq = platform_device_register_data(dev, "raspberrypi-cpufreq", - -1, NULL, 0); - diff --git a/target/linux/bcm27xx/patches-5.4/950-0358-drm-v3d-Set-dma_mask-as-well-as-coherent_dma_mask.patch b/target/linux/bcm27xx/patches-5.4/950-0355-drm-v3d-Set-dma_mask-as-well-as-coherent_dma_mask.patch index 67cdd44f29..67cdd44f29 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0358-drm-v3d-Set-dma_mask-as-well-as-coherent_dma_mask.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0355-drm-v3d-Set-dma_mask-as-well-as-coherent_dma_mask.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0359-arm-dts-2711-Add-pcie0-alias.patch b/target/linux/bcm27xx/patches-5.4/950-0356-arm-dts-2711-Add-pcie0-alias.patch index fa00fb1974..fa00fb1974 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0359-arm-dts-2711-Add-pcie0-alias.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0356-arm-dts-2711-Add-pcie0-alias.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0360-rpi-cirrus-wm5102-overlay-fix-pinctrl-configuration.patch b/target/linux/bcm27xx/patches-5.4/950-0357-rpi-cirrus-wm5102-overlay-fix-pinctrl-configuration.patch index 61f49d5ae4..61f49d5ae4 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0360-rpi-cirrus-wm5102-overlay-fix-pinctrl-configuration.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0357-rpi-cirrus-wm5102-overlay-fix-pinctrl-configuration.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0361-staging-vchiq_arm-Set-up-dma-ranges-on-child-devices.patch b/target/linux/bcm27xx/patches-5.4/950-0358-staging-vchiq_arm-Set-up-dma-ranges-on-child-devices.patch index 0a6660b893..0a6660b893 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0361-staging-vchiq_arm-Set-up-dma-ranges-on-child-devices.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0358-staging-vchiq_arm-Set-up-dma-ranges-on-child-devices.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0362-staging-vchiq-Use-the-old-dma-controller-for-OF-conf.patch b/target/linux/bcm27xx/patches-5.4/950-0359-staging-vchiq-Use-the-old-dma-controller-for-OF-conf.patch index eb35a81023..eb35a81023 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0362-staging-vchiq-Use-the-old-dma-controller-for-OF-conf.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0359-staging-vchiq-Use-the-old-dma-controller-for-OF-conf.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0363-dwc_otg-checking-the-urb-transfer_buffer-too-early-3.patch b/target/linux/bcm27xx/patches-5.4/950-0360-dwc_otg-checking-the-urb-transfer_buffer-too-early-3.patch index 3bc9abde27..3bc9abde27 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0363-dwc_otg-checking-the-urb-transfer_buffer-too-early-3.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0360-dwc_otg-checking-the-urb-transfer_buffer-too-early-3.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0364-overlays-Make-mcp342x-run-time-compatible.patch b/target/linux/bcm27xx/patches-5.4/950-0361-overlays-Make-mcp342x-run-time-compatible.patch index 1b4809db0d..1b4809db0d 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0364-overlays-Make-mcp342x-run-time-compatible.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0361-overlays-Make-mcp342x-run-time-compatible.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0365-rpi-cirrus-wm5102-overlay-use-reset-gpios-instead-of.patch b/target/linux/bcm27xx/patches-5.4/950-0362-rpi-cirrus-wm5102-overlay-use-reset-gpios-instead-of.patch index e1ffd3b0b7..e1ffd3b0b7 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0365-rpi-cirrus-wm5102-overlay-use-reset-gpios-instead-of.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0362-rpi-cirrus-wm5102-overlay-use-reset-gpios-instead-of.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0366-sound-soc-only-first-codec-is-master-in-multicodec-s.patch b/target/linux/bcm27xx/patches-5.4/950-0363-sound-soc-only-first-codec-is-master-in-multicodec-s.patch index 263f3532f3..263f3532f3 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0366-sound-soc-only-first-codec-is-master-in-multicodec-s.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0363-sound-soc-only-first-codec-is-master-in-multicodec-s.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0367-Allow-simultaneous-use-of-JustBoom-DAC-and-Digi.patch b/target/linux/bcm27xx/patches-5.4/950-0364-Allow-simultaneous-use-of-JustBoom-DAC-and-Digi.patch index d0d1e28757..d0d1e28757 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0367-Allow-simultaneous-use-of-JustBoom-DAC-and-Digi.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0364-Allow-simultaneous-use-of-JustBoom-DAC-and-Digi.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0368-overlays-dht11-Allow-multiple-instantiation.patch b/target/linux/bcm27xx/patches-5.4/950-0365-overlays-dht11-Allow-multiple-instantiation.patch index b75f1d4988..b75f1d4988 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0368-overlays-dht11-Allow-multiple-instantiation.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0365-overlays-dht11-Allow-multiple-instantiation.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0369-overlays-i2c-rtc-Add-pcf85363-support.patch b/target/linux/bcm27xx/patches-5.4/950-0366-overlays-i2c-rtc-Add-pcf85363-support.patch index 93a699a66a..93a699a66a 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0369-overlays-i2c-rtc-Add-pcf85363-support.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0366-overlays-i2c-rtc-Add-pcf85363-support.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0370-pinctrl-bcm2835-Remove-gpiochip-on-error.patch b/target/linux/bcm27xx/patches-5.4/950-0367-pinctrl-bcm2835-Remove-gpiochip-on-error.patch index 31a1a24caf..31a1a24caf 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0370-pinctrl-bcm2835-Remove-gpiochip-on-error.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0367-pinctrl-bcm2835-Remove-gpiochip-on-error.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0371-pinctrl-bcm2835-Change-init-order-for-gpio-hogs.patch b/target/linux/bcm27xx/patches-5.4/950-0368-pinctrl-bcm2835-Change-init-order-for-gpio-hogs.patch index 3865ae11fb..3865ae11fb 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0371-pinctrl-bcm2835-Change-init-order-for-gpio-hogs.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0368-pinctrl-bcm2835-Change-init-order-for-gpio-hogs.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0372-Pisound-MIDI-communication-fixes-for-scaled-down-CPU.patch b/target/linux/bcm27xx/patches-5.4/950-0369-Pisound-MIDI-communication-fixes-for-scaled-down-CPU.patch index 0221803993..0221803993 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0372-Pisound-MIDI-communication-fixes-for-scaled-down-CPU.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0369-Pisound-MIDI-communication-fixes-for-scaled-down-CPU.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0373-ARM-dts-bcm283x-Remove-simple-bus-from-fixed-clocks.patch b/target/linux/bcm27xx/patches-5.4/950-0370-ARM-dts-bcm283x-Remove-simple-bus-from-fixed-clocks.patch index db90055343..db90055343 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0373-ARM-dts-bcm283x-Remove-simple-bus-from-fixed-clocks.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0370-ARM-dts-bcm283x-Remove-simple-bus-from-fixed-clocks.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0374-ARM-dts-bcm283x-Move-system-timer-back-to-bcm283x.dt.patch b/target/linux/bcm27xx/patches-5.4/950-0371-ARM-dts-bcm283x-Move-system-timer-back-to-bcm283x.dt.patch index 3ececc8f92..3ececc8f92 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0374-ARM-dts-bcm283x-Move-system-timer-back-to-bcm283x.dt.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0371-ARM-dts-bcm283x-Move-system-timer-back-to-bcm283x.dt.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0375-ARM-dts-bcm283x-Move-pixelvalve-to-bcm2835-common.dt.patch b/target/linux/bcm27xx/patches-5.4/950-0372-ARM-dts-bcm283x-Move-pixelvalve-to-bcm2835-common.dt.patch index 218a846fa3..218a846fa3 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0375-ARM-dts-bcm283x-Move-pixelvalve-to-bcm2835-common.dt.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0372-ARM-dts-bcm283x-Move-pixelvalve-to-bcm2835-common.dt.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0376-ARM-dts-bcm2838-rpi-4-b-Fix-memory-node.patch b/target/linux/bcm27xx/patches-5.4/950-0373-ARM-dts-bcm2838-rpi-4-b-Fix-memory-node.patch index 1acf84b7ce..1acf84b7ce 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0376-ARM-dts-bcm2838-rpi-4-b-Fix-memory-node.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0373-ARM-dts-bcm2838-rpi-4-b-Fix-memory-node.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0377-ARM-dts-bcm2838-rpi-4-b-Backport-BT-part-from-upstre.patch b/target/linux/bcm27xx/patches-5.4/950-0374-ARM-dts-bcm2838-rpi-4-b-Backport-BT-part-from-upstre.patch index ce0b8811d0..ce0b8811d0 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0377-ARM-dts-bcm2838-rpi-4-b-Backport-BT-part-from-upstre.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0374-ARM-dts-bcm2838-rpi-4-b-Backport-BT-part-from-upstre.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0378-ARM-dts-bcm2838-Backport-node-names-from-upstream.patch b/target/linux/bcm27xx/patches-5.4/950-0375-ARM-dts-bcm2838-Backport-node-names-from-upstream.patch index fb3a6a3806..fb3a6a3806 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0378-ARM-dts-bcm2838-Backport-node-names-from-upstream.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0375-ARM-dts-bcm2838-Backport-node-names-from-upstream.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0379-ARM-dts-bcm283x-Move-intc-label-to-bcm2835-common.dt.patch b/target/linux/bcm27xx/patches-5.4/950-0376-ARM-dts-bcm283x-Move-intc-label-to-bcm2835-common.dt.patch index ef26293498..ef26293498 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0379-ARM-dts-bcm283x-Move-intc-label-to-bcm2835-common.dt.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0376-ARM-dts-bcm283x-Move-intc-label-to-bcm2835-common.dt.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0380-ARM-dts-bcm2838-Remove-always-on-from-armv7-timer.patch b/target/linux/bcm27xx/patches-5.4/950-0377-ARM-dts-bcm2838-Remove-always-on-from-armv7-timer.patch index 0e2a78649f..0e2a78649f 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0380-ARM-dts-bcm2838-Remove-always-on-from-armv7-timer.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0377-ARM-dts-bcm2838-Remove-always-on-from-armv7-timer.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0381-net-bcmgenet-Add-RGMII_RXID-support.patch b/target/linux/bcm27xx/patches-5.4/950-0378-net-bcmgenet-Add-RGMII_RXID-support.patch index 0e13394c7f..0e13394c7f 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0381-net-bcmgenet-Add-RGMII_RXID-support.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0378-net-bcmgenet-Add-RGMII_RXID-support.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0382-ARM-dts-bcm2838-Backport-genet-from-upstream.patch b/target/linux/bcm27xx/patches-5.4/950-0379-ARM-dts-bcm2838-Backport-genet-from-upstream.patch index ffd7d8e540..ffd7d8e540 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0382-ARM-dts-bcm2838-Backport-genet-from-upstream.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0379-ARM-dts-bcm2838-Backport-genet-from-upstream.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0383-ARM-bcm-Backport-BCM2711-support-from-upstream.patch b/target/linux/bcm27xx/patches-5.4/950-0380-ARM-bcm-Backport-BCM2711-support-from-upstream.patch index 8d16152154..8d16152154 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0383-ARM-bcm-Backport-BCM2711-support-from-upstream.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0380-ARM-bcm-Backport-BCM2711-support-from-upstream.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0384-hwrng-iproc-rng200-Add-support-for-BCM2711.patch b/target/linux/bcm27xx/patches-5.4/950-0381-hwrng-iproc-rng200-Add-support-for-BCM2711.patch index 7a47128837..7a47128837 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0384-hwrng-iproc-rng200-Add-support-for-BCM2711.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0381-hwrng-iproc-rng200-Add-support-for-BCM2711.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0385-ARM-dts-bcm2838-Add-upstream-RNG-compatible.patch b/target/linux/bcm27xx/patches-5.4/950-0382-ARM-dts-bcm2838-Add-upstream-RNG-compatible.patch index 2feda7389e..2feda7389e 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0385-ARM-dts-bcm2838-Add-upstream-RNG-compatible.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0382-ARM-dts-bcm2838-Add-upstream-RNG-compatible.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0386-driver-char-rpivid-Destroy-the-legacy-device-on-remo.patch b/target/linux/bcm27xx/patches-5.4/950-0383-driver-char-rpivid-Destroy-the-legacy-device-on-remo.patch index 49d885cd73..49d885cd73 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0386-driver-char-rpivid-Destroy-the-legacy-device-on-remo.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0383-driver-char-rpivid-Destroy-the-legacy-device-on-remo.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0387-driver-char-rpivid-Clean-up-error-handling-use-of-ER.patch b/target/linux/bcm27xx/patches-5.4/950-0384-driver-char-rpivid-Clean-up-error-handling-use-of-ER.patch index b61e2c5cfe..b61e2c5cfe 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0387-driver-char-rpivid-Clean-up-error-handling-use-of-ER.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0384-driver-char-rpivid-Clean-up-error-handling-use-of-ER.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0388-driver-char-rpivid-Add-error-handling-to-the-legacy-.patch b/target/linux/bcm27xx/patches-5.4/950-0385-driver-char-rpivid-Add-error-handling-to-the-legacy-.patch index 52aa87ed04..52aa87ed04 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0388-driver-char-rpivid-Add-error-handling-to-the-legacy-.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0385-driver-char-rpivid-Add-error-handling-to-the-legacy-.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0389-driver-char-rpivid-Fix-coding-style-whitespace-issue.patch b/target/linux/bcm27xx/patches-5.4/950-0386-driver-char-rpivid-Fix-coding-style-whitespace-issue.patch index 26d0c9894b..26d0c9894b 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0389-driver-char-rpivid-Fix-coding-style-whitespace-issue.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0386-driver-char-rpivid-Fix-coding-style-whitespace-issue.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0390-driver-char-rpimem-Add-SPDX-licence-header.patch b/target/linux/bcm27xx/patches-5.4/950-0387-driver-char-rpimem-Add-SPDX-licence-header.patch index 86b9400ac6..86b9400ac6 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0390-driver-char-rpimem-Add-SPDX-licence-header.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0387-driver-char-rpimem-Add-SPDX-licence-header.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0391-driver-char-rpivid-Fix-access-to-freed-memory.patch b/target/linux/bcm27xx/patches-5.4/950-0388-driver-char-rpivid-Fix-access-to-freed-memory.patch index 67147fa58d..67147fa58d 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0391-driver-char-rpivid-Fix-access-to-freed-memory.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0388-driver-char-rpivid-Fix-access-to-freed-memory.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0392-add-BME680-to-i2c-sensor-overlay.patch b/target/linux/bcm27xx/patches-5.4/950-0389-add-BME680-to-i2c-sensor-overlay.patch index 0c2fe6aa8a..0c2fe6aa8a 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0392-add-BME680-to-i2c-sensor-overlay.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0389-add-BME680-to-i2c-sensor-overlay.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0393-dwc_otg-constrain-endpoint-max-packet-and-transfer-s.patch b/target/linux/bcm27xx/patches-5.4/950-0390-dwc_otg-constrain-endpoint-max-packet-and-transfer-s.patch index 4de95a5449..4de95a5449 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0393-dwc_otg-constrain-endpoint-max-packet-and-transfer-s.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0390-dwc_otg-constrain-endpoint-max-packet-and-transfer-s.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0394-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch b/target/linux/bcm27xx/patches-5.4/950-0391-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch index 0a7356ffa2..0a7356ffa2 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0394-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0391-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0395-dwc_otg-fiq_fsm-add-a-barrier-on-entry-into-FIQ-hand.patch b/target/linux/bcm27xx/patches-5.4/950-0392-dwc_otg-fiq_fsm-add-a-barrier-on-entry-into-FIQ-hand.patch index e986f425ff..e986f425ff 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0395-dwc_otg-fiq_fsm-add-a-barrier-on-entry-into-FIQ-hand.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0392-dwc_otg-fiq_fsm-add-a-barrier-on-entry-into-FIQ-hand.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0396-Add-universal-device-tree-overlay-for-SPI-devices.patch b/target/linux/bcm27xx/patches-5.4/950-0393-Add-universal-device-tree-overlay-for-SPI-devices.patch index cb8fa91bef..cb8fa91bef 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0396-Add-universal-device-tree-overlay-for-SPI-devices.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0393-Add-universal-device-tree-overlay-for-SPI-devices.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0397-sound-Add-the-HiFiBerry-DAC-HD-version.patch b/target/linux/bcm27xx/patches-5.4/950-0394-sound-Add-the-HiFiBerry-DAC-HD-version.patch index 6b9a6bd29c..6b9a6bd29c 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0397-sound-Add-the-HiFiBerry-DAC-HD-version.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0394-sound-Add-the-HiFiBerry-DAC-HD-version.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0398-Initialise-rpi-firmware-before-clk-bcm2835.patch b/target/linux/bcm27xx/patches-5.4/950-0395-Initialise-rpi-firmware-before-clk-bcm2835.patch index 0e0173085d..0e0173085d 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0398-Initialise-rpi-firmware-before-clk-bcm2835.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0395-Initialise-rpi-firmware-before-clk-bcm2835.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0399-Fix-master-mode-settings-of-HiFiBerry-DAC-ADC-PRO-ca.patch b/target/linux/bcm27xx/patches-5.4/950-0396-Fix-master-mode-settings-of-HiFiBerry-DAC-ADC-PRO-ca.patch index 54b366f65a..54b366f65a 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0399-Fix-master-mode-settings-of-HiFiBerry-DAC-ADC-PRO-ca.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0396-Fix-master-mode-settings-of-HiFiBerry-DAC-ADC-PRO-ca.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0400-overlays-Use-preferred-compatible-strings.patch b/target/linux/bcm27xx/patches-5.4/950-0397-overlays-Use-preferred-compatible-strings.patch index 6a246050cf..6a246050cf 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0400-overlays-Use-preferred-compatible-strings.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0397-overlays-Use-preferred-compatible-strings.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0401-tty-amba-pl011-Add-un-throttle-support.patch b/target/linux/bcm27xx/patches-5.4/950-0398-tty-amba-pl011-Add-un-throttle-support.patch index 7777e2df96..7777e2df96 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0401-tty-amba-pl011-Add-un-throttle-support.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0398-tty-amba-pl011-Add-un-throttle-support.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0402-Fix-i2c-pwm-pca9685a-overlay.patch b/target/linux/bcm27xx/patches-5.4/950-0399-Fix-i2c-pwm-pca9685a-overlay.patch index 17bdf3984e..17bdf3984e 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0402-Fix-i2c-pwm-pca9685a-overlay.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0399-Fix-i2c-pwm-pca9685a-overlay.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0403-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-PRO-sound-.patch b/target/linux/bcm27xx/patches-5.4/950-0400-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-PRO-sound-.patch index 397a2c3331..397a2c3331 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0403-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-PRO-sound-.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0400-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-PRO-sound-.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0404-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-sound-card.patch b/target/linux/bcm27xx/patches-5.4/950-0401-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-sound-card.patch index bd8f405ef8..bd8f405ef8 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0404-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-sound-card.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0401-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-sound-card.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0405-adds-LED-OFF-feature-to-HiFiBerry-DAC-DAC-PRO-sound-.patch b/target/linux/bcm27xx/patches-5.4/950-0402-adds-LED-OFF-feature-to-HiFiBerry-DAC-DAC-PRO-sound-.patch index a5c3d40512..a5c3d40512 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0405-adds-LED-OFF-feature-to-HiFiBerry-DAC-DAC-PRO-sound-.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0402-adds-LED-OFF-feature-to-HiFiBerry-DAC-DAC-PRO-sound-.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0406-pisound-Added-reading-Pisound-board-hardware-revisio.patch b/target/linux/bcm27xx/patches-5.4/950-0403-pisound-Added-reading-Pisound-board-hardware-revisio.patch index df6f526e2e..df6f526e2e 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0406-pisound-Added-reading-Pisound-board-hardware-revisio.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0403-pisound-Added-reading-Pisound-board-hardware-revisio.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0407-mmc-sdhci-iproc-Fix-vmmc-regulators-on-iProc.patch b/target/linux/bcm27xx/patches-5.4/950-0404-mmc-sdhci-iproc-Fix-vmmc-regulators-on-iProc.patch index 4713dde247..4713dde247 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0407-mmc-sdhci-iproc-Fix-vmmc-regulators-on-iProc.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0404-mmc-sdhci-iproc-Fix-vmmc-regulators-on-iProc.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0408-ARM-dts-Declare-RPi-4B-SD-card-power-regulator.patch b/target/linux/bcm27xx/patches-5.4/950-0405-ARM-dts-Declare-RPi-4B-SD-card-power-regulator.patch index a69feb50b4..a69feb50b4 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0408-ARM-dts-Declare-RPi-4B-SD-card-power-regulator.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0405-ARM-dts-Declare-RPi-4B-SD-card-power-regulator.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0409-bcm2838.dtsi-Use-BCM2711-PCIe-compatible-string.patch b/target/linux/bcm27xx/patches-5.4/950-0406-bcm2838.dtsi-Use-BCM2711-PCIe-compatible-string.patch index 2f5d87b004..2f5d87b004 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0409-bcm2838.dtsi-Use-BCM2711-PCIe-compatible-string.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0406-bcm2838.dtsi-Use-BCM2711-PCIe-compatible-string.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0410-ARM-dts-Remove-bcm2838-rpi-4-b.dts.patch b/target/linux/bcm27xx/patches-5.4/950-0407-ARM-dts-Remove-bcm2838-rpi-4-b.dts.patch index 10c5d23b46..10c5d23b46 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0410-ARM-dts-Remove-bcm2838-rpi-4-b.dts.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0407-ARM-dts-Remove-bcm2838-rpi-4-b.dts.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0411-tty-amba-pl011-Avoid-rare-write-when-full-error.patch b/target/linux/bcm27xx/patches-5.4/950-0408-tty-amba-pl011-Avoid-rare-write-when-full-error.patch index b2b27f258f..b2b27f258f 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0411-tty-amba-pl011-Avoid-rare-write-when-full-error.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0408-tty-amba-pl011-Avoid-rare-write-when-full-error.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0412-usb-xhci-Raspberry-Pi-FW-loader-for-VIA-VL805.patch b/target/linux/bcm27xx/patches-5.4/950-0409-usb-xhci-Raspberry-Pi-FW-loader-for-VIA-VL805.patch index 06e979a462..06e979a462 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0412-usb-xhci-Raspberry-Pi-FW-loader-for-VIA-VL805.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0409-usb-xhci-Raspberry-Pi-FW-loader-for-VIA-VL805.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0413-overlays-Correct-the-eth_led-colour-assignments.patch b/target/linux/bcm27xx/patches-5.4/950-0410-overlays-Correct-the-eth_led-colour-assignments.patch index f1f3290bad..f1f3290bad 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0413-overlays-Correct-the-eth_led-colour-assignments.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0410-overlays-Correct-the-eth_led-colour-assignments.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0414-ARM-dts-Add-sd_poll_once-dtparam-to-bcm283x-2711.patch b/target/linux/bcm27xx/patches-5.4/950-0411-ARM-dts-Add-sd_poll_once-dtparam-to-bcm283x-2711.patch index fef644e1c9..fef644e1c9 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0414-ARM-dts-Add-sd_poll_once-dtparam-to-bcm283x-2711.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0411-ARM-dts-Add-sd_poll_once-dtparam-to-bcm283x-2711.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0415-overlays-Add-ssd1306-spi-ssh1106-spi-ssd-1351-spi.patch b/target/linux/bcm27xx/patches-5.4/950-0412-overlays-Add-ssd1306-spi-ssh1106-spi-ssd-1351-spi.patch index 77bf63e2e2..77bf63e2e2 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0415-overlays-Add-ssd1306-spi-ssh1106-spi-ssd-1351-spi.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0412-overlays-Add-ssd1306-spi-ssh1106-spi-ssd-1351-spi.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0416-overlays-dwc2-Increase-RX-FIFO-size.patch b/target/linux/bcm27xx/patches-5.4/950-0413-overlays-dwc2-Increase-RX-FIFO-size.patch index 23bc39b3d8..23bc39b3d8 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0416-overlays-dwc2-Increase-RX-FIFO-size.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0413-overlays-dwc2-Increase-RX-FIFO-size.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0417-overlays-Fix-mcp23017-s-addr-parameter.patch b/target/linux/bcm27xx/patches-5.4/950-0414-overlays-Fix-mcp23017-s-addr-parameter.patch index 29ad87227d..29ad87227d 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0417-overlays-Fix-mcp23017-s-addr-parameter.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0414-overlays-Fix-mcp23017-s-addr-parameter.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0418-SQUASH-Fix-spi-driver-compiler-warnings.patch b/target/linux/bcm27xx/patches-5.4/950-0415-SQUASH-Fix-spi-driver-compiler-warnings.patch index bccf74b410..bccf74b410 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0418-SQUASH-Fix-spi-driver-compiler-warnings.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0415-SQUASH-Fix-spi-driver-compiler-warnings.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0419-overlays-add-hdmi-backlight-hwhack-gpio-overlay.patch b/target/linux/bcm27xx/patches-5.4/950-0416-overlays-add-hdmi-backlight-hwhack-gpio-overlay.patch index e94f15196b..e94f15196b 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0419-overlays-add-hdmi-backlight-hwhack-gpio-overlay.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0416-overlays-add-hdmi-backlight-hwhack-gpio-overlay.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0420-ARM-dts-Revert-all-changes-to-upstream-dts-files.patch b/target/linux/bcm27xx/patches-5.4/950-0417-ARM-dts-Revert-all-changes-to-upstream-dts-files.patch index 403f534baf..403f534baf 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0420-ARM-dts-Revert-all-changes-to-upstream-dts-files.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0417-ARM-dts-Revert-all-changes-to-upstream-dts-files.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0421-ARM-dts-Clean-out-downstream-BCM2711-2838-files.patch b/target/linux/bcm27xx/patches-5.4/950-0418-ARM-dts-Clean-out-downstream-BCM2711-2838-files.patch index a66202a2c9..a66202a2c9 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0421-ARM-dts-Clean-out-downstream-BCM2711-2838-files.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0418-ARM-dts-Clean-out-downstream-BCM2711-2838-files.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0422-ARM-dts-Add-minimal-Raspberry-Pi-4-support.patch b/target/linux/bcm27xx/patches-5.4/950-0419-ARM-dts-Add-minimal-Raspberry-Pi-4-support.patch index 15e4f53a0a..15e4f53a0a 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0422-ARM-dts-Add-minimal-Raspberry-Pi-4-support.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0419-ARM-dts-Add-minimal-Raspberry-Pi-4-support.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0423-ARM-dts-bcm2711-force-CMA-into-first-GB-of-memory.patch b/target/linux/bcm27xx/patches-5.4/950-0420-ARM-dts-bcm2711-force-CMA-into-first-GB-of-memory.patch index 44f60d610f..44f60d610f 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0423-ARM-dts-bcm2711-force-CMA-into-first-GB-of-memory.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0420-ARM-dts-bcm2711-force-CMA-into-first-GB-of-memory.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0424-ARM-dts-bcm2711-rpi-4-Enable-GENET-support.patch b/target/linux/bcm27xx/patches-5.4/950-0421-ARM-dts-bcm2711-rpi-4-Enable-GENET-support.patch index e204859fbd..e204859fbd 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0424-ARM-dts-bcm2711-rpi-4-Enable-GENET-support.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0421-ARM-dts-bcm2711-rpi-4-Enable-GENET-support.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0425-ARM-dts-bcm2711-fix-soc-s-node-dma-ranges.patch b/target/linux/bcm27xx/patches-5.4/950-0422-ARM-dts-bcm2711-fix-soc-s-node-dma-ranges.patch index ed0be9b9a3..ed0be9b9a3 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0425-ARM-dts-bcm2711-fix-soc-s-node-dma-ranges.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0422-ARM-dts-bcm2711-fix-soc-s-node-dma-ranges.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0426-ARM-dts-Rebuild-downstream-DTS-files.patch b/target/linux/bcm27xx/patches-5.4/950-0423-ARM-dts-Rebuild-downstream-DTS-files.patch index 8d230d0edb..8d230d0edb 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0426-ARM-dts-Rebuild-downstream-DTS-files.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0423-ARM-dts-Rebuild-downstream-DTS-files.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0427-staging-vchiq_arm-Fix-bcm2711-compatible-string.patch b/target/linux/bcm27xx/patches-5.4/950-0424-staging-vchiq_arm-Fix-bcm2711-compatible-string.patch index 4436c0a39d..4436c0a39d 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0427-staging-vchiq_arm-Fix-bcm2711-compatible-string.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0424-staging-vchiq_arm-Fix-bcm2711-compatible-string.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0428-thermal-brcmstb_thermal-Correct-SoC-name.patch b/target/linux/bcm27xx/patches-5.4/950-0425-thermal-brcmstb_thermal-Correct-SoC-name.patch index 5ba80afd98..5ba80afd98 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0428-thermal-brcmstb_thermal-Correct-SoC-name.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0425-thermal-brcmstb_thermal-Correct-SoC-name.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0429-hwrng-iproc-rng200-Correct-SoC-name.patch b/target/linux/bcm27xx/patches-5.4/950-0426-hwrng-iproc-rng200-Correct-SoC-name.patch index f4e93308b1..f4e93308b1 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0429-hwrng-iproc-rng200-Correct-SoC-name.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0426-hwrng-iproc-rng200-Correct-SoC-name.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0430-ARM-dts-Correct-SoC-name.patch b/target/linux/bcm27xx/patches-5.4/950-0427-ARM-dts-Correct-SoC-name.patch index c18eb8af3c..c18eb8af3c 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0430-ARM-dts-Correct-SoC-name.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0427-ARM-dts-Correct-SoC-name.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0431-ARM-dts-Remove-CMA-allocation-from-Pi-4-dts.patch b/target/linux/bcm27xx/patches-5.4/950-0428-ARM-dts-Remove-CMA-allocation-from-Pi-4-dts.patch index 2c093459da..2c093459da 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0431-ARM-dts-Remove-CMA-allocation-from-Pi-4-dts.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0428-ARM-dts-Remove-CMA-allocation-from-Pi-4-dts.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0432-staging-vchiq_arm-Give-vchiq-children-DT-nodes.patch b/target/linux/bcm27xx/patches-5.4/950-0429-staging-vchiq_arm-Give-vchiq-children-DT-nodes.patch index 7fb3443fa9..7fb3443fa9 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0432-staging-vchiq_arm-Give-vchiq-children-DT-nodes.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0429-staging-vchiq_arm-Give-vchiq-children-DT-nodes.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0433-staging-vchiq_arm-Add-a-matching-unregister-call.patch b/target/linux/bcm27xx/patches-5.4/950-0430-staging-vchiq_arm-Add-a-matching-unregister-call.patch index 8271b2505a..8271b2505a 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0433-staging-vchiq_arm-Add-a-matching-unregister-call.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0430-staging-vchiq_arm-Add-a-matching-unregister-call.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0434-ARM-dts-Move-audio-node-under-the-vchiq-parent.patch b/target/linux/bcm27xx/patches-5.4/950-0431-ARM-dts-Move-audio-node-under-the-vchiq-parent.patch index b5e59e6464..b5e59e6464 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0434-ARM-dts-Move-audio-node-under-the-vchiq-parent.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0431-ARM-dts-Move-audio-node-under-the-vchiq-parent.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0435-ARM-dts-overlays-Create-custom-clocks-in.patch b/target/linux/bcm27xx/patches-5.4/950-0432-ARM-dts-overlays-Create-custom-clocks-in.patch index ac50884bce..ac50884bce 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0435-ARM-dts-overlays-Create-custom-clocks-in.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0432-ARM-dts-overlays-Create-custom-clocks-in.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0436-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch b/target/linux/bcm27xx/patches-5.4/950-0433-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch index 4774fd2326..4774fd2326 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0436-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0433-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0437-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch b/target/linux/bcm27xx/patches-5.4/950-0434-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch index 213e8b8d9d..213e8b8d9d 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0437-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0434-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0438-of-overlay-Correct-symbol-path-fixups.patch b/target/linux/bcm27xx/patches-5.4/950-0435-of-overlay-Correct-symbol-path-fixups.patch index 4b005876de..4b005876de 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0438-of-overlay-Correct-symbol-path-fixups.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0435-of-overlay-Correct-symbol-path-fixups.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0439-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch b/target/linux/bcm27xx/patches-5.4/950-0436-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch index 636ad26a91..636ad26a91 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0439-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0436-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0440-of-address-Introduce-of_get_next_dma_parent-helper.patch b/target/linux/bcm27xx/patches-5.4/950-0437-of-address-Introduce-of_get_next_dma_parent-helper.patch index 1056cfc60d..1056cfc60d 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0440-of-address-Introduce-of_get_next_dma_parent-helper.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0437-of-address-Introduce-of_get_next_dma_parent-helper.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0441-of-address-Follow-DMA-parent-for-dma-coherent.patch b/target/linux/bcm27xx/patches-5.4/950-0438-of-address-Follow-DMA-parent-for-dma-coherent.patch index dbfb1025cd..dbfb1025cd 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0441-of-address-Follow-DMA-parent-for-dma-coherent.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0438-of-address-Follow-DMA-parent-for-dma-coherent.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0442-of-Factor-out-addr-size-cells-parsing.patch b/target/linux/bcm27xx/patches-5.4/950-0439-of-Factor-out-addr-size-cells-parsing.patch index 045ee9827f..045ee9827f 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0442-of-Factor-out-addr-size-cells-parsing.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0439-of-Factor-out-addr-size-cells-parsing.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0443-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch b/target/linux/bcm27xx/patches-5.4/950-0440-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch index 28d1987a9c..28d1987a9c 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0443-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0440-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0444-of-Make-of_dma_get_range-work-on-bus-nodes.patch b/target/linux/bcm27xx/patches-5.4/950-0441-of-Make-of_dma_get_range-work-on-bus-nodes.patch index 1cac2dfcd8..1cac2dfcd8 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0444-of-Make-of_dma_get_range-work-on-bus-nodes.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0441-of-Make-of_dma_get_range-work-on-bus-nodes.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0445-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch b/target/linux/bcm27xx/patches-5.4/950-0442-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch index a90e7f8587..a90e7f8587 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0445-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0442-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0446-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch b/target/linux/bcm27xx/patches-5.4/950-0443-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch index 3039bfe822..3039bfe822 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0446-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0443-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0447-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch b/target/linux/bcm27xx/patches-5.4/950-0444-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch index 2397c71af7..2397c71af7 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0447-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0444-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0448-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch b/target/linux/bcm27xx/patches-5.4/950-0445-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch index 23811e0b6e..23811e0b6e 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0448-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0445-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0449-resource-Add-a-resource_list_first_type-helper.patch b/target/linux/bcm27xx/patches-5.4/950-0446-resource-Add-a-resource_list_first_type-helper.patch index c2c959a3c1..c2c959a3c1 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0449-resource-Add-a-resource_list_first_type-helper.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0446-resource-Add-a-resource_list_first_type-helper.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0450-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch b/target/linux/bcm27xx/patches-5.4/950-0447-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch index c3ae61c993..c3ae61c993 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0450-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0447-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0451-x86-PCI-sta2x11-use-default-DMA-address-translation.patch b/target/linux/bcm27xx/patches-5.4/950-0448-x86-PCI-sta2x11-use-default-DMA-address-translation.patch index 51fd4be35e..51fd4be35e 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0451-x86-PCI-sta2x11-use-default-DMA-address-translation.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0448-x86-PCI-sta2x11-use-default-DMA-address-translation.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0452-PCI-of-Add-inbound-resource-parsing-to-helpers.patch b/target/linux/bcm27xx/patches-5.4/950-0449-PCI-of-Add-inbound-resource-parsing-to-helpers.patch index 987351c1fc..987351c1fc 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0452-PCI-of-Add-inbound-resource-parsing-to-helpers.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0449-PCI-of-Add-inbound-resource-parsing-to-helpers.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0453-dma-direct-unify-the-dma_capable-definitions.patch b/target/linux/bcm27xx/patches-5.4/950-0450-dma-direct-unify-the-dma_capable-definitions.patch index d115f0eb80..d115f0eb80 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0453-dma-direct-unify-the-dma_capable-definitions.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0450-dma-direct-unify-the-dma_capable-definitions.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0454-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch b/target/linux/bcm27xx/patches-5.4/950-0451-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch index a98f1d3852..a98f1d3852 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0454-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0451-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0455-dma-direct-exclude-dma_direct_map_resource-from-the-.patch b/target/linux/bcm27xx/patches-5.4/950-0452-dma-direct-exclude-dma_direct_map_resource-from-the-.patch index dca3fbfb57..dca3fbfb57 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0455-dma-direct-exclude-dma_direct_map_resource-from-the-.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0452-dma-direct-exclude-dma_direct_map_resource-from-the-.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0456-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch b/target/linux/bcm27xx/patches-5.4/950-0453-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch index 162a91c530..162a91c530 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0456-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0453-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0457-ARM-dts-bcm2711-Enable-PCIe-controller.patch b/target/linux/bcm27xx/patches-5.4/950-0454-ARM-dts-bcm2711-Enable-PCIe-controller.patch index 9f114c1633..9f114c1633 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0457-ARM-dts-bcm2711-Enable-PCIe-controller.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0454-ARM-dts-bcm2711-Enable-PCIe-controller.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0458-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch b/target/linux/bcm27xx/patches-5.4/950-0455-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch index ca97a1966e..ca97a1966e 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0458-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0455-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0459-PCI-brcmstb-Add-MSI-support.patch b/target/linux/bcm27xx/patches-5.4/950-0456-PCI-brcmstb-Add-MSI-support.patch index a27259bd19..a27259bd19 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0459-PCI-brcmstb-Add-MSI-support.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0456-PCI-brcmstb-Add-MSI-support.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0460-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch b/target/linux/bcm27xx/patches-5.4/950-0457-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch index 6bb45ccb1f..6bb45ccb1f 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0460-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0457-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0461-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch b/target/linux/bcm27xx/patches-5.4/950-0458-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch index 729d6e68ba..729d6e68ba 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0461-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0458-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0462-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch b/target/linux/bcm27xx/patches-5.4/950-0459-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch index 16eaeddb29..16eaeddb29 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0462-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0459-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0463-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch b/target/linux/bcm27xx/patches-5.4/950-0460-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch index 4ca345f2ad..4ca345f2ad 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0463-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0460-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0464-overlays-imx219-Correct-link-frequency-to-match-the-.patch b/target/linux/bcm27xx/patches-5.4/950-0461-overlays-imx219-Correct-link-frequency-to-match-the-.patch index e9eed21451..e9eed21451 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0464-overlays-imx219-Correct-link-frequency-to-match-the-.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0461-overlays-imx219-Correct-link-frequency-to-match-the-.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0465-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch b/target/linux/bcm27xx/patches-5.4/950-0462-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch index 6f71797a8b..6f71797a8b 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0465-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0462-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0466-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch b/target/linux/bcm27xx/patches-5.4/950-0463-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch index c7e10cbdc1..c7e10cbdc1 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0466-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0463-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0467-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch b/target/linux/bcm27xx/patches-5.4/950-0464-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch index 5846e96af8..5846e96af8 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0467-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0464-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0468-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch b/target/linux/bcm27xx/patches-5.4/950-0465-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch index 907bab5cfd..907bab5cfd 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0468-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0465-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0469-media-ov5647-Add-extra-10-bit-sensor-modes.patch b/target/linux/bcm27xx/patches-5.4/950-0466-media-ov5647-Add-extra-10-bit-sensor-modes.patch index 9e40b5164a..9e40b5164a 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0469-media-ov5647-Add-extra-10-bit-sensor-modes.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0466-media-ov5647-Add-extra-10-bit-sensor-modes.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0470-media-ov5647-change-defaults-to-better-match-raw-cam.patch b/target/linux/bcm27xx/patches-5.4/950-0467-media-ov5647-change-defaults-to-better-match-raw-cam.patch index 58d23b7bcb..58d23b7bcb 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0470-media-ov5647-change-defaults-to-better-match-raw-cam.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0467-media-ov5647-change-defaults-to-better-match-raw-cam.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0471-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch b/target/linux/bcm27xx/patches-5.4/950-0468-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch index 0b48018c1e..0b48018c1e 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0471-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0468-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0472-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch b/target/linux/bcm27xx/patches-5.4/950-0469-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch index ac4fe1698b..ac4fe1698b 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0472-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0469-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0473-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch b/target/linux/bcm27xx/patches-5.4/950-0470-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch index f6264ff04a..f6264ff04a 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0473-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0470-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0474-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch b/target/linux/bcm27xx/patches-5.4/950-0471-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch index cdd14f7940..cdd14f7940 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0474-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0471-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0475-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch b/target/linux/bcm27xx/patches-5.4/950-0472-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch index 8e66bccf6e..8e66bccf6e 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0475-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0472-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0476-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch b/target/linux/bcm27xx/patches-5.4/950-0473-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch index f549e73fd3..f549e73fd3 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0476-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0473-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0477-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch b/target/linux/bcm27xx/patches-5.4/950-0474-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch index 9504bb3982..9504bb3982 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0477-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0474-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0478-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch b/target/linux/bcm27xx/patches-5.4/950-0475-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch index 9d8090441a..9d8090441a 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0478-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0475-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0479-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch b/target/linux/bcm27xx/patches-5.4/950-0476-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch index 9b514c3a25..9b514c3a25 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0479-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0476-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0480-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch b/target/linux/bcm27xx/patches-5.4/950-0477-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch index 6abe7beb32..6abe7beb32 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0480-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0477-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0481-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch b/target/linux/bcm27xx/patches-5.4/950-0478-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch index 1d356eb6ab..1d356eb6ab 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0481-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0478-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0482-drm-modes-parse_cmdline-Accept-extras-directly-after.patch b/target/linux/bcm27xx/patches-5.4/950-0479-drm-modes-parse_cmdline-Accept-extras-directly-after.patch index 8d9a92ec0d..8d9a92ec0d 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0482-drm-modes-parse_cmdline-Accept-extras-directly-after.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0479-drm-modes-parse_cmdline-Accept-extras-directly-after.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0483-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch b/target/linux/bcm27xx/patches-5.4/950-0480-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch index 1716ebd7be..1716ebd7be 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0483-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0480-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0484-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch b/target/linux/bcm27xx/patches-5.4/950-0481-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch index 6ed952bd45..6ed952bd45 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0484-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0481-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0485-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch b/target/linux/bcm27xx/patches-5.4/950-0482-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch index ac973e1d4e..ac973e1d4e 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0485-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0482-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0486-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch b/target/linux/bcm27xx/patches-5.4/950-0483-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch index 746f35f83b..746f35f83b 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0486-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0483-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0487-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch b/target/linux/bcm27xx/patches-5.4/950-0484-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch index cbda9ecc10..cbda9ecc10 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0487-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0484-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0488-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch b/target/linux/bcm27xx/patches-5.4/950-0485-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch index fb4a7f1cda..fb4a7f1cda 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0488-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0485-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0489-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch b/target/linux/bcm27xx/patches-5.4/950-0486-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch index 372cd0d665..372cd0d665 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0489-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0486-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0490-drm-v3d-Replace-wait_for-macros-to-remove-use-of-msl.patch b/target/linux/bcm27xx/patches-5.4/950-0487-drm-v3d-Replace-wait_for-macros-to-remove-use-of-msl.patch index 830bc1125e..830bc1125e 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0490-drm-v3d-Replace-wait_for-macros-to-remove-use-of-msl.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0487-drm-v3d-Replace-wait_for-macros-to-remove-use-of-msl.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0491-Reduce-noise-from-rpi-poe-hat-fan.patch b/target/linux/bcm27xx/patches-5.4/950-0488-Reduce-noise-from-rpi-poe-hat-fan.patch index 7c50843102..7c50843102 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0491-Reduce-noise-from-rpi-poe-hat-fan.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0488-Reduce-noise-from-rpi-poe-hat-fan.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0492-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch b/target/linux/bcm27xx/patches-5.4/950-0489-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch index 72941b59bb..72941b59bb 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0492-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0489-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0493-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch b/target/linux/bcm27xx/patches-5.4/950-0490-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch index 265533eb3a..265533eb3a 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0493-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0490-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0494-media-add-V4L2_CID_UNIT_CELL_SIZE-control.patch b/target/linux/bcm27xx/patches-5.4/950-0491-media-add-V4L2_CID_UNIT_CELL_SIZE-control.patch index 0c860c7e4a..0c860c7e4a 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0494-media-add-V4L2_CID_UNIT_CELL_SIZE-control.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0491-media-add-V4L2_CID_UNIT_CELL_SIZE-control.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-common-add-pixel-encoding-support.patch b/target/linux/bcm27xx/patches-5.4/950-0492-media-v4l2-common-add-pixel-encoding-support.patch index aa127ab5e7..aa127ab5e7 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-common-add-pixel-encoding-support.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0492-media-v4l2-common-add-pixel-encoding-support.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0496-media-v4l2-common-add-RGB565-and-RGB55-to-v4l2_forma.patch b/target/linux/bcm27xx/patches-5.4/950-0493-media-v4l2-common-add-RGB565-and-RGB55-to-v4l2_forma.patch index 0171cdf821..0171cdf821 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0496-media-v4l2-common-add-RGB565-and-RGB55-to-v4l2_forma.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0493-media-v4l2-common-add-RGB565-and-RGB55-to-v4l2_forma.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0497-media-vb2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch b/target/linux/bcm27xx/patches-5.4/950-0494-media-vb2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch index b114aefacc..b114aefacc 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0497-media-vb2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0494-media-vb2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0498-media-v4l2-mem2mem-support-held-capture-buffers.patch b/target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-mem2mem-support-held-capture-buffers.patch index bb66baf07d..bb66baf07d 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0498-media-v4l2-mem2mem-support-held-capture-buffers.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-mem2mem-support-held-capture-buffers.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0499-media-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch b/target/linux/bcm27xx/patches-5.4/950-0496-media-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch index ef075fdb22..ef075fdb22 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0499-media-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0496-media-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0500-media-v4l2-mem2mem-add-stateless_-try_-decoder_cmd-i.patch b/target/linux/bcm27xx/patches-5.4/950-0497-media-v4l2-mem2mem-add-stateless_-try_-decoder_cmd-i.patch index 0b74dbf123..0b74dbf123 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0500-media-v4l2-mem2mem-add-stateless_-try_-decoder_cmd-i.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0497-media-v4l2-mem2mem-add-stateless_-try_-decoder_cmd-i.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0501-media-v4l2-mem2mem-add-new_frame-detection.patch b/target/linux/bcm27xx/patches-5.4/950-0498-media-v4l2-mem2mem-add-new_frame-detection.patch index 3c777922f1..3c777922f1 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0501-media-v4l2-mem2mem-add-new_frame-detection.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0498-media-v4l2-mem2mem-add-new_frame-detection.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0502-media-Documentation-media-Document-V4L2_CTRL_TYPE_AR.patch b/target/linux/bcm27xx/patches-5.4/950-0499-media-Documentation-media-Document-V4L2_CTRL_TYPE_AR.patch index 1d478fc68a..1d478fc68a 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0502-media-Documentation-media-Document-V4L2_CTRL_TYPE_AR.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0499-media-Documentation-media-Document-V4L2_CTRL_TYPE_AR.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0503-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch b/target/linux/bcm27xx/patches-5.4/950-0500-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch index 0fe0f8cea4..0fe0f8cea4 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0503-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0500-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0504-media-v4l2-mem2mem-Fix-hold-buf-flag-checks.patch b/target/linux/bcm27xx/patches-5.4/950-0501-media-v4l2-mem2mem-Fix-hold-buf-flag-checks.patch index 18073a8f9d..18073a8f9d 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0504-media-v4l2-mem2mem-Fix-hold-buf-flag-checks.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0501-media-v4l2-mem2mem-Fix-hold-buf-flag-checks.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0505-media-pixfmt-Document-the-HEVC-slice-pixel-format.patch b/target/linux/bcm27xx/patches-5.4/950-0502-media-pixfmt-Document-the-HEVC-slice-pixel-format.patch index 7398807782..7398807782 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0505-media-pixfmt-Document-the-HEVC-slice-pixel-format.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0502-media-pixfmt-Document-the-HEVC-slice-pixel-format.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0506-media-uapi-hevc-Add-scaling-matrix-control.patch b/target/linux/bcm27xx/patches-5.4/950-0503-media-uapi-hevc-Add-scaling-matrix-control.patch index c2cf27a40e..c2cf27a40e 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0506-media-uapi-hevc-Add-scaling-matrix-control.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0503-media-uapi-hevc-Add-scaling-matrix-control.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0507-media-uapi-hevc-Add-segment-address-field.patch b/target/linux/bcm27xx/patches-5.4/950-0504-media-uapi-hevc-Add-segment-address-field.patch index 91f195b4ae..91f195b4ae 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0507-media-uapi-hevc-Add-segment-address-field.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0504-media-uapi-hevc-Add-segment-address-field.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0508-media-hevc_ctrls-Add-slice-param-dependent-slice-seg.patch b/target/linux/bcm27xx/patches-5.4/950-0505-media-hevc_ctrls-Add-slice-param-dependent-slice-seg.patch index 1353480476..1353480476 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0508-media-hevc_ctrls-Add-slice-param-dependent-slice-seg.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0505-media-hevc_ctrls-Add-slice-param-dependent-slice-seg.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0509-media-uapi-Add-hevc-ctrls-for-WPP-decoding.patch b/target/linux/bcm27xx/patches-5.4/950-0506-media-uapi-Add-hevc-ctrls-for-WPP-decoding.patch index 234cb82b2a..234cb82b2a 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0509-media-uapi-Add-hevc-ctrls-for-WPP-decoding.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0506-media-uapi-Add-hevc-ctrls-for-WPP-decoding.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0510-media-videodev2.h-Add-a-format-for-column-YUV4-2-0-m.patch b/target/linux/bcm27xx/patches-5.4/950-0507-media-videodev2.h-Add-a-format-for-column-YUV4-2-0-m.patch index 840541cd2e..840541cd2e 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0510-media-videodev2.h-Add-a-format-for-column-YUV4-2-0-m.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0507-media-videodev2.h-Add-a-format-for-column-YUV4-2-0-m.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0511-media-v4l2-mem2mem-allow-request-job-buffer-processi.patch b/target/linux/bcm27xx/patches-5.4/950-0508-media-v4l2-mem2mem-allow-request-job-buffer-processi.patch index a3023cae2b..a3023cae2b 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0511-media-v4l2-mem2mem-allow-request-job-buffer-processi.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0508-media-v4l2-mem2mem-allow-request-job-buffer-processi.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0512-media-dt-bindings-media-Add-binding-for-the-Raspberr.patch b/target/linux/bcm27xx/patches-5.4/950-0509-media-dt-bindings-media-Add-binding-for-the-Raspberr.patch index 203e112466..203e112466 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0512-media-dt-bindings-media-Add-binding-for-the-Raspberr.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0509-media-dt-bindings-media-Add-binding-for-the-Raspberr.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0513-staging-media-Add-Raspberry-Pi-V4L2-H265-decoder.patch b/target/linux/bcm27xx/patches-5.4/950-0510-staging-media-Add-Raspberry-Pi-V4L2-H265-decoder.patch index 134a685f0e..134a685f0e 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0513-staging-media-Add-Raspberry-Pi-V4L2-H265-decoder.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0510-staging-media-Add-Raspberry-Pi-V4L2-H265-decoder.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0514-dtoverlays-Add-overlay-to-enable-the-HEVC-V4L2-drive.patch b/target/linux/bcm27xx/patches-5.4/950-0511-dtoverlays-Add-overlay-to-enable-the-HEVC-V4L2-drive.patch index ee92ada02f..ee92ada02f 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0514-dtoverlays-Add-overlay-to-enable-the-HEVC-V4L2-drive.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0511-dtoverlays-Add-overlay-to-enable-the-HEVC-V4L2-drive.patch diff --git a/target/linux/bcm27xx/patches-5.4/950-0512-mmc-sdhci-Silence-MMC-warnings.patch b/target/linux/bcm27xx/patches-5.4/950-0512-mmc-sdhci-Silence-MMC-warnings.patch new file mode 100644 index 0000000000..41fa6cb3d2 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0512-mmc-sdhci-Silence-MMC-warnings.patch @@ -0,0 +1,42 @@ +From c99941ee53a8c6fcc466a088f8bd7108f04824e5 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Fri, 6 Dec 2019 13:05:27 +0100 +Subject: [PATCH] mmc: sdhci: Silence MMC warnings + +When the MMC isn't plugged in, the driver will spam the console which is +pretty annoying when using NFS. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/mmc/host/sdhci.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/mmc/host/sdhci.c ++++ b/drivers/mmc/host/sdhci.c +@@ -39,7 +39,7 @@ + pr_debug("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x) + + #define SDHCI_DUMP(f, x...) \ +- pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x) ++ pr_debug("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x) + + #define MAX_TUNING_LOOP 40 + +@@ -2754,7 +2754,7 @@ static void sdhci_timeout_timer(struct t + spin_lock_irqsave(&host->lock, flags); + + if (host->cmd && !sdhci_data_line_cmd(host->cmd)) { +- pr_err("%s: Timeout waiting for hardware cmd interrupt.\n", ++ pr_debug("%s: Timeout waiting for hardware cmd interrupt.\n", + mmc_hostname(host->mmc)); + sdhci_dumpregs(host); + +@@ -2776,7 +2776,7 @@ static void sdhci_timeout_data_timer(str + + if (host->data || host->data_cmd || + (host->cmd && sdhci_data_line_cmd(host->cmd))) { +- pr_err("%s: Timeout waiting for hardware interrupt.\n", ++ pr_debug("%s: Timeout waiting for hardware interrupt.\n", + mmc_hostname(host->mmc)); + sdhci_dumpregs(host); + diff --git a/target/linux/bcm27xx/patches-5.4/950-0513-dt-bindings-i2c-brcmstb-Convert-the-BRCMSTB-binding-.patch b/target/linux/bcm27xx/patches-5.4/950-0513-dt-bindings-i2c-brcmstb-Convert-the-BRCMSTB-binding-.patch new file mode 100644 index 0000000000..01bdfee303 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0513-dt-bindings-i2c-brcmstb-Convert-the-BRCMSTB-binding-.patch @@ -0,0 +1,126 @@ +From 1a2a857af4fe6748fea53799e0007672faa7aa57 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 13 Feb 2020 16:55:01 +0100 +Subject: [PATCH] dt-bindings: i2c: brcmstb: Convert the BRCMSTB + binding to a schema + +Switch the DT binding to a YAML schema to enable the DT validation. + +Cc: Kamal Dasu <kdasu.kdev@gmail.com> +Cc: Wolfram Sang <wsa@the-dreams.de> +Cc: bcm-kernel-feedback-list@broadcom.com +Cc: linux-i2c@vger.kernel.org +Cc: devicetree@vger.kernel.org +Acked-by: Florian Fainelli <f.fainelli@gmail.com> +Reviewed-by: Rob Herring <robh+dt@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + .../bindings/i2c/brcm,brcmstb-i2c.yaml | 59 +++++++++++++++++++ + .../devicetree/bindings/i2c/i2c-brcmstb.txt | 26 -------- + MAINTAINERS | 2 +- + 3 files changed, 60 insertions(+), 27 deletions(-) + create mode 100644 Documentation/devicetree/bindings/i2c/brcm,brcmstb-i2c.yaml + delete mode 100644 Documentation/devicetree/bindings/i2c/i2c-brcmstb.txt + +--- /dev/null ++++ b/Documentation/devicetree/bindings/i2c/brcm,brcmstb-i2c.yaml +@@ -0,0 +1,59 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/i2c/brcm,brcmstb-i2c.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Broadcom STB BSC IIC Master Controller ++ ++maintainers: ++ - Kamal Dasu <kdasu.kdev@gmail.com> ++ ++allOf: ++ - $ref: /schemas/i2c/i2c-controller.yaml# ++ ++properties: ++ compatible: ++ enum: ++ - brcm,brcmstb-i2c ++ - brcm,brcmper-i2c ++ ++ reg: ++ maxItems: 1 ++ ++ interrupts: ++ maxItems: 1 ++ ++ interrupt-names: ++ maxItems: 1 ++ ++ clock-frequency: ++ enum: ++ - 46875 ++ - 50000 ++ - 93750 ++ - 97500 ++ - 187500 ++ - 200000 ++ - 375000 ++ - 390000 ++ ++required: ++ - compatible ++ - reg ++ - clock-frequency ++ ++unevaluatedProperties: false ++ ++examples: ++ - | ++ bsca: i2c@f0406200 { ++ clock-frequency = <390000>; ++ compatible = "brcm,brcmstb-i2c"; ++ interrupt-parent = <&irq0_intc>; ++ reg = <0xf0406200 0x58>; ++ interrupts = <0x18>; ++ interrupt-names = "upg_bsca"; ++ }; ++ ++... +--- a/Documentation/devicetree/bindings/i2c/i2c-brcmstb.txt ++++ /dev/null +@@ -1,26 +0,0 @@ +-Broadcom stb bsc iic master controller +- +-Required properties: +- +-- compatible: should be "brcm,brcmstb-i2c" or "brcm,brcmper-i2c" +-- clock-frequency: 32-bit decimal value of iic master clock freqency in Hz +- valid values are 375000, 390000, 187500, 200000 +- 93750, 97500, 46875 and 50000 +-- reg: specifies the base physical address and size of the registers +- +-Optional properties : +- +-- interrupts: specifies the interrupt number, the irq line to be used +-- interrupt-names: Interrupt name string +- +-Example: +- +-bsca: i2c@f0406200 { +- clock-frequency = <390000>; +- compatible = "brcm,brcmstb-i2c"; +- interrupt-parent = <&irq0_intc>; +- reg = <0xf0406200 0x58>; +- interrupts = <0x18>; +- interrupt-names = "upg_bsca"; +-}; +- +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -3349,7 +3349,7 @@ L: linux-i2c@vger.kernel.org + L: bcm-kernel-feedback-list@broadcom.com + S: Supported + F: drivers/i2c/busses/i2c-brcmstb.c +-F: Documentation/devicetree/bindings/i2c/i2c-brcmstb.txt ++F: Documentation/devicetree/bindings/i2c/brcm,brcmstb-i2c.yaml + + BROADCOM BRCMSTB USB2 and USB3 PHY DRIVER + M: Al Cooper <alcooperx@gmail.com> diff --git a/target/linux/bcm27xx/patches-5.4/950-0514-dt-bindings-i2c-brcmstb-Add-BCM2711-BSC-AUTO-I2C-bin.patch b/target/linux/bcm27xx/patches-5.4/950-0514-dt-bindings-i2c-brcmstb-Add-BCM2711-BSC-AUTO-I2C-bin.patch new file mode 100644 index 0000000000..2716a13d25 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0514-dt-bindings-i2c-brcmstb-Add-BCM2711-BSC-AUTO-I2C-bin.patch @@ -0,0 +1,96 @@ +From 16a6810e521eaf24249085b93b467f7bdf8e8a47 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Tue, 17 Dec 2019 09:58:34 +0100 +Subject: [PATCH] dt-bindings: i2c: brcmstb: Add BCM2711 BSC/AUTO-I2C + binding + +The HDMI blocks in the BCM2771 have an i2c controller to retrieve the +EDID. This block is split into two parts, the BSC and the AUTO_I2C, +lying in two separate register areas. + +The AUTO_I2C block has a mailbox-like interface and will take away the +BSC control from the CPU if enabled. However, the BSC is the actually +the same controller than the one supported by the brcmstb driver, and +the AUTO_I2C doesn't really bring any immediate benefit. + +We can model it in the DT as a single device with two register range, +which will allow us to use or or the other in the driver without +changing anything in the DT. + +Cc: Kamal Dasu <kdasu.kdev@gmail.com> +Cc: Wolfram Sang <wsa@the-dreams.de> +Cc: bcm-kernel-feedback-list@broadcom.com +Cc: linux-i2c@vger.kernel.org +Cc: devicetree@vger.kernel.org +Acked-by: Florian Fainelli <f.fainelli@gmail.com> +Reviewed-by: Rob Herring <robh+dt@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + .../bindings/i2c/brcm,brcmstb-i2c.yaml | 40 ++++++++++++++++++- + 1 file changed, 39 insertions(+), 1 deletion(-) + +--- a/Documentation/devicetree/bindings/i2c/brcm,brcmstb-i2c.yaml ++++ b/Documentation/devicetree/bindings/i2c/brcm,brcmstb-i2c.yaml +@@ -15,11 +15,21 @@ allOf: + properties: + compatible: + enum: ++ - brcm,bcm2711-hdmi-i2c + - brcm,brcmstb-i2c + - brcm,brcmper-i2c + + reg: +- maxItems: 1 ++ minItems: 1 ++ maxItems: 2 ++ items: ++ - description: BSC register range ++ - description: Auto-I2C register range ++ ++ reg-names: ++ items: ++ - const: bsc ++ - const: auto-i2c + + interrupts: + maxItems: 1 +@@ -45,6 +55,26 @@ required: + + unevaluatedProperties: false + ++if: ++ properties: ++ compatible: ++ contains: ++ enum: ++ - brcm,bcm2711-hdmi-i2c ++ ++then: ++ properties: ++ reg: ++ minItems: 2 ++ ++ required: ++ - reg-names ++ ++else: ++ properties: ++ reg: ++ maxItems: 1 ++ + examples: + - | + bsca: i2c@f0406200 { +@@ -56,4 +86,12 @@ examples: + interrupt-names = "upg_bsca"; + }; + ++ - | ++ ddc0: i2c@7ef04500 { ++ compatible = "brcm,bcm2711-hdmi-i2c"; ++ reg = <0x7ef04500 0x100>, <0x7ef00b00 0x300>; ++ reg-names = "bsc", "auto-i2c"; ++ clock-frequency = <390000>; ++ }; ++ + ... diff --git a/target/linux/bcm27xx/patches-5.4/950-0515-i2c-brcmstb-Support-BCM2711-HDMI-BSC-controllers.patch b/target/linux/bcm27xx/patches-5.4/950-0515-i2c-brcmstb-Support-BCM2711-HDMI-BSC-controllers.patch new file mode 100644 index 0000000000..76ce7403da --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0515-i2c-brcmstb-Support-BCM2711-HDMI-BSC-controllers.patch @@ -0,0 +1,87 @@ +From 4633a7bc5ffc15fe24c05e52f17a72c346baab6b Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Tue, 17 Dec 2019 09:58:34 +0100 +Subject: [PATCH] i2c: brcmstb: Support BCM2711 HDMI BSC controllers + +The HDMI blocks in the BCM2771 have an i2c controller to retrieve the +EDID. This block is split into two parts, the BSC and the AUTO_I2C, +lying in two separate register areas. + +The AUTO_I2C block has a mailbox-like interface and will take away the +BSC control from the CPU if enabled. However, the BSC is the actually +the same controller than the one supported by the brcmstb driver, and +the AUTO_I2C doesn't really bring any immediate benefit. + +Let's use the BSC then, but let's also tie the AUTO_I2C registers with a +separate compatible so that we can enable AUTO_I2C if needed in the +future. + +The AUTO_I2C is enabled by default at boot though, so we first need to +release the BSC from the AUTO_I2C control. + +Cc: Kamal Dasu <kdasu.kdev@gmail.com> +Cc: Wolfram Sang <wsa@the-dreams.de> +Cc: bcm-kernel-feedback-list@broadcom.com +Cc: linux-i2c@vger.kernel.org +Acked-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/i2c/busses/i2c-brcmstb.c | 33 ++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +--- a/drivers/i2c/busses/i2c-brcmstb.c ++++ b/drivers/i2c/busses/i2c-brcmstb.c +@@ -580,6 +580,31 @@ static void brcmstb_i2c_set_bsc_reg_defa + brcmstb_i2c_set_bus_speed(dev); + } + ++#define AUTOI2C_CTRL0 0x26c ++#define AUTOI2C_CTRL0_RELEASE_BSC BIT(1) ++ ++static int bcm2711_release_bsc(struct brcmstb_i2c_dev *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev->device); ++ struct resource *iomem; ++ void __iomem *autoi2c; ++ ++ /* Map hardware registers */ ++ iomem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "auto-i2c"); ++ autoi2c = devm_ioremap_resource(&pdev->dev, iomem); ++ if (IS_ERR(autoi2c)) ++ return PTR_ERR(autoi2c); ++ ++ writel(AUTOI2C_CTRL0_RELEASE_BSC, autoi2c + AUTOI2C_CTRL0); ++ devm_iounmap(&pdev->dev, autoi2c); ++ ++ /* We need to reset the controller after the release */ ++ dev->bsc_regmap->iic_enable = 0; ++ bsc_writel(dev, dev->bsc_regmap->iic_enable, iic_enable); ++ ++ return 0; ++} ++ + static int brcmstb_i2c_probe(struct platform_device *pdev) + { + int rc = 0; +@@ -609,6 +634,13 @@ static int brcmstb_i2c_probe(struct plat + goto probe_errorout; + } + ++ if (of_device_is_compatible(dev->device->of_node, ++ "brcm,bcm2711-hdmi-i2c")) { ++ rc = bcm2711_release_bsc(dev); ++ if (rc) ++ goto probe_errorout; ++ } ++ + rc = of_property_read_string(dev->device->of_node, "interrupt-names", + &int_name); + if (rc < 0) +@@ -705,6 +737,7 @@ static SIMPLE_DEV_PM_OPS(brcmstb_i2c_pm, + static const struct of_device_id brcmstb_i2c_of_match[] = { + {.compatible = "brcm,brcmstb-i2c"}, + {.compatible = "brcm,brcmper-i2c"}, ++ {.compatible = "brcm,bcm2711-hdmi-i2c"}, + {}, + }; + MODULE_DEVICE_TABLE(of, brcmstb_i2c_of_match); diff --git a/target/linux/bcm27xx/patches-5.4/950-0516-i2c-brcmstb-Allow-to-compile-it-on-BCM2835.patch b/target/linux/bcm27xx/patches-5.4/950-0516-i2c-brcmstb-Allow-to-compile-it-on-BCM2835.patch new file mode 100644 index 0000000000..a21035b69b --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0516-i2c-brcmstb-Allow-to-compile-it-on-BCM2835.patch @@ -0,0 +1,31 @@ +From ec7414dd69a7ea701d0d5676fdb32332cd5f10ec Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Tue, 14 Jan 2020 13:36:42 +0100 +Subject: [PATCH] i2c: brcmstb: Allow to compile it on BCM2835 + +The BCM2711, supported by ARCH_BCM2835, also has a controller by the +brcmstb driver so let's allow it to be compiled on that platform. + +Cc: Kamal Dasu <kdasu.kdev@gmail.com> +Cc: Wolfram Sang <wsa@the-dreams.de> +Cc: bcm-kernel-feedback-list@broadcom.com +Cc: linux-i2c@vger.kernel.org +Acked-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/i2c/busses/Kconfig | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -491,8 +491,8 @@ config I2C_BCM_KONA + + config I2C_BRCMSTB + tristate "BRCM Settop/DSL I2C controller" +- depends on ARCH_BRCMSTB || BMIPS_GENERIC || ARCH_BCM_63XX || \ +- COMPILE_TEST ++ depends on ARCH_BCM2835 || ARCH_BRCMSTB || BMIPS_GENERIC || \ ++ ARCH_BCM_63XX || COMPILE_TEST + default y + help + If you say yes to this option, support will be included for the diff --git a/target/linux/bcm27xx/patches-5.4/950-0517-dt-bindings-clock-Add-a-binding-for-the-RPi-Firmware.patch b/target/linux/bcm27xx/patches-5.4/950-0517-dt-bindings-clock-Add-a-binding-for-the-RPi-Firmware.patch new file mode 100644 index 0000000000..f3587060d1 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0517-dt-bindings-clock-Add-a-binding-for-the-RPi-Firmware.patch @@ -0,0 +1,63 @@ +From 9d4360fc454056fffa9ca487270aca9179906f5d Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 13 Feb 2020 17:51:09 +0100 +Subject: [PATCH] dt-bindings: clock: Add a binding for the RPi + Firmware clocks + +The firmare running on the RPi VideoCore can be used to discover and +change the various clocks running in the BCM2711. Since devices will +need to use them through the DT, let's add a pretty simple binding. + +Cc: Michael Turquette <mturquette@baylibre.com> +Cc: Stephen Boyd <sboyd@kernel.org> +Cc: Rob Herring <robh+dt@kernel.org> +Cc: linux-clk@vger.kernel.org +Cc: devicetree@vger.kernel.org +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + .../clock/raspberrypi,firmware-clocks.yaml | 39 +++++++++++++++++++ + 1 file changed, 39 insertions(+) + create mode 100644 Documentation/devicetree/bindings/clock/raspberrypi,firmware-clocks.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/clock/raspberrypi,firmware-clocks.yaml +@@ -0,0 +1,39 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/clock/raspberrypi,firmware-clocks.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: RaspberryPi Firmware Clocks Device Tree Bindings ++ ++maintainers: ++ - Maxime Ripard <mripard@kernel.org> ++ ++properties: ++ "#clock-cells": ++ const: 1 ++ ++ compatible: ++ const: raspberrypi,firmware-clocks ++ ++ raspberrypi,firmware: ++ $ref: /schemas/types.yaml#/definitions/phandle ++ description: > ++ Phandle to the mailbox node to communicate with the firmware. ++ ++required: ++ - "#clock-cells" ++ - compatible ++ - raspberrypi,firmware ++ ++additionalProperties: false ++ ++examples: ++ - | ++ firmware_clocks: firmware-clocks { ++ compatible = "raspberrypi,firmware-clocks"; ++ raspberrypi,firmware = <&firmware>; ++ #clock-cells = <1>; ++ }; ++ ++... diff --git a/target/linux/bcm27xx/patches-5.4/950-0518-clk-bcm-rpi-Allow-the-driver-to-be-probed-by-DT.patch b/target/linux/bcm27xx/patches-5.4/950-0518-clk-bcm-rpi-Allow-the-driver-to-be-probed-by-DT.patch new file mode 100644 index 0000000000..d622d9ded5 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0518-clk-bcm-rpi-Allow-the-driver-to-be-probed-by-DT.patch @@ -0,0 +1,60 @@ +From faeea753ba262e2a781570f9db23c5773c5d20e7 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Mon, 23 Dec 2019 19:58:08 +0100 +Subject: [PATCH] clk: bcm: rpi: Allow the driver to be probed by DT + +The current firmware clock driver for the RaspberryPi can only be probed by +manually registering an associated platform_device. + +While this works fine for cpufreq where the device gets attached a clkdev +lookup, it would be tedious to maintain a table of all the devices using +one of the clocks exposed by the firmware. + +Since the DT on the other hand is the perfect place to store those +associations, make the firmware clocks driver probe-able through the device +tree so that we can represent it as a node. + +Cc: Michael Turquette <mturquette@baylibre.com> +Cc: Stephen Boyd <sboyd@kernel.org> +Cc: linux-clk@vger.kernel.org +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/clk/bcm/clk-raspberrypi.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +--- a/drivers/clk/bcm/clk-raspberrypi.c ++++ b/drivers/clk/bcm/clk-raspberrypi.c +@@ -255,15 +255,13 @@ static int raspberrypi_clk_probe(struct + struct raspberrypi_clk *rpi; + int ret; + +- firmware_node = of_find_compatible_node(NULL, NULL, +- "raspberrypi,bcm2835-firmware"); ++ firmware_node = of_parse_phandle(dev->of_node, "raspberrypi,firmware", 0); + if (!firmware_node) { + dev_err(dev, "Missing firmware node\n"); + return -ENOENT; + } + + firmware = rpi_firmware_get(firmware_node); +- of_node_put(firmware_node); + if (!firmware) + return -EPROBE_DEFER; + +@@ -300,9 +298,16 @@ static int raspberrypi_clk_remove(struct + return 0; + } + ++static const struct of_device_id raspberrypi_clk_match[] = { ++ { .compatible = "raspberrypi,firmware-clocks" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, raspberrypi_clk_match); ++ + static struct platform_driver raspberrypi_clk_driver = { + .driver = { + .name = "raspberrypi-clk", ++ .of_match_table = raspberrypi_clk_match, + }, + .probe = raspberrypi_clk_probe, + .remove = raspberrypi_clk_remove, diff --git a/target/linux/bcm27xx/patches-5.4/950-0519-clk-bcm-rpi-Statically-init-clk_init_data.patch b/target/linux/bcm27xx/patches-5.4/950-0519-clk-bcm-rpi-Statically-init-clk_init_data.patch new file mode 100644 index 0000000000..5c7cb2b437 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0519-clk-bcm-rpi-Statically-init-clk_init_data.patch @@ -0,0 +1,32 @@ +From 7916b5f66f3becb9f223e8a6d8c0a6c0edd8964c Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Fri, 7 Feb 2020 14:17:54 +0100 +Subject: [PATCH] clk: bcm: rpi: Statically init clk_init_data + +Instead of declaring the clk_init_data and then calling memset on it, just +initialise properly. + +Cc: Michael Turquette <mturquette@baylibre.com> +Cc: Stephen Boyd <sboyd@kernel.org> +Cc: linux-clk@vger.kernel.org +Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/clk/bcm/clk-raspberrypi.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/clk/bcm/clk-raspberrypi.c ++++ b/drivers/clk/bcm/clk-raspberrypi.c +@@ -175,11 +175,10 @@ static const struct clk_ops raspberrypi_ + + static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) + { ++ struct clk_init_data init = {}; + u32 min_rate = 0, max_rate = 0; +- struct clk_init_data init; + int ret; + +- memset(&init, 0, sizeof(init)); + + /* All of the PLLs derive from the external oscillator. */ + init.parent_names = (const char *[]){ "osc" }; diff --git a/target/linux/bcm27xx/patches-5.4/950-0520-clk-bcm-rpi-Use-clk_hw_register-for-pllb_arm.patch b/target/linux/bcm27xx/patches-5.4/950-0520-clk-bcm-rpi-Use-clk_hw_register-for-pllb_arm.patch new file mode 100644 index 0000000000..55f86ae340 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0520-clk-bcm-rpi-Use-clk_hw_register-for-pllb_arm.patch @@ -0,0 +1,56 @@ +From 3a4163613b7f6e628e7b5a0d3a546523d1d03bb7 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Fri, 7 Feb 2020 15:40:00 +0100 +Subject: [PATCH] clk: bcm: rpi: Use clk_hw_register for pllb_arm + +The pllb_arm clock is defined as a fixed factor clock with the pllb clock +as a parent. However, all its configuration is entirely static, and thus we +don't really need to call clk_hw_register_fixed_factor() but can simply call +clk_hw_register() with a static clk_fixed_factor structure. + +Cc: Michael Turquette <mturquette@baylibre.com> +Cc: linux-clk@vger.kernel.org +Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Reviewed-by: Stephen Boyd <sboyd@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/clk/bcm/clk-raspberrypi.c | 24 ++++++++++++++++++------ + 1 file changed, 18 insertions(+), 6 deletions(-) + +--- a/drivers/clk/bcm/clk-raspberrypi.c ++++ b/drivers/clk/bcm/clk-raspberrypi.c +@@ -225,16 +225,28 @@ static int raspberrypi_register_pllb(str + return devm_clk_hw_register(rpi->dev, &rpi->pllb); + } + ++static struct clk_fixed_factor raspberrypi_clk_pllb_arm = { ++ .mult = 1, ++ .div = 2, ++ .hw.init = &(struct clk_init_data) { ++ .name = "pllb_arm", ++ .parent_names = (const char *[]){ "pllb" }, ++ .num_parents = 1, ++ .ops = &clk_fixed_factor_ops, ++ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, ++ }, ++}; ++ + static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) + { +- rpi->pllb_arm = clk_hw_register_fixed_factor(rpi->dev, +- "pllb_arm", "pllb", +- CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, +- 1, 2); +- if (IS_ERR(rpi->pllb_arm)) { ++ int ret; ++ ++ ret = clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); ++ if (ret) { + dev_err(rpi->dev, "Failed to initialize pllb_arm\n"); +- return PTR_ERR(rpi->pllb_arm); ++ return ret; + } ++ rpi->pllb_arm = &raspberrypi_clk_pllb_arm.hw; + + rpi->pllb_arm_lookup = clkdev_hw_create(rpi->pllb_arm, NULL, "cpu0"); + if (!rpi->pllb_arm_lookup) { diff --git a/target/linux/bcm27xx/patches-5.4/950-0521-clk-bcm-rpi-Remove-global-pllb_arm-clock-pointer.patch b/target/linux/bcm27xx/patches-5.4/950-0521-clk-bcm-rpi-Remove-global-pllb_arm-clock-pointer.patch new file mode 100644 index 0000000000..32fc94c675 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0521-clk-bcm-rpi-Remove-global-pllb_arm-clock-pointer.patch @@ -0,0 +1,45 @@ +From 5272507555c0ecf02c0dfd78303e86e8eb096191 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Fri, 7 Feb 2020 15:41:37 +0100 +Subject: [PATCH] clk: bcm: rpi: Remove global pllb_arm clock pointer + +The pllb_arm clk_hw pointer in the raspberry_clk structure isn't used +anywhere but in the raspberrypi_register_pllb_arm. + +Let's remove it, this will make our lives easier in future patches. + +Cc: Michael Turquette <mturquette@baylibre.com> +Cc: Stephen Boyd <sboyd@kernel.org> +Cc: linux-clk@vger.kernel.org +Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/clk/bcm/clk-raspberrypi.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +--- a/drivers/clk/bcm/clk-raspberrypi.c ++++ b/drivers/clk/bcm/clk-raspberrypi.c +@@ -40,7 +40,6 @@ struct raspberrypi_clk { + unsigned long max_rate; + + struct clk_hw pllb; +- struct clk_hw *pllb_arm; + struct clk_lookup *pllb_arm_lookup; + }; + +@@ -246,12 +245,12 @@ static int raspberrypi_register_pllb_arm + dev_err(rpi->dev, "Failed to initialize pllb_arm\n"); + return ret; + } +- rpi->pllb_arm = &raspberrypi_clk_pllb_arm.hw; + +- rpi->pllb_arm_lookup = clkdev_hw_create(rpi->pllb_arm, NULL, "cpu0"); ++ rpi->pllb_arm_lookup = clkdev_hw_create(&raspberrypi_clk_pllb_arm.hw, ++ NULL, "cpu0"); + if (!rpi->pllb_arm_lookup) { + dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n"); +- clk_hw_unregister_fixed_factor(rpi->pllb_arm); ++ clk_hw_unregister_fixed_factor(&raspberrypi_clk_pllb_arm.hw); + return -ENOMEM; + } + diff --git a/target/linux/bcm27xx/patches-5.4/950-0522-clk-bcm-rpi-Make-sure-pllb_arm-is-removed.patch b/target/linux/bcm27xx/patches-5.4/950-0522-clk-bcm-rpi-Make-sure-pllb_arm-is-removed.patch new file mode 100644 index 0000000000..425830d5c4 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0522-clk-bcm-rpi-Make-sure-pllb_arm-is-removed.patch @@ -0,0 +1,40 @@ +From aeb75ab90c35c7bd9778a71d606d52ac3e8ff02d Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Fri, 7 Feb 2020 15:42:40 +0100 +Subject: [PATCH] clk: bcm: rpi: Make sure pllb_arm is removed + +The pllb_arm clock was created at probe time, but was never removed if +something went wrong later in probe, or if the driver was ever removed from +the system. + +Now that we are using clk_hw_register, we can just use its managed variant +to take care of that for us. + +Cc: Michael Turquette <mturquette@baylibre.com> +Cc: linux-clk@vger.kernel.org +Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Reviewed-by: Stephen Boyd <sboyd@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/clk/bcm/clk-raspberrypi.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/clk/bcm/clk-raspberrypi.c ++++ b/drivers/clk/bcm/clk-raspberrypi.c +@@ -240,7 +240,7 @@ static int raspberrypi_register_pllb_arm + { + int ret; + +- ret = clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); ++ ret = devm_clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); + if (ret) { + dev_err(rpi->dev, "Failed to initialize pllb_arm\n"); + return ret; +@@ -250,7 +250,6 @@ static int raspberrypi_register_pllb_arm + NULL, "cpu0"); + if (!rpi->pllb_arm_lookup) { + dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n"); +- clk_hw_unregister_fixed_factor(&raspberrypi_clk_pllb_arm.hw); + return -ENOMEM; + } + diff --git a/target/linux/bcm27xx/patches-5.4/950-0523-clk-bcm-rpi-Remove-pllb_arm_lookup-global-pointer.patch b/target/linux/bcm27xx/patches-5.4/950-0523-clk-bcm-rpi-Remove-pllb_arm_lookup-global-pointer.patch new file mode 100644 index 0000000000..e193b7906a --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0523-clk-bcm-rpi-Remove-pllb_arm_lookup-global-pointer.patch @@ -0,0 +1,51 @@ +From 35be338b9532bb1fda95b9f1123758f57f785f93 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Fri, 7 Feb 2020 15:46:24 +0100 +Subject: [PATCH] clk: bcm: rpi: Remove pllb_arm_lookup global pointer + +The pllb_arm_lookup pointer in the struct raspberrypi_clk is not used for +anything but to store the returned pointer to clkdev_hw_create, and is not +used anywhere else in the driver. + +Let's remove that global pointer from the structure. + +Cc: Michael Turquette <mturquette@baylibre.com> +Cc: linux-clk@vger.kernel.org +Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Reviewed-by: Stephen Boyd <sboyd@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/clk/bcm/clk-raspberrypi.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/clk/bcm/clk-raspberrypi.c ++++ b/drivers/clk/bcm/clk-raspberrypi.c +@@ -40,7 +40,6 @@ struct raspberrypi_clk { + unsigned long max_rate; + + struct clk_hw pllb; +- struct clk_lookup *pllb_arm_lookup; + }; + + /* +@@ -238,6 +237,7 @@ static struct clk_fixed_factor raspberry + + static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) + { ++ struct clk_lookup *pllb_arm_lookup; + int ret; + + ret = devm_clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); +@@ -246,9 +246,9 @@ static int raspberrypi_register_pllb_arm + return ret; + } + +- rpi->pllb_arm_lookup = clkdev_hw_create(&raspberrypi_clk_pllb_arm.hw, +- NULL, "cpu0"); +- if (!rpi->pllb_arm_lookup) { ++ pllb_arm_lookup = clkdev_hw_create(&raspberrypi_clk_pllb_arm.hw, ++ NULL, "cpu0"); ++ if (!pllb_arm_lookup) { + dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n"); + return -ENOMEM; + } diff --git a/target/linux/bcm27xx/patches-5.4/950-0524-clk-bcm-rpi-Switch-to-clk_hw_register_clkdev.patch b/target/linux/bcm27xx/patches-5.4/950-0524-clk-bcm-rpi-Switch-to-clk_hw_register_clkdev.patch new file mode 100644 index 0000000000..e4a129f233 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0524-clk-bcm-rpi-Switch-to-clk_hw_register_clkdev.patch @@ -0,0 +1,45 @@ +From 2d1c30a79cb9d390d2425852ff8c9527c31b3ab8 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Wed, 12 Feb 2020 14:21:45 +0100 +Subject: [PATCH] clk: bcm: rpi: Switch to clk_hw_register_clkdev + +Since we don't care about retrieving the clk_lookup structure pointer +returned by clkdev_hw_create, we can just use the clk_hw_register_clkdev +function. + +Cc: Michael Turquette <mturquette@baylibre.com> +Cc: linux-clk@vger.kernel.org +Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Reviewed-by: Stephen Boyd <sboyd@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/clk/bcm/clk-raspberrypi.c | 11 +++++------ + 1 file changed, 5 insertions(+), 6 deletions(-) + +--- a/drivers/clk/bcm/clk-raspberrypi.c ++++ b/drivers/clk/bcm/clk-raspberrypi.c +@@ -237,7 +237,6 @@ static struct clk_fixed_factor raspberry + + static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) + { +- struct clk_lookup *pllb_arm_lookup; + int ret; + + ret = devm_clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); +@@ -246,11 +245,11 @@ static int raspberrypi_register_pllb_arm + return ret; + } + +- pllb_arm_lookup = clkdev_hw_create(&raspberrypi_clk_pllb_arm.hw, +- NULL, "cpu0"); +- if (!pllb_arm_lookup) { +- dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n"); +- return -ENOMEM; ++ ret = clk_hw_register_clkdev(&raspberrypi_clk_pllb_arm.hw, ++ NULL, "cpu0"); ++ if (ret) { ++ dev_err(rpi->dev, "Failed to initialize clkdev\n"); ++ return ret; + } + + return 0; diff --git a/target/linux/bcm27xx/patches-5.4/950-0525-clk-bcm-rpi-Make-sure-the-clkdev-lookup-is-removed.patch b/target/linux/bcm27xx/patches-5.4/950-0525-clk-bcm-rpi-Make-sure-the-clkdev-lookup-is-removed.patch new file mode 100644 index 0000000000..7c887795ee --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0525-clk-bcm-rpi-Make-sure-the-clkdev-lookup-is-removed.patch @@ -0,0 +1,34 @@ +From 31fe609e3f5f9d4e52f0f88f8ebfd20fb606c672 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Fri, 7 Feb 2020 15:47:13 +0100 +Subject: [PATCH] clk: bcm: rpi: Make sure the clkdev lookup is removed + +The clkdev lookup created for the cpufreq device is never removed if +there's an issue later in probe or at module removal time. + +Let's convert to the managed variant of the clk_hw_register_clkdev function +to make sure it happens. + +Cc: Michael Turquette <mturquette@baylibre.com> +Cc: linux-clk@vger.kernel.org +Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Reviewed-by: Stephen Boyd <sboyd@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/clk/bcm/clk-raspberrypi.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/drivers/clk/bcm/clk-raspberrypi.c ++++ b/drivers/clk/bcm/clk-raspberrypi.c +@@ -245,8 +245,9 @@ static int raspberrypi_register_pllb_arm + return ret; + } + +- ret = clk_hw_register_clkdev(&raspberrypi_clk_pllb_arm.hw, +- NULL, "cpu0"); ++ ret = devm_clk_hw_register_clkdev(rpi->dev, ++ &raspberrypi_clk_pllb_arm.hw, ++ NULL, "cpu0"); + if (ret) { + dev_err(rpi->dev, "Failed to initialize clkdev\n"); + return ret; diff --git a/target/linux/bcm27xx/patches-5.4/950-0526-clk-bcm-rpi-Create-a-data-structure-for-the-clocks.patch b/target/linux/bcm27xx/patches-5.4/950-0526-clk-bcm-rpi-Create-a-data-structure-for-the-clocks.patch new file mode 100644 index 0000000000..85b6f4a6e2 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0526-clk-bcm-rpi-Create-a-data-structure-for-the-clocks.patch @@ -0,0 +1,126 @@ +From 8af8b61bf6b5689af9f29f0e04e57c832dad0406 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Fri, 7 Feb 2020 16:01:33 +0100 +Subject: [PATCH] clk: bcm: rpi: Create a data structure for the clocks + +So far the driver has really only been providing a single clock, and stored +both the data associated to that clock in particular with the data +associated to the "controller". + +Since we will change that in the future, let's decouple the clock data from +the provider data. + +Cc: Michael Turquette <mturquette@baylibre.com> +Cc: linux-clk@vger.kernel.org +Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Reviewed-by: Stephen Boyd <sboyd@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/clk/bcm/clk-raspberrypi.c | 40 ++++++++++++++++++++----------- + 1 file changed, 26 insertions(+), 14 deletions(-) + +--- a/drivers/clk/bcm/clk-raspberrypi.c ++++ b/drivers/clk/bcm/clk-raspberrypi.c +@@ -35,11 +35,15 @@ struct raspberrypi_clk { + struct device *dev; + struct rpi_firmware *firmware; + struct platform_device *cpufreq; ++}; ++ ++struct raspberrypi_clk_data { ++ struct clk_hw hw; + + unsigned long min_rate; + unsigned long max_rate; + +- struct clk_hw pllb; ++ struct raspberrypi_clk *rpi; + }; + + /* +@@ -83,8 +87,9 @@ static int raspberrypi_clock_property(st + + static int raspberrypi_fw_pll_is_on(struct clk_hw *hw) + { +- struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, +- pllb); ++ struct raspberrypi_clk_data *data = ++ container_of(hw, struct raspberrypi_clk_data, hw); ++ struct raspberrypi_clk *rpi = data->rpi; + u32 val = 0; + int ret; + +@@ -101,8 +106,9 @@ static int raspberrypi_fw_pll_is_on(stru + static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, + unsigned long parent_rate) + { +- struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, +- pllb); ++ struct raspberrypi_clk_data *data = ++ container_of(hw, struct raspberrypi_clk_data, hw); ++ struct raspberrypi_clk *rpi = data->rpi; + u32 val = 0; + int ret; + +@@ -119,8 +125,9 @@ static unsigned long raspberrypi_fw_pll_ + static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) + { +- struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, +- pllb); ++ struct raspberrypi_clk_data *data = ++ container_of(hw, struct raspberrypi_clk_data, hw); ++ struct raspberrypi_clk *rpi = data->rpi; + u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE; + int ret; + +@@ -142,13 +149,13 @@ static int raspberrypi_fw_pll_set_rate(s + static int raspberrypi_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) + { +- struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, +- pllb); ++ struct raspberrypi_clk_data *data = ++ container_of(hw, struct raspberrypi_clk_data, hw); + u64 div, final_rate; + u32 ndiv, fdiv; + + /* We can't use req->rate directly as it would overflow */ +- final_rate = clamp(req->rate, rpi->min_rate, rpi->max_rate); ++ final_rate = clamp(req->rate, data->min_rate, data->max_rate); + + div = (u64)final_rate << A2W_PLL_FRAC_BITS; + do_div(div, req->best_parent_rate); +@@ -173,10 +180,15 @@ static const struct clk_ops raspberrypi_ + + static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) + { ++ struct raspberrypi_clk_data *data; + struct clk_init_data init = {}; + u32 min_rate = 0, max_rate = 0; + int ret; + ++ data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ data->rpi = rpi; + + /* All of the PLLs derive from the external oscillator. */ + init.parent_names = (const char *[]){ "osc" }; +@@ -215,12 +227,12 @@ static int raspberrypi_register_pllb(str + dev_info(rpi->dev, "CPU frequency range: min %u, max %u\n", + min_rate, max_rate); + +- rpi->min_rate = min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; +- rpi->max_rate = max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; ++ data->min_rate = min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; ++ data->max_rate = max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; + +- rpi->pllb.init = &init; ++ data->hw.init = &init; + +- return devm_clk_hw_register(rpi->dev, &rpi->pllb); ++ return devm_clk_hw_register(rpi->dev, &data->hw); + } + + static struct clk_fixed_factor raspberrypi_clk_pllb_arm = { diff --git a/target/linux/bcm27xx/patches-5.4/950-0527-clk-bcm-rpi-Add-clock-id-to-data.patch b/target/linux/bcm27xx/patches-5.4/950-0527-clk-bcm-rpi-Add-clock-id-to-data.patch new file mode 100644 index 0000000000..09f023e09c --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0527-clk-bcm-rpi-Add-clock-id-to-data.patch @@ -0,0 +1,86 @@ +From 98d529ffea66937e8a9ba8b69172bb9c599cfa39 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Fri, 7 Feb 2020 16:04:16 +0100 +Subject: [PATCH] clk: bcm: rpi: Add clock id to data + +The driver has really only supported one clock so far and has hardcoded the +ID used in communications with the firmware in all the functions +implementing the clock framework hooks. Let's store that in the clock data +structure so that we can support more clocks later on. + +Cc: Michael Turquette <mturquette@baylibre.com> +Cc: linux-clk@vger.kernel.org +Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Reviewed-by: Stephen Boyd <sboyd@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/clk/bcm/clk-raspberrypi.c | 16 +++++++--------- + 1 file changed, 7 insertions(+), 9 deletions(-) + +--- a/drivers/clk/bcm/clk-raspberrypi.c ++++ b/drivers/clk/bcm/clk-raspberrypi.c +@@ -39,6 +39,7 @@ struct raspberrypi_clk { + + struct raspberrypi_clk_data { + struct clk_hw hw; ++ unsigned id; + + unsigned long min_rate; + unsigned long max_rate; +@@ -95,7 +96,7 @@ static int raspberrypi_fw_pll_is_on(stru + + ret = raspberrypi_clock_property(rpi->firmware, + RPI_FIRMWARE_GET_CLOCK_STATE, +- RPI_FIRMWARE_ARM_CLK_ID, &val); ++ data->id, &val); + if (ret) + return 0; + +@@ -114,8 +115,7 @@ static unsigned long raspberrypi_fw_pll_ + + ret = raspberrypi_clock_property(rpi->firmware, + RPI_FIRMWARE_GET_CLOCK_RATE, +- RPI_FIRMWARE_ARM_CLK_ID, +- &val); ++ data->id, &val); + if (ret) + return ret; + +@@ -133,8 +133,7 @@ static int raspberrypi_fw_pll_set_rate(s + + ret = raspberrypi_clock_property(rpi->firmware, + RPI_FIRMWARE_SET_CLOCK_RATE, +- RPI_FIRMWARE_ARM_CLK_ID, +- &new_rate); ++ data->id, &new_rate); + if (ret) + dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d", + clk_hw_get_name(hw), ret); +@@ -189,6 +188,7 @@ static int raspberrypi_register_pllb(str + if (!data) + return -ENOMEM; + data->rpi = rpi; ++ data->id = RPI_FIRMWARE_ARM_CLK_ID; + + /* All of the PLLs derive from the external oscillator. */ + init.parent_names = (const char *[]){ "osc" }; +@@ -200,8 +200,7 @@ static int raspberrypi_register_pllb(str + /* Get min & max rates set by the firmware */ + ret = raspberrypi_clock_property(rpi->firmware, + RPI_FIRMWARE_GET_MIN_CLOCK_RATE, +- RPI_FIRMWARE_ARM_CLK_ID, +- &min_rate); ++ data->id, &min_rate); + if (ret) { + dev_err(rpi->dev, "Failed to get %s min freq: %d\n", + init.name, ret); +@@ -210,8 +209,7 @@ static int raspberrypi_register_pllb(str + + ret = raspberrypi_clock_property(rpi->firmware, + RPI_FIRMWARE_GET_MAX_CLOCK_RATE, +- RPI_FIRMWARE_ARM_CLK_ID, +- &max_rate); ++ data->id, &max_rate); + if (ret) { + dev_err(rpi->dev, "Failed to get %s max freq: %d\n", + init.name, ret); diff --git a/target/linux/bcm27xx/patches-5.4/950-0528-clk-bcm-rpi-Pass-the-clocks-data-to-the-firmware-fun.patch b/target/linux/bcm27xx/patches-5.4/950-0528-clk-bcm-rpi-Pass-the-clocks-data-to-the-firmware-fun.patch new file mode 100644 index 0000000000..7822ff0d11 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0528-clk-bcm-rpi-Pass-the-clocks-data-to-the-firmware-fun.patch @@ -0,0 +1,96 @@ +From 1231dbeb8bfeda68c53854cc68016acd74665079 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Fri, 7 Feb 2020 16:08:17 +0100 +Subject: [PATCH] clk: bcm: rpi: Pass the clocks data to the firmware + function + +The raspberry_clock_property only takes the clock ID as an argument, but +now that we have a clock data structure it makes more sense to just pass +that structure instead. + +Cc: Michael Turquette <mturquette@baylibre.com> +Cc: Stephen Boyd <sboyd@kernel.org> +Cc: linux-clk@vger.kernel.org +Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/clk/bcm/clk-raspberrypi.c | 29 ++++++++++++++--------------- + 1 file changed, 14 insertions(+), 15 deletions(-) + +--- a/drivers/clk/bcm/clk-raspberrypi.c ++++ b/drivers/clk/bcm/clk-raspberrypi.c +@@ -67,11 +67,12 @@ struct raspberrypi_firmware_prop { + __le32 disable_turbo; + } __packed; + +-static int raspberrypi_clock_property(struct rpi_firmware *firmware, u32 tag, +- u32 clk, u32 *val) ++static int raspberrypi_clock_property(struct rpi_firmware *firmware, ++ const struct raspberrypi_clk_data *data, ++ u32 tag, u32 *val) + { + struct raspberrypi_firmware_prop msg = { +- .id = cpu_to_le32(clk), ++ .id = cpu_to_le32(data->id), + .val = cpu_to_le32(*val), + .disable_turbo = cpu_to_le32(1), + }; +@@ -94,9 +95,8 @@ static int raspberrypi_fw_pll_is_on(stru + u32 val = 0; + int ret; + +- ret = raspberrypi_clock_property(rpi->firmware, +- RPI_FIRMWARE_GET_CLOCK_STATE, +- data->id, &val); ++ ret = raspberrypi_clock_property(rpi->firmware, data, ++ RPI_FIRMWARE_GET_CLOCK_STATE, &val); + if (ret) + return 0; + +@@ -113,9 +113,8 @@ static unsigned long raspberrypi_fw_pll_ + u32 val = 0; + int ret; + +- ret = raspberrypi_clock_property(rpi->firmware, +- RPI_FIRMWARE_GET_CLOCK_RATE, +- data->id, &val); ++ ret = raspberrypi_clock_property(rpi->firmware, data, ++ RPI_FIRMWARE_GET_CLOCK_RATE, &val); + if (ret) + return ret; + +@@ -131,9 +130,9 @@ static int raspberrypi_fw_pll_set_rate(s + u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE; + int ret; + +- ret = raspberrypi_clock_property(rpi->firmware, ++ ret = raspberrypi_clock_property(rpi->firmware, data, + RPI_FIRMWARE_SET_CLOCK_RATE, +- data->id, &new_rate); ++ &new_rate); + if (ret) + dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d", + clk_hw_get_name(hw), ret); +@@ -198,18 +197,18 @@ static int raspberrypi_register_pllb(str + init.flags = CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED; + + /* Get min & max rates set by the firmware */ +- ret = raspberrypi_clock_property(rpi->firmware, ++ ret = raspberrypi_clock_property(rpi->firmware, data, + RPI_FIRMWARE_GET_MIN_CLOCK_RATE, +- data->id, &min_rate); ++ &min_rate); + if (ret) { + dev_err(rpi->dev, "Failed to get %s min freq: %d\n", + init.name, ret); + return ret; + } + +- ret = raspberrypi_clock_property(rpi->firmware, ++ ret = raspberrypi_clock_property(rpi->firmware, data, + RPI_FIRMWARE_GET_MAX_CLOCK_RATE, +- data->id, &max_rate); ++ &max_rate); + if (ret) { + dev_err(rpi->dev, "Failed to get %s max freq: %d\n", + init.name, ret); diff --git a/target/linux/bcm27xx/patches-5.4/950-0529-clk-bcm-rpi-Rename-is_prepared-function.patch b/target/linux/bcm27xx/patches-5.4/950-0529-clk-bcm-rpi-Rename-is_prepared-function.patch new file mode 100644 index 0000000000..1a6c9813de --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0529-clk-bcm-rpi-Rename-is_prepared-function.patch @@ -0,0 +1,40 @@ +From c37a4cc3fc34b6c53c331d0d079df322082ff183 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 20 Feb 2020 12:45:47 +0100 +Subject: [PATCH] clk: bcm: rpi: Rename is_prepared function + +The raspberrypi_fw_pll_is_on function doesn't only apply to PLL +registered in the driver, but any clock exposed by the firmware. + +Since we also implement the is_prepared hook, make the function +consistent with the other function names. + +Cc: Michael Turquette <mturquette@baylibre.com> +Cc: linux-clk@vger.kernel.org +Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Reviewed-by: Stephen Boyd <sboyd@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/clk/bcm/clk-raspberrypi.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/clk/bcm/clk-raspberrypi.c ++++ b/drivers/clk/bcm/clk-raspberrypi.c +@@ -87,7 +87,7 @@ static int raspberrypi_clock_property(st + return 0; + } + +-static int raspberrypi_fw_pll_is_on(struct clk_hw *hw) ++static int raspberrypi_fw_is_prepared(struct clk_hw *hw) + { + struct raspberrypi_clk_data *data = + container_of(hw, struct raspberrypi_clk_data, hw); +@@ -170,7 +170,7 @@ static int raspberrypi_pll_determine_rat + } + + static const struct clk_ops raspberrypi_firmware_pll_clk_ops = { +- .is_prepared = raspberrypi_fw_pll_is_on, ++ .is_prepared = raspberrypi_fw_is_prepared, + .recalc_rate = raspberrypi_fw_pll_get_rate, + .set_rate = raspberrypi_fw_pll_set_rate, + .determine_rate = raspberrypi_pll_determine_rate, diff --git a/target/linux/bcm27xx/patches-5.4/950-0530-clk-bcm-rpi-Split-pllb-clock-hooks.patch b/target/linux/bcm27xx/patches-5.4/950-0530-clk-bcm-rpi-Split-pllb-clock-hooks.patch new file mode 100644 index 0000000000..38720b89bd --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0530-clk-bcm-rpi-Split-pllb-clock-hooks.patch @@ -0,0 +1,80 @@ +From e2537b383e247198347e7124876b9ead531dbeef Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Fri, 7 Feb 2020 16:14:18 +0100 +Subject: [PATCH] clk: bcm: rpi: Split pllb clock hooks + +The driver only supports the pllb for now and all the clock framework hooks +are a mix of the generic firmware interface and the specifics of the pllb. +Since we will support more clocks in the future let's split the generic and +specific hooks + +Cc: Michael Turquette <mturquette@baylibre.com> +Cc: linux-clk@vger.kernel.org +Reviewed-by: Stephen Boyd <sboyd@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/clk/bcm/clk-raspberrypi.c | 30 ++++++++++++++++++++++-------- + 1 file changed, 22 insertions(+), 8 deletions(-) + +--- a/drivers/clk/bcm/clk-raspberrypi.c ++++ b/drivers/clk/bcm/clk-raspberrypi.c +@@ -104,8 +104,8 @@ static int raspberrypi_fw_is_prepared(st + } + + +-static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, +- unsigned long parent_rate) ++static unsigned long raspberrypi_fw_get_rate(struct clk_hw *hw, ++ unsigned long parent_rate) + { + struct raspberrypi_clk_data *data = + container_of(hw, struct raspberrypi_clk_data, hw); +@@ -118,21 +118,27 @@ static unsigned long raspberrypi_fw_pll_ + if (ret) + return ret; + +- return val * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; ++ return val; + } + +-static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, +- unsigned long parent_rate) ++static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ return raspberrypi_fw_get_rate(hw, parent_rate) * ++ RPI_FIRMWARE_PLLB_ARM_DIV_RATE; ++} ++ ++static int raspberrypi_fw_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) + { + struct raspberrypi_clk_data *data = + container_of(hw, struct raspberrypi_clk_data, hw); + struct raspberrypi_clk *rpi = data->rpi; +- u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE; ++ u32 _rate = rate; + int ret; + + ret = raspberrypi_clock_property(rpi->firmware, data, +- RPI_FIRMWARE_SET_CLOCK_RATE, +- &new_rate); ++ RPI_FIRMWARE_SET_CLOCK_RATE, &_rate); + if (ret) + dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d", + clk_hw_get_name(hw), ret); +@@ -140,6 +146,14 @@ static int raspberrypi_fw_pll_set_rate(s + return ret; + } + ++static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE; ++ ++ return raspberrypi_fw_set_rate(hw, new_rate, parent_rate); ++} ++ + /* + * Sadly there is no firmware rate rounding interface. We borrowed it from + * clk-bcm2835. diff --git a/target/linux/bcm27xx/patches-5.4/950-0531-clk-bcm-rpi-Make-the-PLLB-registration-function-retu.patch b/target/linux/bcm27xx/patches-5.4/950-0531-clk-bcm-rpi-Make-the-PLLB-registration-function-retu.patch new file mode 100644 index 0000000000..633cf18782 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0531-clk-bcm-rpi-Make-the-PLLB-registration-function-retu.patch @@ -0,0 +1,144 @@ +From 5272bad5ff927362e5d12da82eb819a8d1444da6 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Fri, 7 Feb 2020 16:30:01 +0100 +Subject: [PATCH] clk: bcm: rpi: Make the PLLB registration function + return a clk_hw + +The raspberrypi_register_pllb has been returning an integer so far to +notify whether the functions has exited successfully or not. + +However, the OF provider functions in the clock framework require access to +the clk_hw structure so that we can expose those clocks to device tree +consumers. + +Since we'll want that for the future clocks, let's return a clk_hw pointer +instead of the return code. + +Cc: Michael Turquette <mturquette@baylibre.com> +Cc: linux-clk@vger.kernel.org +Reviewed-by: Stephen Boyd <sboyd@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/clk/bcm/clk-raspberrypi.c | 40 +++++++++++++++++-------------- + 1 file changed, 22 insertions(+), 18 deletions(-) + +--- a/drivers/clk/bcm/clk-raspberrypi.c ++++ b/drivers/clk/bcm/clk-raspberrypi.c +@@ -190,7 +190,7 @@ static const struct clk_ops raspberrypi_ + .determine_rate = raspberrypi_pll_determine_rate, + }; + +-static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) ++static struct clk_hw *raspberrypi_register_pllb(struct raspberrypi_clk *rpi) + { + struct raspberrypi_clk_data *data; + struct clk_init_data init = {}; +@@ -199,7 +199,7 @@ static int raspberrypi_register_pllb(str + + data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL); + if (!data) +- return -ENOMEM; ++ return ERR_PTR(-ENOMEM); + data->rpi = rpi; + data->id = RPI_FIRMWARE_ARM_CLK_ID; + +@@ -217,7 +217,7 @@ static int raspberrypi_register_pllb(str + if (ret) { + dev_err(rpi->dev, "Failed to get %s min freq: %d\n", + init.name, ret); +- return ret; ++ return ERR_PTR(ret); + } + + ret = raspberrypi_clock_property(rpi->firmware, data, +@@ -226,13 +226,13 @@ static int raspberrypi_register_pllb(str + if (ret) { + dev_err(rpi->dev, "Failed to get %s max freq: %d\n", + init.name, ret); +- return ret; ++ return ERR_PTR(ret); + } + + if (!min_rate || !max_rate) { + dev_err(rpi->dev, "Unexpected frequency range: min %u, max %u\n", + min_rate, max_rate); +- return -EINVAL; ++ return ERR_PTR(-EINVAL); + } + + dev_info(rpi->dev, "CPU frequency range: min %u, max %u\n", +@@ -243,7 +243,11 @@ static int raspberrypi_register_pllb(str + + data->hw.init = &init; + +- return devm_clk_hw_register(rpi->dev, &data->hw); ++ ret = devm_clk_hw_register(rpi->dev, &data->hw); ++ if (ret) ++ return ERR_PTR(ret); ++ ++ return &data->hw; + } + + static struct clk_fixed_factor raspberrypi_clk_pllb_arm = { +@@ -258,14 +262,14 @@ static struct clk_fixed_factor raspberry + }, + }; + +-static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) ++static struct clk_hw *raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) + { + int ret; + + ret = devm_clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); + if (ret) { + dev_err(rpi->dev, "Failed to initialize pllb_arm\n"); +- return ret; ++ return ERR_PTR(ret); + } + + ret = devm_clk_hw_register_clkdev(rpi->dev, +@@ -273,10 +277,10 @@ static int raspberrypi_register_pllb_arm + NULL, "cpu0"); + if (ret) { + dev_err(rpi->dev, "Failed to initialize clkdev\n"); +- return ret; ++ return ERR_PTR(ret); + } + +- return 0; ++ return &raspberrypi_clk_pllb_arm.hw; + } + + static int raspberrypi_clk_probe(struct platform_device *pdev) +@@ -285,7 +289,7 @@ static int raspberrypi_clk_probe(struct + struct device *dev = &pdev->dev; + struct rpi_firmware *firmware; + struct raspberrypi_clk *rpi; +- int ret; ++ struct clk_hw *hw; + + firmware_node = of_parse_phandle(dev->of_node, "raspberrypi,firmware", 0); + if (!firmware_node) { +@@ -305,15 +309,15 @@ static int raspberrypi_clk_probe(struct + rpi->firmware = firmware; + platform_set_drvdata(pdev, rpi); + +- ret = raspberrypi_register_pllb(rpi); +- if (ret) { +- dev_err(dev, "Failed to initialize pllb, %d\n", ret); +- return ret; ++ hw = raspberrypi_register_pllb(rpi); ++ if (IS_ERR(hw)) { ++ dev_err(dev, "Failed to initialize pllb, %ld\n", PTR_ERR(hw)); ++ return PTR_ERR(hw); + } + +- ret = raspberrypi_register_pllb_arm(rpi); +- if (ret) +- return ret; ++ hw = raspberrypi_register_pllb_arm(rpi); ++ if (IS_ERR(hw)) ++ return PTR_ERR(hw); + + rpi->cpufreq = platform_device_register_data(dev, "raspberrypi-cpufreq", + -1, NULL, 0); diff --git a/target/linux/bcm27xx/patches-5.4/950-0532-clk-bcm-rpi-Add-DT-provider-for-the-clocks.patch b/target/linux/bcm27xx/patches-5.4/950-0532-clk-bcm-rpi-Add-DT-provider-for-the-clocks.patch new file mode 100644 index 0000000000..99fdf8fbd6 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0532-clk-bcm-rpi-Add-DT-provider-for-the-clocks.patch @@ -0,0 +1,67 @@ +From 19f7515528fbd1dc0d45e4b5ce6531c1406fc8d8 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Fri, 7 Feb 2020 17:03:46 +0100 +Subject: [PATCH] clk: bcm: rpi: Add DT provider for the clocks + +For the upcoming registration of the clocks provided by the firmware, make +sure it's exposed to the device tree providers. + +Cc: Michael Turquette <mturquette@baylibre.com> +Cc: linux-clk@vger.kernel.org +Reviewed-by: Stephen Boyd <sboyd@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/clk/bcm/clk-raspberrypi.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +--- a/drivers/clk/bcm/clk-raspberrypi.c ++++ b/drivers/clk/bcm/clk-raspberrypi.c +@@ -31,6 +31,8 @@ + + #define A2W_PLL_FRAC_BITS 20 + ++#define NUM_FW_CLKS 16 ++ + struct raspberrypi_clk { + struct device *dev; + struct rpi_firmware *firmware; +@@ -285,11 +287,13 @@ static struct clk_hw *raspberrypi_regist + + static int raspberrypi_clk_probe(struct platform_device *pdev) + { ++ struct clk_hw_onecell_data *clk_data; + struct device_node *firmware_node; + struct device *dev = &pdev->dev; + struct rpi_firmware *firmware; + struct raspberrypi_clk *rpi; + struct clk_hw *hw; ++ int ret; + + firmware_node = of_parse_phandle(dev->of_node, "raspberrypi,firmware", 0); + if (!firmware_node) { +@@ -309,6 +313,11 @@ static int raspberrypi_clk_probe(struct + rpi->firmware = firmware; + platform_set_drvdata(pdev, rpi); + ++ clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, NUM_FW_CLKS), ++ GFP_KERNEL); ++ if (!clk_data) ++ return -ENOMEM; ++ + hw = raspberrypi_register_pllb(rpi); + if (IS_ERR(hw)) { + dev_err(dev, "Failed to initialize pllb, %ld\n", PTR_ERR(hw)); +@@ -318,6 +327,13 @@ static int raspberrypi_clk_probe(struct + hw = raspberrypi_register_pllb_arm(rpi); + if (IS_ERR(hw)) + return PTR_ERR(hw); ++ clk_data->hws[RPI_FIRMWARE_ARM_CLK_ID] = hw; ++ clk_data->num = RPI_FIRMWARE_ARM_CLK_ID + 1; ++ ++ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, ++ clk_data); ++ if (ret) ++ return ret; + + rpi->cpufreq = platform_device_register_data(dev, "raspberrypi-cpufreq", + -1, NULL, 0); diff --git a/target/linux/bcm27xx/patches-5.4/950-0533-clk-bcm-rpi-Discover-the-firmware-clocks.patch b/target/linux/bcm27xx/patches-5.4/950-0533-clk-bcm-rpi-Discover-the-firmware-clocks.patch new file mode 100644 index 0000000000..eaa4a81141 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0533-clk-bcm-rpi-Discover-the-firmware-clocks.patch @@ -0,0 +1,173 @@ +From 54276fe20c0735dd18d298891b71b664ea54962d Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Mon, 10 Feb 2020 14:06:09 +0100 +Subject: [PATCH] clk: bcm: rpi: Discover the firmware clocks + +The RaspberryPi4 firmware actually exposes more clocks than are currently +handled by the driver and we will need to change some of them directly +based on the pixel rate for the display related clocks, or the load for the +GPU. + +This rate change can have a number of side-effects, including adjusting the +various PLL voltages or the PLL parents. The firmware will also update +those clocks by itself for example if the SoC runs too hot. + +In order to make Linux play as nice as possible with those constraints, it +makes sense to rely on the firmware clocks as much as possible. + +Fortunately,t he firmware has an interface to discover the clocks it +exposes. + +Let's use it to discover, register the clocks in the clocks framework and +then expose them through the device tree for consumers to use them. + +Cc: Michael Turquette <mturquette@baylibre.com> +Cc: Stephen Boyd <sboyd@kernel.org> +Cc: linux-clk@vger.kernel.org +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/clk/bcm/clk-raspberrypi.c | 104 ++++++++++++++++++--- + include/soc/bcm2835/raspberrypi-firmware.h | 5 + + 2 files changed, 97 insertions(+), 12 deletions(-) + +--- a/drivers/clk/bcm/clk-raspberrypi.c ++++ b/drivers/clk/bcm/clk-raspberrypi.c +@@ -285,6 +285,95 @@ static struct clk_hw *raspberrypi_regist + return &raspberrypi_clk_pllb_arm.hw; + } + ++static long raspberrypi_fw_dumb_round_rate(struct clk_hw *hw, ++ unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ /* ++ * The firmware will do the rounding but that isn't part of ++ * the interface with the firmware, so we just do our best ++ * here. ++ */ ++ return rate; ++} ++ ++static const struct clk_ops raspberrypi_firmware_clk_ops = { ++ .is_prepared = raspberrypi_fw_is_prepared, ++ .recalc_rate = raspberrypi_fw_get_rate, ++ .round_rate = raspberrypi_fw_dumb_round_rate, ++ .set_rate = raspberrypi_fw_set_rate, ++}; ++ ++static struct clk_hw *raspberrypi_clk_register(struct raspberrypi_clk *rpi, ++ unsigned int parent, ++ unsigned int id) ++{ ++ struct raspberrypi_clk_data *data; ++ struct clk_init_data init = {}; ++ int ret; ++ ++ if (id == RPI_FIRMWARE_ARM_CLK_ID) { ++ struct clk_hw *hw; ++ ++ hw = raspberrypi_register_pllb(rpi); ++ if (IS_ERR(hw)) { ++ dev_err(rpi->dev, "Failed to initialize pllb, %ld\n", ++ PTR_ERR(hw)); ++ return hw; ++ } ++ ++ return raspberrypi_register_pllb_arm(rpi); ++ } ++ ++ data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return ERR_PTR(-ENOMEM); ++ data->rpi = rpi; ++ data->id = id; ++ ++ init.name = devm_kasprintf(rpi->dev, GFP_KERNEL, "fw-clk-%u", id); ++ init.ops = &raspberrypi_firmware_clk_ops; ++ init.flags = CLK_GET_RATE_NOCACHE; ++ ++ data->hw.init = &init; ++ ++ ret = devm_clk_hw_register(rpi->dev, &data->hw); ++ if (ret) ++ return ERR_PTR(ret); ++ ++ return &data->hw; ++} ++ ++static int raspberrypi_discover_clocks(struct raspberrypi_clk *rpi, ++ struct clk_hw_onecell_data *data) ++{ ++ struct rpi_firmware_get_clocks_response *clks; ++ int ret; ++ ++ clks = devm_kcalloc(rpi->dev, sizeof(*clks), NUM_FW_CLKS, GFP_KERNEL); ++ if (!clks) ++ return -ENOMEM; ++ ++ ret = rpi_firmware_property(rpi->firmware, RPI_FIRMWARE_GET_CLOCKS, ++ clks, sizeof(*clks) * NUM_FW_CLKS); ++ if (ret) ++ return ret; ++ ++ while (clks->id) { ++ struct clk_hw *hw; ++ ++ hw = raspberrypi_clk_register(rpi, clks->parent, clks->id); ++ if (IS_ERR(hw)) ++ return PTR_ERR(hw); ++ ++ data->hws[clks->id] = hw; ++ data->num = clks->id + 1; ++ clks++; ++ } ++ ++ return 0; ++} ++ + static int raspberrypi_clk_probe(struct platform_device *pdev) + { + struct clk_hw_onecell_data *clk_data; +@@ -292,7 +381,6 @@ static int raspberrypi_clk_probe(struct + struct device *dev = &pdev->dev; + struct rpi_firmware *firmware; + struct raspberrypi_clk *rpi; +- struct clk_hw *hw; + int ret; + + firmware_node = of_parse_phandle(dev->of_node, "raspberrypi,firmware", 0); +@@ -318,17 +406,9 @@ static int raspberrypi_clk_probe(struct + if (!clk_data) + return -ENOMEM; + +- hw = raspberrypi_register_pllb(rpi); +- if (IS_ERR(hw)) { +- dev_err(dev, "Failed to initialize pllb, %ld\n", PTR_ERR(hw)); +- return PTR_ERR(hw); +- } +- +- hw = raspberrypi_register_pllb_arm(rpi); +- if (IS_ERR(hw)) +- return PTR_ERR(hw); +- clk_data->hws[RPI_FIRMWARE_ARM_CLK_ID] = hw; +- clk_data->num = RPI_FIRMWARE_ARM_CLK_ID + 1; ++ ret = raspberrypi_discover_clocks(rpi, clk_data); ++ if (ret) ++ return ret; + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + clk_data); +--- a/include/soc/bcm2835/raspberrypi-firmware.h ++++ b/include/soc/bcm2835/raspberrypi-firmware.h +@@ -160,6 +160,11 @@ enum rpi_firmware_property_tag { + + #define GET_DISPLAY_SETTINGS_PAYLOAD_SIZE 64 + ++struct rpi_firmware_get_clocks_response { ++ __le32 parent; ++ __le32 id; ++}; ++ + #if IS_ENABLED(CONFIG_RASPBERRYPI_FIRMWARE) + int rpi_firmware_property(struct rpi_firmware *fw, + u32 tag, void *data, size_t len); diff --git a/target/linux/bcm27xx/patches-5.4/950-0534-ARM-dts-bcm2711-Add-firmware-clocks-node.patch b/target/linux/bcm27xx/patches-5.4/950-0534-ARM-dts-bcm2711-Add-firmware-clocks-node.patch new file mode 100644 index 0000000000..098903d2e1 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0534-ARM-dts-bcm2711-Add-firmware-clocks-node.patch @@ -0,0 +1,39 @@ +From a0ebfa1829b5d3a1f698426c29f99078640b498f Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Mon, 23 Dec 2019 19:58:30 +0100 +Subject: [PATCH] ARM: dts: bcm2711: Add firmware clocks node + +Now that we have a clock driver for the clocks exposed by the firmware, +let's add the device tree nodes for it. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + arch/arm/boot/dts/bcm2711-rpi.dtsi | 2 +- + arch/arm/boot/dts/bcm2711.dtsi | 5 +++++ + 2 files changed, 6 insertions(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi ++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi +@@ -33,7 +33,7 @@ + + power-domains = <&pm BCM2835_POWER_DOMAIN_GRAFX_V3D>; + resets = <&pm BCM2835_RESET_V3D>; +- clocks = <&clocks BCM2835_CLOCK_V3D>; ++ clocks = <&firmware_clocks 5>; + interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; +--- a/arch/arm/boot/dts/bcm2711.dtsi ++++ b/arch/arm/boot/dts/bcm2711.dtsi +@@ -31,6 +31,11 @@ + }; + }; + ++ firmware_clocks: firmware-clocks { ++ compatible = "raspberrypi,firmware-clocks"; ++ raspberrypi,firmware = <&firmware>; ++ #clock-cells = <1>; ++ }; + + soc { + /* diff --git a/target/linux/bcm27xx/patches-5.4/950-0535-reset-Move-reset-simple-header-out-of-drivers-reset.patch b/target/linux/bcm27xx/patches-5.4/950-0535-reset-Move-reset-simple-header-out-of-drivers-reset.patch new file mode 100644 index 0000000000..3df4fde439 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0535-reset-Move-reset-simple-header-out-of-drivers-reset.patch @@ -0,0 +1,168 @@ +From e108e2c34b3acc70ec55b7d0772abb79c96319b2 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Tue, 28 Jan 2020 09:33:52 +0100 +Subject: [PATCH] reset: Move reset-simple header out of drivers/reset + +The reset-simple code can be useful for drivers outside of drivers/reset +that have a few reset controls as part of their features. Let's move it to +include/linux/reset. + +Cc: Philipp Zabel <p.zabel@pengutronix.de> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/reset/reset-simple.c | 3 +-- + drivers/reset/reset-socfpga.c | 3 +-- + drivers/reset/reset-sunxi.c | 3 +-- + drivers/reset/reset-uniphier-glue.c | 3 +-- + {drivers => include/linux}/reset/reset-simple.h | 0 + 5 files changed, 4 insertions(+), 8 deletions(-) + rename {drivers => include/linux}/reset/reset-simple.h (100%) + +--- a/drivers/reset/reset-simple.c ++++ b/drivers/reset/reset-simple.c +@@ -18,10 +18,9 @@ + #include <linux/of_device.h> + #include <linux/platform_device.h> + #include <linux/reset-controller.h> ++#include <linux/reset/reset-simple.h> + #include <linux/spinlock.h> + +-#include "reset-simple.h" +- + static inline struct reset_simple_data * + to_reset_simple_data(struct reset_controller_dev *rcdev) + { +--- a/drivers/reset/reset-socfpga.c ++++ b/drivers/reset/reset-socfpga.c +@@ -11,13 +11,12 @@ + #include <linux/of_address.h> + #include <linux/platform_device.h> + #include <linux/reset-controller.h> ++#include <linux/reset/reset-simple.h> + #include <linux/reset/socfpga.h> + #include <linux/slab.h> + #include <linux/spinlock.h> + #include <linux/types.h> + +-#include "reset-simple.h" +- + #define SOCFPGA_NR_BANKS 8 + + static int a10_reset_init(struct device_node *np) +--- a/drivers/reset/reset-sunxi.c ++++ b/drivers/reset/reset-sunxi.c +@@ -14,13 +14,12 @@ + #include <linux/of_address.h> + #include <linux/platform_device.h> + #include <linux/reset-controller.h> ++#include <linux/reset/reset-simple.h> + #include <linux/reset/sunxi.h> + #include <linux/slab.h> + #include <linux/spinlock.h> + #include <linux/types.h> + +-#include "reset-simple.h" +- + static int sunxi_reset_init(struct device_node *np) + { + struct reset_simple_data *data; +--- a/drivers/reset/reset-uniphier-glue.c ++++ b/drivers/reset/reset-uniphier-glue.c +@@ -9,8 +9,7 @@ + #include <linux/of_device.h> + #include <linux/platform_device.h> + #include <linux/reset.h> +- +-#include "reset-simple.h" ++#include <linux/reset/reset-simple.h> + + #define MAX_CLKS 2 + #define MAX_RSTS 2 +--- a/drivers/reset/reset-simple.h ++++ /dev/null +@@ -1,41 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * Simple Reset Controller ops +- * +- * Based on Allwinner SoCs Reset Controller driver +- * +- * Copyright 2013 Maxime Ripard +- * +- * Maxime Ripard <maxime.ripard@free-electrons.com> +- */ +- +-#ifndef __RESET_SIMPLE_H__ +-#define __RESET_SIMPLE_H__ +- +-#include <linux/io.h> +-#include <linux/reset-controller.h> +-#include <linux/spinlock.h> +- +-/** +- * struct reset_simple_data - driver data for simple reset controllers +- * @lock: spinlock to protect registers during read-modify-write cycles +- * @membase: memory mapped I/O register range +- * @rcdev: reset controller device base structure +- * @active_low: if true, bits are cleared to assert the reset. Otherwise, bits +- * are set to assert the reset. Note that this says nothing about +- * the voltage level of the actual reset line. +- * @status_active_low: if true, bits read back as cleared while the reset is +- * asserted. Otherwise, bits read back as set while the +- * reset is asserted. +- */ +-struct reset_simple_data { +- spinlock_t lock; +- void __iomem *membase; +- struct reset_controller_dev rcdev; +- bool active_low; +- bool status_active_low; +-}; +- +-extern const struct reset_control_ops reset_simple_ops; +- +-#endif /* __RESET_SIMPLE_H__ */ +--- /dev/null ++++ b/include/linux/reset/reset-simple.h +@@ -0,0 +1,41 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Simple Reset Controller ops ++ * ++ * Based on Allwinner SoCs Reset Controller driver ++ * ++ * Copyright 2013 Maxime Ripard ++ * ++ * Maxime Ripard <maxime.ripard@free-electrons.com> ++ */ ++ ++#ifndef __RESET_SIMPLE_H__ ++#define __RESET_SIMPLE_H__ ++ ++#include <linux/io.h> ++#include <linux/reset-controller.h> ++#include <linux/spinlock.h> ++ ++/** ++ * struct reset_simple_data - driver data for simple reset controllers ++ * @lock: spinlock to protect registers during read-modify-write cycles ++ * @membase: memory mapped I/O register range ++ * @rcdev: reset controller device base structure ++ * @active_low: if true, bits are cleared to assert the reset. Otherwise, bits ++ * are set to assert the reset. Note that this says nothing about ++ * the voltage level of the actual reset line. ++ * @status_active_low: if true, bits read back as cleared while the reset is ++ * asserted. Otherwise, bits read back as set while the ++ * reset is asserted. ++ */ ++struct reset_simple_data { ++ spinlock_t lock; ++ void __iomem *membase; ++ struct reset_controller_dev rcdev; ++ bool active_low; ++ bool status_active_low; ++}; ++ ++extern const struct reset_control_ops reset_simple_ops; ++ ++#endif /* __RESET_SIMPLE_H__ */ diff --git a/target/linux/bcm27xx/patches-5.4/950-0536-reset-simple-Add-reset-callback.patch b/target/linux/bcm27xx/patches-5.4/950-0536-reset-simple-Add-reset-callback.patch new file mode 100644 index 0000000000..035c9d6aa0 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0536-reset-simple-Add-reset-callback.patch @@ -0,0 +1,85 @@ +From 66deff85fee24ecd7b0ffa2901711aa8f026fcfa Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Tue, 28 Jan 2020 16:22:20 +0100 +Subject: [PATCH] reset: simple: Add reset callback + +The reset-simple code lacks a reset callback that is still pretty easy to +implement. The only real thing to consider is the delay needed for a device +to be reset, so let's expose that as part of the reset-simple driver data. + +Cc: Philipp Zabel <p.zabel@pengutronix.de> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/reset/reset-simple.c | 24 ++++++++++++++++++++++++ + include/linux/reset/reset-simple.h | 6 ++++++ + 2 files changed, 30 insertions(+) + +--- a/drivers/reset/reset-simple.c ++++ b/drivers/reset/reset-simple.c +@@ -11,6 +11,7 @@ + * Maxime Ripard <maxime.ripard@free-electrons.com> + */ + ++#include <linux/delay.h> + #include <linux/device.h> + #include <linux/err.h> + #include <linux/io.h> +@@ -63,6 +64,28 @@ static int reset_simple_deassert(struct + return reset_simple_update(rcdev, id, false); + } + ++static int reset_simple_reset(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ struct reset_simple_data *data = to_reset_simple_data(rcdev); ++ int ret; ++ ++ if (!data->reset_us) ++ return -ENOTSUPP; ++ ++ ret = reset_simple_assert(rcdev, id); ++ if (ret) ++ return ret; ++ ++ usleep_range(data->reset_us, data->reset_us * 2); ++ ++ ret = reset_simple_deassert(rcdev, id); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ + static int reset_simple_status(struct reset_controller_dev *rcdev, + unsigned long id) + { +@@ -80,6 +103,7 @@ static int reset_simple_status(struct re + const struct reset_control_ops reset_simple_ops = { + .assert = reset_simple_assert, + .deassert = reset_simple_deassert, ++ .reset = reset_simple_reset, + .status = reset_simple_status, + }; + EXPORT_SYMBOL_GPL(reset_simple_ops); +--- a/include/linux/reset/reset-simple.h ++++ b/include/linux/reset/reset-simple.h +@@ -27,6 +27,11 @@ + * @status_active_low: if true, bits read back as cleared while the reset is + * asserted. Otherwise, bits read back as set while the + * reset is asserted. ++ * @reset_us: Minimum delay in microseconds needed that needs to be ++ * waited for between an assert and a deassert to reset the ++ * device. If multiple consumers with different delay ++ * requirements are connected to this controller, it must ++ * be the largest minimum delay. + */ + struct reset_simple_data { + spinlock_t lock; +@@ -34,6 +39,7 @@ struct reset_simple_data { + struct reset_controller_dev rcdev; + bool active_low; + bool status_active_low; ++ unsigned int reset_us; + }; + + extern const struct reset_control_ops reset_simple_ops; diff --git a/target/linux/bcm27xx/patches-5.4/950-0537-dt-bindings-clock-Add-BCM2711-DVP-binding.patch b/target/linux/bcm27xx/patches-5.4/950-0537-dt-bindings-clock-Add-BCM2711-DVP-binding.patch new file mode 100644 index 0000000000..ca87cbac83 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0537-dt-bindings-clock-Add-BCM2711-DVP-binding.patch @@ -0,0 +1,68 @@ +From 67405a5468f8972f3a3db44292aff8fc05188db9 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 13 Feb 2020 17:50:31 +0100 +Subject: [PATCH] dt-bindings: clock: Add BCM2711 DVP binding + +The BCM2711 has a unit controlling the HDMI0 and HDMI1 clock and reset +signals. Let's add a binding for it. + +Cc: Philipp Zabel <p.zabel@pengutronix.de> +Cc: Rob Herring <robh+dt@kernel.org> +Cc: devicetree@vger.kernel.org +Reviewed-by: Rob Herring <robh+dt@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + .../bindings/clock/brcm,bcm2711-dvp.yaml | 47 +++++++++++++++++++ + 1 file changed, 47 insertions(+) + create mode 100644 Documentation/devicetree/bindings/clock/brcm,bcm2711-dvp.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/clock/brcm,bcm2711-dvp.yaml +@@ -0,0 +1,47 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/clock/brcm,bcm2711-dvp.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Broadcom BCM2711 HDMI DVP Device Tree Bindings ++ ++maintainers: ++ - Maxime Ripard <mripard@kernel.org> ++ ++properties: ++ "#clock-cells": ++ const: 1 ++ ++ "#reset-cells": ++ const: 1 ++ ++ compatible: ++ const: brcm,brcm2711-dvp ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ maxItems: 1 ++ ++required: ++ - "#clock-cells" ++ - "#reset-cells" ++ - compatible ++ - reg ++ - clocks ++ ++additionalProperties: false ++ ++examples: ++ - | ++ dvp: clock@7ef00000 { ++ compatible = "brcm,brcm2711-dvp"; ++ reg = <0x7ef00000 0x10>; ++ clocks = <&clk_108MHz>; ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ }; ++ ++... diff --git a/target/linux/bcm27xx/patches-5.4/950-0538-clk-bcm-Add-BCM2711-DVP-driver.patch b/target/linux/bcm27xx/patches-5.4/950-0538-clk-bcm-Add-BCM2711-DVP-driver.patch new file mode 100644 index 0000000000..23105057a9 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0538-clk-bcm-Add-BCM2711-DVP-driver.patch @@ -0,0 +1,172 @@ +From 0a2b9668e391b5fef4c54f992d7f8f99e5f50ef3 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Tue, 28 Jan 2020 09:36:27 +0100 +Subject: [PATCH] clk: bcm: Add BCM2711 DVP driver + +The HDMI block has a block that controls clocks and reset signals to the +HDMI0 and HDMI1 controllers. + +Let's expose that through a clock driver implementing a clock and reset +provider. + +Cc: Michael Turquette <mturquette@baylibre.com> +Cc: Stephen Boyd <sboyd@kernel.org> +Cc: Rob Herring <robh+dt@kernel.org> +Cc: linux-clk@vger.kernel.org +Cc: devicetree@vger.kernel.org +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/clk/bcm/Kconfig | 1 + + drivers/clk/bcm/Makefile | 1 + + drivers/clk/bcm/clk-bcm2711-dvp.c | 125 ++++++++++++++++++++++++++++++ + 3 files changed, 127 insertions(+) + create mode 100644 drivers/clk/bcm/clk-bcm2711-dvp.c + +--- a/drivers/clk/bcm/Kconfig ++++ b/drivers/clk/bcm/Kconfig +@@ -4,6 +4,7 @@ config CLK_BCM2835 + depends on ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST + depends on COMMON_CLK + default ARCH_BCM2835 || ARCH_BRCMSTB ++ select RESET_SIMPLE + help + Enable common clock framework support for Broadcom BCM2835 + SoCs. +--- a/drivers/clk/bcm/Makefile ++++ b/drivers/clk/bcm/Makefile +@@ -6,6 +6,7 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-s + obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o + obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o + obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o ++obj-$(CONFIG_CLK_BCM2835) += clk-bcm2711-dvp.o + obj-$(CONFIG_CLK_BCM2835) += clk-bcm2835.o + obj-$(CONFIG_CLK_BCM2835) += clk-bcm2835-aux.o + obj-$(CONFIG_CLK_RASPBERRYPI) += clk-raspberrypi.o +--- /dev/null ++++ b/drivers/clk/bcm/clk-bcm2711-dvp.c +@@ -0,0 +1,125 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++// Copyright 2020 Cerno ++ ++#include <linux/clk-provider.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/reset-controller.h> ++#include <linux/reset/reset-simple.h> ++ ++#define DVP_HT_RPI_SW_INIT 0x04 ++#define DVP_HT_RPI_MISC_CONFIG 0x08 ++ ++#define NR_CLOCKS 2 ++#define NR_RESETS 6 ++ ++struct clk_dvp { ++ struct clk_hw_onecell_data *data; ++ struct reset_simple_data reset; ++}; ++ ++static int clk_dvp_probe(struct platform_device *pdev) ++{ ++ struct clk_hw_onecell_data *data; ++ struct resource *res; ++ struct clk_dvp *dvp; ++ void __iomem *base; ++ const char *parent; ++ int ret; ++ ++ dvp = devm_kzalloc(&pdev->dev, sizeof(*dvp), GFP_KERNEL); ++ if (!dvp) ++ return -ENOMEM; ++ platform_set_drvdata(pdev, dvp); ++ ++ dvp->data = devm_kzalloc(&pdev->dev, ++ struct_size(dvp->data, hws, NR_CLOCKS), ++ GFP_KERNEL); ++ if (!dvp->data) ++ return -ENOMEM; ++ data = dvp->data; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ dvp->reset.rcdev.owner = THIS_MODULE; ++ dvp->reset.rcdev.nr_resets = NR_RESETS; ++ dvp->reset.rcdev.ops = &reset_simple_ops; ++ dvp->reset.rcdev.of_node = pdev->dev.of_node; ++ dvp->reset.membase = base + DVP_HT_RPI_SW_INIT; ++ spin_lock_init(&dvp->reset.lock); ++ ++ ret = reset_controller_register(&dvp->reset.rcdev); ++ if (ret) ++ return ret; ++ ++ parent = of_clk_get_parent_name(pdev->dev.of_node, 0); ++ if (!parent) ++ goto unregister_reset; ++ ++ data->hws[0] = clk_hw_register_gate(&pdev->dev, "hdmi0-108MHz", ++ parent, 0, ++ base + DVP_HT_RPI_MISC_CONFIG, 3, ++ CLK_GATE_SET_TO_DISABLE, &dvp->reset.lock); ++ if (IS_ERR(data->hws[0])) { ++ ret = PTR_ERR(data->hws[0]); ++ goto unregister_reset; ++ } ++ ++ data->hws[1] = clk_hw_register_gate(&pdev->dev, "hdmi1-108MHz", ++ parent, 0, ++ base + DVP_HT_RPI_MISC_CONFIG, 4, ++ CLK_GATE_SET_TO_DISABLE, &dvp->reset.lock); ++ if (IS_ERR(data->hws[1])) { ++ ret = PTR_ERR(data->hws[1]); ++ goto unregister_clk0; ++ } ++ ++ data->num = NR_CLOCKS; ++ ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, ++ data); ++ if (ret) ++ goto unregister_clk1; ++ ++ return 0; ++ ++ ++unregister_clk1: ++ clk_hw_unregister_gate(data->hws[1]); ++ ++unregister_clk0: ++ clk_hw_unregister_gate(data->hws[0]); ++ ++unregister_reset: ++ reset_controller_unregister(&dvp->reset.rcdev); ++ return ret; ++}; ++ ++static int clk_dvp_remove(struct platform_device *pdev) ++{ ++ struct clk_dvp *dvp = platform_get_drvdata(pdev); ++ struct clk_hw_onecell_data *data = dvp->data; ++ ++ clk_hw_unregister_gate(data->hws[1]); ++ clk_hw_unregister_gate(data->hws[0]); ++ reset_controller_unregister(&dvp->reset.rcdev); ++ ++ return 0; ++} ++ ++static const struct of_device_id clk_dvp_dt_ids[] = { ++ { .compatible = "brcm,brcm2711-dvp", }, ++ { /* sentinel */ } ++}; ++ ++static struct platform_driver clk_dvp_driver = { ++ .probe = clk_dvp_probe, ++ .remove = clk_dvp_remove, ++ .driver = { ++ .name = "brcm2711-dvp", ++ .of_match_table = clk_dvp_dt_ids, ++ }, ++}; ++module_platform_driver(clk_dvp_driver); diff --git a/target/linux/bcm27xx/patches-5.4/950-0539-ARM-dts-bcm2711-Add-HDMI-DVP.patch b/target/linux/bcm27xx/patches-5.4/950-0539-ARM-dts-bcm2711-Add-HDMI-DVP.patch new file mode 100644 index 0000000000..c4d230e730 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0539-ARM-dts-bcm2711-Add-HDMI-DVP.patch @@ -0,0 +1,43 @@ +From 119c9cdf9beab785d10ebf8a804ce20b3b0fd779 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Tue, 28 Jan 2020 09:37:06 +0100 +Subject: [PATCH] ARM: dts: bcm2711: Add HDMI DVP + +Now that we have a driver for the DVP, let's add its DT node. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + arch/arm/boot/dts/bcm2711.dtsi | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +--- a/arch/arm/boot/dts/bcm2711.dtsi ++++ b/arch/arm/boot/dts/bcm2711.dtsi +@@ -31,6 +31,13 @@ + }; + }; + ++ clk_108MHz: clk-108M { ++ #clock-cells = <0>; ++ compatible = "fixed-clock"; ++ clock-frequency = <108000000>; ++ clock-output-names = "108MHz-clock"; ++ }; ++ + firmware_clocks: firmware-clocks { + compatible = "raspberrypi,firmware-clocks"; + raspberrypi,firmware = <&firmware>; +@@ -268,6 +275,14 @@ + hvs@7e400000 { + interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>; + }; ++ ++ dvp: clock@7ef00000 { ++ compatible = "brcm,brcm2711-dvp"; ++ reg = <0x7ef00000 0x10>; ++ clocks = <&clk_108MHz>; ++ #clock-cells = <1>; ++ #reset-cells = <1>; ++ }; + }; + + arm-pmu { diff --git a/target/linux/bcm27xx/patches-5.4/950-0540-dt-bindings-display-Convert-VC4-bindings-to-schemas.patch b/target/linux/bcm27xx/patches-5.4/950-0540-dt-bindings-display-Convert-VC4-bindings-to-schemas.patch new file mode 100644 index 0000000000..3e47c475ff --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0540-dt-bindings-display-Convert-VC4-bindings-to-schemas.patch @@ -0,0 +1,706 @@ +From 193065956ba3e285df2c67f7c3bdeb3bdaae6ee9 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 13 Feb 2020 15:42:05 +0100 +Subject: [PATCH] dt-bindings: display: Convert VC4 bindings to schemas + +The BCM283x SoCs have a display pipeline composed of several controllers +with device tree bindings that are supported by Linux. + +Now that we have the DT validation in place, let's split into separate +files and convert the device tree bindings for those controllers to +schemas. + +This is just a 1:1 conversion though, and some bindings were incomplete so +it results in example validation warnings that are going to be addressed in +the following patches. + +Cc: Rob Herring <robh+dt@kernel.org> +Cc: devicetree@vger.kernel.org +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + .../bindings/display/brcm,bcm-vc4.txt | 174 ------------------ + .../bindings/display/brcm,bcm2835-dpi.yaml | 66 +++++++ + .../bindings/display/brcm,bcm2835-dsi0.yaml | 73 ++++++++ + .../bindings/display/brcm,bcm2835-hdmi.yaml | 75 ++++++++ + .../bindings/display/brcm,bcm2835-hvs.yaml | 37 ++++ + .../display/brcm,bcm2835-pixelvalve0.yaml | 40 ++++ + .../bindings/display/brcm,bcm2835-txp.yaml | 37 ++++ + .../bindings/display/brcm,bcm2835-v3d.yaml | 42 +++++ + .../bindings/display/brcm,bcm2835-vc4.yaml | 34 ++++ + .../bindings/display/brcm,bcm2835-vec.yaml | 44 +++++ + MAINTAINERS | 2 +- + 11 files changed, 449 insertions(+), 175 deletions(-) + delete mode 100644 Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt + create mode 100644 Documentation/devicetree/bindings/display/brcm,bcm2835-dpi.yaml + create mode 100644 Documentation/devicetree/bindings/display/brcm,bcm2835-dsi0.yaml + create mode 100644 Documentation/devicetree/bindings/display/brcm,bcm2835-hdmi.yaml + create mode 100644 Documentation/devicetree/bindings/display/brcm,bcm2835-hvs.yaml + create mode 100644 Documentation/devicetree/bindings/display/brcm,bcm2835-pixelvalve0.yaml + create mode 100644 Documentation/devicetree/bindings/display/brcm,bcm2835-txp.yaml + create mode 100644 Documentation/devicetree/bindings/display/brcm,bcm2835-v3d.yaml + create mode 100644 Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml + create mode 100644 Documentation/devicetree/bindings/display/brcm,bcm2835-vec.yaml + +--- a/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt ++++ /dev/null +@@ -1,174 +0,0 @@ +-Broadcom VC4 (VideoCore4) GPU +- +-The VC4 device present on the Raspberry Pi includes a display system +-with HDMI output and the HVS (Hardware Video Scaler) for compositing +-display planes. +- +-Required properties for VC4: +-- compatible: Should be "brcm,bcm2835-vc4" or "brcm,cygnus-vc4" +- +-Required properties for Pixel Valve: +-- compatible: Should be one of "brcm,bcm2835-pixelvalve0", +- "brcm,bcm2835-pixelvalve1", or "brcm,bcm2835-pixelvalve2" +-- reg: Physical base address and length of the PV's registers +-- interrupts: The interrupt number +- See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt +- +-Required properties for HVS: +-- compatible: Should be "brcm,bcm2835-hvs" +-- reg: Physical base address and length of the HVS's registers +-- interrupts: The interrupt number +- See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt +- +-Required properties for HDMI +-- compatible: Should be "brcm,bcm2835-hdmi" +-- reg: Physical base address and length of the two register ranges +- ("HDMI" and "HD", in that order) +-- interrupts: The interrupt numbers +- See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt +-- ddc: phandle of the I2C controller used for DDC EDID probing +-- clocks: a) hdmi: The HDMI state machine clock +- b) pixel: The pixel clock. +- +-Optional properties for HDMI: +-- hpd-gpios: The GPIO pin for HDMI hotplug detect (if it doesn't appear +- as an interrupt/status bit in the HDMI controller +- itself). See bindings/pinctrl/brcm,bcm2835-gpio.txt +-- dmas: Should contain one entry pointing to the DMA channel used to +- transfer audio data +-- dma-names: Should contain "audio-rx" +- +-Required properties for DPI: +-- compatible: Should be "brcm,bcm2835-dpi" +-- reg: Physical base address and length of the registers +-- clocks: a) core: The core clock the unit runs on +- b) pixel: The pixel clock that feeds the pixelvalve +-- port: Port node with a single endpoint connecting to the panel +- device, as defined in [1] +- +-Required properties for VEC: +-- compatible: Should be "brcm,bcm2835-vec" +-- reg: Physical base address and length of the registers +-- clocks: The core clock the unit runs on +-- interrupts: The interrupt number +- See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt +- +-Required properties for V3D: +-- compatible: Should be "brcm,bcm2835-v3d" or "brcm,cygnus-v3d" +-- reg: Physical base address and length of the V3D's registers +-- interrupts: The interrupt number +- See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt +- +-Optional properties for V3D: +-- clocks: The clock the unit runs on +- +-Required properties for DSI: +-- compatible: Should be "brcm,bcm2835-dsi0" or "brcm,bcm2835-dsi1" +-- reg: Physical base address and length of the DSI block's registers +-- interrupts: The interrupt number +- See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt +-- clocks: a) phy: The DSI PLL clock feeding the DSI analog PHY +- b) escape: The DSI ESC clock from CPRMAN +- c) pixel: The DSI pixel clock from CPRMAN +-- clock-output-names: +- The 3 clocks output from the DSI analog PHY: dsi[01]_byte, +- dsi[01]_ddr2, and dsi[01]_ddr +- +-Required properties for the TXP (writeback) block: +-- compatible: Should be "brcm,bcm2835-txp" +-- reg: Physical base address and length of the TXP block's registers +-- interrupts: The interrupt number +- See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt +- +-[1] Documentation/devicetree/bindings/media/video-interfaces.txt +- +-Example: +-pixelvalve@7e807000 { +- compatible = "brcm,bcm2835-pixelvalve2"; +- reg = <0x7e807000 0x100>; +- interrupts = <2 10>; /* pixelvalve */ +-}; +- +-hvs@7e400000 { +- compatible = "brcm,bcm2835-hvs"; +- reg = <0x7e400000 0x6000>; +- interrupts = <2 1>; +-}; +- +-hdmi: hdmi@7e902000 { +- compatible = "brcm,bcm2835-hdmi"; +- reg = <0x7e902000 0x600>, +- <0x7e808000 0x100>; +- interrupts = <2 8>, <2 9>; +- ddc = <&i2c2>; +- hpd-gpios = <&gpio 46 GPIO_ACTIVE_HIGH>; +- clocks = <&clocks BCM2835_PLLH_PIX>, +- <&clocks BCM2835_CLOCK_HSM>; +- clock-names = "pixel", "hdmi"; +-}; +- +-dpi: dpi@7e208000 { +- compatible = "brcm,bcm2835-dpi"; +- reg = <0x7e208000 0x8c>; +- clocks = <&clocks BCM2835_CLOCK_VPU>, +- <&clocks BCM2835_CLOCK_DPI>; +- clock-names = "core", "pixel"; +- #address-cells = <1>; +- #size-cells = <0>; +- +- port { +- dpi_out: endpoint@0 { +- remote-endpoint = <&panel_in>; +- }; +- }; +-}; +- +-dsi1: dsi@7e700000 { +- compatible = "brcm,bcm2835-dsi1"; +- reg = <0x7e700000 0x8c>; +- interrupts = <2 12>; +- #address-cells = <1>; +- #size-cells = <0>; +- #clock-cells = <1>; +- +- clocks = <&clocks BCM2835_PLLD_DSI1>, +- <&clocks BCM2835_CLOCK_DSI1E>, +- <&clocks BCM2835_CLOCK_DSI1P>; +- clock-names = "phy", "escape", "pixel"; +- +- clock-output-names = "dsi1_byte", "dsi1_ddr2", "dsi1_ddr"; +- +- pitouchscreen: panel@0 { +- compatible = "raspberrypi,touchscreen"; +- reg = <0>; +- +- <...> +- }; +-}; +- +-vec: vec@7e806000 { +- compatible = "brcm,bcm2835-vec"; +- reg = <0x7e806000 0x1000>; +- clocks = <&clocks BCM2835_CLOCK_VEC>; +- interrupts = <2 27>; +-}; +- +-v3d: v3d@7ec00000 { +- compatible = "brcm,bcm2835-v3d"; +- reg = <0x7ec00000 0x1000>; +- interrupts = <1 10>; +-}; +- +-vc4: gpu { +- compatible = "brcm,bcm2835-vc4"; +-}; +- +-panel: panel { +- compatible = "ontat,yx700wv03", "simple-panel"; +- +- port { +- panel_in: endpoint { +- remote-endpoint = <&dpi_out>; +- }; +- }; +-}; +--- /dev/null ++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-dpi.yaml +@@ -0,0 +1,66 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/display/brcm,bcm2835-dpi.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Broadcom VC4 (VideoCore4) DPI Controller ++ ++maintainers: ++ - Eric Anholt <eric@anholt.net> ++ ++properties: ++ compatible: ++ const: brcm,bcm2835-dpi ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ items: ++ - description: The core clock the unit runs on ++ - description: The pixel clock that feeds the pixelvalve ++ ++ port: ++ type: object ++ description: > ++ Port node with a single endpoint connecting to the panel, as ++ defined in Documentation/devicetree/bindings/media/video-interfaces.txt. ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - port ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include <dt-bindings/clock/bcm2835.h> ++ ++ panel: panel { ++ compatible = "ontat,yx700wv03", "simple-panel"; ++ ++ port { ++ panel_in: endpoint { ++ remote-endpoint = <&dpi_out>; ++ }; ++ }; ++ }; ++ ++ dpi: dpi@7e208000 { ++ compatible = "brcm,bcm2835-dpi"; ++ reg = <0x7e208000 0x8c>; ++ clocks = <&clocks BCM2835_CLOCK_VPU>, ++ <&clocks BCM2835_CLOCK_DPI>; ++ clock-names = "core", "pixel"; ++ ++ port { ++ dpi_out: endpoint { ++ remote-endpoint = <&panel_in>; ++ }; ++ }; ++ }; ++ ++... +--- /dev/null ++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-dsi0.yaml +@@ -0,0 +1,73 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/display/brcm,bcm2835-dsi0.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Broadcom VC4 (VideoCore4) DSI Controller ++ ++maintainers: ++ - Eric Anholt <eric@anholt.net> ++ ++properties: ++ compatible: ++ enum: ++ - brcm,bcm2835-dsi0 ++ - brcm,bcm2835-dsi1 ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ items: ++ - description: The DSI PLL clock feeding the DSI analog PHY ++ - description: The DSI ESC clock ++ - description: The DSI pixel clock ++ ++ clock-output-names: true ++ # FIXME: The meta-schemas don't seem to allow it for now ++ # items: ++ # - description: The DSI byte clock for the PHY ++ # - description: The DSI DDR2 clock ++ # - description: The DSI DDR clock ++ ++ interrupts: ++ maxItems: 1 ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - clock-output-names ++ - interrupts ++ ++unevaluatedProperties: false ++ ++examples: ++ - | ++ #include <dt-bindings/clock/bcm2835.h> ++ ++ dsi1: dsi@7e700000 { ++ compatible = "brcm,bcm2835-dsi1"; ++ reg = <0x7e700000 0x8c>; ++ interrupts = <2 12>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ #clock-cells = <1>; ++ ++ clocks = <&clocks BCM2835_PLLD_DSI1>, ++ <&clocks BCM2835_CLOCK_DSI1E>, ++ <&clocks BCM2835_CLOCK_DSI1P>; ++ clock-names = "phy", "escape", "pixel"; ++ ++ clock-output-names = "dsi1_byte", "dsi1_ddr2", "dsi1_ddr"; ++ ++ pitouchscreen: panel@0 { ++ compatible = "raspberrypi,touchscreen"; ++ reg = <0>; ++ ++ /* ... */ ++ }; ++ }; ++ ++... +--- /dev/null ++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-hdmi.yaml +@@ -0,0 +1,75 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/display/brcm,bcm2835-hdmi.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Broadcom VC4 (VideoCore4) HDMI Controller ++ ++maintainers: ++ - Eric Anholt <eric@anholt.net> ++ ++properties: ++ compatible: ++ const: brcm,bcm2835-hdmi ++ ++ reg: ++ items: ++ - description: HDMI register range ++ - description: HD register range ++ ++ interrupts: ++ minItems: 2 ++ ++ clocks: ++ items: ++ - description: The HDMI state machine clock ++ - description: The pixel clock ++ ++ ddc: ++ allOf: ++ - $ref: /schemas/types.yaml#/definitions/phandle ++ description: > ++ Phandle of the I2C controller used for DDC EDID probing ++ ++ hpd-gpios: ++ description: > ++ The GPIO pin for the HDMI hotplug detect (if it doesn't appear ++ as an interrupt/status bit in the HDMI controller itself) ++ ++ dmas: ++ maxItems: 1 ++ description: > ++ Should contain one entry pointing to the DMA channel used to ++ transfer audio data. ++ ++ dma-names: ++ const: audio-rx ++ ++required: ++ - compatible ++ - reg ++ - interrupts ++ - clocks ++ - ddc ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include <dt-bindings/clock/bcm2835.h> ++ #include <dt-bindings/gpio/gpio.h> ++ ++ hdmi: hdmi@7e902000 { ++ compatible = "brcm,bcm2835-hdmi"; ++ reg = <0x7e902000 0x600>, ++ <0x7e808000 0x100>; ++ interrupts = <2 8>, <2 9>; ++ ddc = <&i2c2>; ++ hpd-gpios = <&gpio 46 GPIO_ACTIVE_HIGH>; ++ clocks = <&clocks BCM2835_PLLH_PIX>, ++ <&clocks BCM2835_CLOCK_HSM>; ++ clock-names = "pixel", "hdmi"; ++ }; ++ ++... +--- /dev/null ++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-hvs.yaml +@@ -0,0 +1,37 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/display/brcm,bcm2835-hvs.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Broadcom VC4 (VideoCore4) Hardware Video Scaler ++ ++maintainers: ++ - Eric Anholt <eric@anholt.net> ++ ++properties: ++ compatible: ++ const: brcm,bcm2835-hvs ++ ++ reg: ++ maxItems: 1 ++ ++ interrupts: ++ maxItems: 1 ++ ++required: ++ - compatible ++ - reg ++ - interrupts ++ ++additionalProperties: false ++ ++examples: ++ - | ++ hvs@7e400000 { ++ compatible = "brcm,bcm2835-hvs"; ++ reg = <0x7e400000 0x6000>; ++ interrupts = <2 1>; ++ }; ++ ++... +--- /dev/null ++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-pixelvalve0.yaml +@@ -0,0 +1,40 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/display/brcm,bcm2835-pixelvalve0.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Broadcom VC4 (VideoCore4) PixelValve ++ ++maintainers: ++ - Eric Anholt <eric@anholt.net> ++ ++properties: ++ compatible: ++ enum: ++ - brcm,bcm2835-pixelvalve0 ++ - brcm,bcm2835-pixelvalve1 ++ - brcm,bcm2835-pixelvalve2 ++ ++ reg: ++ maxItems: 1 ++ ++ interrupts: ++ maxItems: 1 ++ ++required: ++ - compatible ++ - reg ++ - interrupts ++ ++additionalProperties: false ++ ++examples: ++ - | ++ pixelvalve@7e807000 { ++ compatible = "brcm,bcm2835-pixelvalve2"; ++ reg = <0x7e807000 0x100>; ++ interrupts = <2 10>; /* pixelvalve */ ++ }; ++ ++... +--- /dev/null ++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-txp.yaml +@@ -0,0 +1,37 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/display/brcm,bcm2835-txp.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Broadcom VC4 (VideoCore4) TXP (writeback) Controller ++ ++maintainers: ++ - Eric Anholt <eric@anholt.net> ++ ++properties: ++ compatible: ++ const: brcm,bcm2835-txp ++ ++ reg: ++ maxItems: 1 ++ ++ interrupts: ++ maxItems: 1 ++ ++required: ++ - compatible ++ - reg ++ - interrupts ++ ++additionalProperties: false ++ ++examples: ++ - | ++ txp: txp@7e004000 { ++ compatible = "brcm,bcm2835-txp"; ++ reg = <0x7e004000 0x20>; ++ interrupts = <1 11>; ++ }; ++ ++... +--- /dev/null ++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-v3d.yaml +@@ -0,0 +1,42 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/display/brcm,bcm2835-v3d.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Broadcom VC4 (VideoCore4) V3D GPU ++ ++maintainers: ++ - Eric Anholt <eric@anholt.net> ++ ++properties: ++ compatible: ++ enum: ++ - brcm,bcm2835-v3d ++ - brcm,cygnus-v3d ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ maxItems: 1 ++ ++ interrupts: ++ maxItems: 1 ++ ++required: ++ - compatible ++ - reg ++ - interrupts ++ ++additionalProperties: false ++ ++examples: ++ - | ++ v3d: v3d@7ec00000 { ++ compatible = "brcm,bcm2835-v3d"; ++ reg = <0x7ec00000 0x1000>; ++ interrupts = <1 10>; ++ }; ++ ++... +--- /dev/null ++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml +@@ -0,0 +1,34 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/display/brcm,bcm2835-vc4.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Broadcom VC4 (VideoCore4) GPU ++ ++maintainers: ++ - Eric Anholt <eric@anholt.net> ++ ++description: > ++ The VC4 device present on the Raspberry Pi includes a display system ++ with HDMI output and the HVS (Hardware Video Scaler) for compositing ++ display planes. ++ ++properties: ++ compatible: ++ enum: ++ - brcm,bcm2835-vc4 ++ - brcm,cygnus-vc4 ++ ++required: ++ - compatible ++ ++additionalProperties: false ++ ++examples: ++ - | ++ vc4: gpu { ++ compatible = "brcm,bcm2835-vc4"; ++ }; ++ ++... +--- /dev/null ++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-vec.yaml +@@ -0,0 +1,44 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/display/brcm,bcm2835-vec.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Broadcom VC4 (VideoCore4) VEC ++ ++maintainers: ++ - Eric Anholt <eric@anholt.net> ++ ++properties: ++ compatible: ++ const: brcm,bcm2835-vec ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ maxItems: 1 ++ ++ interrupts: ++ maxItems: 1 ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - interrupts ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include <dt-bindings/clock/bcm2835.h> ++ ++ vec: vec@7e806000 { ++ compatible = "brcm,bcm2835-vec"; ++ reg = <0x7e806000 0x1000>; ++ clocks = <&clocks BCM2835_CLOCK_VEC>; ++ interrupts = <2 27>; ++ }; ++ ++... +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -5573,7 +5573,7 @@ T: git git://github.com/anholt/linux + S: Supported + F: drivers/gpu/drm/vc4/ + F: include/uapi/drm/vc4_drm.h +-F: Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt ++F: Documentation/devicetree/bindings/display/brcm,bcm2835-*.yaml + T: git git://anongit.freedesktop.org/drm/drm-misc + + DRM DRIVERS FOR VIVANTE GPU IP diff --git a/target/linux/bcm27xx/patches-5.4/950-0541-dt-bindings-display-vc4-dpi-Add-missing-clock-names-.patch b/target/linux/bcm27xx/patches-5.4/950-0541-dt-bindings-display-vc4-dpi-Add-missing-clock-names-.patch new file mode 100644 index 0000000000..2150f0abec --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0541-dt-bindings-display-vc4-dpi-Add-missing-clock-names-.patch @@ -0,0 +1,38 @@ +From 9a624b11291306b4b4c49c70bc75ef7d72d81405 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 13 Feb 2020 15:47:18 +0100 +Subject: [PATCH] dt-bindings: display: vc4: dpi: Add missing + clock-names property + +While the device tree and the driver expected a clock-names property, it +wasn't explicitly documented in the previous binding. Make sure it is now. + +Cc: devicetree@vger.kernel.org +Reviewed-by: Rob Herring <robh+dt@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + .../devicetree/bindings/display/brcm,bcm2835-dpi.yaml | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-dpi.yaml ++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-dpi.yaml +@@ -21,6 +21,11 @@ properties: + - description: The core clock the unit runs on + - description: The pixel clock that feeds the pixelvalve + ++ clock-names: ++ items: ++ - const: core ++ - const: pixel ++ + port: + type: object + description: > +@@ -31,6 +36,7 @@ required: + - compatible + - reg + - clocks ++ - clock-names + - port + + additionalProperties: false diff --git a/target/linux/bcm27xx/patches-5.4/950-0542-dt-bindings-display-vc4-dsi-Add-missing-clock-proper.patch b/target/linux/bcm27xx/patches-5.4/950-0542-dt-bindings-display-vc4-dsi-Add-missing-clock-proper.patch new file mode 100644 index 0000000000..4cb313f152 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0542-dt-bindings-display-vc4-dsi-Add-missing-clock-proper.patch @@ -0,0 +1,54 @@ +From 12abb6775e99482ff9fc71e16292ccf4dfeacee5 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 13 Feb 2020 15:47:18 +0100 +Subject: [PATCH] dt-bindings: display: vc4: dsi: Add missing clock + properties + +While the device tree and the driver expected a clock-names and a +clock-cells properties, it wasn't explicitly documented in the previous +binding. Make sure it is now. + +Cc: devicetree@vger.kernel.org +Reviewed-by: Rob Herring <robh+dt@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + .../bindings/display/brcm,bcm2835-dsi0.yaml | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-dsi0.yaml ++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-dsi0.yaml +@@ -10,6 +10,9 @@ maintainers: + - Eric Anholt <eric@anholt.net> + + properties: ++ "#clock-cells": ++ const: 1 ++ + compatible: + enum: + - brcm,bcm2835-dsi0 +@@ -24,6 +27,12 @@ properties: + - description: The DSI ESC clock + - description: The DSI pixel clock + ++ clock-names: ++ items: ++ - const: phy ++ - const: escape ++ - const: pixel ++ + clock-output-names: true + # FIXME: The meta-schemas don't seem to allow it for now + # items: +@@ -35,9 +44,11 @@ properties: + maxItems: 1 + + required: ++ - "#clock-cells" + - compatible + - reg + - clocks ++ - clock-names + - clock-output-names + - interrupts + diff --git a/target/linux/bcm27xx/patches-5.4/950-0543-dt-bindings-display-vc4-hdmi-Add-missing-clock-names.patch b/target/linux/bcm27xx/patches-5.4/950-0543-dt-bindings-display-vc4-hdmi-Add-missing-clock-names.patch new file mode 100644 index 0000000000..5ef4eaeab9 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0543-dt-bindings-display-vc4-hdmi-Add-missing-clock-names.patch @@ -0,0 +1,34 @@ +From 293db446089f00599f0a22b933a6a5a13ccfc5e2 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 13 Feb 2020 15:47:18 +0100 +Subject: [PATCH] dt-bindings: display: vc4: hdmi: Add missing + clock-names property + +While the device tree and the driver expected a clock-names property, it +wasn't explicitly documented in the previous binding. The documented order +was wrong too, so make sure clock-names is there and in the proper order. + +Cc: devicetree@vger.kernel.org +Reviewed-by: Rob Herring <robh+dt@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + .../devicetree/bindings/display/brcm,bcm2835-hdmi.yaml | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-hdmi.yaml ++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-hdmi.yaml +@@ -23,8 +23,13 @@ properties: + + clocks: + items: +- - description: The HDMI state machine clock + - description: The pixel clock ++ - description: The HDMI state machine clock ++ ++ clock-names: ++ items: ++ - const: pixel ++ - const: hdmi + + ddc: + allOf: diff --git a/target/linux/bcm27xx/patches-5.4/950-0544-dt-bindings-display-vc4-Document-BCM2711-VC5.patch b/target/linux/bcm27xx/patches-5.4/950-0544-dt-bindings-display-vc4-Document-BCM2711-VC5.patch new file mode 100644 index 0000000000..2499dee0c9 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0544-dt-bindings-display-vc4-Document-BCM2711-VC5.patch @@ -0,0 +1,24 @@ +From a12c5df87364ef6965a750345b1e28a5aba5cb14 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 13 Feb 2020 17:40:56 +0100 +Subject: [PATCH] dt-bindings: display: vc4: Document BCM2711 VC5 + +The BCM2711 comes with a new VideoCore. Add a compatible for it. + +Cc: devicetree@vger.kernel.org +Reviewed-by: Rob Herring <robh+dt@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml | 1 + + 1 file changed, 1 insertion(+) + +--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml ++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml +@@ -17,6 +17,7 @@ description: > + properties: + compatible: + enum: ++ - brcm,bcm2711-vc5 + - brcm,bcm2835-vc4 + - brcm,cygnus-vc4 + diff --git a/target/linux/bcm27xx/patches-5.4/950-0545-drm-vc4-drv-Add-include-guards.patch b/target/linux/bcm27xx/patches-5.4/950-0545-drm-vc4-drv-Add-include-guards.patch new file mode 100644 index 0000000000..16f8b0ad82 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0545-drm-vc4-drv-Add-include-guards.patch @@ -0,0 +1,30 @@ +From 42566c11972c9edace45d5a787e276588214cb79 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 19 Dec 2019 18:08:48 +0100 +Subject: [PATCH] drm/vc4: drv: Add include guards + +vc4_drv.h doesn't have any include guards which prevents it from being +included twice. Let's add them. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_drv.h | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -2,6 +2,8 @@ + /* + * Copyright (C) 2015 Broadcom + */ ++#ifndef _VC4_DRV_H_ ++#define _VC4_DRV_H_ + + #include <linux/delay.h> + #include <linux/refcount.h> +@@ -899,3 +901,5 @@ int vc4_perfmon_destroy_ioctl(struct drm + struct drm_file *file_priv); + int vc4_perfmon_get_values_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); ++ ++#endif /* _VC4_DRV_H_ */ diff --git a/target/linux/bcm27xx/patches-5.4/950-0546-drm-vc4-drv-Support-BCM2711.patch b/target/linux/bcm27xx/patches-5.4/950-0546-drm-vc4-drv-Support-BCM2711.patch new file mode 100644 index 0000000000..00bac3a2b9 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0546-drm-vc4-drv-Support-BCM2711.patch @@ -0,0 +1,109 @@ +From d52f29a5e0ee9882f6f734c057224686b9820152 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 6 Feb 2020 15:40:34 +0100 +Subject: [PATCH] drm/vc4: drv: Support BCM2711 + +The BCM2711 has a reworked display pipeline, and the load tracker needs +some adjustement to operate properly. Let's add a compatible for BCM2711 +and disable the load tracker until properly supported. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_drv.c | 1 + + drivers/gpu/drm/vc4/vc4_drv.h | 3 +++ + drivers/gpu/drm/vc4/vc4_kms.c | 32 +++++++++++++++++++++----------- + drivers/gpu/drm/vc4/vc4_plane.c | 5 +++++ + 4 files changed, 30 insertions(+), 11 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_drv.c ++++ b/drivers/gpu/drm/vc4/vc4_drv.c +@@ -368,6 +368,7 @@ static int vc4_platform_drm_remove(struc + } + + static const struct of_device_id vc4_of_match[] = { ++ { .compatible = "brcm,bcm2711-vc5", }, + { .compatible = "brcm,bcm2835-vc4", }, + { .compatible = "brcm,cygnus-vc4", }, + {}, +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -205,6 +205,9 @@ struct vc4_dev { + + int power_refcount; + ++ /* Set to true when the load tracker is supported. */ ++ bool load_tracker_available; ++ + /* Set to true when the load tracker is active. */ + bool load_tracker_enabled; + +--- a/drivers/gpu/drm/vc4/vc4_kms.c ++++ b/drivers/gpu/drm/vc4/vc4_kms.c +@@ -421,6 +421,9 @@ static int vc4_load_tracker_atomic_check + struct drm_plane *plane; + int i; + ++ if (!vc4->load_tracker_available) ++ return 0; ++ + priv_state = drm_atomic_get_private_obj_state(state, + &vc4->load_tracker); + if (IS_ERR(priv_state)) +@@ -520,10 +523,14 @@ int vc4_kms_load(struct drm_device *dev) + struct vc4_load_tracker_state *load_state; + int ret; + +- /* Start with the load tracker enabled. Can be disabled through the +- * debugfs load_tracker file. +- */ +- vc4->load_tracker_enabled = true; ++ if (!of_device_is_compatible(dev->dev->of_node, "brcm,bcm2711-vc5")) { ++ vc4->load_tracker_available = true; ++ ++ /* Start with the load tracker enabled. Can be ++ * disabled through the debugfs load_tracker file. ++ */ ++ vc4->load_tracker_enabled = true; ++ } + + sema_init(&vc4->async_modeset, 1); + +@@ -560,14 +567,17 @@ int vc4_kms_load(struct drm_device *dev) + drm_atomic_private_obj_init(dev, &vc4->ctm_manager, &ctm_state->base, + &vc4_ctm_state_funcs); + +- load_state = kzalloc(sizeof(*load_state), GFP_KERNEL); +- if (!load_state) { +- drm_atomic_private_obj_fini(&vc4->ctm_manager); +- return -ENOMEM; +- } ++ if (vc4->load_tracker_available) { ++ load_state = kzalloc(sizeof(*load_state), GFP_KERNEL); ++ if (!load_state) { ++ drm_atomic_private_obj_fini(&vc4->ctm_manager); ++ return -ENOMEM; ++ } + +- drm_atomic_private_obj_init(dev, &vc4->load_tracker, &load_state->base, +- &vc4_load_tracker_state_funcs); ++ drm_atomic_private_obj_init(dev, &vc4->load_tracker, ++ &load_state->base, ++ &vc4_load_tracker_state_funcs); ++ } + + drm_mode_config_reset(dev); + +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -492,6 +492,11 @@ static void vc4_plane_calc_load(struct d + struct vc4_plane_state *vc4_state; + struct drm_crtc_state *crtc_state; + unsigned int vscale_factor; ++ struct vc4_dev *vc4; ++ ++ vc4 = to_vc4_dev(state->plane->dev); ++ if (!vc4->load_tracker_available) ++ return; + + vc4_state = to_vc4_plane_state(state); + crtc_state = drm_atomic_get_existing_crtc_state(state->state, diff --git a/target/linux/bcm27xx/patches-5.4/950-0547-drm-vc4-drv-Add-support-for-the-BCM2711-HVS5.patch b/target/linux/bcm27xx/patches-5.4/950-0547-drm-vc4-drv-Add-support-for-the-BCM2711-HVS5.patch new file mode 100644 index 0000000000..0f6259e347 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0547-drm-vc4-drv-Add-support-for-the-BCM2711-HVS5.patch @@ -0,0 +1,497 @@ +From 354d70a82947041b3d7b87f69641a6741febfc95 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Thu, 8 Aug 2019 17:51:07 +0100 +Subject: [PATCH] drm/vc4: drv: Add support for the BCM2711 HVS5 + +The HVS found in the BCM2711 is slightly different from the previous +generations. + +Most notably, the display list layout changes a bit, the LBM doesn't have +the same size and the formats ordering for some formats is swapped. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 24 +++- + drivers/gpu/drm/vc4/vc4_drv.h | 4 + + drivers/gpu/drm/vc4/vc4_hvs.c | 17 ++- + drivers/gpu/drm/vc4/vc4_plane.c | 194 +++++++++++++++++++++++--------- + drivers/gpu/drm/vc4/vc4_regs.h | 67 +++++++++++ + 5 files changed, 247 insertions(+), 59 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -550,6 +550,7 @@ static void vc4_crtc_atomic_enable(struc + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); + struct drm_display_mode *mode = &crtc->state->adjusted_mode; ++ u32 dispctrl; + + require_hvs_enabled(dev); + +@@ -564,11 +565,24 @@ static void vc4_crtc_atomic_enable(struc + * When feeding the transposer, we should operate in oneshot + * mode. + */ +- HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel), +- VC4_SET_FIELD(mode->hdisplay, SCALER_DISPCTRLX_WIDTH) | +- VC4_SET_FIELD(mode->vdisplay, SCALER_DISPCTRLX_HEIGHT) | +- SCALER_DISPCTRLX_ENABLE | +- (vc4_state->feed_txp ? SCALER_DISPCTRLX_ONESHOT : 0)); ++ dispctrl = SCALER_DISPCTRLX_ENABLE; ++ ++ if (!vc4->hvs->hvs5) ++ dispctrl |= VC4_SET_FIELD(mode->hdisplay, ++ SCALER_DISPCTRLX_WIDTH) | ++ VC4_SET_FIELD(mode->vdisplay, ++ SCALER_DISPCTRLX_HEIGHT) | ++ (vc4_state->feed_txp ? ++ SCALER_DISPCTRLX_ONESHOT : 0); ++ else ++ dispctrl |= VC4_SET_FIELD(mode->hdisplay, ++ SCALER5_DISPCTRLX_WIDTH) | ++ VC4_SET_FIELD(mode->vdisplay, ++ SCALER5_DISPCTRLX_HEIGHT) | ++ (vc4_state->feed_txp ? ++ SCALER5_DISPCTRLX_ONESHOT : 0); ++ ++ HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel), dispctrl); + + /* When feeding the transposer block the pixelvalve is unneeded and + * should not be enabled. +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -336,7 +336,11 @@ struct vc4_hvs { + spinlock_t mm_lock; + + struct drm_mm_node mitchell_netravali_filter; ++ + struct debugfs_regset32 regset; ++ ++ /* HVS version 5 flag, therefore requires updated dlist structures */ ++ bool hvs5; + }; + + struct vc4_plane { +--- a/drivers/gpu/drm/vc4/vc4_hvs.c ++++ b/drivers/gpu/drm/vc4/vc4_hvs.c +@@ -223,6 +223,7 @@ static int vc4_hvs_bind(struct device *d + struct vc4_hvs *hvs = NULL; + int ret; + u32 dispctrl; ++ unsigned int hvs_version; + + hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL); + if (!hvs) +@@ -238,7 +239,14 @@ static int vc4_hvs_bind(struct device *d + hvs->regset.regs = hvs_regs; + hvs->regset.nregs = ARRAY_SIZE(hvs_regs); + +- hvs->dlist = hvs->regs + SCALER_DLIST_START; ++ hvs_version = readl(hvs->regs + SCALER_DISPLSTAT) >> 24; ++ if (hvs_version >= 0x40) ++ hvs->hvs5 = true; ++ ++ if (!hvs->hvs5) ++ hvs->dlist = hvs->regs + SCALER_DLIST_START; ++ else ++ hvs->dlist = hvs->regs + SCALER5_DLIST_START; + + spin_lock_init(&hvs->mm_lock); + +@@ -256,7 +264,12 @@ static int vc4_hvs_bind(struct device *d + * between planes when they don't overlap on the screen, but + * for now we just allocate globally. + */ +- drm_mm_init(&hvs->lbm_mm, 0, 96 * 1024); ++ if (!hvs->hvs5) ++ /* 96kB */ ++ drm_mm_init(&hvs->lbm_mm, 0, 96 * 1024); ++ else ++ /* 70k words */ ++ drm_mm_init(&hvs->lbm_mm, 0, 70 * 2 * 1024); + + /* Upload filter kernels. We only have the one for now, so we + * keep it around for the lifetime of the driver. +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -32,45 +32,60 @@ static const struct hvs_format { + u32 drm; /* DRM_FORMAT_* */ + u32 hvs; /* HVS_FORMAT_* */ + u32 pixel_order; ++ u32 pixel_order_hvs5; + } hvs_formats[] = { + { +- .drm = DRM_FORMAT_XRGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888, ++ .drm = DRM_FORMAT_XRGB8888, ++ .hvs = HVS_PIXEL_FORMAT_RGBA8888, + .pixel_order = HVS_PIXEL_ORDER_ABGR, ++ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB, + }, + { +- .drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888, ++ .drm = DRM_FORMAT_ARGB8888, ++ .hvs = HVS_PIXEL_FORMAT_RGBA8888, + .pixel_order = HVS_PIXEL_ORDER_ABGR, ++ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB, + }, + { +- .drm = DRM_FORMAT_ABGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888, ++ .drm = DRM_FORMAT_ABGR8888, ++ .hvs = HVS_PIXEL_FORMAT_RGBA8888, + .pixel_order = HVS_PIXEL_ORDER_ARGB, ++ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR, + }, + { +- .drm = DRM_FORMAT_XBGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888, ++ .drm = DRM_FORMAT_XBGR8888, ++ .hvs = HVS_PIXEL_FORMAT_RGBA8888, + .pixel_order = HVS_PIXEL_ORDER_ARGB, ++ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR, + }, + { +- .drm = DRM_FORMAT_RGB565, .hvs = HVS_PIXEL_FORMAT_RGB565, ++ .drm = DRM_FORMAT_RGB565, ++ .hvs = HVS_PIXEL_FORMAT_RGB565, + .pixel_order = HVS_PIXEL_ORDER_XRGB, + }, + { +- .drm = DRM_FORMAT_BGR565, .hvs = HVS_PIXEL_FORMAT_RGB565, ++ .drm = DRM_FORMAT_BGR565, ++ .hvs = HVS_PIXEL_FORMAT_RGB565, + .pixel_order = HVS_PIXEL_ORDER_XBGR, + }, + { +- .drm = DRM_FORMAT_ARGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551, ++ .drm = DRM_FORMAT_ARGB1555, ++ .hvs = HVS_PIXEL_FORMAT_RGBA5551, + .pixel_order = HVS_PIXEL_ORDER_ABGR, + }, + { +- .drm = DRM_FORMAT_XRGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551, ++ .drm = DRM_FORMAT_XRGB1555, ++ .hvs = HVS_PIXEL_FORMAT_RGBA5551, + .pixel_order = HVS_PIXEL_ORDER_ABGR, + }, + { +- .drm = DRM_FORMAT_RGB888, .hvs = HVS_PIXEL_FORMAT_RGB888, ++ .drm = DRM_FORMAT_RGB888, ++ .hvs = HVS_PIXEL_FORMAT_RGB888, + .pixel_order = HVS_PIXEL_ORDER_XRGB, + }, + { +- .drm = DRM_FORMAT_BGR888, .hvs = HVS_PIXEL_FORMAT_RGB888, ++ .drm = DRM_FORMAT_BGR888, ++ .hvs = HVS_PIXEL_FORMAT_RGB888, + .pixel_order = HVS_PIXEL_ORDER_XBGR, + }, + { +@@ -828,35 +843,6 @@ static int vc4_plane_mode_set(struct drm + return -EINVAL; + } + +- /* Control word */ +- vc4_dlist_write(vc4_state, +- SCALER_CTL0_VALID | +- (rotation & DRM_MODE_REFLECT_X ? SCALER_CTL0_HFLIP : 0) | +- (rotation & DRM_MODE_REFLECT_Y ? SCALER_CTL0_VFLIP : 0) | +- VC4_SET_FIELD(SCALER_CTL0_RGBA_EXPAND_ROUND, SCALER_CTL0_RGBA_EXPAND) | +- (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) | +- (hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) | +- VC4_SET_FIELD(tiling, SCALER_CTL0_TILING) | +- (vc4_state->is_unity ? SCALER_CTL0_UNITY : 0) | +- VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) | +- VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1)); +- +- /* Position Word 0: Image Positions and Alpha Value */ +- vc4_state->pos0_offset = vc4_state->dlist_count; +- vc4_dlist_write(vc4_state, +- VC4_SET_FIELD(state->alpha >> 8, SCALER_POS0_FIXED_ALPHA) | +- VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) | +- VC4_SET_FIELD(vc4_state->crtc_y, SCALER_POS0_START_Y)); +- +- /* Position Word 1: Scaled Image Dimensions. */ +- if (!vc4_state->is_unity) { +- vc4_dlist_write(vc4_state, +- VC4_SET_FIELD(vc4_state->crtc_w, +- SCALER_POS1_SCL_WIDTH) | +- VC4_SET_FIELD(vc4_state->crtc_h, +- SCALER_POS1_SCL_HEIGHT)); +- } +- + /* Don't waste cycles mixing with plane alpha if the set alpha + * is opaque or there is no per-pixel alpha information. + * In any case we use the alpha property value as the fixed alpha. +@@ -864,20 +850,120 @@ static int vc4_plane_mode_set(struct drm + mix_plane_alpha = state->alpha != DRM_BLEND_ALPHA_OPAQUE && + fb->format->has_alpha; + +- /* Position Word 2: Source Image Size, Alpha */ +- vc4_state->pos2_offset = vc4_state->dlist_count; +- vc4_dlist_write(vc4_state, +- VC4_SET_FIELD(fb->format->has_alpha ? +- SCALER_POS2_ALPHA_MODE_PIPELINE : +- SCALER_POS2_ALPHA_MODE_FIXED, +- SCALER_POS2_ALPHA_MODE) | +- (mix_plane_alpha ? SCALER_POS2_ALPHA_MIX : 0) | +- (fb->format->has_alpha ? SCALER_POS2_ALPHA_PREMULT : 0) | +- VC4_SET_FIELD(vc4_state->src_w[0], SCALER_POS2_WIDTH) | +- VC4_SET_FIELD(vc4_state->src_h[0], SCALER_POS2_HEIGHT)); ++ if (!vc4->hvs->hvs5) { ++ /* Control word */ ++ vc4_dlist_write(vc4_state, ++ SCALER_CTL0_VALID | ++ (rotation & DRM_MODE_REFLECT_X ? SCALER_CTL0_HFLIP : 0) | ++ (rotation & DRM_MODE_REFLECT_Y ? SCALER_CTL0_VFLIP : 0) | ++ VC4_SET_FIELD(SCALER_CTL0_RGBA_EXPAND_ROUND, SCALER_CTL0_RGBA_EXPAND) | ++ (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) | ++ (hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) | ++ VC4_SET_FIELD(tiling, SCALER_CTL0_TILING) | ++ (vc4_state->is_unity ? SCALER_CTL0_UNITY : 0) | ++ VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) | ++ VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1)); ++ ++ /* Position Word 0: Image Positions and Alpha Value */ ++ vc4_state->pos0_offset = vc4_state->dlist_count; ++ vc4_dlist_write(vc4_state, ++ VC4_SET_FIELD(state->alpha >> 8, SCALER_POS0_FIXED_ALPHA) | ++ VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) | ++ VC4_SET_FIELD(vc4_state->crtc_y, SCALER_POS0_START_Y)); ++ ++ /* Position Word 1: Scaled Image Dimensions. */ ++ if (!vc4_state->is_unity) { ++ vc4_dlist_write(vc4_state, ++ VC4_SET_FIELD(vc4_state->crtc_w, ++ SCALER_POS1_SCL_WIDTH) | ++ VC4_SET_FIELD(vc4_state->crtc_h, ++ SCALER_POS1_SCL_HEIGHT)); ++ } ++ ++ /* Position Word 2: Source Image Size, Alpha */ ++ vc4_state->pos2_offset = vc4_state->dlist_count; ++ vc4_dlist_write(vc4_state, ++ VC4_SET_FIELD(fb->format->has_alpha ? ++ SCALER_POS2_ALPHA_MODE_PIPELINE : ++ SCALER_POS2_ALPHA_MODE_FIXED, ++ SCALER_POS2_ALPHA_MODE) | ++ (mix_plane_alpha ? SCALER_POS2_ALPHA_MIX : 0) | ++ (fb->format->has_alpha ? ++ SCALER_POS2_ALPHA_PREMULT : 0) | ++ VC4_SET_FIELD(vc4_state->src_w[0], ++ SCALER_POS2_WIDTH) | ++ VC4_SET_FIELD(vc4_state->src_h[0], ++ SCALER_POS2_HEIGHT)); + +- /* Position Word 3: Context. Written by the HVS. */ +- vc4_dlist_write(vc4_state, 0xc0c0c0c0); ++ /* Position Word 3: Context. Written by the HVS. */ ++ vc4_dlist_write(vc4_state, 0xc0c0c0c0); ++ ++ } else { ++ u32 hvs_pixel_order = format->pixel_order; ++ ++ if (format->pixel_order_hvs5) ++ hvs_pixel_order = format->pixel_order_hvs5; ++ ++ /* Control word */ ++ vc4_dlist_write(vc4_state, ++ SCALER_CTL0_VALID | ++ (hvs_pixel_order << SCALER_CTL0_ORDER_SHIFT) | ++ (hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) | ++ VC4_SET_FIELD(tiling, SCALER_CTL0_TILING) | ++ (vc4_state->is_unity ? ++ SCALER5_CTL0_UNITY : 0) | ++ VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) | ++ VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1) | ++ SCALER5_CTL0_ALPHA_EXPAND | ++ SCALER5_CTL0_RGB_EXPAND); ++ ++ /* Position Word 0: Image Positions and Alpha Value */ ++ vc4_state->pos0_offset = vc4_state->dlist_count; ++ vc4_dlist_write(vc4_state, ++ (rotation & DRM_MODE_REFLECT_Y ? ++ SCALER5_POS0_VFLIP : 0) | ++ VC4_SET_FIELD(vc4_state->crtc_x, ++ SCALER_POS0_START_X) | ++ (rotation & DRM_MODE_REFLECT_X ? ++ SCALER5_POS0_HFLIP : 0) | ++ VC4_SET_FIELD(vc4_state->crtc_y, ++ SCALER5_POS0_START_Y) ++ ); ++ ++ /* Control Word 2 */ ++ vc4_dlist_write(vc4_state, ++ VC4_SET_FIELD(state->alpha >> 4, ++ SCALER5_CTL2_ALPHA) | ++ fb->format->has_alpha ? ++ SCALER5_CTL2_ALPHA_PREMULT : 0 | ++ (mix_plane_alpha ? ++ SCALER5_CTL2_ALPHA_MIX : 0) | ++ VC4_SET_FIELD(fb->format->has_alpha ? ++ SCALER5_CTL2_ALPHA_MODE_PIPELINE : ++ SCALER5_CTL2_ALPHA_MODE_FIXED, ++ SCALER5_CTL2_ALPHA_MODE) ++ ); ++ ++ /* Position Word 1: Scaled Image Dimensions. */ ++ if (!vc4_state->is_unity) { ++ vc4_dlist_write(vc4_state, ++ VC4_SET_FIELD(vc4_state->crtc_w, ++ SCALER_POS1_SCL_WIDTH) | ++ VC4_SET_FIELD(vc4_state->crtc_h, ++ SCALER_POS1_SCL_HEIGHT)); ++ } ++ ++ /* Position Word 2: Source Image Size */ ++ vc4_state->pos2_offset = vc4_state->dlist_count; ++ vc4_dlist_write(vc4_state, ++ VC4_SET_FIELD(vc4_state->src_w[0], ++ SCALER5_POS2_WIDTH) | ++ VC4_SET_FIELD(vc4_state->src_h[0], ++ SCALER5_POS2_HEIGHT)); ++ ++ /* Position Word 3: Context. Written by the HVS. */ ++ vc4_dlist_write(vc4_state, 0xc0c0c0c0); ++ } + + + /* Pointer Word 0/1/2: RGB / Y / Cb / Cr Pointers +@@ -1266,6 +1352,10 @@ static bool vc4_format_mod_supported(str + default: + return false; + } ++ case DRM_FORMAT_RGBX1010102: ++ case DRM_FORMAT_BGRX1010102: ++ case DRM_FORMAT_RGBA1010102: ++ case DRM_FORMAT_BGRA1010102: + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YVU422: + case DRM_FORMAT_YUV420: +--- a/drivers/gpu/drm/vc4/vc4_regs.h ++++ b/drivers/gpu/drm/vc4/vc4_regs.h +@@ -328,6 +328,20 @@ + # define SCALER_DISPCTRLX_HEIGHT_MASK VC4_MASK(11, 0) + # define SCALER_DISPCTRLX_HEIGHT_SHIFT 0 + ++# define SCALER5_DISPCTRLX_WIDTH_MASK VC4_MASK(28, 16) ++# define SCALER5_DISPCTRLX_WIDTH_SHIFT 16 ++/* Generates a single frame when VSTART is seen and stops at the last ++ * pixel read from the FIFO. ++ */ ++# define SCALER5_DISPCTRLX_ONESHOT BIT(15) ++/* Processes a single context in the dlist and then task switch, ++ * instead of an entire line. ++ */ ++# define SCALER5_DISPCTRLX_ONECTX_MASK VC4_MASK(14, 13) ++# define SCALER5_DISPCTRLX_ONECTX_SHIFT 13 ++# define SCALER5_DISPCTRLX_HEIGHT_MASK VC4_MASK(12, 0) ++# define SCALER5_DISPCTRLX_HEIGHT_SHIFT 0 ++ + #define SCALER_DISPBKGND0 0x00000044 + # define SCALER_DISPBKGND_AUTOHS BIT(31) + # define SCALER_DISPBKGND_INTERLACE BIT(30) +@@ -461,6 +475,8 @@ + #define SCALER_DLIST_START 0x00002000 + #define SCALER_DLIST_SIZE 0x00004000 + ++#define SCALER5_DLIST_START 0x00004000 ++ + #define VC4_HDMI_CORE_REV 0x000 + + #define VC4_HDMI_SW_RESET_CONTROL 0x004 +@@ -826,6 +842,8 @@ enum hvs_pixel_format { + HVS_PIXEL_FORMAT_PALETTE = 13, + HVS_PIXEL_FORMAT_YUV444_RGB = 14, + HVS_PIXEL_FORMAT_AYUV444_RGB = 15, ++ HVS_PIXEL_FORMAT_RGBA1010102 = 16, ++ HVS_PIXEL_FORMAT_YCBCR_10BIT = 17, + }; + + /* Note: the LSB is the rightmost character shown. Only valid for +@@ -880,6 +898,10 @@ enum hvs_pixel_format { + #define SCALER_CTL0_RGBA_EXPAND_MSB 2 + #define SCALER_CTL0_RGBA_EXPAND_ROUND 3 + ++#define SCALER5_CTL0_ALPHA_EXPAND BIT(12) ++ ++#define SCALER5_CTL0_RGB_EXPAND BIT(11) ++ + #define SCALER_CTL0_SCL1_MASK VC4_MASK(10, 8) + #define SCALER_CTL0_SCL1_SHIFT 8 + +@@ -897,10 +919,13 @@ enum hvs_pixel_format { + + /* Set to indicate no scaling. */ + #define SCALER_CTL0_UNITY BIT(4) ++#define SCALER5_CTL0_UNITY BIT(15) + + #define SCALER_CTL0_PIXEL_FORMAT_MASK VC4_MASK(3, 0) + #define SCALER_CTL0_PIXEL_FORMAT_SHIFT 0 + ++#define SCALER5_CTL0_PIXEL_FORMAT_MASK VC4_MASK(4, 0) ++ + #define SCALER_POS0_FIXED_ALPHA_MASK VC4_MASK(31, 24) + #define SCALER_POS0_FIXED_ALPHA_SHIFT 24 + +@@ -910,12 +935,48 @@ enum hvs_pixel_format { + #define SCALER_POS0_START_X_MASK VC4_MASK(11, 0) + #define SCALER_POS0_START_X_SHIFT 0 + ++#define SCALER5_POS0_START_Y_MASK VC4_MASK(27, 16) ++#define SCALER5_POS0_START_Y_SHIFT 16 ++ ++#define SCALER5_POS0_START_X_MASK VC4_MASK(13, 0) ++#define SCALER5_POS0_START_X_SHIFT 0 ++ ++#define SCALER5_POS0_VFLIP BIT(31) ++#define SCALER5_POS0_HFLIP BIT(15) ++ ++#define SCALER5_CTL2_ALPHA_MODE_MASK VC4_MASK(31, 30) ++#define SCALER5_CTL2_ALPHA_MODE_SHIFT 30 ++#define SCALER5_CTL2_ALPHA_MODE_PIPELINE 0 ++#define SCALER5_CTL2_ALPHA_MODE_FIXED 1 ++#define SCALER5_CTL2_ALPHA_MODE_FIXED_NONZERO 2 ++#define SCALER5_CTL2_ALPHA_MODE_FIXED_OVER_0x07 3 ++ ++#define SCALER5_CTL2_ALPHA_PREMULT BIT(29) ++ ++#define SCALER5_CTL2_ALPHA_MIX BIT(28) ++ ++#define SCALER5_CTL2_ALPHA_LOC BIT(25) ++ ++#define SCALER5_CTL2_MAP_SEL_MASK VC4_MASK(18, 17) ++#define SCALER5_CTL2_MAP_SEL_SHIFT 17 ++ ++#define SCALER5_CTL2_GAMMA BIT(16) ++ ++#define SCALER5_CTL2_ALPHA_MASK VC4_MASK(15, 4) ++#define SCALER5_CTL2_ALPHA_SHIFT 4 ++ + #define SCALER_POS1_SCL_HEIGHT_MASK VC4_MASK(27, 16) + #define SCALER_POS1_SCL_HEIGHT_SHIFT 16 + + #define SCALER_POS1_SCL_WIDTH_MASK VC4_MASK(11, 0) + #define SCALER_POS1_SCL_WIDTH_SHIFT 0 + ++#define SCALER5_POS1_SCL_HEIGHT_MASK VC4_MASK(28, 16) ++#define SCALER5_POS1_SCL_HEIGHT_SHIFT 16 ++ ++#define SCALER5_POS1_SCL_WIDTH_MASK VC4_MASK(12, 0) ++#define SCALER5_POS1_SCL_WIDTH_SHIFT 0 ++ + #define SCALER_POS2_ALPHA_MODE_MASK VC4_MASK(31, 30) + #define SCALER_POS2_ALPHA_MODE_SHIFT 30 + #define SCALER_POS2_ALPHA_MODE_PIPELINE 0 +@@ -931,6 +992,12 @@ enum hvs_pixel_format { + #define SCALER_POS2_WIDTH_MASK VC4_MASK(11, 0) + #define SCALER_POS2_WIDTH_SHIFT 0 + ++#define SCALER5_POS2_HEIGHT_MASK VC4_MASK(28, 16) ++#define SCALER5_POS2_HEIGHT_SHIFT 16 ++ ++#define SCALER5_POS2_WIDTH_MASK VC4_MASK(12, 0) ++#define SCALER5_POS2_WIDTH_SHIFT 0 ++ + /* Color Space Conversion words. Some values are S2.8 signed + * integers, except that the 2 integer bits map as {0x0: 0, 0x1: 1, + * 0x2: 2, 0x3: -1} diff --git a/target/linux/bcm27xx/patches-5.4/950-0548-drm-vc4-plane-Improve-LBM-usage.patch b/target/linux/bcm27xx/patches-5.4/950-0548-drm-vc4-plane-Improve-LBM-usage.patch new file mode 100644 index 0000000000..df7a98fc54 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0548-drm-vc4-plane-Improve-LBM-usage.patch @@ -0,0 +1,98 @@ +From 81072e19a85bfa3f80c23acdff6156522d945efa Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Tue, 11 Feb 2020 16:55:02 +0000 +Subject: [PATCH] drm/vc4: plane: Improve LBM usage + +LBM allocations were always taking the worst case sizing of +max(src_width, dst_width) * 16. This is significantly over +the required sizing, and stops us rendering multiple 4k images +to the screen. + +Add some of the additional constraints to more accurately +describe the LBM requirements. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_plane.c | 31 ++++++++++++++++++++----------- + 1 file changed, 20 insertions(+), 11 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -142,9 +142,10 @@ static const struct hvs_format *vc4_get_ + return NULL; + } + +-static enum vc4_scaling_mode vc4_get_scaling_mode(u32 src, u32 dst) ++static enum vc4_scaling_mode vc4_get_scaling_mode(u32 src, u32 dst, ++ bool chroma_vrep) + { +- if (dst == src) ++ if (dst == src && !chroma_vrep) + return VC4_SCALING_NONE; + if (3 * dst >= 2 * src) + return VC4_SCALING_PPF; +@@ -369,9 +370,11 @@ static int vc4_plane_setup_clipping_and_ + return ret; + + vc4_state->x_scaling[0] = vc4_get_scaling_mode(vc4_state->src_w[0], +- vc4_state->crtc_w); ++ vc4_state->crtc_w, ++ false); + vc4_state->y_scaling[0] = vc4_get_scaling_mode(vc4_state->src_h[0], +- vc4_state->crtc_h); ++ vc4_state->crtc_h, ++ false); + + vc4_state->is_unity = (vc4_state->x_scaling[0] == VC4_SCALING_NONE && + vc4_state->y_scaling[0] == VC4_SCALING_NONE); +@@ -384,10 +387,12 @@ static int vc4_plane_setup_clipping_and_ + + vc4_state->x_scaling[1] = + vc4_get_scaling_mode(vc4_state->src_w[1], +- vc4_state->crtc_w); ++ vc4_state->crtc_w, ++ v_subsample == 2); + vc4_state->y_scaling[1] = + vc4_get_scaling_mode(vc4_state->src_h[1], +- vc4_state->crtc_h); ++ vc4_state->crtc_h, ++ v_subsample == 2); + + /* YUV conversion requires that horizontal scaling be enabled + * on the UV plane even if vc4_get_scaling_mode() returned +@@ -437,10 +442,7 @@ static void vc4_write_ppf(struct vc4_pla + static u32 vc4_lbm_size(struct drm_plane_state *state) + { + struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); +- /* This is the worst case number. One of the two sizes will +- * be used depending on the scaling configuration. +- */ +- u32 pix_per_line = max(vc4_state->src_w[0], (u32)vc4_state->crtc_w); ++ u32 pix_per_line; + u32 lbm; + + /* LBM is not needed when there's no vertical scaling. */ +@@ -448,6 +450,11 @@ static u32 vc4_lbm_size(struct drm_plane + vc4_state->y_scaling[1] == VC4_SCALING_NONE) + return 0; + ++ if (vc4_state->x_scaling[0] == VC4_SCALING_TPZ) ++ pix_per_line = vc4_state->crtc_w; ++ else ++ pix_per_line = vc4_state->src_w[0]; ++ + if (!vc4_state->is_yuv) { + if (vc4_state->y_scaling[0] == VC4_SCALING_TPZ) + lbm = pix_per_line * 8; +@@ -583,7 +590,9 @@ static int vc4_plane_allocate_lbm(struct + spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags); + ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm, + &vc4_state->lbm, +- lbm_size, 32, 0, 0); ++ lbm_size, ++ vc4->hvs->hvs5 ? 64 : 32, ++ 0, 0); + spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags); + + if (ret) diff --git a/target/linux/bcm27xx/patches-5.4/950-0549-drm-vc4-plane-Move-planes-creation-to-its-own-functi.patch b/target/linux/bcm27xx/patches-5.4/950-0549-drm-vc4-plane-Move-planes-creation-to-its-own-functi.patch new file mode 100644 index 0000000000..7c3c470849 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0549-drm-vc4-plane-Move-planes-creation-to-its-own-functi.patch @@ -0,0 +1,125 @@ +From ac2c812856c3a496354b9f19d0a43458e108844d Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 6 Feb 2020 14:32:57 +0100 +Subject: [PATCH] drm/vc4: plane: Move planes creation to its own + function + +The planes so far were created as part of the CRTC binding code with +each planes created associated only to one CRTC. However, the hardware +in the vc4 doesn't really have such constraint and can be used with any +CRTC. + +In order to rework this, let's first move the overlay and cursor planes +creation to a function of its own. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 33 ++++------------------------ + drivers/gpu/drm/vc4/vc4_drv.h | 2 ++ + drivers/gpu/drm/vc4/vc4_plane.c | 38 +++++++++++++++++++++++++++++++++ + 3 files changed, 44 insertions(+), 29 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -1142,7 +1142,7 @@ static int vc4_crtc_bind(struct device * + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_crtc *vc4_crtc; + struct drm_crtc *crtc; +- struct drm_plane *primary_plane, *cursor_plane, *destroy_plane, *temp; ++ struct drm_plane *primary_plane, *destroy_plane, *temp; + const struct of_device_id *match; + int ret, i; + +@@ -1190,34 +1190,9 @@ static int vc4_crtc_bind(struct device * + */ + drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size); + +- /* Set up some arbitrary number of planes. We're not limited +- * by a set number of physical registers, just the space in +- * the HVS (16k) and how small an plane can be (28 bytes). +- * However, each plane we set up takes up some memory, and +- * increases the cost of looping over planes, which atomic +- * modesetting does quite a bit. As a result, we pick a +- * modest number of planes to expose, that should hopefully +- * still cover any sane usecase. +- */ +- for (i = 0; i < 8; i++) { +- struct drm_plane *plane = +- vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY); +- +- if (IS_ERR(plane)) +- continue; +- +- plane->possible_crtcs = drm_crtc_mask(crtc); +- } +- +- /* Set up the legacy cursor after overlay initialization, +- * since we overlay planes on the CRTC in the order they were +- * initialized. +- */ +- cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR); +- if (!IS_ERR(cursor_plane)) { +- cursor_plane->possible_crtcs = drm_crtc_mask(crtc); +- crtc->cursor = cursor_plane; +- } ++ ret = vc4_plane_create_additional_planes(drm, crtc); ++ if (ret) ++ goto err_destroy_planes; + + vc4_crtc_get_cob_allocation(vc4_crtc); + +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -855,6 +855,8 @@ int vc4_kms_load(struct drm_device *dev) + /* vc4_plane.c */ + struct drm_plane *vc4_plane_init(struct drm_device *dev, + enum drm_plane_type type); ++int vc4_plane_create_additional_planes(struct drm_device *dev, ++ struct drm_crtc *crtc); + u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); + u32 vc4_plane_dlist_size(const struct drm_plane_state *state); + void vc4_plane_async_set_fb(struct drm_plane *plane, +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -1437,3 +1437,41 @@ struct drm_plane *vc4_plane_init(struct + + return plane; + } ++ ++int vc4_plane_create_additional_planes(struct drm_device *drm, ++ struct drm_crtc *crtc) ++{ ++ struct drm_plane *cursor_plane; ++ unsigned int i; ++ ++ /* Set up some arbitrary number of planes. We're not limited ++ * by a set number of physical registers, just the space in ++ * the HVS (16k) and how small an plane can be (28 bytes). ++ * However, each plane we set up takes up some memory, and ++ * increases the cost of looping over planes, which atomic ++ * modesetting does quite a bit. As a result, we pick a ++ * modest number of planes to expose, that should hopefully ++ * still cover any sane usecase. ++ */ ++ for (i = 0; i < 8; i++) { ++ struct drm_plane *plane = ++ vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY); ++ ++ if (IS_ERR(plane)) ++ continue; ++ ++ plane->possible_crtcs = drm_crtc_mask(crtc); ++ } ++ ++ /* Set up the legacy cursor after overlay initialization, ++ * since we overlay planes on the CRTC in the order they were ++ * initialized. ++ */ ++ cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR); ++ if (!IS_ERR(cursor_plane)) { ++ cursor_plane->possible_crtcs = drm_crtc_mask(crtc); ++ crtc->cursor = cursor_plane; ++ } ++ ++ return 0; ++} diff --git a/target/linux/bcm27xx/patches-5.4/950-0550-drm-vc4-plane-Move-additional-planes-creation-to-dri.patch b/target/linux/bcm27xx/patches-5.4/950-0550-drm-vc4-plane-Move-additional-planes-creation-to-dri.patch new file mode 100644 index 0000000000..b2e9f15647 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0550-drm-vc4-plane-Move-additional-planes-creation-to-dri.patch @@ -0,0 +1,75 @@ +From 5331cbb3d9cfb172ed134f08a35740e0a52d1107 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 6 Feb 2020 14:41:41 +0100 +Subject: [PATCH] drm/vc4: plane: Move additional planes creation to + driver + +So far the plane creation was done when each CRTC was bound, and those +planes were only tied to the CRTC that was registering them. + +This causes two main issues: + - The planes in the vc4 hardware are actually not tied to any CRTC, but + can be used with every combination + + - More importantly, so far, we allocate 10 planes per CRTC, with 3 CRTCs. + However, the next generation of hardware will have 5 CRTCs, putting us + well above the maximum of 32 planes currently allowed by DRM. + +This patch is the first one in a series of patches that will take down both +of these issues so that we can support the next generation of hardware +while keeping a good amount of planes. + +We start by changing the way the planes are registered to first registering +the primary planes for each CRTC in the CRTC bind function as we used to, +but moving the overlay and cursor creation to the main driver bind +function, after all the CRTCs have been bound. + +This will slightly change the ID order of the planes, since the primary +planes of all CRTCs will be first, and then a pattern of 8 overlays, 1 +cursor plane for each CRTC. + +This shouldn't cause any trouble since the ordering between the planes is +preserved though. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 4 ---- + drivers/gpu/drm/vc4/vc4_drv.c | 7 +++++++ + 2 files changed, 7 insertions(+), 4 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -1190,10 +1190,6 @@ static int vc4_crtc_bind(struct device * + */ + drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size); + +- ret = vc4_plane_create_additional_planes(drm, crtc); +- if (ret) +- goto err_destroy_planes; +- + vc4_crtc_get_cob_allocation(vc4_crtc); + + CRTC_WRITE(PV_INTEN, 0); +--- a/drivers/gpu/drm/vc4/vc4_drv.c ++++ b/drivers/gpu/drm/vc4/vc4_drv.c +@@ -253,6 +253,7 @@ static int vc4_drm_bind(struct device *d + { + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm; ++ struct drm_crtc *crtc; + struct vc4_dev *vc4; + struct device_node *node; + int ret = 0; +@@ -291,6 +292,12 @@ static int vc4_drm_bind(struct device *d + if (ret) + goto gem_destroy; + ++ drm_for_each_crtc(crtc, drm) { ++ ret = vc4_plane_create_additional_planes(drm, crtc); ++ if (ret) ++ continue; ++ } ++ + drm_fb_helper_remove_conflicting_framebuffers(NULL, "vc4drmfb", false); + + ret = vc4_kms_load(drm); diff --git a/target/linux/bcm27xx/patches-5.4/950-0551-drm-vc4-plane-Register-all-the-planes-at-once.patch b/target/linux/bcm27xx/patches-5.4/950-0551-drm-vc4-plane-Register-all-the-planes-at-once.patch new file mode 100644 index 0000000000..e917410b86 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0551-drm-vc4-plane-Register-all-the-planes-at-once.patch @@ -0,0 +1,128 @@ +From bb2b068209d73b320cac7222a3b8ecef9b0dcc9a Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 6 Feb 2020 14:46:14 +0100 +Subject: [PATCH] drm/vc4: plane: Register all the planes at once + +Instead of creating planes for each CRTC, we eventually want to create all +the planes for each CRTCs. + +In order to make that more convenient, let's iterate on the CRTCs in the +plane creation function instead of its caller. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_drv.c | 9 ++---- + drivers/gpu/drm/vc4/vc4_drv.h | 3 +- + drivers/gpu/drm/vc4/vc4_plane.c | 54 +++++++++++++++++---------------- + 3 files changed, 32 insertions(+), 34 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_drv.c ++++ b/drivers/gpu/drm/vc4/vc4_drv.c +@@ -253,7 +253,6 @@ static int vc4_drm_bind(struct device *d + { + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm; +- struct drm_crtc *crtc; + struct vc4_dev *vc4; + struct device_node *node; + int ret = 0; +@@ -292,11 +291,9 @@ static int vc4_drm_bind(struct device *d + if (ret) + goto gem_destroy; + +- drm_for_each_crtc(crtc, drm) { +- ret = vc4_plane_create_additional_planes(drm, crtc); +- if (ret) +- continue; +- } ++ ret = vc4_plane_create_additional_planes(drm); ++ if (ret) ++ goto unbind_all; + + drm_fb_helper_remove_conflicting_framebuffers(NULL, "vc4drmfb", false); + +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -855,8 +855,7 @@ int vc4_kms_load(struct drm_device *dev) + /* vc4_plane.c */ + struct drm_plane *vc4_plane_init(struct drm_device *dev, + enum drm_plane_type type); +-int vc4_plane_create_additional_planes(struct drm_device *dev, +- struct drm_crtc *crtc); ++int vc4_plane_create_additional_planes(struct drm_device *dev); + u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); + u32 vc4_plane_dlist_size(const struct drm_plane_state *state); + void vc4_plane_async_set_fb(struct drm_plane *plane, +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -1438,39 +1438,41 @@ struct drm_plane *vc4_plane_init(struct + return plane; + } + +-int vc4_plane_create_additional_planes(struct drm_device *drm, +- struct drm_crtc *crtc) ++int vc4_plane_create_additional_planes(struct drm_device *drm) + { + struct drm_plane *cursor_plane; ++ struct drm_crtc *crtc; + unsigned int i; + +- /* Set up some arbitrary number of planes. We're not limited +- * by a set number of physical registers, just the space in +- * the HVS (16k) and how small an plane can be (28 bytes). +- * However, each plane we set up takes up some memory, and +- * increases the cost of looping over planes, which atomic +- * modesetting does quite a bit. As a result, we pick a +- * modest number of planes to expose, that should hopefully +- * still cover any sane usecase. +- */ +- for (i = 0; i < 8; i++) { +- struct drm_plane *plane = +- vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY); +- +- if (IS_ERR(plane)) +- continue; +- +- plane->possible_crtcs = drm_crtc_mask(crtc); +- } +- +- /* Set up the legacy cursor after overlay initialization, +- * since we overlay planes on the CRTC in the order they were +- * initialized. +- */ +- cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR); +- if (!IS_ERR(cursor_plane)) { +- cursor_plane->possible_crtcs = drm_crtc_mask(crtc); +- crtc->cursor = cursor_plane; ++ drm_for_each_crtc(crtc, drm) { ++ /* Set up some arbitrary number of planes. We're not limited ++ * by a set number of physical registers, just the space in ++ * the HVS (16k) and how small an plane can be (28 bytes). ++ * However, each plane we set up takes up some memory, and ++ * increases the cost of looping over planes, which atomic ++ * modesetting does quite a bit. As a result, we pick a ++ * modest number of planes to expose, that should hopefully ++ * still cover any sane usecase. ++ */ ++ for (i = 0; i < 8; i++) { ++ struct drm_plane *plane = ++ vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY); ++ ++ if (IS_ERR(plane)) ++ continue; ++ ++ plane->possible_crtcs = drm_crtc_mask(crtc); ++ } ++ ++ /* Set up the legacy cursor after overlay initialization, ++ * since we overlay planes on the CRTC in the order they were ++ * initialized. ++ */ ++ cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR); ++ if (!IS_ERR(cursor_plane)) { ++ cursor_plane->possible_crtcs = drm_crtc_mask(crtc); ++ crtc->cursor = cursor_plane; ++ } + } + + return 0; diff --git a/target/linux/bcm27xx/patches-5.4/950-0552-drm-vc4-plane-Create-overlays-for-any-CRTC.patch b/target/linux/bcm27xx/patches-5.4/950-0552-drm-vc4-plane-Create-overlays-for-any-CRTC.patch new file mode 100644 index 0000000000..54d04e59e1 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0552-drm-vc4-plane-Create-overlays-for-any-CRTC.patch @@ -0,0 +1,70 @@ +From b65167e0bcce67f2e7b7e813dba536f1cca3ef9f Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 6 Feb 2020 14:50:06 +0100 +Subject: [PATCH] drm/vc4: plane: Create overlays for any CRTC + +Now that we have everything in place, we can now register all the overlay +planes that can be assigned to all the CRTCs. + +This has two side effects: + + - The number of overlay planes is reduced from 24 to 8. This is temporary + and will be increased again in the next patch. + + - The ID of the various planes is changed again, and we will now have all + the primary planes, then all the overlay planes and finally the cursor + planes. This shouldn't cause any issue since the ordering between + primary, overlay and cursor planes is preserved. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_plane.c | 35 +++++++++++++++++---------------- + 1 file changed, 18 insertions(+), 17 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -1444,26 +1444,27 @@ int vc4_plane_create_additional_planes(s + struct drm_crtc *crtc; + unsigned int i; + +- drm_for_each_crtc(crtc, drm) { +- /* Set up some arbitrary number of planes. We're not limited +- * by a set number of physical registers, just the space in +- * the HVS (16k) and how small an plane can be (28 bytes). +- * However, each plane we set up takes up some memory, and +- * increases the cost of looping over planes, which atomic +- * modesetting does quite a bit. As a result, we pick a +- * modest number of planes to expose, that should hopefully +- * still cover any sane usecase. +- */ +- for (i = 0; i < 8; i++) { +- struct drm_plane *plane = +- vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY); ++ /* Set up some arbitrary number of planes. We're not limited ++ * by a set number of physical registers, just the space in ++ * the HVS (16k) and how small an plane can be (28 bytes). ++ * However, each plane we set up takes up some memory, and ++ * increases the cost of looping over planes, which atomic ++ * modesetting does quite a bit. As a result, we pick a ++ * modest number of planes to expose, that should hopefully ++ * still cover any sane usecase. ++ */ ++ for (i = 0; i < 8; i++) { ++ struct drm_plane *plane = ++ vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY); + +- if (IS_ERR(plane)) +- continue; ++ if (IS_ERR(plane)) ++ continue; + +- plane->possible_crtcs = drm_crtc_mask(crtc); +- } ++ plane->possible_crtcs = ++ GENMASK(drm->mode_config.num_crtc - 1, 0); ++ } + ++ drm_for_each_crtc(crtc, drm) { + /* Set up the legacy cursor after overlay initialization, + * since we overlay planes on the CRTC in the order they were + * initialized. diff --git a/target/linux/bcm27xx/patches-5.4/950-0553-drm-vc4-plane-Create-more-planes.patch b/target/linux/bcm27xx/patches-5.4/950-0553-drm-vc4-plane-Create-more-planes.patch new file mode 100644 index 0000000000..08383e3708 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0553-drm-vc4-plane-Create-more-planes.patch @@ -0,0 +1,32 @@ +From b79a33509a3aa863cdf54d24e1a4a0cc2c6fe84c Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 6 Feb 2020 14:52:42 +0100 +Subject: [PATCH] drm/vc4: plane: Create more planes + +Let's now create more planes that can be affected to all the CRTCs. + +vc4 has 3 CRTCs, 1 primary and 1 cursor each, and was having 24 (8 +planes per CRTC) overlays. + +However, vc5 has 5 CRTCs, so keeping the same logic would put us at 50 +planes which is well above the 32 planes limit imposed by DRM. + +Using 16 seems like a good tradeoff between staying under 32 and yet +providing enough planes. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_plane.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -1453,7 +1453,7 @@ int vc4_plane_create_additional_planes(s + * modest number of planes to expose, that should hopefully + * still cover any sane usecase. + */ +- for (i = 0; i < 8; i++) { ++ for (i = 0; i < 16; i++) { + struct drm_plane *plane = + vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY); + diff --git a/target/linux/bcm27xx/patches-5.4/950-0554-drm-vc4-crtc-Rename-SoC-data-structures.patch b/target/linux/bcm27xx/patches-5.4/950-0554-drm-vc4-crtc-Rename-SoC-data-structures.patch new file mode 100644 index 0000000000..8b8eeea737 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0554-drm-vc4-crtc-Rename-SoC-data-structures.patch @@ -0,0 +1,56 @@ +From f071c70678b875d2e5411ead123015381647e9f9 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 26 Dec 2019 11:45:04 +0100 +Subject: [PATCH] drm/vc4: crtc: Rename SoC data structures + +Since we're going to introduce pixelvalve data structures for other SoCs +than the BCM2835, let's rename the structures defined in the code to +make it obvious which SoC we're targetting. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -1056,7 +1056,7 @@ static const struct drm_crtc_helper_func + .atomic_disable = vc4_crtc_atomic_disable, + }; + +-static const struct vc4_crtc_data pv0_data = { ++static const struct vc4_crtc_data bcm2835_pv0_data = { + .hvs_channel = 0, + .debugfs_name = "crtc0_regs", + .encoder_types = { +@@ -1065,7 +1065,7 @@ static const struct vc4_crtc_data pv0_da + }, + }; + +-static const struct vc4_crtc_data pv1_data = { ++static const struct vc4_crtc_data bcm2835_pv1_data = { + .hvs_channel = 2, + .debugfs_name = "crtc1_regs", + .encoder_types = { +@@ -1074,7 +1074,7 @@ static const struct vc4_crtc_data pv1_da + }, + }; + +-static const struct vc4_crtc_data pv2_data = { ++static const struct vc4_crtc_data bcm2835_pv2_data = { + .hvs_channel = 1, + .debugfs_name = "crtc2_regs", + .encoder_types = { +@@ -1084,9 +1084,9 @@ static const struct vc4_crtc_data pv2_da + }; + + static const struct of_device_id vc4_crtc_dt_match[] = { +- { .compatible = "brcm,bcm2835-pixelvalve0", .data = &pv0_data }, +- { .compatible = "brcm,bcm2835-pixelvalve1", .data = &pv1_data }, +- { .compatible = "brcm,bcm2835-pixelvalve2", .data = &pv2_data }, ++ { .compatible = "brcm,bcm2835-pixelvalve0", .data = &bcm2835_pv0_data }, ++ { .compatible = "brcm,bcm2835-pixelvalve1", .data = &bcm2835_pv1_data }, ++ { .compatible = "brcm,bcm2835-pixelvalve2", .data = &bcm2835_pv2_data }, + {} + }; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0555-drm-vc4-crtc-Move-crtc-state-to-common-header.patch b/target/linux/bcm27xx/patches-5.4/950-0555-drm-vc4-crtc-Move-crtc-state-to-common-header.patch new file mode 100644 index 0000000000..6108f86787 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0555-drm-vc4-crtc-Move-crtc-state-to-common-header.patch @@ -0,0 +1,74 @@ +From 05293c3b61cdeb0004722cc86e03123183557de1 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 26 Dec 2019 15:45:04 +0100 +Subject: [PATCH] drm/vc4: crtc: Move crtc state to common header + +We'll need to access the crtc_state from outside of vc4_crtc.c, so let's +move it to vc4_drv.h + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 21 --------------------- + drivers/gpu/drm/vc4/vc4_drv.h | 21 +++++++++++++++++++++ + 2 files changed, 21 insertions(+), 21 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -44,27 +44,6 @@ + #include "vc4_drv.h" + #include "vc4_regs.h" + +-struct vc4_crtc_state { +- struct drm_crtc_state base; +- /* Dlist area for this CRTC configuration. */ +- struct drm_mm_node mm; +- bool feed_txp; +- bool txp_armed; +- +- struct { +- unsigned int left; +- unsigned int right; +- unsigned int top; +- unsigned int bottom; +- } margins; +-}; +- +-static inline struct vc4_crtc_state * +-to_vc4_crtc_state(struct drm_crtc_state *crtc_state) +-{ +- return (struct vc4_crtc_state *)crtc_state; +-} +- + #define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset)) + #define CRTC_READ(offset) readl(vc4_crtc->regs + (offset)) + +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -488,6 +488,27 @@ to_vc4_crtc(struct drm_crtc *crtc) + return (struct vc4_crtc *)crtc; + } + ++struct vc4_crtc_state { ++ struct drm_crtc_state base; ++ /* Dlist area for this CRTC configuration. */ ++ struct drm_mm_node mm; ++ bool feed_txp; ++ bool txp_armed; ++ ++ struct { ++ unsigned int left; ++ unsigned int right; ++ unsigned int top; ++ unsigned int bottom; ++ } margins; ++}; ++ ++static inline struct vc4_crtc_state * ++to_vc4_crtc_state(struct drm_crtc_state *crtc_state) ++{ ++ return (struct vc4_crtc_state *)crtc_state; ++} ++ + #define V3D_READ(offset) readl(vc4->v3d->regs + offset) + #define V3D_WRITE(offset, val) writel(val, vc4->v3d->regs + offset) + #define HVS_READ(offset) readl(vc4->hvs->regs + offset) diff --git a/target/linux/bcm27xx/patches-5.4/950-0556-drm-vc4-crtc-Deal-with-different-number-of-pixel-per.patch b/target/linux/bcm27xx/patches-5.4/950-0556-drm-vc4-crtc-Deal-with-different-number-of-pixel-per.patch new file mode 100644 index 0000000000..5949e616f9 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0556-drm-vc4-crtc-Deal-with-different-number-of-pixel-per.patch @@ -0,0 +1,86 @@ +From b8714036be64c86a274ea49ba0066af0a81c6b98 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 26 Dec 2019 11:36:50 +0100 +Subject: [PATCH] drm/vc4: crtc: Deal with different number of pixel + per clock + +Some of the HDMI pixelvalves in vc5 output two pixels per clock cycle. +Let's put the number of pixel output per clock cycle in the CRTC data and +update the various calculations to reflect that. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 17 ++++++++++------- + drivers/gpu/drm/vc4/vc4_drv.h | 3 +++ + 2 files changed, 13 insertions(+), 7 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -281,6 +281,7 @@ static void vc4_crtc_config_pv(struct dr + bool is_dsi = (vc4_encoder->type == VC4_ENCODER_TYPE_DSI0 || + vc4_encoder->type == VC4_ENCODER_TYPE_DSI1); + u32 format = is_dsi ? PV_CONTROL_FORMAT_DSIV_24 : PV_CONTROL_FORMAT_24; ++ u8 ppc = vc4_crtc->data->pixels_per_clock; + + /* Reset the PV fifo. */ + CRTC_WRITE(PV_CONTROL, 0); +@@ -288,17 +289,16 @@ static void vc4_crtc_config_pv(struct dr + CRTC_WRITE(PV_CONTROL, 0); + + CRTC_WRITE(PV_HORZA, +- VC4_SET_FIELD((mode->htotal - +- mode->hsync_end) * pixel_rep, ++ VC4_SET_FIELD((mode->htotal - mode->hsync_end) * pixel_rep / ppc, + PV_HORZA_HBP) | +- VC4_SET_FIELD((mode->hsync_end - +- mode->hsync_start) * pixel_rep, ++ VC4_SET_FIELD((mode->hsync_end - mode->hsync_start) * pixel_rep / ppc, + PV_HORZA_HSYNC)); ++ + CRTC_WRITE(PV_HORZB, +- VC4_SET_FIELD((mode->hsync_start - +- mode->hdisplay) * pixel_rep, ++ VC4_SET_FIELD((mode->hsync_start - mode->hdisplay) * pixel_rep / ppc, + PV_HORZB_HFP) | +- VC4_SET_FIELD(mode->hdisplay * pixel_rep, PV_HORZB_HACTIVE)); ++ VC4_SET_FIELD(mode->hdisplay * pixel_rep / ppc, ++ PV_HORZB_HACTIVE)); + + CRTC_WRITE(PV_VERTA, + VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end, +@@ -1038,6 +1038,7 @@ static const struct drm_crtc_helper_func + static const struct vc4_crtc_data bcm2835_pv0_data = { + .hvs_channel = 0, + .debugfs_name = "crtc0_regs", ++ .pixels_per_clock = 1, + .encoder_types = { + [PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI0, + [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_DPI, +@@ -1047,6 +1048,7 @@ static const struct vc4_crtc_data bcm283 + static const struct vc4_crtc_data bcm2835_pv1_data = { + .hvs_channel = 2, + .debugfs_name = "crtc1_regs", ++ .pixels_per_clock = 1, + .encoder_types = { + [PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI1, + [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_SMI, +@@ -1056,6 +1058,7 @@ static const struct vc4_crtc_data bcm283 + static const struct vc4_crtc_data bcm2835_pv2_data = { + .hvs_channel = 1, + .debugfs_name = "crtc2_regs", ++ .pixels_per_clock = 1, + .encoder_types = { + [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_HDMI, + [PV_CONTROL_CLK_SELECT_VEC] = VC4_ENCODER_TYPE_VEC, +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -455,6 +455,9 @@ struct vc4_crtc_data { + /* Which channel of the HVS this pixelvalve sources from. */ + int hvs_channel; + ++ /* Number of pixels output per clock period */ ++ u8 pixels_per_clock; ++ + enum vc4_encoder_type encoder_types[4]; + const char *debugfs_name; + }; diff --git a/target/linux/bcm27xx/patches-5.4/950-0557-drm-vc4-crtc-Use-a-shared-interrupt.patch b/target/linux/bcm27xx/patches-5.4/950-0557-drm-vc4-crtc-Use-a-shared-interrupt.patch new file mode 100644 index 0000000000..b517b5414b --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0557-drm-vc4-crtc-Use-a-shared-interrupt.patch @@ -0,0 +1,26 @@ +From 5451dc04ff87dcf514c422f180ea5e23b7b60151 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 9 Jan 2020 18:40:49 +0100 +Subject: [PATCH] drm/vc4: crtc: Use a shared interrupt + +Some pixelvalves in vc5 use the same interrupt line so let's register our +interrupt handler as a shared one. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -1177,7 +1177,9 @@ static int vc4_crtc_bind(struct device * + CRTC_WRITE(PV_INTEN, 0); + CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START); + ret = devm_request_irq(dev, platform_get_irq(pdev, 0), +- vc4_crtc_irq_handler, 0, "vc4 crtc", vc4_crtc); ++ vc4_crtc_irq_handler, ++ IRQF_SHARED, ++ "vc4 crtc", vc4_crtc); + if (ret) + goto err_destroy_planes; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0558-drm-vc4-crtc-Turn-static-const-variable-into-a-defin.patch b/target/linux/bcm27xx/patches-5.4/950-0558-drm-vc4-crtc-Turn-static-const-variable-into-a-defin.patch new file mode 100644 index 0000000000..3deefc361f --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0558-drm-vc4-crtc-Turn-static-const-variable-into-a-defin.patch @@ -0,0 +1,50 @@ +From c017882242d671cf81256301a3e9a6fc9eefdc13 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Mon, 13 Jan 2020 13:39:32 +0100 +Subject: [PATCH] drm/vc4: crtc: Turn static const variable into a + define + +The hvs_latency_pix variable doesn't need to be a variable and can just be +defined. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -44,6 +44,8 @@ + #include "vc4_drv.h" + #include "vc4_regs.h" + ++#define HVS_FIFO_LATENCY_PIX 6 ++ + #define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset)) + #define CRTC_READ(offset) readl(vc4_crtc->regs + (offset)) + +@@ -227,21 +229,21 @@ vc4_crtc_update_gamma_lut(struct drm_crt + vc4_crtc_lut_load(crtc); + } + ++ + static u32 vc4_get_fifo_full_level(u32 format) + { + static const u32 fifo_len_bytes = 64; +- static const u32 hvs_latency_pix = 6; + + switch (format) { + case PV_CONTROL_FORMAT_DSIV_16: + case PV_CONTROL_FORMAT_DSIC_16: +- return fifo_len_bytes - 2 * hvs_latency_pix; ++ return fifo_len_bytes - 2 * HVS_FIFO_LATENCY_PIX; + case PV_CONTROL_FORMAT_DSIV_18: + return fifo_len_bytes - 14; + case PV_CONTROL_FORMAT_24: + case PV_CONTROL_FORMAT_DSIV_24: + default: +- return fifo_len_bytes - 3 * hvs_latency_pix; ++ return fifo_len_bytes - 3 * HVS_FIFO_LATENCY_PIX; + } + } + diff --git a/target/linux/bcm27xx/patches-5.4/950-0559-drm-vc4-crtc-Move-the-cob-allocation-outside-of-bind.patch b/target/linux/bcm27xx/patches-5.4/950-0559-drm-vc4-crtc-Move-the-cob-allocation-outside-of-bind.patch new file mode 100644 index 0000000000..83c49132a3 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0559-drm-vc4-crtc-Move-the-cob-allocation-outside-of-bind.patch @@ -0,0 +1,110 @@ +From e93fc4ed811c7dcc6b0c93716f760431fc645ba2 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 26 Dec 2019 15:48:09 +0100 +Subject: [PATCH] drm/vc4: crtc: Move the cob allocation outside of + bind + +The COB allocation depends on the HVS channel used for a given +pixelvalve. + +While the channel allocation was entirely static in vc4, vc5 changes +that and at bind time, a pixelvalve can be assigned to multiple +HVS channels. + +Let's prepare that rework by allocating the COB when it's actually +needed. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 39 +++++++++++++++++----------------- + drivers/gpu/drm/vc4/vc4_drv.h | 2 -- + 2 files changed, 20 insertions(+), 21 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -65,6 +65,23 @@ static const struct debugfs_reg32 crtc_r + VC4_REG32(PV_HACT_ACT), + }; + ++static unsigned int ++vc4_crtc_get_cob_allocation(struct vc4_crtc *vc4_crtc, unsigned int channel) ++{ ++ struct drm_device *drm = vc4_crtc->base.dev; ++ struct vc4_dev *vc4 = to_vc4_dev(drm); ++ ++ u32 dispbase = HVS_READ(SCALER_DISPBASEX(channel)); ++ /* Top/base are supposed to be 4-pixel aligned, but the ++ * Raspberry Pi firmware fills the low bits (which are ++ * presumably ignored). ++ */ ++ u32 top = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_TOP) & ~3; ++ u32 base = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_BASE) & ~3; ++ ++ return top - base + 4; ++} ++ + bool vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id, + bool in_vblank_irq, int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime, +@@ -73,6 +90,7 @@ bool vc4_crtc_get_scanoutpos(struct drm_ + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); ++ unsigned int cob_size; + u32 val; + int fifo_lines; + int vblank_lines; +@@ -108,8 +126,9 @@ bool vc4_crtc_get_scanoutpos(struct drm_ + *hpos += mode->crtc_htotal / 2; + } + ++ cob_size = vc4_crtc_get_cob_allocation(vc4_crtc, vc4_crtc->channel); + /* This is the offset we need for translating hvs -> pv scanout pos. */ +- fifo_lines = vc4_crtc->cob_size / mode->crtc_hdisplay; ++ fifo_lines = cob_size / mode->crtc_hdisplay; + + if (fifo_lines > 0) + ret = true; +@@ -1104,22 +1123,6 @@ static void vc4_set_crtc_possible_masks( + } + } + +-static void +-vc4_crtc_get_cob_allocation(struct vc4_crtc *vc4_crtc) +-{ +- struct drm_device *drm = vc4_crtc->base.dev; +- struct vc4_dev *vc4 = to_vc4_dev(drm); +- u32 dispbase = HVS_READ(SCALER_DISPBASEX(vc4_crtc->channel)); +- /* Top/base are supposed to be 4-pixel aligned, but the +- * Raspberry Pi firmware fills the low bits (which are +- * presumably ignored). +- */ +- u32 top = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_TOP) & ~3; +- u32 base = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_BASE) & ~3; +- +- vc4_crtc->cob_size = top - base + 4; +-} +- + static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) + { + struct platform_device *pdev = to_platform_device(dev); +@@ -1174,8 +1177,6 @@ static int vc4_crtc_bind(struct device * + */ + drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size); + +- vc4_crtc_get_cob_allocation(vc4_crtc); +- + CRTC_WRITE(PV_INTEN, 0); + CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START); + ret = devm_request_irq(dev, platform_get_irq(pdev, 0), +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -477,8 +477,6 @@ struct vc4_crtc { + u8 lut_r[256]; + u8 lut_g[256]; + u8 lut_b[256]; +- /* Size in pixels of the COB memory allocated to this CRTC. */ +- u32 cob_size; + + struct drm_pending_vblank_event *event; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0560-drm-vc4-crtc-Rename-HVS-channel-to-output.patch b/target/linux/bcm27xx/patches-5.4/950-0560-drm-vc4-crtc-Rename-HVS-channel-to-output.patch new file mode 100644 index 0000000000..cf854fceb8 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0560-drm-vc4-crtc-Rename-HVS-channel-to-output.patch @@ -0,0 +1,80 @@ +From a106e57a643c957af9a71eb2ec3a62df69a1f371 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 26 Dec 2019 13:49:17 +0100 +Subject: [PATCH] drm/vc4: crtc: Rename HVS channel to output + +In vc5, the HVS has 6 outputs and 3 FIFOs (or channels), with +pixelvalves each being assigned to a given output, but each output can +then be muxed to feed from multiple FIFOs. + +Since vc4 had that entirely static, both were probably equivalent, but +since that changes, let's rename hvs_channel to hvs_output in the +vc4_crtc_data, since a pixelvalve is really connected to an output, and +not to a FIFO. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 10 +++++----- + drivers/gpu/drm/vc4/vc4_drv.h | 4 ++-- + 2 files changed, 7 insertions(+), 7 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -1057,7 +1057,7 @@ static const struct drm_crtc_helper_func + }; + + static const struct vc4_crtc_data bcm2835_pv0_data = { +- .hvs_channel = 0, ++ .hvs_output = 0, + .debugfs_name = "crtc0_regs", + .pixels_per_clock = 1, + .encoder_types = { +@@ -1067,7 +1067,7 @@ static const struct vc4_crtc_data bcm283 + }; + + static const struct vc4_crtc_data bcm2835_pv1_data = { +- .hvs_channel = 2, ++ .hvs_output = 2, + .debugfs_name = "crtc1_regs", + .pixels_per_clock = 1, + .encoder_types = { +@@ -1077,7 +1077,7 @@ static const struct vc4_crtc_data bcm283 + }; + + static const struct vc4_crtc_data bcm2835_pv2_data = { +- .hvs_channel = 1, ++ .hvs_output = 1, + .debugfs_name = "crtc2_regs", + .pixels_per_clock = 1, + .encoder_types = { +@@ -1106,7 +1106,7 @@ static void vc4_set_crtc_possible_masks( + int i; + + /* HVS FIFO2 can feed the TXP IP. */ +- if (crtc_data->hvs_channel == 2 && ++ if (crtc_data->hvs_output == 2 && + encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL) { + encoder->possible_crtcs |= drm_crtc_mask(crtc); + continue; +@@ -1168,7 +1168,7 @@ static int vc4_crtc_bind(struct device * + drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL, + &vc4_crtc_funcs, NULL); + drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs); +- vc4_crtc->channel = vc4_crtc->data->hvs_channel; ++ vc4_crtc->channel = vc4_crtc->data->hvs_output; + drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r)); + drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size); + +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -452,8 +452,8 @@ to_vc4_encoder(struct drm_encoder *encod + } + + struct vc4_crtc_data { +- /* Which channel of the HVS this pixelvalve sources from. */ +- int hvs_channel; ++ /* Which output of the HVS this pixelvalve sources from. */ ++ int hvs_output; + + /* Number of pixels output per clock period */ + u8 pixels_per_clock; diff --git a/target/linux/bcm27xx/patches-5.4/950-0561-drm-vc4-crtc-Use-local-chan-variable.patch b/target/linux/bcm27xx/patches-5.4/950-0561-drm-vc4-crtc-Use-local-chan-variable.patch new file mode 100644 index 0000000000..6f77bace01 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0561-drm-vc4-crtc-Use-local-chan-variable.patch @@ -0,0 +1,24 @@ +From 888e5149bdb810e67996828bb26955a57a482d4c Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Tue, 14 Jan 2020 13:37:27 +0100 +Subject: [PATCH] drm/vc4: crtc: Use local chan variable + +The vc4_crtc_handle_page_flip already has a local variable holding the +value of vc4_crtc->channel, so let's use it instead. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -816,7 +816,7 @@ static void vc4_crtc_handle_page_flip(st + * underruns. This can be seen when reconfiguring the CRTC. + */ + if (vc4->hvs) +- vc4_hvs_unmask_underrun(dev, vc4_crtc->channel); ++ vc4_hvs_unmask_underrun(dev, chan); + } + spin_unlock_irqrestore(&dev->event_lock, flags); + } diff --git a/target/linux/bcm27xx/patches-5.4/950-0562-drm-vc4-crtc-Enable-and-disable-the-PV-in-atomic_ena.patch b/target/linux/bcm27xx/patches-5.4/950-0562-drm-vc4-crtc-Enable-and-disable-the-PV-in-atomic_ena.patch new file mode 100644 index 0000000000..c5f06f20ea --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0562-drm-vc4-crtc-Enable-and-disable-the-PV-in-atomic_ena.patch @@ -0,0 +1,55 @@ +From 7bbbfef1c98e832cbd55e66ac2d7f13ec0a2b11e Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Fri, 21 Feb 2020 14:34:31 +0100 +Subject: [PATCH] drm/vc4: crtc: Enable and disable the PV in + atomic_enable / disable + +The VIDEN bit in the pixelvalve currently being used to enable or disable +the pixelvalve seems to not be enough in some situations, which whill end +up with the pixelvalve stalling. + +In such a case, even re-enabling VIDEN doesn't bring it back and we need to +clear the FIFO. This can only be done if the pixelvalve is disabled though. + +In order to overcome this, we can configure the pixelvalve during +mode_set_no_fb, but only enable it in atomic_enable and flush the FIFO +there, and in atomic_disable disable the pixelvalve again. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -374,9 +374,7 @@ static void vc4_crtc_config_pv(struct dr + PV_CONTROL_TRIGGER_UNDERFLOW | + PV_CONTROL_WAIT_HSTART | + VC4_SET_FIELD(vc4_encoder->clock_select, +- PV_CONTROL_CLK_SELECT) | +- PV_CONTROL_FIFO_CLR | +- PV_CONTROL_EN); ++ PV_CONTROL_CLK_SELECT)); + } + + static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) +@@ -467,6 +465,8 @@ static void vc4_crtc_atomic_disable(stru + ret = wait_for(!(CRTC_READ(PV_V_CONTROL) & PV_VCONTROL_VIDEN), 1); + WARN_ONCE(ret, "Timeout waiting for !PV_VCONTROL_VIDEN\n"); + ++ CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) & ~PV_CONTROL_EN); ++ + if (HVS_READ(SCALER_DISPCTRLX(chan)) & + SCALER_DISPCTRLX_ENABLE) { + HVS_WRITE(SCALER_DISPCTRLX(chan), +@@ -554,6 +554,10 @@ static void vc4_crtc_atomic_enable(struc + + require_hvs_enabled(dev); + ++ /* Reset the PV fifo. */ ++ CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) | ++ PV_CONTROL_FIFO_CLR | PV_CONTROL_EN); ++ + /* Enable vblank irq handling before crtc is started otherwise + * drm_crtc_get_vblank() fails in vc4_crtc_update_dlist(). + */ diff --git a/target/linux/bcm27xx/patches-5.4/950-0563-drm-vc4-crtc-Assign-output-to-channel-automatically.patch b/target/linux/bcm27xx/patches-5.4/950-0563-drm-vc4-crtc-Assign-output-to-channel-automatically.patch new file mode 100644 index 0000000000..d470f3b7f0 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0563-drm-vc4-crtc-Assign-output-to-channel-automatically.patch @@ -0,0 +1,459 @@ +From 9efecb2ccd14a6d226ba2afa04f6e70b96026b3e Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 26 Dec 2019 17:53:18 +0100 +Subject: [PATCH] drm/vc4: crtc: Assign output to channel automatically + +The HVS found in the BCM2711 has 6 outputs and 3 FIFOs, with each output +being connected to a pixelvalve, and some muxing between the FIFOs and +outputs. + +Any output cannot feed from any FIFO though, and they all have a bunch of +constraints. + +In order to support this, let's store the possible FIFOs each output can be +assigned to in the vc4_crtc_data, and use that information at atomic_check +time to iterate over all the CRTCs enabled and assign them FIFOs. + +The channel assigned is then set in the vc4_crtc_state so that the rest of +the driver can use it. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 37 +++++---- + drivers/gpu/drm/vc4/vc4_drv.h | 7 +- + drivers/gpu/drm/vc4/vc4_kms.c | 146 +++++++++++++++++++++++++++++++-- + drivers/gpu/drm/vc4/vc4_regs.h | 10 +++ + 4 files changed, 175 insertions(+), 25 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -90,6 +90,7 @@ bool vc4_crtc_get_scanoutpos(struct drm_ + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); ++ struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state); + unsigned int cob_size; + u32 val; + int fifo_lines; +@@ -106,7 +107,7 @@ bool vc4_crtc_get_scanoutpos(struct drm_ + * Read vertical scanline which is currently composed for our + * pixelvalve by the HVS, and also the scaler status. + */ +- val = HVS_READ(SCALER_DISPSTATX(vc4_crtc->channel)); ++ val = HVS_READ(SCALER_DISPSTATX(vc4_crtc_state->assigned_channel)); + + /* Get optional system timestamp after query. */ + if (etime) +@@ -126,7 +127,7 @@ bool vc4_crtc_get_scanoutpos(struct drm_ + *hpos += mode->crtc_htotal / 2; + } + +- cob_size = vc4_crtc_get_cob_allocation(vc4_crtc, vc4_crtc->channel); ++ cob_size = vc4_crtc_get_cob_allocation(vc4_crtc, vc4_crtc_state->assigned_channel); + /* This is the offset we need for translating hvs -> pv scanout pos. */ + fifo_lines = cob_size / mode->crtc_hdisplay; + +@@ -213,6 +214,7 @@ vc4_crtc_lut_load(struct drm_crtc *crtc) + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); ++ struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state); + u32 i; + + /* The LUT memory is laid out with each HVS channel in order, +@@ -221,7 +223,7 @@ vc4_crtc_lut_load(struct drm_crtc *crtc) + */ + HVS_WRITE(SCALER_GAMADDR, + SCALER_GAMADDR_AUTOINC | +- (vc4_crtc->channel * 3 * crtc->gamma_size)); ++ (vc4_crtc_state->assigned_channel * 3 * crtc->gamma_size)); + + for (i = 0; i < crtc->gamma_size; i++) + HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_r[i]); +@@ -394,7 +396,7 @@ static void vc4_crtc_mode_set_nofb(struc + drm_print_regset32(&p, &vc4_crtc->regset); + } + +- if (vc4_crtc->channel == 2) { ++ if (vc4_crtc->data->hvs_output == 2) { + u32 dispctrl; + u32 dsp3_mux; + +@@ -421,7 +423,7 @@ static void vc4_crtc_mode_set_nofb(struc + if (!vc4_state->feed_txp) + vc4_crtc_config_pv(crtc); + +- HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), ++ HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel), + SCALER_DISPBKGND_AUTOHS | + SCALER_DISPBKGND_GAMMA | + (interlace ? SCALER_DISPBKGND_INTERLACE : 0)); +@@ -453,7 +455,8 @@ static void vc4_crtc_atomic_disable(stru + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); +- u32 chan = vc4_crtc->channel; ++ struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(old_state); ++ u32 chan = vc4_crtc_state->assigned_channel; + int ret; + require_hvs_enabled(dev); + +@@ -532,12 +535,12 @@ static void vc4_crtc_update_dlist(struct + crtc->state->event = NULL; + } + +- HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), ++ HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel), + vc4_state->mm.start); + + spin_unlock_irqrestore(&dev->event_lock, flags); + } else { +- HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), ++ HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel), + vc4_state->mm.start); + } + } +@@ -586,7 +589,7 @@ static void vc4_crtc_atomic_enable(struc + (vc4_state->feed_txp ? + SCALER5_DISPCTRLX_ONESHOT : 0); + +- HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel), dispctrl); ++ HVS_WRITE(SCALER_DISPCTRLX(vc4_state->assigned_channel), dispctrl); + + /* When feeding the transposer block the pixelvalve is unneeded and + * should not be enabled. +@@ -702,7 +705,6 @@ static void vc4_crtc_atomic_flush(struct + { + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); +- struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); + struct drm_plane *plane; + struct vc4_plane_state *vc4_plane_state; +@@ -744,8 +746,8 @@ static void vc4_crtc_atomic_flush(struct + /* This sets a black background color fill, as is the case + * with other DRM drivers. + */ +- HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), +- HVS_READ(SCALER_DISPBKGNDX(vc4_crtc->channel)) | ++ HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel), ++ HVS_READ(SCALER_DISPBKGNDX(vc4_state->assigned_channel)) | + SCALER_DISPBKGND_FILL); + + /* Only update DISPLIST if the CRTC was already running and is not +@@ -759,7 +761,7 @@ static void vc4_crtc_atomic_flush(struct + vc4_crtc_update_dlist(crtc); + + if (crtc->state->color_mgmt_changed) { +- u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_crtc->channel)); ++ u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_state->assigned_channel)); + + if (crtc->state->gamma_lut) { + vc4_crtc_update_gamma_lut(crtc); +@@ -771,7 +773,7 @@ static void vc4_crtc_atomic_flush(struct + */ + dispbkgndx &= ~SCALER_DISPBKGND_GAMMA; + } +- HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), dispbkgndx); ++ HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel), dispbkgndx); + } + + if (debug_dump_regs) { +@@ -802,7 +804,7 @@ static void vc4_crtc_handle_page_flip(st + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); +- u32 chan = vc4_crtc->channel; ++ u32 chan = vc4_state->assigned_channel; + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); +@@ -1002,6 +1004,7 @@ static struct drm_crtc_state *vc4_crtc_d + old_vc4_state = to_vc4_crtc_state(crtc->state); + vc4_state->feed_txp = old_vc4_state->feed_txp; + vc4_state->margins = old_vc4_state->margins; ++ vc4_state->assigned_channel = old_vc4_state->assigned_channel; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base); + return &vc4_state->base; +@@ -1061,6 +1064,7 @@ static const struct drm_crtc_helper_func + }; + + static const struct vc4_crtc_data bcm2835_pv0_data = { ++ .hvs_available_channels = BIT(0), + .hvs_output = 0, + .debugfs_name = "crtc0_regs", + .pixels_per_clock = 1, +@@ -1071,6 +1075,7 @@ static const struct vc4_crtc_data bcm283 + }; + + static const struct vc4_crtc_data bcm2835_pv1_data = { ++ .hvs_available_channels = BIT(2), + .hvs_output = 2, + .debugfs_name = "crtc1_regs", + .pixels_per_clock = 1, +@@ -1081,6 +1086,7 @@ static const struct vc4_crtc_data bcm283 + }; + + static const struct vc4_crtc_data bcm2835_pv2_data = { ++ .hvs_available_channels = BIT(1), + .hvs_output = 1, + .debugfs_name = "crtc2_regs", + .pixels_per_clock = 1, +@@ -1172,7 +1178,6 @@ static int vc4_crtc_bind(struct device * + drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL, + &vc4_crtc_funcs, NULL); + drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs); +- vc4_crtc->channel = vc4_crtc->data->hvs_output; + drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r)); + drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size); + +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -452,6 +452,9 @@ to_vc4_encoder(struct drm_encoder *encod + } + + struct vc4_crtc_data { ++ /* Which channels of the HVS can the output source from */ ++ unsigned int hvs_available_channels; ++ + /* Which output of the HVS this pixelvalve sources from. */ + int hvs_output; + +@@ -471,9 +474,6 @@ struct vc4_crtc { + /* Timestamp at start of vblank irq - unaffected by lock delays. */ + ktime_t t_vblank; + +- /* Which HVS channel we're using for our CRTC. */ +- int channel; +- + u8 lut_r[256]; + u8 lut_g[256]; + u8 lut_b[256]; +@@ -495,6 +495,7 @@ struct vc4_crtc_state { + struct drm_mm_node mm; + bool feed_txp; + bool txp_armed; ++ unsigned int assigned_channel; + + struct { + unsigned int left; +--- a/drivers/gpu/drm/vc4/vc4_kms.c ++++ b/drivers/gpu/drm/vc4/vc4_kms.c +@@ -11,6 +11,9 @@ + * crtc, HDMI encoder). + */ + ++#include <linux/bitfield.h> ++#include <linux/bitops.h> ++ + #include <drm/drm_atomic.h> + #include <drm/drm_atomic_helper.h> + #include <drm/drm_crtc.h> +@@ -148,6 +151,72 @@ vc4_ctm_commit(struct vc4_dev *vc4, stru + VC4_SET_FIELD(ctm_state->fifo, SCALER_OLEDOFFS_DISPFIFO)); + } + ++static void vc4_hvs_pv_muxing_commit(struct vc4_dev *vc4, ++ struct drm_atomic_state *state) ++{ ++ struct drm_crtc_state *crtc_state; ++ struct drm_crtc *crtc; ++ unsigned char dsp2_mux = 0; ++ unsigned char dsp3_mux = 3; ++ unsigned char dsp4_mux = 3; ++ unsigned char dsp5_mux = 3; ++ unsigned int i; ++ u32 reg; ++ ++ for_each_new_crtc_in_state(state, crtc, crtc_state, i) { ++ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state); ++ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); ++ ++ if (!crtc_state->active) ++ continue; ++ ++ switch (vc4_crtc->data->hvs_output) { ++ case 2: ++ dsp2_mux = (vc4_state->assigned_channel == 2) ? 1 : 0; ++ break; ++ ++ case 3: ++ dsp3_mux = vc4_state->assigned_channel; ++ break; ++ ++ case 4: ++ dsp4_mux = vc4_state->assigned_channel; ++ break; ++ ++ case 5: ++ dsp5_mux = vc4_state->assigned_channel; ++ break; ++ ++ default: ++ break; ++ } ++ } ++ ++ reg = HVS_READ(SCALER_DISPECTRL); ++ if (FIELD_GET(SCALER_DISPECTRL_DSP2_MUX_MASK, reg) != dsp2_mux) ++ HVS_WRITE(SCALER_DISPECTRL, ++ (reg & ~SCALER_DISPECTRL_DSP2_MUX_MASK) | ++ VC4_SET_FIELD(dsp2_mux, SCALER_DISPECTRL_DSP2_MUX)); ++ ++ reg = HVS_READ(SCALER_DISPCTRL); ++ if (FIELD_GET(SCALER_DISPCTRL_DSP3_MUX_MASK, reg) != dsp3_mux) ++ HVS_WRITE(SCALER_DISPCTRL, ++ (reg & ~SCALER_DISPCTRL_DSP3_MUX_MASK) | ++ VC4_SET_FIELD(dsp3_mux, SCALER_DISPCTRL_DSP3_MUX)); ++ ++ reg = HVS_READ(SCALER_DISPEOLN); ++ if (FIELD_GET(SCALER_DISPEOLN_DSP4_MUX_MASK, reg) != dsp4_mux) ++ HVS_WRITE(SCALER_DISPEOLN, ++ (reg & ~SCALER_DISPEOLN_DSP4_MUX_MASK) | ++ VC4_SET_FIELD(dsp4_mux, SCALER_DISPEOLN_DSP4_MUX)); ++ ++ reg = HVS_READ(SCALER_DISPDITHER); ++ if (FIELD_GET(SCALER_DISPDITHER_DSP5_MUX_MASK, reg) != dsp5_mux) ++ HVS_WRITE(SCALER_DISPDITHER, ++ (reg & ~SCALER_DISPDITHER_DSP5_MUX_MASK) | ++ VC4_SET_FIELD(dsp5_mux, SCALER_DISPDITHER_DSP5_MUX)); ++} ++ + static void + vc4_atomic_complete_commit(struct drm_atomic_state *state) + { +@@ -157,11 +226,15 @@ vc4_atomic_complete_commit(struct drm_at + int i; + + for (i = 0; vc4->hvs && i < dev->mode_config.num_crtc; i++) { +- if (!state->crtcs[i].ptr || !state->crtcs[i].commit) ++ struct __drm_crtcs_state *_state = &state->crtcs[i]; ++ struct vc4_crtc_state *vc4_crtc_state; ++ ++ if (!_state->ptr || !_state->commit) + continue; + +- vc4_crtc = to_vc4_crtc(state->crtcs[i].ptr); +- vc4_hvs_mask_underrun(dev, vc4_crtc->channel); ++ vc4_crtc = to_vc4_crtc(_state->ptr); ++ vc4_crtc_state = to_vc4_crtc_state(_state->state); ++ vc4_hvs_mask_underrun(dev, vc4_crtc_state->assigned_channel); + } + + drm_atomic_helper_wait_for_fences(dev, state, false); +@@ -170,8 +243,10 @@ vc4_atomic_complete_commit(struct drm_at + + drm_atomic_helper_commit_modeset_disables(dev, state); + +- if (!vc4->firmware_kms) ++ if (!vc4->firmware_kms) { + vc4_ctm_commit(vc4, state); ++ vc4_hvs_pv_muxing_commit(vc4, state); ++ } + + drm_atomic_helper_commit_planes(dev, state, 0); + +@@ -380,8 +455,11 @@ vc4_ctm_atomic_check(struct drm_device * + + /* CTM is being enabled or the matrix changed. */ + if (new_crtc_state->ctm) { ++ struct vc4_crtc_state *vc4_crtc_state = ++ to_vc4_crtc_state(new_crtc_state); ++ + /* fifo is 1-based since 0 disables CTM. */ +- int fifo = to_vc4_crtc(crtc)->channel + 1; ++ int fifo = vc4_crtc_state->assigned_channel + 1; + + /* Check userland isn't trying to turn on CTM for more + * than one CRTC at a time. +@@ -494,10 +572,66 @@ static const struct drm_private_state_fu + .atomic_destroy_state = vc4_load_tracker_destroy_state, + }; + ++#define NUM_OUTPUTS 6 ++#define NUM_CHANNELS 3 ++ + static int + vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) + { +- int ret; ++ unsigned long unassigned_channels = GENMASK(NUM_CHANNELS - 1, 0); ++ struct drm_crtc_state *crtc_state; ++ struct drm_crtc *crtc; ++ int i, ret; ++ ++ for_each_new_crtc_in_state(state, crtc, crtc_state, i) { ++ struct vc4_crtc_state *vc4_crtc_state = ++ to_vc4_crtc_state(crtc_state); ++ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); ++ bool is_assigned = false; ++ unsigned int channel; ++ ++ if (!crtc_state->active) ++ continue; ++ ++ /* ++ * The problem we have to solve here is that we have ++ * up to 7 encoders, connected to up to 6 CRTCs. ++ * ++ * Those CRTCs, depending on the instance, can be ++ * routed to 1, 2 or 3 HVS FIFOs, and we need to set ++ * the change the muxing between FIFOs and outputs in ++ * the HVS accordingly. ++ * ++ * It would be pretty hard to come up with an ++ * algorithm that would generically solve ++ * this. However, the current routing trees we support ++ * allow us to simplify a bit the problem. ++ * ++ * Indeed, with the current supported layouts, if we ++ * try to assign in the ascending crtc index order the ++ * FIFOs, we can't fall into the situation where an ++ * earlier CRTC that had multiple routes is assigned ++ * one that was the only option for a later CRTC. ++ * ++ * If the layout changes and doesn't give us that in ++ * the future, we will need to have something smarter, ++ * but it works so far. ++ */ ++ for_each_set_bit(channel, &unassigned_channels, ++ sizeof(unassigned_channels)) { ++ ++ if (!(BIT(channel) & vc4_crtc->data->hvs_available_channels)) ++ continue; ++ ++ vc4_crtc_state->assigned_channel = channel; ++ unassigned_channels &= ~BIT(channel); ++ is_assigned = true; ++ break; ++ } ++ ++ if (!is_assigned) ++ return -EINVAL; ++ } + + ret = vc4_ctm_atomic_check(dev, state); + if (ret < 0) +--- a/drivers/gpu/drm/vc4/vc4_regs.h ++++ b/drivers/gpu/drm/vc4/vc4_regs.h +@@ -287,9 +287,19 @@ + + #define SCALER_DISPID 0x00000008 + #define SCALER_DISPECTRL 0x0000000c ++# define SCALER_DISPECTRL_DSP2_MUX_SHIFT 31 ++# define SCALER_DISPECTRL_DSP2_MUX_MASK VC4_MASK(31, 31) ++ + #define SCALER_DISPPROF 0x00000010 ++ + #define SCALER_DISPDITHER 0x00000014 ++# define SCALER_DISPDITHER_DSP5_MUX_SHIFT 30 ++# define SCALER_DISPDITHER_DSP5_MUX_MASK VC4_MASK(31, 30) ++ + #define SCALER_DISPEOLN 0x00000018 ++# define SCALER_DISPEOLN_DSP4_MUX_SHIFT 30 ++# define SCALER_DISPEOLN_DSP4_MUX_MASK VC4_MASK(31, 30) ++ + #define SCALER_DISPLIST0 0x00000020 + #define SCALER_DISPLIST1 0x00000024 + #define SCALER_DISPLIST2 0x00000028 diff --git a/target/linux/bcm27xx/patches-5.4/950-0564-drm-vc4-crtc-Add-FIFO-depth-to-vc4_crtc_data.patch b/target/linux/bcm27xx/patches-5.4/950-0564-drm-vc4-crtc-Add-FIFO-depth-to-vc4_crtc_data.patch new file mode 100644 index 0000000000..e920a0be64 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0564-drm-vc4-crtc-Add-FIFO-depth-to-vc4_crtc_data.patch @@ -0,0 +1,86 @@ +From a294de7c4782f91fe724e4e5b05fd99798d50760 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Mon, 13 Jan 2020 13:39:20 +0100 +Subject: [PATCH] drm/vc4: crtc: Add FIFO depth to vc4_crtc_data + +Not all pixelvalve FIFOs in vc5 have the same depth, so we need to add that +to our vc4_crtc_data structure to be able to compute the fill level +properly later on. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 20 ++++++++++++++++---- + drivers/gpu/drm/vc4/vc4_drv.h | 3 +++ + 2 files changed, 19 insertions(+), 4 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -250,11 +250,20 @@ vc4_crtc_update_gamma_lut(struct drm_crt + vc4_crtc_lut_load(crtc); + } + +- +-static u32 vc4_get_fifo_full_level(u32 format) ++static u32 vc4_get_fifo_full_level(struct vc4_crtc *vc4_crtc, u32 format) + { +- static const u32 fifo_len_bytes = 64; ++ u32 fifo_len_bytes = vc4_crtc->data->fifo_depth; + ++ /* ++ * Pixels are pulled from the HVS if the number of bytes is ++ * lower than the FIFO full level. ++ * ++ * The latency of the pixel fetch mechanism is 6 pixels, so we ++ * need to convert those 6 pixels in bytes, depending on the ++ * format, and then substract that from the length of the FIFO ++ * to make sure we never end up in a situation where the FIFO ++ * is full. ++ */ + switch (format) { + case PV_CONTROL_FORMAT_DSIV_16: + case PV_CONTROL_FORMAT_DSIC_16: +@@ -369,7 +378,7 @@ static void vc4_crtc_config_pv(struct dr + + CRTC_WRITE(PV_CONTROL, + VC4_SET_FIELD(format, PV_CONTROL_FORMAT) | +- VC4_SET_FIELD(vc4_get_fifo_full_level(format), ++ VC4_SET_FIELD(vc4_get_fifo_full_level(vc4_crtc, format), + PV_CONTROL_FIFO_LEVEL) | + VC4_SET_FIELD(pixel_rep - 1, PV_CONTROL_PIXEL_REP) | + PV_CONTROL_CLR_AT_START | +@@ -1067,6 +1076,7 @@ static const struct vc4_crtc_data bcm283 + .hvs_available_channels = BIT(0), + .hvs_output = 0, + .debugfs_name = "crtc0_regs", ++ .fifo_depth = 64, + .pixels_per_clock = 1, + .encoder_types = { + [PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI0, +@@ -1078,6 +1088,7 @@ static const struct vc4_crtc_data bcm283 + .hvs_available_channels = BIT(2), + .hvs_output = 2, + .debugfs_name = "crtc1_regs", ++ .fifo_depth = 64, + .pixels_per_clock = 1, + .encoder_types = { + [PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI1, +@@ -1089,6 +1100,7 @@ static const struct vc4_crtc_data bcm283 + .hvs_available_channels = BIT(1), + .hvs_output = 1, + .debugfs_name = "crtc2_regs", ++ .fifo_depth = 64, + .pixels_per_clock = 1, + .encoder_types = { + [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_HDMI, +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -452,6 +452,9 @@ to_vc4_encoder(struct drm_encoder *encod + } + + struct vc4_crtc_data { ++ /* Depth of the PixelValve FIFO in bytes */ ++ unsigned int fifo_depth; ++ + /* Which channels of the HVS can the output source from */ + unsigned int hvs_available_channels; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0565-drm-vc4-crtc-Add-function-to-compute-FIFO-level-bits.patch b/target/linux/bcm27xx/patches-5.4/950-0565-drm-vc4-crtc-Add-function-to-compute-FIFO-level-bits.patch new file mode 100644 index 0000000000..77952c0eea --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0565-drm-vc4-crtc-Add-function-to-compute-FIFO-level-bits.patch @@ -0,0 +1,44 @@ +From 2c241c25b76d105f798881e1a3c6e3c09c3b27ff Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Mon, 13 Jan 2020 13:40:37 +0100 +Subject: [PATCH] drm/vc4: crtc: Add function to compute FIFO level + bits + +The longer FIFOs in vc5 pixelvalves means that the FIFO full level +doesn't fit in the original register field and that we also have a +secondary field. In order to prepare for this, let's move the registers +fill part to a helper function. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -277,6 +277,14 @@ static u32 vc4_get_fifo_full_level(struc + } + } + ++static u32 vc4_crtc_get_fifo_full_level_bits(struct vc4_crtc *vc4_crtc, ++ u32 format) ++{ ++ u32 level = vc4_get_fifo_full_level(vc4_crtc, format); ++ return VC4_SET_FIELD(level & 0x3f, ++ PV_CONTROL_FIFO_LEVEL); ++} ++ + /* + * Returns the encoder attached to the CRTC. + * +@@ -377,9 +385,8 @@ static void vc4_crtc_config_pv(struct dr + CRTC_WRITE(PV_HACT_ACT, mode->hdisplay * pixel_rep); + + CRTC_WRITE(PV_CONTROL, ++ vc4_crtc_get_fifo_full_level_bits(vc4_crtc, format) | + VC4_SET_FIELD(format, PV_CONTROL_FORMAT) | +- VC4_SET_FIELD(vc4_get_fifo_full_level(vc4_crtc, format), +- PV_CONTROL_FIFO_LEVEL) | + VC4_SET_FIELD(pixel_rep - 1, PV_CONTROL_PIXEL_REP) | + PV_CONTROL_CLR_AT_START | + PV_CONTROL_TRIGGER_UNDERFLOW | diff --git a/target/linux/bcm27xx/patches-5.4/950-0566-drm-vc4-crtc-Rename-HDMI-encoder-type-to-HDMI0.patch b/target/linux/bcm27xx/patches-5.4/950-0566-drm-vc4-crtc-Rename-HDMI-encoder-type-to-HDMI0.patch new file mode 100644 index 0000000000..17362dafe3 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0566-drm-vc4-crtc-Rename-HDMI-encoder-type-to-HDMI0.patch @@ -0,0 +1,49 @@ +From ad4c39a27e141626c93b7b97df621d258bfdcbbe Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 9 Jan 2020 18:35:13 +0100 +Subject: [PATCH] drm/vc4: crtc: Rename HDMI encoder type to HDMI0 + +The previous generations were only supporting a single HDMI controller, but +that's about to change, so put an index as well to differentiate between +the two controllers. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 2 +- + drivers/gpu/drm/vc4/vc4_drv.h | 2 +- + drivers/gpu/drm/vc4/vc4_hdmi.c | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -1110,7 +1110,7 @@ static const struct vc4_crtc_data bcm283 + .fifo_depth = 64, + .pixels_per_clock = 1, + .encoder_types = { +- [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_HDMI, ++ [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_HDMI0, + [PV_CONTROL_CLK_SELECT_VEC] = VC4_ENCODER_TYPE_VEC, + }, + }; +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -431,7 +431,7 @@ to_vc4_plane_state(struct drm_plane_stat + + enum vc4_encoder_type { + VC4_ENCODER_TYPE_NONE, +- VC4_ENCODER_TYPE_HDMI, ++ VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_DSI1, +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -1319,7 +1319,7 @@ static int vc4_hdmi_bind(struct device * + GFP_KERNEL); + if (!vc4_hdmi_encoder) + return -ENOMEM; +- vc4_hdmi_encoder->base.type = VC4_ENCODER_TYPE_HDMI; ++ vc4_hdmi_encoder->base.type = VC4_ENCODER_TYPE_HDMI0; + hdmi->encoder = &vc4_hdmi_encoder->base.base; + + hdmi->pdev = pdev; diff --git a/target/linux/bcm27xx/patches-5.4/950-0567-drm-vc4-crtc-Add-HDMI1-encoder-type.patch b/target/linux/bcm27xx/patches-5.4/950-0567-drm-vc4-crtc-Add-HDMI1-encoder-type.patch new file mode 100644 index 0000000000..00ad755492 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0567-drm-vc4-crtc-Add-HDMI1-encoder-type.patch @@ -0,0 +1,23 @@ +From 9df4f0e2da72c825d86f4f637983c712173ed272 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 9 Jan 2020 18:39:30 +0100 +Subject: [PATCH] drm/vc4: crtc: Add HDMI1 encoder type + +The BCM2711 sports a second HDMI controller, so let's add that second HDMI +encoder type. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_drv.h | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -432,6 +432,7 @@ to_vc4_plane_state(struct drm_plane_stat + enum vc4_encoder_type { + VC4_ENCODER_TYPE_NONE, + VC4_ENCODER_TYPE_HDMI0, ++ VC4_ENCODER_TYPE_HDMI1, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_DSI1, diff --git a/target/linux/bcm27xx/patches-5.4/950-0568-drm-vc4-crtc-Remove-redundant-call-to-drm_crtc_enabl.patch b/target/linux/bcm27xx/patches-5.4/950-0568-drm-vc4-crtc-Remove-redundant-call-to-drm_crtc_enabl.patch new file mode 100644 index 0000000000..5704952569 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0568-drm-vc4-crtc-Remove-redundant-call-to-drm_crtc_enabl.patch @@ -0,0 +1,24 @@ +From 278c3da2ce8c2cda6cb60946c55b5e7040dfc35a Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Fri, 21 Feb 2020 16:48:19 +0100 +Subject: [PATCH] drm/vc4: crtc: Remove redundant call to + drm_crtc_enable_color_mgmt + +The driver calls the helper to add the color management properties twice, +which is redundant. Remove the first one. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -1198,7 +1198,6 @@ static int vc4_crtc_bind(struct device * + &vc4_crtc_funcs, NULL); + drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs); + drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r)); +- drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size); + + /* We support CTM, but only for one CRTC at a time. It's therefore + * implemented as private driver state in vc4_kms, not here. diff --git a/target/linux/bcm27xx/patches-5.4/950-0569-drm-vc4-crtc-Disable-color-management-for-HVS5.patch b/target/linux/bcm27xx/patches-5.4/950-0569-drm-vc4-crtc-Disable-color-management-for-HVS5.patch new file mode 100644 index 0000000000..bd487df5c4 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0569-drm-vc4-crtc-Disable-color-management-for-HVS5.patch @@ -0,0 +1,54 @@ +From 9e134cea82d5c69e5d564e87cda2b5cf3ec14768 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Fri, 21 Feb 2020 16:54:21 +0100 +Subject: [PATCH] drm/vc4: crtc: Disable color management for HVS5 + +The HVS5 uses different color matrices. Disable color management support +for now. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 17 +++++++++++------ + 1 file changed, 11 insertions(+), 6 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -441,7 +441,7 @@ static void vc4_crtc_mode_set_nofb(struc + + HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel), + SCALER_DISPBKGND_AUTOHS | +- SCALER_DISPBKGND_GAMMA | ++ ((!vc4->hvs->hvs5) ? SCALER_DISPBKGND_GAMMA : 0) | + (interlace ? SCALER_DISPBKGND_INTERLACE : 0)); + + /* Reload the LUT, since the SRAMs would have been disabled if +@@ -1156,6 +1156,7 @@ static int vc4_crtc_bind(struct device * + { + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = dev_get_drvdata(master); ++ struct vc4_dev *vc4 = to_vc4_dev(drm); + struct vc4_crtc *vc4_crtc; + struct drm_crtc *crtc; + struct drm_plane *primary_plane, *destroy_plane, *temp; +@@ -1197,12 +1198,16 @@ static int vc4_crtc_bind(struct device * + drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL, + &vc4_crtc_funcs, NULL); + drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs); +- drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r)); + +- /* We support CTM, but only for one CRTC at a time. It's therefore +- * implemented as private driver state in vc4_kms, not here. +- */ +- drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size); ++ if (!vc4->hvs->hvs5) { ++ drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r)); ++ ++ /* We support CTM, but only for one CRTC at a ++ * time. It's therefore implemented as private driver ++ * state in vc4_kms, not here. ++ */ ++ drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size); ++ } + + CRTC_WRITE(PV_INTEN, 0); + CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START); diff --git a/target/linux/bcm27xx/patches-5.4/950-0570-dt-bindings-display-vc4-pv-Add-BCM2711-pixel-valves.patch b/target/linux/bcm27xx/patches-5.4/950-0570-dt-bindings-display-vc4-pv-Add-BCM2711-pixel-valves.patch new file mode 100644 index 0000000000..6bf6ab74c4 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0570-dt-bindings-display-vc4-pv-Add-BCM2711-pixel-valves.patch @@ -0,0 +1,30 @@ +From 26613c79b74224154703d75c4f2d2aa120cc2e84 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 13 Feb 2020 17:07:02 +0100 +Subject: [PATCH] dt-bindings: display: vc4: pv: Add BCM2711 pixel + valves + +The BCM2711 comes with other pixelvalves that have different requirements +and capabilities. Let's document their compatible. + +Cc: devicetree@vger.kernel.org +Reviewed-by: Rob Herring <robh+dt@kernel.org> +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + .../bindings/display/brcm,bcm2835-pixelvalve0.yaml | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-pixelvalve0.yaml ++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-pixelvalve0.yaml +@@ -15,6 +15,11 @@ properties: + - brcm,bcm2835-pixelvalve0 + - brcm,bcm2835-pixelvalve1 + - brcm,bcm2835-pixelvalve2 ++ - brcm,bcm2711-pixelvalve0 ++ - brcm,bcm2711-pixelvalve1 ++ - brcm,bcm2711-pixelvalve2 ++ - brcm,bcm2711-pixelvalve3 ++ - brcm,bcm2711-pixelvalve4 + + reg: + maxItems: 1 diff --git a/target/linux/bcm27xx/patches-5.4/950-0571-drm-vc4-crtc-Add-BCM2711-pixelvalves.patch b/target/linux/bcm27xx/patches-5.4/950-0571-drm-vc4-crtc-Add-BCM2711-pixelvalves.patch new file mode 100644 index 0000000000..3ff57ef3df --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0571-drm-vc4-crtc-Add-BCM2711-pixelvalves.patch @@ -0,0 +1,152 @@ +From aa43601d97bf9136b657259f44c03a6a30b70d07 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 26 Dec 2019 11:35:58 +0100 +Subject: [PATCH] drm/vc4: crtc: Add BCM2711 pixelvalves + +The BCM2711 has 5 pixelvalves, so now that our driver is ready, let's add +support for them. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 82 +++++++++++++++++++++++++++++++++- + drivers/gpu/drm/vc4/vc4_regs.h | 6 +++ + 2 files changed, 86 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -273,6 +273,13 @@ static u32 vc4_get_fifo_full_level(struc + case PV_CONTROL_FORMAT_24: + case PV_CONTROL_FORMAT_DSIV_24: + default: ++ /* ++ * For some reason, the pixelvalve4 doesn't work with ++ * the usual formula and will only work with 32. ++ */ ++ if (vc4_crtc->data->hvs_output == 5) ++ return 32; ++ + return fifo_len_bytes - 3 * HVS_FIFO_LATENCY_PIX; + } + } +@@ -281,8 +288,14 @@ static u32 vc4_crtc_get_fifo_full_level_ + u32 format) + { + u32 level = vc4_get_fifo_full_level(vc4_crtc, format); +- return VC4_SET_FIELD(level & 0x3f, +- PV_CONTROL_FIFO_LEVEL); ++ u32 ret = 0; ++ ++ if (level > 0x3f) ++ ret |= VC4_SET_FIELD((level >> 6) & 0x3, ++ PV5_CONTROL_FIFO_LEVEL_HIGH); ++ ++ return ret | VC4_SET_FIELD(level & 0x3f, ++ PV_CONTROL_FIFO_LEVEL); + } + + /* +@@ -328,6 +341,9 @@ static void vc4_crtc_config_pv(struct dr + CRTC_WRITE(PV_CONTROL, PV_CONTROL_FIFO_CLR | PV_CONTROL_EN); + CRTC_WRITE(PV_CONTROL, 0); + ++ CRTC_WRITE(PV_MUX_CFG, ++ VC4_SET_FIELD(8, PV_MUX_CFG_RGB_PIXEL_MUX_MODE)); ++ + CRTC_WRITE(PV_HORZA, + VC4_SET_FIELD((mode->htotal - mode->hsync_end) * pixel_rep / ppc, + PV_HORZA_HBP) | +@@ -1115,10 +1131,72 @@ static const struct vc4_crtc_data bcm283 + }, + }; + ++static const struct vc4_crtc_data bcm2711_pv0_data = { ++ .debugfs_name = "crtc0_regs", ++ .hvs_available_channels = BIT(0), ++ .hvs_output = 0, ++ .fifo_depth = 64, ++ .pixels_per_clock = 1, ++ .encoder_types = { ++ [0] = VC4_ENCODER_TYPE_DSI0, ++ [1] = VC4_ENCODER_TYPE_DPI, ++ }, ++}; ++ ++static const struct vc4_crtc_data bcm2711_pv1_data = { ++ .debugfs_name = "crtc1_regs", ++ .hvs_available_channels = BIT(0) | BIT(1) | BIT(2), ++ .hvs_output = 3, ++ .fifo_depth = 64, ++ .pixels_per_clock = 1, ++ .encoder_types = { ++ [0] = VC4_ENCODER_TYPE_DSI1, ++ [1] = VC4_ENCODER_TYPE_SMI, ++ }, ++}; ++ ++static const struct vc4_crtc_data bcm2711_pv2_data = { ++ .debugfs_name = "crtc2_regs", ++ .hvs_available_channels = BIT(0) | BIT(1) | BIT(2), ++ .hvs_output = 4, ++ .fifo_depth = 256, ++ .pixels_per_clock = 2, ++ .encoder_types = { ++ [0] = VC4_ENCODER_TYPE_HDMI0, ++ }, ++}; ++ ++static const struct vc4_crtc_data bcm2711_pv3_data = { ++ .debugfs_name = "crtc3_regs", ++ .hvs_available_channels = BIT(1), ++ .hvs_output = 1, ++ .fifo_depth = 64, ++ .pixels_per_clock = 1, ++ .encoder_types = { ++ [0] = VC4_ENCODER_TYPE_VEC, ++ }, ++}; ++ ++static const struct vc4_crtc_data bcm2711_pv4_data = { ++ .debugfs_name = "crtc4_regs", ++ .hvs_available_channels = BIT(0) | BIT(1) | BIT(2), ++ .hvs_output = 5, ++ .fifo_depth = 64, ++ .pixels_per_clock = 2, ++ .encoder_types = { ++ [0] = VC4_ENCODER_TYPE_HDMI1, ++ }, ++}; ++ + static const struct of_device_id vc4_crtc_dt_match[] = { + { .compatible = "brcm,bcm2835-pixelvalve0", .data = &bcm2835_pv0_data }, + { .compatible = "brcm,bcm2835-pixelvalve1", .data = &bcm2835_pv1_data }, + { .compatible = "brcm,bcm2835-pixelvalve2", .data = &bcm2835_pv2_data }, ++ { .compatible = "brcm,bcm2711-pixelvalve0", .data = &bcm2711_pv0_data }, ++ { .compatible = "brcm,bcm2711-pixelvalve1", .data = &bcm2711_pv1_data }, ++ { .compatible = "brcm,bcm2711-pixelvalve2", .data = &bcm2711_pv2_data }, ++ { .compatible = "brcm,bcm2711-pixelvalve3", .data = &bcm2711_pv3_data }, ++ { .compatible = "brcm,bcm2711-pixelvalve4", .data = &bcm2711_pv4_data }, + {} + }; + +--- a/drivers/gpu/drm/vc4/vc4_regs.h ++++ b/drivers/gpu/drm/vc4/vc4_regs.h +@@ -130,6 +130,8 @@ + #define V3D_ERRSTAT 0x00f20 + + #define PV_CONTROL 0x00 ++# define PV5_CONTROL_FIFO_LEVEL_HIGH_MASK VC4_MASK(26, 25) ++# define PV5_CONTROL_FIFO_LEVEL_HIGH_SHIFT 25 + # define PV_CONTROL_FORMAT_MASK VC4_MASK(23, 21) + # define PV_CONTROL_FORMAT_SHIFT 21 + # define PV_CONTROL_FORMAT_24 0 +@@ -209,6 +211,10 @@ + + #define PV_HACT_ACT 0x30 + ++#define PV_MUX_CFG 0x34 ++# define PV_MUX_CFG_RGB_PIXEL_MUX_MODE_MASK VC4_MASK(5, 2) ++# define PV_MUX_CFG_RGB_PIXEL_MUX_MODE_SHIFT 2 ++ + #define SCALER_CHANNELS_COUNT 3 + + #define SCALER_DISPCTRL 0x00000000 diff --git a/target/linux/bcm27xx/patches-5.4/950-0572-drm-vc4-hdmi-Use-debugfs-private-field.patch b/target/linux/bcm27xx/patches-5.4/950-0572-drm-vc4-hdmi-Use-debugfs-private-field.patch new file mode 100644 index 0000000000..89d8aae28d --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0572-drm-vc4-hdmi-Use-debugfs-private-field.patch @@ -0,0 +1,30 @@ +From 26fbd25a3f99a85c09d5f1ed44980c506a3eac81 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Tue, 14 Jan 2020 17:24:32 +0100 +Subject: [PATCH] drm/vc4: hdmi: Use debugfs private field + +We're calling vc4_debugfs_add_file with our struct vc4_hdmi pointer set +in the private field, but we don't use that field and go through the +main struct vc4_dev to get it. + +Let's use the private field directly, that will save us some trouble +later on. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -182,9 +182,7 @@ static const struct debugfs_reg32 hd_reg + static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) + { + struct drm_info_node *node = (struct drm_info_node *)m->private; +- struct drm_device *dev = node->minor->dev; +- struct vc4_dev *vc4 = to_vc4_dev(dev); +- struct vc4_hdmi *hdmi = vc4->hdmi; ++ struct vc4_hdmi *hdmi = node->info_ent->data; + struct drm_printer p = drm_seq_file_printer(m); + + drm_print_regset32(&p, &hdmi->hdmi_regset); diff --git a/target/linux/bcm27xx/patches-5.4/950-0573-drm-vc4-hdmi-Move-structure-to-header.patch b/target/linux/bcm27xx/patches-5.4/950-0573-drm-vc4-hdmi-Move-structure-to-header.patch new file mode 100644 index 0000000000..430fc0459e --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0573-drm-vc4-hdmi-Move-structure-to-header.patch @@ -0,0 +1,195 @@ +From 13bb65d33681b0095214033a5e80186faa325854 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Wed, 18 Dec 2019 18:35:12 +0100 +Subject: [PATCH] drm/vc4: hdmi: Move structure to header + +We will need to share the vc4_hdmi and related structures with multiple +files, so let's create a header for it. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 76 +----------------------------- + drivers/gpu/drm/vc4/vc4_hdmi.h | 86 ++++++++++++++++++++++++++++++++++ + 2 files changed, 87 insertions(+), 75 deletions(-) + create mode 100644 drivers/gpu/drm/vc4/vc4_hdmi.h + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -48,87 +48,13 @@ + #include <sound/soc.h> + #include "media/cec.h" + #include "vc4_drv.h" ++#include "vc4_hdmi.h" + #include "vc4_regs.h" + + #define HSM_CLOCK_FREQ 163682864 + #define CEC_CLOCK_FREQ 40000 + #define CEC_CLOCK_DIV (HSM_CLOCK_FREQ / CEC_CLOCK_FREQ) + +-/* HDMI audio information */ +-struct vc4_hdmi_audio { +- struct snd_soc_card card; +- struct snd_soc_dai_link link; +- struct snd_soc_dai_link_component cpu; +- struct snd_soc_dai_link_component codec; +- struct snd_soc_dai_link_component platform; +- int samplerate; +- int channels; +- struct snd_dmaengine_dai_dma_data dma_data; +- struct snd_pcm_substream *substream; +-}; +- +-/* General HDMI hardware state. */ +-struct vc4_hdmi { +- struct platform_device *pdev; +- +- struct drm_encoder *encoder; +- struct drm_connector *connector; +- +- struct vc4_hdmi_audio audio; +- +- struct i2c_adapter *ddc; +- void __iomem *hdmicore_regs; +- void __iomem *hd_regs; +- int hpd_gpio; +- bool hpd_active_low; +- +- struct cec_adapter *cec_adap; +- struct cec_msg cec_rx_msg; +- bool cec_tx_ok; +- bool cec_irq_was_rx; +- +- struct clk *pixel_clock; +- struct clk *hsm_clock; +- +- struct debugfs_regset32 hdmi_regset; +- struct debugfs_regset32 hd_regset; +-}; +- +-#define HDMI_READ(offset) readl(vc4->hdmi->hdmicore_regs + offset) +-#define HDMI_WRITE(offset, val) writel(val, vc4->hdmi->hdmicore_regs + offset) +-#define HD_READ(offset) readl(vc4->hdmi->hd_regs + offset) +-#define HD_WRITE(offset, val) writel(val, vc4->hdmi->hd_regs + offset) +- +-/* VC4 HDMI encoder KMS struct */ +-struct vc4_hdmi_encoder { +- struct vc4_encoder base; +- bool hdmi_monitor; +- bool limited_rgb_range; +-}; +- +-static inline struct vc4_hdmi_encoder * +-to_vc4_hdmi_encoder(struct drm_encoder *encoder) +-{ +- return container_of(encoder, struct vc4_hdmi_encoder, base.base); +-} +- +-/* VC4 HDMI connector KMS struct */ +-struct vc4_hdmi_connector { +- struct drm_connector base; +- +- /* Since the connector is attached to just the one encoder, +- * this is the reference to it so we can do the best_encoder() +- * hook. +- */ +- struct drm_encoder *encoder; +-}; +- +-static inline struct vc4_hdmi_connector * +-to_vc4_hdmi_connector(struct drm_connector *connector) +-{ +- return container_of(connector, struct vc4_hdmi_connector, base); +-} +- + static const struct debugfs_reg32 hdmi_regs[] = { + VC4_REG32(VC4_HDMI_CORE_REV), + VC4_REG32(VC4_HDMI_SW_RESET_CONTROL), +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -0,0 +1,86 @@ ++#ifndef _VC4_HDMI_H_ ++#define _VC4_HDMI_H_ ++ ++#include <drm/drm_connector.h> ++#include <media/cec.h> ++#include <sound/dmaengine_pcm.h> ++#include <sound/soc.h> ++ ++#include "vc4_drv.h" ++ ++/* HDMI audio information */ ++struct vc4_hdmi_audio { ++ struct snd_soc_card card; ++ struct snd_soc_dai_link link; ++ struct snd_soc_dai_link_component cpu; ++ struct snd_soc_dai_link_component codec; ++ struct snd_soc_dai_link_component platform; ++ int samplerate; ++ int channels; ++ struct snd_dmaengine_dai_dma_data dma_data; ++ struct snd_pcm_substream *substream; ++}; ++ ++/* General HDMI hardware state. */ ++struct vc4_hdmi { ++ struct platform_device *pdev; ++ ++ struct drm_encoder *encoder; ++ struct drm_connector *connector; ++ ++ struct vc4_hdmi_audio audio; ++ ++ struct i2c_adapter *ddc; ++ void __iomem *hdmicore_regs; ++ void __iomem *hd_regs; ++ int hpd_gpio; ++ bool hpd_active_low; ++ ++ struct cec_adapter *cec_adap; ++ struct cec_msg cec_rx_msg; ++ bool cec_tx_ok; ++ bool cec_irq_was_rx; ++ ++ struct clk *pixel_clock; ++ struct clk *hsm_clock; ++ ++ struct debugfs_regset32 hdmi_regset; ++ struct debugfs_regset32 hd_regset; ++}; ++ ++#define HDMI_READ(offset) readl(vc4->hdmi->hdmicore_regs + offset) ++#define HDMI_WRITE(offset, val) writel(val, vc4->hdmi->hdmicore_regs + offset) ++#define HD_READ(offset) readl(vc4->hdmi->hd_regs + offset) ++#define HD_WRITE(offset, val) writel(val, vc4->hdmi->hd_regs + offset) ++ ++/* VC4 HDMI encoder KMS struct */ ++struct vc4_hdmi_encoder { ++ struct vc4_encoder base; ++ bool hdmi_monitor; ++ bool limited_rgb_range; ++}; ++ ++static inline struct vc4_hdmi_encoder * ++to_vc4_hdmi_encoder(struct drm_encoder *encoder) ++{ ++ return container_of(encoder, struct vc4_hdmi_encoder, base.base); ++} ++ ++/* VC4 HDMI connector KMS struct */ ++struct vc4_hdmi_connector { ++ struct drm_connector base; ++ ++ /* Since the connector is attached to just the one encoder, ++ * this is the reference to it so we can do the best_encoder() ++ * hook. ++ */ ++ struct drm_encoder *encoder; ++}; ++ ++static inline struct vc4_hdmi_connector * ++to_vc4_hdmi_connector(struct drm_connector *connector) ++{ ++ return container_of(connector, struct vc4_hdmi_connector, base); ++} ++ ++#endif /* _VC4_HDMI_H_ */ diff --git a/target/linux/bcm27xx/patches-5.4/950-0574-drm-vc4-hdmi-rework-connectors-and-encoders.patch b/target/linux/bcm27xx/patches-5.4/950-0574-drm-vc4-hdmi-rework-connectors-and-encoders.patch new file mode 100644 index 0000000000..71125e3e25 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0574-drm-vc4-hdmi-rework-connectors-and-encoders.patch @@ -0,0 +1,348 @@ +From 50e7e810cf403c4b217c68c8ae2544d16f8063d1 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Mon, 6 Jan 2020 17:17:29 +0100 +Subject: [PATCH] drm/vc4: hdmi: rework connectors and encoders + +the vc4_hdmi driver has some custom structures to hold the data it needs to +associate with the drm_encoder and drm_connector structures. + +However, it allocates them separately from the vc4_hdmi structure which +makes it more complicated than it needs to be. + +Move those structures to be contained by vc4_hdmi and update the code +accordingly. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 84 ++++++++++++++++------------------ + drivers/gpu/drm/vc4/vc4_hdmi.h | 64 +++++++++++++------------- + 2 files changed, 71 insertions(+), 77 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -190,19 +190,14 @@ static const struct drm_connector_helper + .get_modes = vc4_hdmi_connector_get_modes, + }; + +-static struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev, +- struct drm_encoder *encoder) ++static int vc4_hdmi_connector_init(struct drm_device *dev, ++ struct vc4_hdmi *vc4_hdmi) + { +- struct drm_connector *connector; +- struct vc4_hdmi_connector *hdmi_connector; ++ struct vc4_hdmi_connector *hdmi_connector = &vc4_hdmi->connector; ++ struct drm_connector *connector = &hdmi_connector->base; ++ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; + int ret; + +- hdmi_connector = devm_kzalloc(dev->dev, sizeof(*hdmi_connector), +- GFP_KERNEL); +- if (!hdmi_connector) +- return ERR_PTR(-ENOMEM); +- connector = &hdmi_connector->base; +- + hdmi_connector->encoder = encoder; + + drm_connector_init(dev, connector, &vc4_hdmi_connector_funcs, +@@ -212,7 +207,7 @@ static struct drm_connector *vc4_hdmi_co + /* Create and attach TV margin props to this connector. */ + ret = drm_mode_create_tv_margin_properties(dev); + if (ret) +- return ERR_PTR(ret); ++ return ret; + + drm_connector_attach_tv_margin_properties(connector); + +@@ -224,7 +219,7 @@ static struct drm_connector *vc4_hdmi_co + + drm_connector_attach_encoder(connector, encoder); + +- return connector; ++ return 0; + } + + static void vc4_hdmi_encoder_destroy(struct drm_encoder *encoder) +@@ -303,21 +298,22 @@ static void vc4_hdmi_set_avi_infoframe(s + struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); + struct vc4_dev *vc4 = encoder->dev->dev_private; + struct vc4_hdmi *hdmi = vc4->hdmi; +- struct drm_connector_state *cstate = hdmi->connector->state; ++ struct drm_connector *connector = &hdmi->connector.base; ++ struct drm_connector_state *cstate = connector->state; + struct drm_crtc *crtc = encoder->crtc; + const struct drm_display_mode *mode = &crtc->state->adjusted_mode; + union hdmi_infoframe frame; + int ret; + + ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, +- hdmi->connector, mode); ++ connector, mode); + if (ret < 0) { + DRM_ERROR("couldn't fill AVI infoframe\n"); + return; + } + + drm_hdmi_avi_infoframe_quant_range(&frame.avi, +- hdmi->connector, mode, ++ connector, mode, + vc4_encoder->limited_rgb_range ? + HDMI_QUANTIZATION_RANGE_LIMITED : + HDMI_QUANTIZATION_RANGE_FULL); +@@ -636,7 +632,8 @@ static const struct drm_encoder_helper_f + /* HDMI audio codec callbacks */ + static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *hdmi) + { +- struct drm_device *drm = hdmi->encoder->dev; ++ struct drm_encoder *encoder = &hdmi->encoder.base.base; ++ struct drm_device *drm = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + u32 hsm_clock = clk_get_rate(hdmi->hsm_clock); + unsigned long n, m; +@@ -655,7 +652,7 @@ static void vc4_hdmi_audio_set_mai_clock + + static void vc4_hdmi_set_n_cts(struct vc4_hdmi *hdmi) + { +- struct drm_encoder *encoder = hdmi->encoder; ++ struct drm_encoder *encoder = &hdmi->encoder.base.base; + struct drm_crtc *crtc = encoder->crtc; + struct drm_device *drm = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); +@@ -693,7 +690,8 @@ static int vc4_hdmi_audio_startup(struct + struct snd_soc_dai *dai) + { + struct vc4_hdmi *hdmi = dai_to_hdmi(dai); +- struct drm_encoder *encoder = hdmi->encoder; ++ struct drm_encoder *encoder = &hdmi->encoder.base.base; ++ struct drm_connector *connector = &hdmi->connector.base; + struct vc4_dev *vc4 = to_vc4_dev(encoder->dev); + int ret; + +@@ -710,8 +708,7 @@ static int vc4_hdmi_audio_startup(struct + VC4_HDMI_RAM_PACKET_ENABLE)) + return -ENODEV; + +- ret = snd_pcm_hw_constraint_eld(substream->runtime, +- hdmi->connector->eld); ++ ret = snd_pcm_hw_constraint_eld(substream->runtime, connector->eld); + if (ret) + return ret; + +@@ -725,7 +722,7 @@ static int vc4_hdmi_audio_set_fmt(struct + + static void vc4_hdmi_audio_reset(struct vc4_hdmi *hdmi) + { +- struct drm_encoder *encoder = hdmi->encoder; ++ struct drm_encoder *encoder = &hdmi->encoder.base.base; + struct drm_device *drm = encoder->dev; + struct device *dev = &hdmi->pdev->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); +@@ -759,7 +756,7 @@ static int vc4_hdmi_audio_hw_params(stru + struct snd_soc_dai *dai) + { + struct vc4_hdmi *hdmi = dai_to_hdmi(dai); +- struct drm_encoder *encoder = hdmi->encoder; ++ struct drm_encoder *encoder = &hdmi->encoder.base.base; + struct drm_device *drm = encoder->dev; + struct device *dev = &hdmi->pdev->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); +@@ -832,7 +829,7 @@ static int vc4_hdmi_audio_trigger(struct + struct snd_soc_dai *dai) + { + struct vc4_hdmi *hdmi = dai_to_hdmi(dai); +- struct drm_encoder *encoder = hdmi->encoder; ++ struct drm_encoder *encoder = &hdmi->encoder.base.base; + struct drm_device *drm = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + +@@ -876,9 +873,10 @@ static int vc4_hdmi_audio_eld_ctl_info(s + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct vc4_hdmi *hdmi = snd_component_to_hdmi(component); ++ struct drm_connector *connector = &hdmi->connector.base; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; +- uinfo->count = sizeof(hdmi->connector->eld); ++ uinfo->count = sizeof(connector->eld); + + return 0; + } +@@ -888,9 +886,10 @@ static int vc4_hdmi_audio_eld_ctl_get(st + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct vc4_hdmi *hdmi = snd_component_to_hdmi(component); ++ struct drm_connector *connector = &hdmi->connector.base; + +- memcpy(ucontrol->value.bytes.data, hdmi->connector->eld, +- sizeof(hdmi->connector->eld)); ++ memcpy(ucontrol->value.bytes.data, connector->eld, ++ sizeof(connector->eld)); + + return 0; + } +@@ -1230,7 +1229,7 @@ static int vc4_hdmi_bind(struct device * + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = drm->dev_private; + struct vc4_hdmi *hdmi; +- struct vc4_hdmi_encoder *vc4_hdmi_encoder; ++ struct drm_encoder *encoder; + struct device_node *ddc_node; + u32 value; + int ret; +@@ -1239,14 +1238,10 @@ static int vc4_hdmi_bind(struct device * + if (!hdmi) + return -ENOMEM; + +- vc4_hdmi_encoder = devm_kzalloc(dev, sizeof(*vc4_hdmi_encoder), +- GFP_KERNEL); +- if (!vc4_hdmi_encoder) +- return -ENOMEM; +- vc4_hdmi_encoder->base.type = VC4_ENCODER_TYPE_HDMI0; +- hdmi->encoder = &vc4_hdmi_encoder->base.base; +- + hdmi->pdev = pdev; ++ encoder = &hdmi->encoder.base.base; ++ encoder->base.type = VC4_ENCODER_TYPE_HDMI0; ++ + hdmi->hdmicore_regs = vc4_ioremap_regs(pdev, 0); + if (IS_ERR(hdmi->hdmicore_regs)) + return PTR_ERR(hdmi->hdmicore_regs); +@@ -1332,15 +1327,14 @@ static int vc4_hdmi_bind(struct device * + } + pm_runtime_enable(dev); + +- drm_encoder_init(drm, hdmi->encoder, &vc4_hdmi_encoder_funcs, ++ drm_encoder_init(drm, encoder, &vc4_hdmi_encoder_funcs, + DRM_MODE_ENCODER_TMDS, NULL); +- drm_encoder_helper_add(hdmi->encoder, &vc4_hdmi_encoder_helper_funcs); ++ drm_encoder_helper_add(encoder, &vc4_hdmi_encoder_helper_funcs); + +- hdmi->connector = vc4_hdmi_connector_init(drm, hdmi->encoder); +- if (IS_ERR(hdmi->connector)) { +- ret = PTR_ERR(hdmi->connector); ++ ret = vc4_hdmi_connector_init(drm, hdmi); ++ if (ret) + goto err_destroy_encoder; +- } ++ + #ifdef CONFIG_DRM_VC4_HDMI_CEC + hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops, + vc4, "vc4", +@@ -1350,7 +1344,7 @@ static int vc4_hdmi_bind(struct device * + if (ret < 0) + goto err_destroy_conn; + +- cec_fill_conn_info_from_drm(&conn_info, hdmi->connector); ++ cec_fill_conn_info_from_drm(&conn_info, &hdmi->connector.base); + cec_s_conn_info(hdmi->cec_adap, &conn_info); + + HDMI_WRITE(VC4_HDMI_CPU_MASK_SET, 0xffffffff); +@@ -1387,10 +1381,10 @@ static int vc4_hdmi_bind(struct device * + err_delete_cec_adap: + cec_delete_adapter(hdmi->cec_adap); + err_destroy_conn: +- vc4_hdmi_connector_destroy(hdmi->connector); ++ vc4_hdmi_connector_destroy(&hdmi->connector.base); + #endif + err_destroy_encoder: +- vc4_hdmi_encoder_destroy(hdmi->encoder); ++ vc4_hdmi_encoder_destroy(encoder); + err_unprepare_hsm: + clk_disable_unprepare(hdmi->hsm_clock); + pm_runtime_disable(dev); +@@ -1408,8 +1402,8 @@ static void vc4_hdmi_unbind(struct devic + struct vc4_hdmi *hdmi = vc4->hdmi; + + cec_unregister_adapter(hdmi->cec_adap); +- vc4_hdmi_connector_destroy(hdmi->connector); +- vc4_hdmi_encoder_destroy(hdmi->encoder); ++ vc4_hdmi_connector_destroy(&hdmi->connector.base); ++ vc4_hdmi_encoder_destroy(&hdmi->encoder.base.base); + + clk_disable_unprepare(hdmi->hsm_clock); + pm_runtime_disable(dev); +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -8,6 +8,36 @@ + + #include "vc4_drv.h" + ++/* VC4 HDMI encoder KMS struct */ ++struct vc4_hdmi_encoder { ++ struct vc4_encoder base; ++ bool hdmi_monitor; ++ bool limited_rgb_range; ++}; ++ ++static inline struct vc4_hdmi_encoder * ++to_vc4_hdmi_encoder(struct drm_encoder *encoder) ++{ ++ return container_of(encoder, struct vc4_hdmi_encoder, base.base); ++} ++ ++/* VC4 HDMI connector KMS struct */ ++struct vc4_hdmi_connector { ++ struct drm_connector base; ++ ++ /* Since the connector is attached to just the one encoder, ++ * this is the reference to it so we can do the best_encoder() ++ * hook. ++ */ ++ struct drm_encoder *encoder; ++}; ++ ++static inline struct vc4_hdmi_connector * ++to_vc4_hdmi_connector(struct drm_connector *connector) ++{ ++ return container_of(connector, struct vc4_hdmi_connector, base); ++} ++ + /* HDMI audio information */ + struct vc4_hdmi_audio { + struct snd_soc_card card; +@@ -25,8 +55,8 @@ struct vc4_hdmi_audio { + struct vc4_hdmi { + struct platform_device *pdev; + +- struct drm_encoder *encoder; +- struct drm_connector *connector; ++ struct vc4_hdmi_encoder encoder; ++ struct vc4_hdmi_connector connector; + + struct vc4_hdmi_audio audio; + +@@ -53,34 +83,4 @@ struct vc4_hdmi { + #define HD_READ(offset) readl(vc4->hdmi->hd_regs + offset) + #define HD_WRITE(offset, val) writel(val, vc4->hdmi->hd_regs + offset) + +-/* VC4 HDMI encoder KMS struct */ +-struct vc4_hdmi_encoder { +- struct vc4_encoder base; +- bool hdmi_monitor; +- bool limited_rgb_range; +-}; +- +-static inline struct vc4_hdmi_encoder * +-to_vc4_hdmi_encoder(struct drm_encoder *encoder) +-{ +- return container_of(encoder, struct vc4_hdmi_encoder, base.base); +-} +- +-/* VC4 HDMI connector KMS struct */ +-struct vc4_hdmi_connector { +- struct drm_connector base; +- +- /* Since the connector is attached to just the one encoder, +- * this is the reference to it so we can do the best_encoder() +- * hook. +- */ +- struct drm_encoder *encoder; +-}; +- +-static inline struct vc4_hdmi_connector * +-to_vc4_hdmi_connector(struct drm_connector *connector) +-{ +- return container_of(connector, struct vc4_hdmi_connector, base); +-} +- + #endif /* _VC4_HDMI_H_ */ diff --git a/target/linux/bcm27xx/patches-5.4/950-0575-drm-vc4-hdmi-Rename-hdmi-to-vc4_hdmi.patch b/target/linux/bcm27xx/patches-5.4/950-0575-drm-vc4-hdmi-Rename-hdmi-to-vc4_hdmi.patch new file mode 100644 index 0000000000..75c44a564a --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0575-drm-vc4-hdmi-Rename-hdmi-to-vc4_hdmi.patch @@ -0,0 +1,682 @@ +From 02b7a6ed6b9fc110dd26598d26c31c0837af6184 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Mon, 6 Jan 2020 18:07:05 +0100 +Subject: [PATCH] drm/vc4: hdmi: Rename hdmi to vc4_hdmi + +The driver isn't consistent with the name given to the vc4_hdmi +structure pointer in its functions. Make sure to use a consistent name. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 271 +++++++++++++++++---------------- + 1 file changed, 136 insertions(+), 135 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -108,11 +108,11 @@ static const struct debugfs_reg32 hd_reg + static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) + { + struct drm_info_node *node = (struct drm_info_node *)m->private; +- struct vc4_hdmi *hdmi = node->info_ent->data; ++ struct vc4_hdmi *vc4_hdmi = node->info_ent->data; + struct drm_printer p = drm_seq_file_printer(m); + +- drm_print_regset32(&p, &hdmi->hdmi_regset); +- drm_print_regset32(&p, &hdmi->hd_regset); ++ drm_print_regset32(&p, &vc4_hdmi->hdmi_regset); ++ drm_print_regset32(&p, &vc4_hdmi->hd_regset); + + return 0; + } +@@ -297,8 +297,8 @@ static void vc4_hdmi_set_avi_infoframe(s + { + struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); + struct vc4_dev *vc4 = encoder->dev->dev_private; +- struct vc4_hdmi *hdmi = vc4->hdmi; +- struct drm_connector *connector = &hdmi->connector.base; ++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi; ++ struct drm_connector *connector = &vc4_hdmi->connector.base; + struct drm_connector_state *cstate = connector->state; + struct drm_crtc *crtc = encoder->crtc; + const struct drm_display_mode *mode = &crtc->state->adjusted_mode; +@@ -346,7 +346,7 @@ static void vc4_hdmi_set_audio_infoframe + { + struct drm_device *drm = encoder->dev; + struct vc4_dev *vc4 = drm->dev_private; +- struct vc4_hdmi *hdmi = vc4->hdmi; ++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi; + union hdmi_infoframe frame; + int ret; + +@@ -355,7 +355,7 @@ static void vc4_hdmi_set_audio_infoframe + frame.audio.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; + frame.audio.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; + frame.audio.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; +- frame.audio.channels = hdmi->audio.channels; ++ frame.audio.channels = vc4_hdmi->audio.channels; + + vc4_hdmi_write_infoframe(encoder, &frame); + } +@@ -370,7 +370,7 @@ static void vc4_hdmi_encoder_disable(str + { + struct drm_device *dev = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); +- struct vc4_hdmi *hdmi = vc4->hdmi; ++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi; + int ret; + + HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0); +@@ -379,9 +379,9 @@ static void vc4_hdmi_encoder_disable(str + HD_WRITE(VC4_HD_VID_CTL, + HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE); + +- clk_disable_unprepare(hdmi->pixel_clock); ++ clk_disable_unprepare(vc4_hdmi->pixel_clock); + +- ret = pm_runtime_put(&hdmi->pdev->dev); ++ ret = pm_runtime_put(&vc4_hdmi->pdev->dev); + if (ret < 0) + DRM_ERROR("Failed to release power domain: %d\n", ret); + } +@@ -392,7 +392,7 @@ static void vc4_hdmi_encoder_enable(stru + struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); + struct drm_device *dev = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); +- struct vc4_hdmi *hdmi = vc4->hdmi; ++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi; + bool debug_dump_regs = false; + bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; + bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC; +@@ -414,13 +414,13 @@ static void vc4_hdmi_encoder_enable(stru + u32 csc_ctl; + int ret; + +- ret = pm_runtime_get_sync(&hdmi->pdev->dev); ++ ret = pm_runtime_get_sync(&vc4_hdmi->pdev->dev); + if (ret < 0) { + DRM_ERROR("Failed to retain power domain: %d\n", ret); + return; + } + +- ret = clk_set_rate(hdmi->pixel_clock, ++ ret = clk_set_rate(vc4_hdmi->pixel_clock, + mode->clock * 1000 * + ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1)); + if (ret) { +@@ -428,7 +428,7 @@ static void vc4_hdmi_encoder_enable(stru + return; + } + +- ret = clk_prepare_enable(hdmi->pixel_clock); ++ ret = clk_prepare_enable(vc4_hdmi->pixel_clock); + if (ret) { + DRM_ERROR("Failed to turn on pixel clock: %d\n", ret); + return; +@@ -448,11 +448,11 @@ static void vc4_hdmi_encoder_enable(stru + HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0); + + if (debug_dump_regs) { +- struct drm_printer p = drm_info_printer(&hdmi->pdev->dev); ++ struct drm_printer p = drm_info_printer(&vc4_hdmi->pdev->dev); + +- dev_info(&hdmi->pdev->dev, "HDMI regs before:\n"); +- drm_print_regset32(&p, &hdmi->hdmi_regset); +- drm_print_regset32(&p, &hdmi->hd_regset); ++ dev_info(&vc4_hdmi->pdev->dev, "HDMI regs before:\n"); ++ drm_print_regset32(&p, &vc4_hdmi->hdmi_regset); ++ drm_print_regset32(&p, &vc4_hdmi->hd_regset); + } + + HD_WRITE(VC4_HD_VID_CTL, 0); +@@ -527,11 +527,11 @@ static void vc4_hdmi_encoder_enable(stru + HDMI_WRITE(VC4_HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N); + + if (debug_dump_regs) { +- struct drm_printer p = drm_info_printer(&hdmi->pdev->dev); ++ struct drm_printer p = drm_info_printer(&vc4_hdmi->pdev->dev); + +- dev_info(&hdmi->pdev->dev, "HDMI regs after:\n"); +- drm_print_regset32(&p, &hdmi->hdmi_regset); +- drm_print_regset32(&p, &hdmi->hd_regset); ++ dev_info(&vc4_hdmi->pdev->dev, "HDMI regs after:\n"); ++ drm_print_regset32(&p, &vc4_hdmi->hdmi_regset); ++ drm_print_regset32(&p, &vc4_hdmi->hd_regset); + } + + HD_WRITE(VC4_HD_VID_CTL, +@@ -630,15 +630,15 @@ static const struct drm_encoder_helper_f + }; + + /* HDMI audio codec callbacks */ +-static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *hdmi) ++static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *vc4_hdmi) + { +- struct drm_encoder *encoder = &hdmi->encoder.base.base; ++ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; + struct drm_device *drm = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); +- u32 hsm_clock = clk_get_rate(hdmi->hsm_clock); ++ u32 hsm_clock = clk_get_rate(vc4_hdmi->hsm_clock); + unsigned long n, m; + +- rational_best_approximation(hsm_clock, hdmi->audio.samplerate, ++ rational_best_approximation(hsm_clock, vc4_hdmi->audio.samplerate, + VC4_HD_MAI_SMP_N_MASK >> + VC4_HD_MAI_SMP_N_SHIFT, + (VC4_HD_MAI_SMP_M_MASK >> +@@ -650,14 +650,14 @@ static void vc4_hdmi_audio_set_mai_clock + VC4_SET_FIELD(m - 1, VC4_HD_MAI_SMP_M)); + } + +-static void vc4_hdmi_set_n_cts(struct vc4_hdmi *hdmi) ++static void vc4_hdmi_set_n_cts(struct vc4_hdmi *vc4_hdmi) + { +- struct drm_encoder *encoder = &hdmi->encoder.base.base; ++ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; + struct drm_crtc *crtc = encoder->crtc; + struct drm_device *drm = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + const struct drm_display_mode *mode = &crtc->state->adjusted_mode; +- u32 samplerate = hdmi->audio.samplerate; ++ u32 samplerate = vc4_hdmi->audio.samplerate; + u32 n, cts; + u64 tmp; + +@@ -689,16 +689,16 @@ static inline struct vc4_hdmi *dai_to_hd + static int vc4_hdmi_audio_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { +- struct vc4_hdmi *hdmi = dai_to_hdmi(dai); +- struct drm_encoder *encoder = &hdmi->encoder.base.base; +- struct drm_connector *connector = &hdmi->connector.base; ++ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai); ++ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; ++ struct drm_connector *connector = &vc4_hdmi->connector.base; + struct vc4_dev *vc4 = to_vc4_dev(encoder->dev); + int ret; + +- if (hdmi->audio.substream && hdmi->audio.substream != substream) ++ if (vc4_hdmi->audio.substream && vc4_hdmi->audio.substream != substream) + return -EINVAL; + +- hdmi->audio.substream = substream; ++ vc4_hdmi->audio.substream = substream; + + /* + * If the HDMI encoder hasn't probed, or the encoder is +@@ -720,11 +720,11 @@ static int vc4_hdmi_audio_set_fmt(struct + return 0; + } + +-static void vc4_hdmi_audio_reset(struct vc4_hdmi *hdmi) ++static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi) + { +- struct drm_encoder *encoder = &hdmi->encoder.base.base; ++ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; + struct drm_device *drm = encoder->dev; +- struct device *dev = &hdmi->pdev->dev; ++ struct device *dev = &vc4_hdmi->pdev->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + int ret; + +@@ -740,14 +740,14 @@ static void vc4_hdmi_audio_reset(struct + static void vc4_hdmi_audio_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { +- struct vc4_hdmi *hdmi = dai_to_hdmi(dai); ++ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai); + +- if (substream != hdmi->audio.substream) ++ if (substream != vc4_hdmi->audio.substream) + return; + +- vc4_hdmi_audio_reset(hdmi); ++ vc4_hdmi_audio_reset(vc4_hdmi); + +- hdmi->audio.substream = NULL; ++ vc4_hdmi->audio.substream = NULL; + } + + /* HDMI audio codec callbacks */ +@@ -755,23 +755,23 @@ static int vc4_hdmi_audio_hw_params(stru + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) + { +- struct vc4_hdmi *hdmi = dai_to_hdmi(dai); +- struct drm_encoder *encoder = &hdmi->encoder.base.base; ++ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai); ++ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; + struct drm_device *drm = encoder->dev; +- struct device *dev = &hdmi->pdev->dev; ++ struct device *dev = &vc4_hdmi->pdev->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + u32 audio_packet_config, channel_mask; + u32 channel_map, i; + +- if (substream != hdmi->audio.substream) ++ if (substream != vc4_hdmi->audio.substream) + return -EINVAL; + + dev_dbg(dev, "%s: %u Hz, %d bit, %d channels\n", __func__, + params_rate(params), params_width(params), + params_channels(params)); + +- hdmi->audio.channels = params_channels(params); +- hdmi->audio.samplerate = params_rate(params); ++ vc4_hdmi->audio.channels = params_channels(params); ++ vc4_hdmi->audio.samplerate = params_rate(params); + + HD_WRITE(VC4_HD_MAI_CTL, + VC4_HD_MAI_CTL_RESET | +@@ -780,23 +780,23 @@ static int vc4_hdmi_audio_hw_params(stru + VC4_HD_MAI_CTL_ERRORE | + VC4_HD_MAI_CTL_ERRORF); + +- vc4_hdmi_audio_set_mai_clock(hdmi); ++ vc4_hdmi_audio_set_mai_clock(vc4_hdmi); + + audio_packet_config = + VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_SAMPLE_FLAT | + VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS | + VC4_SET_FIELD(0xf, VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER); + +- channel_mask = GENMASK(hdmi->audio.channels - 1, 0); ++ channel_mask = GENMASK(vc4_hdmi->audio.channels - 1, 0); + audio_packet_config |= VC4_SET_FIELD(channel_mask, + VC4_HDMI_AUDIO_PACKET_CEA_MASK); + + /* Set the MAI threshold. This logic mimics the firmware's. */ +- if (hdmi->audio.samplerate > 96000) { ++ if (vc4_hdmi->audio.samplerate > 96000) { + HD_WRITE(VC4_HD_MAI_THR, + VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQHIGH) | + VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW)); +- } else if (hdmi->audio.samplerate > 48000) { ++ } else if (vc4_hdmi->audio.samplerate > 48000) { + HD_WRITE(VC4_HD_MAI_THR, + VC4_SET_FIELD(0x14, VC4_HD_MAI_THR_DREQHIGH) | + VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW)); +@@ -820,7 +820,7 @@ static int vc4_hdmi_audio_hw_params(stru + + HDMI_WRITE(VC4_HDMI_MAI_CHANNEL_MAP, channel_map); + HDMI_WRITE(VC4_HDMI_AUDIO_PACKET_CONFIG, audio_packet_config); +- vc4_hdmi_set_n_cts(hdmi); ++ vc4_hdmi_set_n_cts(vc4_hdmi); + + return 0; + } +@@ -828,8 +828,8 @@ static int vc4_hdmi_audio_hw_params(stru + static int vc4_hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) + { +- struct vc4_hdmi *hdmi = dai_to_hdmi(dai); +- struct drm_encoder *encoder = &hdmi->encoder.base.base; ++ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai); ++ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; + struct drm_device *drm = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + +@@ -840,7 +840,7 @@ static int vc4_hdmi_audio_trigger(struct + HDMI_READ(VC4_HDMI_TX_PHY_CTL0) & + ~VC4_HDMI_TX_PHY_RNG_PWRDN); + HD_WRITE(VC4_HD_MAI_CTL, +- VC4_SET_FIELD(hdmi->audio.channels, ++ VC4_SET_FIELD(vc4_hdmi->audio.channels, + VC4_HD_MAI_CTL_CHNUM) | + VC4_HD_MAI_CTL_ENABLE); + break; +@@ -872,8 +872,8 @@ static int vc4_hdmi_audio_eld_ctl_info(s + struct snd_ctl_elem_info *uinfo) + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); +- struct vc4_hdmi *hdmi = snd_component_to_hdmi(component); +- struct drm_connector *connector = &hdmi->connector.base; ++ struct vc4_hdmi *vc4_hdmi = snd_component_to_hdmi(component); ++ struct drm_connector *connector = &vc4_hdmi->connector.base; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(connector->eld); +@@ -885,8 +885,8 @@ static int vc4_hdmi_audio_eld_ctl_get(st + struct snd_ctl_elem_value *ucontrol) + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); +- struct vc4_hdmi *hdmi = snd_component_to_hdmi(component); +- struct drm_connector *connector = &hdmi->connector.base; ++ struct vc4_hdmi *vc4_hdmi = snd_component_to_hdmi(component); ++ struct drm_connector *connector = &vc4_hdmi->connector.base; + + memcpy(ucontrol->value.bytes.data, connector->eld, + sizeof(connector->eld)); +@@ -954,9 +954,9 @@ static const struct snd_soc_component_dr + + static int vc4_hdmi_audio_cpu_dai_probe(struct snd_soc_dai *dai) + { +- struct vc4_hdmi *hdmi = dai_to_hdmi(dai); ++ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai); + +- snd_soc_dai_init_dma_data(dai, &hdmi->audio.dma_data, NULL); ++ snd_soc_dai_init_dma_data(dai, &vc4_hdmi->audio.dma_data, NULL); + + return 0; + } +@@ -982,11 +982,11 @@ static const struct snd_dmaengine_pcm_co + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + }; + +-static int vc4_hdmi_audio_init(struct vc4_hdmi *hdmi) ++static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi) + { +- struct snd_soc_dai_link *dai_link = &hdmi->audio.link; +- struct snd_soc_card *card = &hdmi->audio.card; +- struct device *dev = &hdmi->pdev->dev; ++ struct snd_soc_dai_link *dai_link = &vc4_hdmi->audio.link; ++ struct snd_soc_card *card = &vc4_hdmi->audio.card; ++ struct device *dev = &vc4_hdmi->pdev->dev; + const __be32 *addr; + int ret; + int len; +@@ -1006,9 +1006,9 @@ static int vc4_hdmi_audio_init(struct vc + * This VC/MMU should probably be exposed to avoid this kind of hacks. + */ + addr = of_get_address(dev->of_node, 1, NULL, NULL); +- hdmi->audio.dma_data.addr = be32_to_cpup(addr) + VC4_HD_MAI_DATA; +- hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; +- hdmi->audio.dma_data.maxburst = 2; ++ vc4_hdmi->audio.dma_data.addr = be32_to_cpup(addr) + VC4_HD_MAI_DATA; ++ vc4_hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ vc4_hdmi->audio.dma_data.maxburst = 2; + + ret = devm_snd_dmaengine_pcm_register(dev, &pcm_conf, 0); + if (ret) { +@@ -1031,9 +1031,9 @@ static int vc4_hdmi_audio_init(struct vc + return ret; + } + +- dai_link->cpus = &hdmi->audio.cpu; +- dai_link->codecs = &hdmi->audio.codec; +- dai_link->platforms = &hdmi->audio.platform; ++ dai_link->cpus = &vc4_hdmi->audio.cpu; ++ dai_link->codecs = &vc4_hdmi->audio.codec; ++ dai_link->platforms = &vc4_hdmi->audio.platform; + + dai_link->num_cpus = 1; + dai_link->num_codecs = 1; +@@ -1058,7 +1058,7 @@ static int vc4_hdmi_audio_init(struct vc + * now stored in card->drvdata and should be retrieved with + * snd_soc_card_get_drvdata() if needed. + */ +- snd_soc_card_set_drvdata(card, hdmi); ++ snd_soc_card_set_drvdata(card, vc4_hdmi); + ret = devm_snd_soc_register_card(dev, card); + if (ret) + dev_err(dev, "Could not register sound card: %d\n", ret); +@@ -1071,20 +1071,21 @@ static int vc4_hdmi_audio_init(struct vc + static irqreturn_t vc4_cec_irq_handler_thread(int irq, void *priv) + { + struct vc4_dev *vc4 = priv; +- struct vc4_hdmi *hdmi = vc4->hdmi; ++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi; + +- if (hdmi->cec_irq_was_rx) { +- if (hdmi->cec_rx_msg.len) +- cec_received_msg(hdmi->cec_adap, &hdmi->cec_rx_msg); +- } else if (hdmi->cec_tx_ok) { +- cec_transmit_done(hdmi->cec_adap, CEC_TX_STATUS_OK, ++ if (vc4_hdmi->cec_irq_was_rx) { ++ if (vc4_hdmi->cec_rx_msg.len) ++ cec_received_msg(vc4_hdmi->cec_adap, ++ &vc4_hdmi->cec_rx_msg); ++ } else if (vc4_hdmi->cec_tx_ok) { ++ cec_transmit_done(vc4_hdmi->cec_adap, CEC_TX_STATUS_OK, + 0, 0, 0, 0); + } else { + /* + * This CEC implementation makes 1 retry, so if we + * get a NACK, then that means it made 2 attempts. + */ +- cec_transmit_done(hdmi->cec_adap, CEC_TX_STATUS_NACK, ++ cec_transmit_done(vc4_hdmi->cec_adap, CEC_TX_STATUS_NACK, + 0, 2, 0, 0); + } + return IRQ_HANDLED; +@@ -1110,23 +1111,23 @@ static void vc4_cec_read_msg(struct vc4_ + static irqreturn_t vc4_cec_irq_handler(int irq, void *priv) + { + struct vc4_dev *vc4 = priv; +- struct vc4_hdmi *hdmi = vc4->hdmi; ++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi; + u32 stat = HDMI_READ(VC4_HDMI_CPU_STATUS); + u32 cntrl1, cntrl5; + + if (!(stat & VC4_HDMI_CPU_CEC)) + return IRQ_NONE; +- hdmi->cec_rx_msg.len = 0; ++ vc4_hdmi->cec_rx_msg.len = 0; + cntrl1 = HDMI_READ(VC4_HDMI_CEC_CNTRL_1); + cntrl5 = HDMI_READ(VC4_HDMI_CEC_CNTRL_5); +- hdmi->cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT; +- if (hdmi->cec_irq_was_rx) { ++ vc4_hdmi->cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT; ++ if (vc4_hdmi->cec_irq_was_rx) { + vc4_cec_read_msg(vc4, cntrl1); + cntrl1 |= VC4_HDMI_CEC_CLEAR_RECEIVE_OFF; + HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, cntrl1); + cntrl1 &= ~VC4_HDMI_CEC_CLEAR_RECEIVE_OFF; + } else { +- hdmi->cec_tx_ok = cntrl1 & VC4_HDMI_CEC_TX_STATUS_GOOD; ++ vc4_hdmi->cec_tx_ok = cntrl1 & VC4_HDMI_CEC_TX_STATUS_GOOD; + cntrl1 &= ~VC4_HDMI_CEC_START_XMIT_BEGIN; + } + HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, cntrl1); +@@ -1228,44 +1229,44 @@ static int vc4_hdmi_bind(struct device * + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = drm->dev_private; +- struct vc4_hdmi *hdmi; ++ struct vc4_hdmi *vc4_hdmi; + struct drm_encoder *encoder; + struct device_node *ddc_node; + u32 value; + int ret; + +- hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); +- if (!hdmi) ++ vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL); ++ if (!vc4_hdmi) + return -ENOMEM; + +- hdmi->pdev = pdev; +- encoder = &hdmi->encoder.base.base; ++ vc4_hdmi->pdev = pdev; ++ encoder = &vc4_hdmi->encoder.base.base; + encoder->base.type = VC4_ENCODER_TYPE_HDMI0; + +- hdmi->hdmicore_regs = vc4_ioremap_regs(pdev, 0); +- if (IS_ERR(hdmi->hdmicore_regs)) +- return PTR_ERR(hdmi->hdmicore_regs); +- +- hdmi->hd_regs = vc4_ioremap_regs(pdev, 1); +- if (IS_ERR(hdmi->hd_regs)) +- return PTR_ERR(hdmi->hd_regs); +- +- hdmi->hdmi_regset.base = hdmi->hdmicore_regs; +- hdmi->hdmi_regset.regs = hdmi_regs; +- hdmi->hdmi_regset.nregs = ARRAY_SIZE(hdmi_regs); +- hdmi->hd_regset.base = hdmi->hd_regs; +- hdmi->hd_regset.regs = hd_regs; +- hdmi->hd_regset.nregs = ARRAY_SIZE(hd_regs); ++ vc4_hdmi->hdmicore_regs = vc4_ioremap_regs(pdev, 0); ++ if (IS_ERR(vc4_hdmi->hdmicore_regs)) ++ return PTR_ERR(vc4_hdmi->hdmicore_regs); ++ ++ vc4_hdmi->hd_regs = vc4_ioremap_regs(pdev, 1); ++ if (IS_ERR(vc4_hdmi->hd_regs)) ++ return PTR_ERR(vc4_hdmi->hd_regs); ++ ++ vc4_hdmi->hdmi_regset.base = vc4_hdmi->hdmicore_regs; ++ vc4_hdmi->hdmi_regset.regs = hdmi_regs; ++ vc4_hdmi->hdmi_regset.nregs = ARRAY_SIZE(hdmi_regs); ++ vc4_hdmi->hd_regset.base = vc4_hdmi->hd_regs; ++ vc4_hdmi->hd_regset.regs = hd_regs; ++ vc4_hdmi->hd_regset.nregs = ARRAY_SIZE(hd_regs); + +- hdmi->pixel_clock = devm_clk_get(dev, "pixel"); +- if (IS_ERR(hdmi->pixel_clock)) { ++ vc4_hdmi->pixel_clock = devm_clk_get(dev, "pixel"); ++ if (IS_ERR(vc4_hdmi->pixel_clock)) { + DRM_ERROR("Failed to get pixel clock\n"); +- return PTR_ERR(hdmi->pixel_clock); ++ return PTR_ERR(vc4_hdmi->pixel_clock); + } +- hdmi->hsm_clock = devm_clk_get(dev, "hdmi"); +- if (IS_ERR(hdmi->hsm_clock)) { ++ vc4_hdmi->hsm_clock = devm_clk_get(dev, "hdmi"); ++ if (IS_ERR(vc4_hdmi->hsm_clock)) { + DRM_ERROR("Failed to get HDMI state machine clock\n"); +- return PTR_ERR(hdmi->hsm_clock); ++ return PTR_ERR(vc4_hdmi->hsm_clock); + } + + ddc_node = of_parse_phandle(dev->of_node, "ddc", 0); +@@ -1274,9 +1275,9 @@ static int vc4_hdmi_bind(struct device * + return -ENODEV; + } + +- hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node); ++ vc4_hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node); + of_node_put(ddc_node); +- if (!hdmi->ddc) { ++ if (!vc4_hdmi->ddc) { + DRM_DEBUG("Failed to get ddc i2c adapter by node\n"); + return -EPROBE_DEFER; + } +@@ -1285,13 +1286,13 @@ static int vc4_hdmi_bind(struct device * + * needs to be a bit higher than the pixel clock rate + * (generally 148.5Mhz). + */ +- ret = clk_set_rate(hdmi->hsm_clock, HSM_CLOCK_FREQ); ++ ret = clk_set_rate(vc4_hdmi->hsm_clock, HSM_CLOCK_FREQ); + if (ret) { + DRM_ERROR("Failed to set HSM clock rate: %d\n", ret); + goto err_put_i2c; + } + +- ret = clk_prepare_enable(hdmi->hsm_clock); ++ ret = clk_prepare_enable(vc4_hdmi->hsm_clock); + if (ret) { + DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n", + ret); +@@ -1304,18 +1305,18 @@ static int vc4_hdmi_bind(struct device * + if (of_find_property(dev->of_node, "hpd-gpios", &value)) { + enum of_gpio_flags hpd_gpio_flags; + +- hdmi->hpd_gpio = of_get_named_gpio_flags(dev->of_node, ++ vc4_hdmi->hpd_gpio = of_get_named_gpio_flags(dev->of_node, + "hpd-gpios", 0, + &hpd_gpio_flags); +- if (hdmi->hpd_gpio < 0) { +- ret = hdmi->hpd_gpio; ++ if (vc4_hdmi->hpd_gpio < 0) { ++ ret = vc4_hdmi->hpd_gpio; + goto err_unprepare_hsm; + } + +- hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW; ++ vc4_hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW; + } + +- vc4->hdmi = hdmi; ++ vc4->hdmi = vc4_hdmi; + + /* HDMI core must be enabled. */ + if (!(HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE)) { +@@ -1331,21 +1332,21 @@ static int vc4_hdmi_bind(struct device * + DRM_MODE_ENCODER_TMDS, NULL); + drm_encoder_helper_add(encoder, &vc4_hdmi_encoder_helper_funcs); + +- ret = vc4_hdmi_connector_init(drm, hdmi); ++ ret = vc4_hdmi_connector_init(drm, vc4_hdmi); + if (ret) + goto err_destroy_encoder; + + #ifdef CONFIG_DRM_VC4_HDMI_CEC +- hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops, ++ vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops, + vc4, "vc4", + CEC_CAP_DEFAULTS | + CEC_CAP_CONNECTOR_INFO, 1); +- ret = PTR_ERR_OR_ZERO(hdmi->cec_adap); ++ ret = PTR_ERR_OR_ZERO(vc4_hdmi->cec_adap); + if (ret < 0) + goto err_destroy_conn; + +- cec_fill_conn_info_from_drm(&conn_info, &hdmi->connector.base); +- cec_s_conn_info(hdmi->cec_adap, &conn_info); ++ cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector.base); ++ cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info); + + HDMI_WRITE(VC4_HDMI_CPU_MASK_SET, 0xffffffff); + value = HDMI_READ(VC4_HDMI_CEC_CNTRL_1); +@@ -1364,32 +1365,32 @@ static int vc4_hdmi_bind(struct device * + "vc4 hdmi cec", vc4); + if (ret) + goto err_delete_cec_adap; +- ret = cec_register_adapter(hdmi->cec_adap, dev); ++ ret = cec_register_adapter(vc4_hdmi->cec_adap, dev); + if (ret < 0) + goto err_delete_cec_adap; + #endif + +- ret = vc4_hdmi_audio_init(hdmi); ++ ret = vc4_hdmi_audio_init(vc4_hdmi); + if (ret) + goto err_destroy_encoder; + +- vc4_debugfs_add_file(drm, "hdmi_regs", vc4_hdmi_debugfs_regs, hdmi); ++ vc4_debugfs_add_file(drm, "hdmi_regs", vc4_hdmi_debugfs_regs, vc4_hdmi); + + return 0; + + #ifdef CONFIG_DRM_VC4_HDMI_CEC + err_delete_cec_adap: +- cec_delete_adapter(hdmi->cec_adap); ++ cec_delete_adapter(vc4_hdmi->cec_adap); + err_destroy_conn: +- vc4_hdmi_connector_destroy(&hdmi->connector.base); ++ vc4_hdmi_connector_destroy(&vc4_hdmi->connector.base); + #endif + err_destroy_encoder: + vc4_hdmi_encoder_destroy(encoder); + err_unprepare_hsm: +- clk_disable_unprepare(hdmi->hsm_clock); ++ clk_disable_unprepare(vc4_hdmi->hsm_clock); + pm_runtime_disable(dev); + err_put_i2c: +- put_device(&hdmi->ddc->dev); ++ put_device(&vc4_hdmi->ddc->dev); + + return ret; + } +@@ -1399,16 +1400,16 @@ static void vc4_hdmi_unbind(struct devic + { + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = drm->dev_private; +- struct vc4_hdmi *hdmi = vc4->hdmi; ++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi; + +- cec_unregister_adapter(hdmi->cec_adap); +- vc4_hdmi_connector_destroy(&hdmi->connector.base); +- vc4_hdmi_encoder_destroy(&hdmi->encoder.base.base); ++ cec_unregister_adapter(vc4_hdmi->cec_adap); ++ vc4_hdmi_connector_destroy(&vc4_hdmi->connector.base); ++ vc4_hdmi_encoder_destroy(&vc4_hdmi->encoder.base.base); + +- clk_disable_unprepare(hdmi->hsm_clock); ++ clk_disable_unprepare(vc4_hdmi->hsm_clock); + pm_runtime_disable(dev); + +- put_device(&hdmi->ddc->dev); ++ put_device(&vc4_hdmi->ddc->dev); + + vc4->hdmi = NULL; + } diff --git a/target/linux/bcm27xx/patches-5.4/950-0576-drm-vc4-hdmi-Move-accessors-to-vc4_hdmi.patch b/target/linux/bcm27xx/patches-5.4/950-0576-drm-vc4-hdmi-Move-accessors-to-vc4_hdmi.patch new file mode 100644 index 0000000000..53296ecb4b --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0576-drm-vc4-hdmi-Move-accessors-to-vc4_hdmi.patch @@ -0,0 +1,152 @@ +From d1ced662ff5ed90a489b6610144d480bfd7a64e9 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Mon, 6 Jan 2020 18:21:44 +0100 +Subject: [PATCH] drm/vc4: hdmi: Move accessors to vc4_hdmi + +The current driver only supports a single HDMI controller, and part of +the issue is that the main vc4_dev structure holds a pointer to its +(only) HDMI controller, and the HDMI registers accessors will use it to +retrieve the mapped addresses. + +Let's modify those accessors to use directly the vc4_hdmi structure so +that we can eventually get rid of that single global pointer. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 22 ++++++++-------------- + drivers/gpu/drm/vc4/vc4_hdmi.h | 8 ++++---- + 2 files changed, 12 insertions(+), 18 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -122,6 +122,7 @@ vc4_hdmi_connector_detect(struct drm_con + { + struct drm_device *dev = connector->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi; + + if (vc4->hdmi->hpd_gpio) { + if (gpio_get_value_cansleep(vc4->hdmi->hpd_gpio) ^ +@@ -236,6 +237,7 @@ static int vc4_hdmi_stop_packet(struct d + { + struct drm_device *dev = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi; + u32 packet_id = type - 0x80; + + HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, +@@ -250,6 +252,7 @@ static void vc4_hdmi_write_infoframe(str + { + struct drm_device *dev = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi; + u32 packet_id = frame->any.type - 0x80; + u32 packet_reg = VC4_HDMI_RAM_PACKET(packet_id); + uint8_t buffer[VC4_HDMI_PACKET_STRIDE]; +@@ -632,9 +635,6 @@ static const struct drm_encoder_helper_f + /* HDMI audio codec callbacks */ + static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *vc4_hdmi) + { +- struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; +- struct drm_device *drm = encoder->dev; +- struct vc4_dev *vc4 = to_vc4_dev(drm); + u32 hsm_clock = clk_get_rate(vc4_hdmi->hsm_clock); + unsigned long n, m; + +@@ -654,8 +654,6 @@ static void vc4_hdmi_set_n_cts(struct vc + { + struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; + struct drm_crtc *crtc = encoder->crtc; +- struct drm_device *drm = encoder->dev; +- struct vc4_dev *vc4 = to_vc4_dev(drm); + const struct drm_display_mode *mode = &crtc->state->adjusted_mode; + u32 samplerate = vc4_hdmi->audio.samplerate; + u32 n, cts; +@@ -692,7 +690,6 @@ static int vc4_hdmi_audio_startup(struct + struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai); + struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; + struct drm_connector *connector = &vc4_hdmi->connector.base; +- struct vc4_dev *vc4 = to_vc4_dev(encoder->dev); + int ret; + + if (vc4_hdmi->audio.substream && vc4_hdmi->audio.substream != substream) +@@ -723,9 +720,7 @@ static int vc4_hdmi_audio_set_fmt(struct + static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi) + { + struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; +- struct drm_device *drm = encoder->dev; + struct device *dev = &vc4_hdmi->pdev->dev; +- struct vc4_dev *vc4 = to_vc4_dev(drm); + int ret; + + ret = vc4_hdmi_stop_packet(encoder, HDMI_INFOFRAME_TYPE_AUDIO); +@@ -756,10 +751,7 @@ static int vc4_hdmi_audio_hw_params(stru + struct snd_soc_dai *dai) + { + struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai); +- struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; +- struct drm_device *drm = encoder->dev; + struct device *dev = &vc4_hdmi->pdev->dev; +- struct vc4_dev *vc4 = to_vc4_dev(drm); + u32 audio_packet_config, channel_mask; + u32 channel_map, i; + +@@ -830,8 +822,6 @@ static int vc4_hdmi_audio_trigger(struct + { + struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai); + struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; +- struct drm_device *drm = encoder->dev; +- struct vc4_dev *vc4 = to_vc4_dev(drm); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: +@@ -1093,7 +1083,8 @@ static irqreturn_t vc4_cec_irq_handler_t + + static void vc4_cec_read_msg(struct vc4_dev *vc4, u32 cntrl1) + { +- struct cec_msg *msg = &vc4->hdmi->cec_rx_msg; ++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi; ++ struct cec_msg *msg = &vc4_hdmi->cec_rx_msg; + unsigned int i; + + msg->len = 1 + ((cntrl1 & VC4_HDMI_CEC_REC_WRD_CNT_MASK) >> +@@ -1139,6 +1130,7 @@ static irqreturn_t vc4_cec_irq_handler(i + static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable) + { + struct vc4_dev *vc4 = cec_get_drvdata(adap); ++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi; + /* clock period in microseconds */ + const u32 usecs = 1000000 / CEC_CLOCK_FREQ; + u32 val = HDMI_READ(VC4_HDMI_CEC_CNTRL_5); +@@ -1182,6 +1174,7 @@ static int vc4_hdmi_cec_adap_enable(stru + static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) + { + struct vc4_dev *vc4 = cec_get_drvdata(adap); ++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi; + + HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, + (HDMI_READ(VC4_HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) | +@@ -1193,6 +1186,7 @@ static int vc4_hdmi_cec_adap_transmit(st + u32 signal_free_time, struct cec_msg *msg) + { + struct vc4_dev *vc4 = cec_get_drvdata(adap); ++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi; + u32 val; + unsigned int i; + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -78,9 +78,9 @@ struct vc4_hdmi { + struct debugfs_regset32 hd_regset; + }; + +-#define HDMI_READ(offset) readl(vc4->hdmi->hdmicore_regs + offset) +-#define HDMI_WRITE(offset, val) writel(val, vc4->hdmi->hdmicore_regs + offset) +-#define HD_READ(offset) readl(vc4->hdmi->hd_regs + offset) +-#define HD_WRITE(offset, val) writel(val, vc4->hdmi->hd_regs + offset) ++#define HDMI_READ(offset) readl(vc4_hdmi->hdmicore_regs + offset) ++#define HDMI_WRITE(offset, val) writel(val, vc4_hdmi->hdmicore_regs + offset) ++#define HD_READ(offset) readl(vc4_hdmi->hd_regs + offset) ++#define HD_WRITE(offset, val) writel(val, vc4_hdmi->hd_regs + offset) + + #endif /* _VC4_HDMI_H_ */ diff --git a/target/linux/bcm27xx/patches-5.4/950-0577-drm-vc4-hdmi-Use-local-vc4_hdmi-directly.patch b/target/linux/bcm27xx/patches-5.4/950-0577-drm-vc4-hdmi-Use-local-vc4_hdmi-directly.patch new file mode 100644 index 0000000000..9b60fbb72b --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0577-drm-vc4-hdmi-Use-local-vc4_hdmi-directly.patch @@ -0,0 +1,45 @@ +From 985efd0f9da3d2b60e34d10efee969e4dfd85a12 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Mon, 6 Jan 2020 18:44:36 +0100 +Subject: [PATCH] drm/vc4: hdmi: Use local vc4_hdmi directly + +The function vc4_hdmi_connector_detect access its vc4_hdmi struct by +dereferencing the pointer in the structure vc4_dev. This will cause some +issues when we will have multiple HDMI controllers, so let's just use the +local variable for now instead of dereferencing that pointer all the time, +and we'll fix the local variable later. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -124,20 +124,20 @@ vc4_hdmi_connector_detect(struct drm_con + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_hdmi *vc4_hdmi = vc4->hdmi; + +- if (vc4->hdmi->hpd_gpio) { +- if (gpio_get_value_cansleep(vc4->hdmi->hpd_gpio) ^ +- vc4->hdmi->hpd_active_low) ++ if (vc4_hdmi->hpd_gpio) { ++ if (gpio_get_value_cansleep(vc4_hdmi->hpd_gpio) ^ ++ vc4_hdmi->hpd_active_low) + return connector_status_connected; +- cec_phys_addr_invalidate(vc4->hdmi->cec_adap); ++ cec_phys_addr_invalidate(vc4_hdmi->cec_adap); + return connector_status_disconnected; + } + +- if (drm_probe_ddc(vc4->hdmi->ddc)) ++ if (drm_probe_ddc(vc4_hdmi->ddc)) + return connector_status_connected; + + if (HDMI_READ(VC4_HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED) + return connector_status_connected; +- cec_phys_addr_invalidate(vc4->hdmi->cec_adap); ++ cec_phys_addr_invalidate(vc4_hdmi->cec_adap); + return connector_status_disconnected; + } + diff --git a/target/linux/bcm27xx/patches-5.4/950-0578-drm-vc4-hdmi-Add-container_of-macros-for-encoders-an.patch b/target/linux/bcm27xx/patches-5.4/950-0578-drm-vc4-hdmi-Add-container_of-macros-for-encoders-an.patch new file mode 100644 index 0000000000..1d70ca851c --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0578-drm-vc4-hdmi-Add-container_of-macros-for-encoders-an.patch @@ -0,0 +1,151 @@ +From fe19f02dbfd020df9b028cf2c580417c4edc31b3 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Mon, 6 Jan 2020 18:45:46 +0100 +Subject: [PATCH] drm/vc4: hdmi: Add container_of macros for encoders + and connectors + +Whenever the code needs to access the vc4_hdmi structure from a DRM +connector or encoder, it first accesses the drm_device associated to the +connector, then retrieve the drm_dev private data which gives it a +pointer to our vc4_dev, and will finally follow the vc4_hdmi pointer in +that structure. + +That will also give us some trouble when having multiple controllers, +but now that we have our encoder and connector structures that are part +of vc4_hdmi, we can simply call container_of on the DRM connector or +encoder and retrieve the vc4_hdmi structure directly. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 41 ++++++++++------------------------ + drivers/gpu/drm/vc4/vc4_hdmi.h | 16 +++++++++++++ + 2 files changed, 28 insertions(+), 29 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -120,9 +120,7 @@ static int vc4_hdmi_debugfs_regs(struct + static enum drm_connector_status + vc4_hdmi_connector_detect(struct drm_connector *connector, bool force) + { +- struct drm_device *dev = connector->dev; +- struct vc4_dev *vc4 = to_vc4_dev(dev); +- struct vc4_hdmi *vc4_hdmi = vc4->hdmi; ++ struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector); + + if (vc4_hdmi->hpd_gpio) { + if (gpio_get_value_cansleep(vc4_hdmi->hpd_gpio) ^ +@@ -149,17 +147,13 @@ static void vc4_hdmi_connector_destroy(s + + static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) + { +- struct vc4_hdmi_connector *vc4_connector = +- to_vc4_hdmi_connector(connector); +- struct drm_encoder *encoder = vc4_connector->encoder; +- struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); +- struct drm_device *dev = connector->dev; +- struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector); ++ struct vc4_hdmi_encoder *vc4_encoder = &vc4_hdmi->encoder; + int ret = 0; + struct edid *edid; + +- edid = drm_get_edid(connector, vc4->hdmi->ddc); +- cec_s_phys_addr_from_edid(vc4->hdmi->cec_adap, edid); ++ edid = drm_get_edid(connector, vc4_hdmi->ddc); ++ cec_s_phys_addr_from_edid(vc4_hdmi->cec_adap, edid); + if (!edid) + return -ENODEV; + +@@ -235,9 +229,7 @@ static const struct drm_encoder_funcs vc + static int vc4_hdmi_stop_packet(struct drm_encoder *encoder, + enum hdmi_infoframe_type type) + { +- struct drm_device *dev = encoder->dev; +- struct vc4_dev *vc4 = to_vc4_dev(dev); +- struct vc4_hdmi *vc4_hdmi = vc4->hdmi; ++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + u32 packet_id = type - 0x80; + + HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, +@@ -250,9 +242,7 @@ static int vc4_hdmi_stop_packet(struct d + static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, + union hdmi_infoframe *frame) + { +- struct drm_device *dev = encoder->dev; +- struct vc4_dev *vc4 = to_vc4_dev(dev); +- struct vc4_hdmi *vc4_hdmi = vc4->hdmi; ++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + u32 packet_id = frame->any.type - 0x80; + u32 packet_reg = VC4_HDMI_RAM_PACKET(packet_id); + uint8_t buffer[VC4_HDMI_PACKET_STRIDE]; +@@ -298,9 +288,8 @@ static void vc4_hdmi_write_infoframe(str + + static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder) + { ++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); +- struct vc4_dev *vc4 = encoder->dev->dev_private; +- struct vc4_hdmi *vc4_hdmi = vc4->hdmi; + struct drm_connector *connector = &vc4_hdmi->connector.base; + struct drm_connector_state *cstate = connector->state; + struct drm_crtc *crtc = encoder->crtc; +@@ -347,9 +336,7 @@ static void vc4_hdmi_set_spd_infoframe(s + + static void vc4_hdmi_set_audio_infoframe(struct drm_encoder *encoder) + { +- struct drm_device *drm = encoder->dev; +- struct vc4_dev *vc4 = drm->dev_private; +- struct vc4_hdmi *vc4_hdmi = vc4->hdmi; ++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + union hdmi_infoframe frame; + int ret; + +@@ -371,9 +358,7 @@ static void vc4_hdmi_set_infoframes(stru + + static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder) + { +- struct drm_device *dev = encoder->dev; +- struct vc4_dev *vc4 = to_vc4_dev(dev); +- struct vc4_hdmi *vc4_hdmi = vc4->hdmi; ++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + int ret; + + HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0); +@@ -392,10 +377,8 @@ static void vc4_hdmi_encoder_disable(str + static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder) + { + struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; +- struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); +- struct drm_device *dev = encoder->dev; +- struct vc4_dev *vc4 = to_vc4_dev(dev); +- struct vc4_hdmi *vc4_hdmi = vc4->hdmi; ++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); ++ struct vc4_hdmi_encoder *vc4_encoder = &vc4_hdmi->encoder; + bool debug_dump_regs = false; + bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; + bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC; +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -78,6 +78,22 @@ struct vc4_hdmi { + struct debugfs_regset32 hd_regset; + }; + ++static inline struct vc4_hdmi * ++connector_to_vc4_hdmi(struct drm_connector *connector) ++{ ++ struct vc4_hdmi_connector *_connector = to_vc4_hdmi_connector(connector); ++ ++ return container_of(_connector, struct vc4_hdmi, connector); ++} ++ ++static inline struct vc4_hdmi * ++encoder_to_vc4_hdmi(struct drm_encoder *encoder) ++{ ++ struct vc4_hdmi_encoder *_encoder = to_vc4_hdmi_encoder(encoder); ++ ++ return container_of(_encoder, struct vc4_hdmi, encoder); ++} ++ + #define HDMI_READ(offset) readl(vc4_hdmi->hdmicore_regs + offset) + #define HDMI_WRITE(offset, val) writel(val, vc4_hdmi->hdmicore_regs + offset) + #define HD_READ(offset) readl(vc4_hdmi->hd_regs + offset) diff --git a/target/linux/bcm27xx/patches-5.4/950-0579-drm-vc4-hdmi-Pass-vc4_hdmi-to-CEC-code.patch b/target/linux/bcm27xx/patches-5.4/950-0579-drm-vc4-hdmi-Pass-vc4_hdmi-to-CEC-code.patch new file mode 100644 index 0000000000..b0abafc699 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0579-drm-vc4-hdmi-Pass-vc4_hdmi-to-CEC-code.patch @@ -0,0 +1,107 @@ +From 8af2552e862100e843b8d1f36543b718dde393ad Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Mon, 6 Jan 2020 18:47:53 +0100 +Subject: [PATCH] drm/vc4: hdmi: Pass vc4_hdmi to CEC code + +Our CEC code also retrieves the associated vc4_hdmi by setting the +vc4_dev pointer as its private data, and then dereferences its vc4_hdmi +pointer. + +In order to eventually get rid of that pointer, we can simply pass the +vc4_hdmi pointer directly. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 24 +++++++++--------------- + 1 file changed, 9 insertions(+), 15 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -1043,8 +1043,7 @@ static int vc4_hdmi_audio_init(struct vc + #ifdef CONFIG_DRM_VC4_HDMI_CEC + static irqreturn_t vc4_cec_irq_handler_thread(int irq, void *priv) + { +- struct vc4_dev *vc4 = priv; +- struct vc4_hdmi *vc4_hdmi = vc4->hdmi; ++ struct vc4_hdmi *vc4_hdmi = priv; + + if (vc4_hdmi->cec_irq_was_rx) { + if (vc4_hdmi->cec_rx_msg.len) +@@ -1064,9 +1063,8 @@ static irqreturn_t vc4_cec_irq_handler_t + return IRQ_HANDLED; + } + +-static void vc4_cec_read_msg(struct vc4_dev *vc4, u32 cntrl1) ++static void vc4_cec_read_msg(struct vc4_hdmi *vc4_hdmi, u32 cntrl1) + { +- struct vc4_hdmi *vc4_hdmi = vc4->hdmi; + struct cec_msg *msg = &vc4_hdmi->cec_rx_msg; + unsigned int i; + +@@ -1084,8 +1082,7 @@ static void vc4_cec_read_msg(struct vc4_ + + static irqreturn_t vc4_cec_irq_handler(int irq, void *priv) + { +- struct vc4_dev *vc4 = priv; +- struct vc4_hdmi *vc4_hdmi = vc4->hdmi; ++ struct vc4_hdmi *vc4_hdmi = priv; + u32 stat = HDMI_READ(VC4_HDMI_CPU_STATUS); + u32 cntrl1, cntrl5; + +@@ -1096,7 +1093,7 @@ static irqreturn_t vc4_cec_irq_handler(i + cntrl5 = HDMI_READ(VC4_HDMI_CEC_CNTRL_5); + vc4_hdmi->cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT; + if (vc4_hdmi->cec_irq_was_rx) { +- vc4_cec_read_msg(vc4, cntrl1); ++ vc4_cec_read_msg(vc4_hdmi, cntrl1); + cntrl1 |= VC4_HDMI_CEC_CLEAR_RECEIVE_OFF; + HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, cntrl1); + cntrl1 &= ~VC4_HDMI_CEC_CLEAR_RECEIVE_OFF; +@@ -1112,8 +1109,7 @@ static irqreturn_t vc4_cec_irq_handler(i + + static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable) + { +- struct vc4_dev *vc4 = cec_get_drvdata(adap); +- struct vc4_hdmi *vc4_hdmi = vc4->hdmi; ++ struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); + /* clock period in microseconds */ + const u32 usecs = 1000000 / CEC_CLOCK_FREQ; + u32 val = HDMI_READ(VC4_HDMI_CEC_CNTRL_5); +@@ -1156,8 +1152,7 @@ static int vc4_hdmi_cec_adap_enable(stru + + static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) + { +- struct vc4_dev *vc4 = cec_get_drvdata(adap); +- struct vc4_hdmi *vc4_hdmi = vc4->hdmi; ++ struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); + + HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, + (HDMI_READ(VC4_HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) | +@@ -1168,8 +1163,7 @@ static int vc4_hdmi_cec_adap_log_addr(st + static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) + { +- struct vc4_dev *vc4 = cec_get_drvdata(adap); +- struct vc4_hdmi *vc4_hdmi = vc4->hdmi; ++ struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); + u32 val; + unsigned int i; + +@@ -1315,7 +1309,7 @@ static int vc4_hdmi_bind(struct device * + + #ifdef CONFIG_DRM_VC4_HDMI_CEC + vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops, +- vc4, "vc4", ++ vc4_hdmi, "vc4", + CEC_CAP_DEFAULTS | + CEC_CAP_CONNECTOR_INFO, 1); + ret = PTR_ERR_OR_ZERO(vc4_hdmi->cec_adap); +@@ -1339,7 +1333,7 @@ static int vc4_hdmi_bind(struct device * + ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0), + vc4_cec_irq_handler, + vc4_cec_irq_handler_thread, 0, +- "vc4 hdmi cec", vc4); ++ "vc4 hdmi cec", vc4_hdmi); + if (ret) + goto err_delete_cec_adap; + ret = cec_register_adapter(vc4_hdmi->cec_adap, dev); diff --git a/target/linux/bcm27xx/patches-5.4/950-0580-drm-vc4-hdmi-Remove-vc4_dev-hdmi-pointer.patch b/target/linux/bcm27xx/patches-5.4/950-0580-drm-vc4-hdmi-Remove-vc4_dev-hdmi-pointer.patch new file mode 100644 index 0000000000..ff9fa1a158 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0580-drm-vc4-hdmi-Remove-vc4_dev-hdmi-pointer.patch @@ -0,0 +1,67 @@ +From 9e56da09cb8d8f65a26cfa0a957e295646ca47f8 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Mon, 6 Jan 2020 18:49:11 +0100 +Subject: [PATCH] drm/vc4: hdmi: Remove vc4_dev hdmi pointer + +Now that we don't have any users anymore, we can kill that pointer. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_drv.h | 1 - + drivers/gpu/drm/vc4/vc4_hdmi.c | 14 ++++++-------- + 2 files changed, 6 insertions(+), 9 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -76,7 +76,6 @@ struct vc4_dev { + bool firmware_kms; + struct rpi_firmware *firmware; + +- struct vc4_hdmi *hdmi; + struct vc4_hvs *hvs; + struct vc4_v3d *v3d; + struct vc4_dpi *dpi; +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -1199,7 +1199,6 @@ static int vc4_hdmi_bind(struct device * + #endif + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = dev_get_drvdata(master); +- struct vc4_dev *vc4 = drm->dev_private; + struct vc4_hdmi *vc4_hdmi; + struct drm_encoder *encoder; + struct device_node *ddc_node; +@@ -1287,8 +1286,6 @@ static int vc4_hdmi_bind(struct device * + vc4_hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW; + } + +- vc4->hdmi = vc4_hdmi; +- + /* HDMI core must be enabled. */ + if (!(HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE)) { + HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST); +@@ -1369,9 +1366,12 @@ err_put_i2c: + static void vc4_hdmi_unbind(struct device *dev, struct device *master, + void *data) + { +- struct drm_device *drm = dev_get_drvdata(master); +- struct vc4_dev *vc4 = drm->dev_private; +- struct vc4_hdmi *vc4_hdmi = vc4->hdmi; ++ /* ++ * snd_soc_register_card will set the device drvdata pointer ++ * to the card being registered. ++ */ ++ struct snd_soc_card *card = dev_get_drvdata(dev); ++ struct vc4_hdmi *vc4_hdmi = snd_soc_card_get_drvdata(card); + + cec_unregister_adapter(vc4_hdmi->cec_adap); + vc4_hdmi_connector_destroy(&vc4_hdmi->connector.base); +@@ -1381,8 +1381,6 @@ static void vc4_hdmi_unbind(struct devic + pm_runtime_disable(dev); + + put_device(&vc4_hdmi->ddc->dev); +- +- vc4->hdmi = NULL; + } + + static const struct component_ops vc4_hdmi_ops = { diff --git a/target/linux/bcm27xx/patches-5.4/950-0581-drm-vc4-hdmi-Remove-vc4_hdmi_connector.patch b/target/linux/bcm27xx/patches-5.4/950-0581-drm-vc4-hdmi-Remove-vc4_hdmi_connector.patch new file mode 100644 index 0000000000..4ef3d69b2c --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0581-drm-vc4-hdmi-Remove-vc4_hdmi_connector.patch @@ -0,0 +1,141 @@ +From 6fdf2c94a028e04e1e20791aae5e0adaf905df77 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Mon, 6 Jan 2020 18:57:16 +0100 +Subject: [PATCH] drm/vc4: hdmi: Remove vc4_hdmi_connector + +The vc4_hdmi_connector was only used to switch between drm_connector to +drm_encoder. However, we can now use vc4_hdmi to do the switch, so that +structure is redundant. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 19 ++++++++----------- + drivers/gpu/drm/vc4/vc4_hdmi.h | 23 ++--------------------- + 2 files changed, 10 insertions(+), 32 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -188,13 +188,10 @@ static const struct drm_connector_helper + static int vc4_hdmi_connector_init(struct drm_device *dev, + struct vc4_hdmi *vc4_hdmi) + { +- struct vc4_hdmi_connector *hdmi_connector = &vc4_hdmi->connector; +- struct drm_connector *connector = &hdmi_connector->base; ++ struct drm_connector *connector = &vc4_hdmi->connector; + struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; + int ret; + +- hdmi_connector->encoder = encoder; +- + drm_connector_init(dev, connector, &vc4_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA); + drm_connector_helper_add(connector, &vc4_hdmi_connector_helper_funcs); +@@ -290,7 +287,7 @@ static void vc4_hdmi_set_avi_infoframe(s + { + struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); +- struct drm_connector *connector = &vc4_hdmi->connector.base; ++ struct drm_connector *connector = &vc4_hdmi->connector; + struct drm_connector_state *cstate = connector->state; + struct drm_crtc *crtc = encoder->crtc; + const struct drm_display_mode *mode = &crtc->state->adjusted_mode; +@@ -672,7 +669,7 @@ static int vc4_hdmi_audio_startup(struct + { + struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai); + struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; +- struct drm_connector *connector = &vc4_hdmi->connector.base; ++ struct drm_connector *connector = &vc4_hdmi->connector; + int ret; + + if (vc4_hdmi->audio.substream && vc4_hdmi->audio.substream != substream) +@@ -846,7 +843,7 @@ static int vc4_hdmi_audio_eld_ctl_info(s + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct vc4_hdmi *vc4_hdmi = snd_component_to_hdmi(component); +- struct drm_connector *connector = &vc4_hdmi->connector.base; ++ struct drm_connector *connector = &vc4_hdmi->connector; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(connector->eld); +@@ -859,7 +856,7 @@ static int vc4_hdmi_audio_eld_ctl_get(st + { + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct vc4_hdmi *vc4_hdmi = snd_component_to_hdmi(component); +- struct drm_connector *connector = &vc4_hdmi->connector.base; ++ struct drm_connector *connector = &vc4_hdmi->connector; + + memcpy(ucontrol->value.bytes.data, connector->eld, + sizeof(connector->eld)); +@@ -1313,7 +1310,7 @@ static int vc4_hdmi_bind(struct device * + if (ret < 0) + goto err_destroy_conn; + +- cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector.base); ++ cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector); + cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info); + + HDMI_WRITE(VC4_HDMI_CPU_MASK_SET, 0xffffffff); +@@ -1350,7 +1347,7 @@ static int vc4_hdmi_bind(struct device * + err_delete_cec_adap: + cec_delete_adapter(vc4_hdmi->cec_adap); + err_destroy_conn: +- vc4_hdmi_connector_destroy(&vc4_hdmi->connector.base); ++ vc4_hdmi_connector_destroy(&vc4_hdmi->connector); + #endif + err_destroy_encoder: + vc4_hdmi_encoder_destroy(encoder); +@@ -1374,7 +1371,7 @@ static void vc4_hdmi_unbind(struct devic + struct vc4_hdmi *vc4_hdmi = snd_soc_card_get_drvdata(card); + + cec_unregister_adapter(vc4_hdmi->cec_adap); +- vc4_hdmi_connector_destroy(&vc4_hdmi->connector.base); ++ vc4_hdmi_connector_destroy(&vc4_hdmi->connector); + vc4_hdmi_encoder_destroy(&vc4_hdmi->encoder.base.base); + + clk_disable_unprepare(vc4_hdmi->hsm_clock); +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -21,23 +21,6 @@ to_vc4_hdmi_encoder(struct drm_encoder * + return container_of(encoder, struct vc4_hdmi_encoder, base.base); + } + +-/* VC4 HDMI connector KMS struct */ +-struct vc4_hdmi_connector { +- struct drm_connector base; +- +- /* Since the connector is attached to just the one encoder, +- * this is the reference to it so we can do the best_encoder() +- * hook. +- */ +- struct drm_encoder *encoder; +-}; +- +-static inline struct vc4_hdmi_connector * +-to_vc4_hdmi_connector(struct drm_connector *connector) +-{ +- return container_of(connector, struct vc4_hdmi_connector, base); +-} +- + /* HDMI audio information */ + struct vc4_hdmi_audio { + struct snd_soc_card card; +@@ -56,7 +39,7 @@ struct vc4_hdmi { + struct platform_device *pdev; + + struct vc4_hdmi_encoder encoder; +- struct vc4_hdmi_connector connector; ++ struct drm_connector connector; + + struct vc4_hdmi_audio audio; + +@@ -81,9 +64,7 @@ struct vc4_hdmi { + static inline struct vc4_hdmi * + connector_to_vc4_hdmi(struct drm_connector *connector) + { +- struct vc4_hdmi_connector *_connector = to_vc4_hdmi_connector(connector); +- +- return container_of(_connector, struct vc4_hdmi, connector); ++ return container_of(connector, struct vc4_hdmi, connector); + } + + static inline struct vc4_hdmi * diff --git a/target/linux/bcm27xx/patches-5.4/950-0582-drm-vc4-hdmi-Introduce-resource-init-and-variant.patch b/target/linux/bcm27xx/patches-5.4/950-0582-drm-vc4-hdmi-Introduce-resource-init-and-variant.patch new file mode 100644 index 0000000000..82fae40246 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0582-drm-vc4-hdmi-Introduce-resource-init-and-variant.patch @@ -0,0 +1,151 @@ +From 9fa3342da883f6e111952768b36ca1df4d529660 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Wed, 18 Dec 2019 11:30:54 +0100 +Subject: [PATCH] drm/vc4: hdmi: Introduce resource init and variant + +The HDMI controllers found in the BCM2711 has a pretty different clock and +registers areas than found in the older BCM283x SoCs. + +Let's create a variant structure to store the various adjustments we'll +need later on, and a function to get the resources needed for one +particular version. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 67 ++++++++++++++++++++++------------ + drivers/gpu/drm/vc4/vc4_hdmi.h | 10 +++++ + 2 files changed, 54 insertions(+), 23 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -1189,38 +1189,23 @@ static const struct cec_adap_ops vc4_hdm + }; + #endif + +-static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) ++static int vc4_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi) + { +-#ifdef CONFIG_DRM_VC4_HDMI_CEC +- struct cec_connector_info conn_info; +-#endif +- struct platform_device *pdev = to_platform_device(dev); +- struct drm_device *drm = dev_get_drvdata(master); +- struct vc4_hdmi *vc4_hdmi; +- struct drm_encoder *encoder; +- struct device_node *ddc_node; +- u32 value; +- int ret; +- +- vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL); +- if (!vc4_hdmi) +- return -ENOMEM; +- +- vc4_hdmi->pdev = pdev; +- encoder = &vc4_hdmi->encoder.base.base; +- encoder->base.type = VC4_ENCODER_TYPE_HDMI0; ++ struct platform_device *pdev = vc4_hdmi->pdev; ++ struct device *dev = &pdev->dev; + + vc4_hdmi->hdmicore_regs = vc4_ioremap_regs(pdev, 0); + if (IS_ERR(vc4_hdmi->hdmicore_regs)) + return PTR_ERR(vc4_hdmi->hdmicore_regs); + ++ vc4_hdmi->hdmi_regset.base = vc4_hdmi->hdmicore_regs; ++ vc4_hdmi->hdmi_regset.regs = hdmi_regs; ++ vc4_hdmi->hdmi_regset.nregs = ARRAY_SIZE(hdmi_regs); ++ + vc4_hdmi->hd_regs = vc4_ioremap_regs(pdev, 1); + if (IS_ERR(vc4_hdmi->hd_regs)) + return PTR_ERR(vc4_hdmi->hd_regs); + +- vc4_hdmi->hdmi_regset.base = vc4_hdmi->hdmicore_regs; +- vc4_hdmi->hdmi_regset.regs = hdmi_regs; +- vc4_hdmi->hdmi_regset.nregs = ARRAY_SIZE(hdmi_regs); + vc4_hdmi->hd_regset.base = vc4_hdmi->hd_regs; + vc4_hdmi->hd_regset.regs = hd_regs; + vc4_hdmi->hd_regset.nregs = ARRAY_SIZE(hd_regs); +@@ -1230,12 +1215,44 @@ static int vc4_hdmi_bind(struct device * + DRM_ERROR("Failed to get pixel clock\n"); + return PTR_ERR(vc4_hdmi->pixel_clock); + } ++ + vc4_hdmi->hsm_clock = devm_clk_get(dev, "hdmi"); + if (IS_ERR(vc4_hdmi->hsm_clock)) { + DRM_ERROR("Failed to get HDMI state machine clock\n"); + return PTR_ERR(vc4_hdmi->hsm_clock); + } + ++ return 0; ++} ++ ++static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) ++{ ++#ifdef CONFIG_DRM_VC4_HDMI_CEC ++ struct cec_connector_info conn_info; ++#endif ++ struct platform_device *pdev = to_platform_device(dev); ++ struct drm_device *drm = dev_get_drvdata(master); ++ const struct vc4_hdmi_variant *variant; ++ struct vc4_hdmi *vc4_hdmi; ++ struct drm_encoder *encoder; ++ struct device_node *ddc_node; ++ u32 value; ++ int ret; ++ ++ vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL); ++ if (!vc4_hdmi) ++ return -ENOMEM; ++ ++ vc4_hdmi->pdev = pdev; ++ variant = of_device_get_match_data(dev); ++ vc4_hdmi->variant = variant; ++ vc4_hdmi->encoder.base.type = VC4_ENCODER_TYPE_HDMI0; ++ encoder = &vc4_hdmi->encoder.base.base; ++ ++ ret = variant->init_resources(vc4_hdmi); ++ if (ret) ++ return ret; ++ + ddc_node = of_parse_phandle(dev->of_node, "ddc", 0); + if (!ddc_node) { + DRM_ERROR("Failed to find ddc node in device tree\n"); +@@ -1396,8 +1413,12 @@ static int vc4_hdmi_dev_remove(struct pl + return 0; + } + ++static const struct vc4_hdmi_variant bcm2835_variant = { ++ .init_resources = vc4_hdmi_init_resources, ++}; ++ + static const struct of_device_id vc4_hdmi_dt_match[] = { +- { .compatible = "brcm,bcm2835-hdmi" }, ++ { .compatible = "brcm,bcm2835-hdmi", .data = &bcm2835_variant }, + {} + }; + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -21,6 +21,15 @@ to_vc4_hdmi_encoder(struct drm_encoder * + return container_of(encoder, struct vc4_hdmi_encoder, base.base); + } + ++struct vc4_hdmi; ++ ++struct vc4_hdmi_variant { ++ /* Callback to get the resources (memory region, interrupts, ++ * clocks, etc) for that variant. ++ */ ++ int (*init_resources)(struct vc4_hdmi *vc4_hdmi); ++}; ++ + /* HDMI audio information */ + struct vc4_hdmi_audio { + struct snd_soc_card card; +@@ -37,6 +46,7 @@ struct vc4_hdmi_audio { + /* General HDMI hardware state. */ + struct vc4_hdmi { + struct platform_device *pdev; ++ const struct vc4_hdmi_variant *variant; + + struct vc4_hdmi_encoder encoder; + struct drm_connector connector; diff --git a/target/linux/bcm27xx/patches-5.4/950-0583-drm-vc4-hdmi-Implement-a-register-layout-abstraction.patch b/target/linux/bcm27xx/patches-5.4/950-0583-drm-vc4-hdmi-Implement-a-register-layout-abstraction.patch new file mode 100644 index 0000000000..eaeca92bfd --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0583-drm-vc4-hdmi-Implement-a-register-layout-abstraction.patch @@ -0,0 +1,1310 @@ +From 261b3072275937fe64af287c1b61cbb63aca830e Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Wed, 18 Dec 2019 19:15:08 +0100 +Subject: [PATCH] drm/vc4: hdmi: Implement a register layout + abstraction + +The HDMI controllers found in the BCM2711 have most of the registers +reorganized in multiple registers areas and at different offsets than +previously found. + +The logic however remains pretty much the same, so it doesn't really make +sense to create a whole new driver and we should share the code as much as +possible. + +Let's implement some indirection to wrap around a register and depending on +the variant will lookup the associated register on that particular variant. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 354 ++++++++++++++-------------- + drivers/gpu/drm/vc4/vc4_hdmi.h | 12 +- + drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 250 ++++++++++++++++++++ + drivers/gpu/drm/vc4/vc4_regs.h | 92 -------- + 4 files changed, 437 insertions(+), 271 deletions(-) + create mode 100644 drivers/gpu/drm/vc4/vc4_hdmi_regs.h + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -49,62 +49,13 @@ + #include "media/cec.h" + #include "vc4_drv.h" + #include "vc4_hdmi.h" ++#include "vc4_hdmi_regs.h" + #include "vc4_regs.h" + + #define HSM_CLOCK_FREQ 163682864 + #define CEC_CLOCK_FREQ 40000 + #define CEC_CLOCK_DIV (HSM_CLOCK_FREQ / CEC_CLOCK_FREQ) + +-static const struct debugfs_reg32 hdmi_regs[] = { +- VC4_REG32(VC4_HDMI_CORE_REV), +- VC4_REG32(VC4_HDMI_SW_RESET_CONTROL), +- VC4_REG32(VC4_HDMI_HOTPLUG_INT), +- VC4_REG32(VC4_HDMI_HOTPLUG), +- VC4_REG32(VC4_HDMI_MAI_CHANNEL_MAP), +- VC4_REG32(VC4_HDMI_MAI_CONFIG), +- VC4_REG32(VC4_HDMI_MAI_FORMAT), +- VC4_REG32(VC4_HDMI_AUDIO_PACKET_CONFIG), +- VC4_REG32(VC4_HDMI_RAM_PACKET_CONFIG), +- VC4_REG32(VC4_HDMI_HORZA), +- VC4_REG32(VC4_HDMI_HORZB), +- VC4_REG32(VC4_HDMI_FIFO_CTL), +- VC4_REG32(VC4_HDMI_SCHEDULER_CONTROL), +- VC4_REG32(VC4_HDMI_VERTA0), +- VC4_REG32(VC4_HDMI_VERTA1), +- VC4_REG32(VC4_HDMI_VERTB0), +- VC4_REG32(VC4_HDMI_VERTB1), +- VC4_REG32(VC4_HDMI_TX_PHY_RESET_CTL), +- VC4_REG32(VC4_HDMI_TX_PHY_CTL0), +- +- VC4_REG32(VC4_HDMI_CEC_CNTRL_1), +- VC4_REG32(VC4_HDMI_CEC_CNTRL_2), +- VC4_REG32(VC4_HDMI_CEC_CNTRL_3), +- VC4_REG32(VC4_HDMI_CEC_CNTRL_4), +- VC4_REG32(VC4_HDMI_CEC_CNTRL_5), +- VC4_REG32(VC4_HDMI_CPU_STATUS), +- VC4_REG32(VC4_HDMI_CPU_MASK_STATUS), +- +- VC4_REG32(VC4_HDMI_CEC_RX_DATA_1), +- VC4_REG32(VC4_HDMI_CEC_RX_DATA_2), +- VC4_REG32(VC4_HDMI_CEC_RX_DATA_3), +- VC4_REG32(VC4_HDMI_CEC_RX_DATA_4), +- VC4_REG32(VC4_HDMI_CEC_TX_DATA_1), +- VC4_REG32(VC4_HDMI_CEC_TX_DATA_2), +- VC4_REG32(VC4_HDMI_CEC_TX_DATA_3), +- VC4_REG32(VC4_HDMI_CEC_TX_DATA_4), +-}; +- +-static const struct debugfs_reg32 hd_regs[] = { +- VC4_REG32(VC4_HD_M_CTL), +- VC4_REG32(VC4_HD_MAI_CTL), +- VC4_REG32(VC4_HD_MAI_THR), +- VC4_REG32(VC4_HD_MAI_FMT), +- VC4_REG32(VC4_HD_MAI_SMP), +- VC4_REG32(VC4_HD_VID_CTL), +- VC4_REG32(VC4_HD_CSC_CTL), +- VC4_REG32(VC4_HD_FRAME_COUNT), +-}; +- + static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) + { + struct drm_info_node *node = (struct drm_info_node *)m->private; +@@ -133,7 +84,7 @@ vc4_hdmi_connector_detect(struct drm_con + if (drm_probe_ddc(vc4_hdmi->ddc)) + return connector_status_connected; + +- if (HDMI_READ(VC4_HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED) ++ if (HDMI_READ(HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED) + return connector_status_connected; + cec_phys_addr_invalidate(vc4_hdmi->cec_adap); + return connector_status_disconnected; +@@ -229,10 +180,10 @@ static int vc4_hdmi_stop_packet(struct d + struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + u32 packet_id = type - 0x80; + +- HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, +- HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) & ~BIT(packet_id)); ++ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, ++ HDMI_READ(HDMI_RAM_PACKET_CONFIG) & ~BIT(packet_id)); + +- return wait_for(!(HDMI_READ(VC4_HDMI_RAM_PACKET_STATUS) & ++ return wait_for(!(HDMI_READ(HDMI_RAM_PACKET_STATUS) & + BIT(packet_id)), 100); + } + +@@ -241,12 +192,16 @@ static void vc4_hdmi_write_infoframe(str + { + struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + u32 packet_id = frame->any.type - 0x80; +- u32 packet_reg = VC4_HDMI_RAM_PACKET(packet_id); ++ const struct vc4_hdmi_register *ram_packet_start = ++ &vc4_hdmi->variant->registers[HDMI_RAM_PACKET_START]; ++ u32 packet_reg = ram_packet_start->offset + VC4_HDMI_PACKET_STRIDE * packet_id; ++ void __iomem *base = __vc4_hdmi_get_field_base(vc4_hdmi, ++ ram_packet_start->reg); + uint8_t buffer[VC4_HDMI_PACKET_STRIDE]; + ssize_t len, i; + int ret; + +- WARN_ONCE(!(HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) & ++ WARN_ONCE(!(HDMI_READ(HDMI_RAM_PACKET_CONFIG) & + VC4_HDMI_RAM_PACKET_ENABLE), + "Packet RAM has to be on to store the packet."); + +@@ -261,23 +216,23 @@ static void vc4_hdmi_write_infoframe(str + } + + for (i = 0; i < len; i += 7) { +- HDMI_WRITE(packet_reg, +- buffer[i + 0] << 0 | +- buffer[i + 1] << 8 | +- buffer[i + 2] << 16); ++ writel(buffer[i + 0] << 0 | ++ buffer[i + 1] << 8 | ++ buffer[i + 2] << 16, ++ base + packet_reg); + packet_reg += 4; + +- HDMI_WRITE(packet_reg, +- buffer[i + 3] << 0 | +- buffer[i + 4] << 8 | +- buffer[i + 5] << 16 | +- buffer[i + 6] << 24); ++ writel(buffer[i + 3] << 0 | ++ buffer[i + 4] << 8 | ++ buffer[i + 5] << 16 | ++ buffer[i + 6] << 24, ++ base + packet_reg); + packet_reg += 4; + } + +- HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, +- HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) | BIT(packet_id)); +- ret = wait_for((HDMI_READ(VC4_HDMI_RAM_PACKET_STATUS) & ++ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, ++ HDMI_READ(HDMI_RAM_PACKET_CONFIG) | BIT(packet_id)); ++ ret = wait_for((HDMI_READ(HDMI_RAM_PACKET_STATUS) & + BIT(packet_id)), 100); + if (ret) + DRM_ERROR("Failed to wait for infoframe to start: %d\n", ret); +@@ -358,11 +313,11 @@ static void vc4_hdmi_encoder_disable(str + struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + int ret; + +- HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0); ++ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0); + +- HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16); +- HD_WRITE(VC4_HD_VID_CTL, +- HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE); ++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16); ++ HDMI_WRITE(HDMI_VID_CTL, ++ HDMI_READ(HDMI_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE); + + clk_disable_unprepare(vc4_hdmi->pixel_clock); + +@@ -417,18 +372,18 @@ static void vc4_hdmi_encoder_enable(stru + return; + } + +- HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, ++ HDMI_WRITE(HDMI_SW_RESET_CONTROL, + VC4_HDMI_SW_RESET_HDMI | + VC4_HDMI_SW_RESET_FORMAT_DETECT); + +- HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, 0); ++ HDMI_WRITE(HDMI_SW_RESET_CONTROL, 0); + + /* PHY should be in reset, like + * vc4_hdmi_encoder_disable() does. + */ +- HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16); ++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16); + +- HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0); ++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0); + + if (debug_dump_regs) { + struct drm_printer p = drm_info_printer(&vc4_hdmi->pdev->dev); +@@ -438,20 +393,20 @@ static void vc4_hdmi_encoder_enable(stru + drm_print_regset32(&p, &vc4_hdmi->hd_regset); + } + +- HD_WRITE(VC4_HD_VID_CTL, 0); ++ HDMI_WRITE(HDMI_VID_CTL, 0); + +- HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL, +- HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) | ++ HDMI_WRITE(HDMI_SCHEDULER_CONTROL, ++ HDMI_READ(HDMI_SCHEDULER_CONTROL) | + VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT | + VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS); + +- HDMI_WRITE(VC4_HDMI_HORZA, ++ HDMI_WRITE(HDMI_HORZA, + (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) | + (hsync_pos ? VC4_HDMI_HORZA_HPOS : 0) | + VC4_SET_FIELD(mode->hdisplay * pixel_rep, + VC4_HDMI_HORZA_HAP)); + +- HDMI_WRITE(VC4_HDMI_HORZB, ++ HDMI_WRITE(HDMI_HORZB, + VC4_SET_FIELD((mode->htotal - + mode->hsync_end) * pixel_rep, + VC4_HDMI_HORZB_HBP) | +@@ -462,13 +417,13 @@ static void vc4_hdmi_encoder_enable(stru + mode->hdisplay) * pixel_rep, + VC4_HDMI_HORZB_HFP)); + +- HDMI_WRITE(VC4_HDMI_VERTA0, verta); +- HDMI_WRITE(VC4_HDMI_VERTA1, verta); ++ HDMI_WRITE(HDMI_VERTA0, verta); ++ HDMI_WRITE(HDMI_VERTA1, verta); + +- HDMI_WRITE(VC4_HDMI_VERTB0, vertb_even); +- HDMI_WRITE(VC4_HDMI_VERTB1, vertb); ++ HDMI_WRITE(HDMI_VERTB0, vertb_even); ++ HDMI_WRITE(HDMI_VERTB1, vertb); + +- HD_WRITE(VC4_HD_VID_CTL, ++ HDMI_WRITE(HDMI_VID_CTL, + (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) | + (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW)); + +@@ -493,21 +448,21 @@ static void vc4_hdmi_encoder_enable(stru + csc_ctl |= VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM, + VC4_HD_CSC_CTL_MODE); + +- HD_WRITE(VC4_HD_CSC_12_11, (0x000 << 16) | 0x000); +- HD_WRITE(VC4_HD_CSC_14_13, (0x100 << 16) | 0x6e0); +- HD_WRITE(VC4_HD_CSC_22_21, (0x6e0 << 16) | 0x000); +- HD_WRITE(VC4_HD_CSC_24_23, (0x100 << 16) | 0x000); +- HD_WRITE(VC4_HD_CSC_32_31, (0x000 << 16) | 0x6e0); +- HD_WRITE(VC4_HD_CSC_34_33, (0x100 << 16) | 0x000); ++ HDMI_WRITE(HDMI_CSC_12_11, (0x000 << 16) | 0x000); ++ HDMI_WRITE(HDMI_CSC_14_13, (0x100 << 16) | 0x6e0); ++ HDMI_WRITE(HDMI_CSC_22_21, (0x6e0 << 16) | 0x000); ++ HDMI_WRITE(HDMI_CSC_24_23, (0x100 << 16) | 0x000); ++ HDMI_WRITE(HDMI_CSC_32_31, (0x000 << 16) | 0x6e0); ++ HDMI_WRITE(HDMI_CSC_34_33, (0x100 << 16) | 0x000); + vc4_encoder->limited_rgb_range = true; + } else { + vc4_encoder->limited_rgb_range = false; + } + + /* The RGB order applies even when CSC is disabled. */ +- HD_WRITE(VC4_HD_CSC_CTL, csc_ctl); ++ HDMI_WRITE(HDMI_CSC_CTL, csc_ctl); + +- HDMI_WRITE(VC4_HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N); ++ HDMI_WRITE(HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N); + + if (debug_dump_regs) { + struct drm_printer p = drm_info_printer(&vc4_hdmi->pdev->dev); +@@ -517,30 +472,30 @@ static void vc4_hdmi_encoder_enable(stru + drm_print_regset32(&p, &vc4_hdmi->hd_regset); + } + +- HD_WRITE(VC4_HD_VID_CTL, +- HD_READ(VC4_HD_VID_CTL) | +- VC4_HD_VID_CTL_ENABLE | +- VC4_HD_VID_CTL_UNDERFLOW_ENABLE | +- VC4_HD_VID_CTL_FRAME_COUNTER_RESET); ++ HDMI_WRITE(HDMI_VID_CTL, ++ HDMI_READ(HDMI_VID_CTL) | ++ VC4_HD_VID_CTL_ENABLE | ++ VC4_HD_VID_CTL_UNDERFLOW_ENABLE | ++ VC4_HD_VID_CTL_FRAME_COUNTER_RESET); + + if (vc4_encoder->hdmi_monitor) { +- HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL, +- HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) | ++ HDMI_WRITE(HDMI_SCHEDULER_CONTROL, ++ HDMI_READ(HDMI_SCHEDULER_CONTROL) | + VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI); + +- ret = wait_for(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) & ++ ret = wait_for(HDMI_READ(HDMI_SCHEDULER_CONTROL) & + VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE, 1000); + WARN_ONCE(ret, "Timeout waiting for " + "VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE\n"); + } else { +- HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, +- HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) & ++ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, ++ HDMI_READ(HDMI_RAM_PACKET_CONFIG) & + ~(VC4_HDMI_RAM_PACKET_ENABLE)); +- HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL, +- HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) & ++ HDMI_WRITE(HDMI_SCHEDULER_CONTROL, ++ HDMI_READ(HDMI_SCHEDULER_CONTROL) & + ~VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI); + +- ret = wait_for(!(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) & ++ ret = wait_for(!(HDMI_READ(HDMI_SCHEDULER_CONTROL) & + VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE), 1000); + WARN_ONCE(ret, "Timeout waiting for " + "!VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE\n"); +@@ -549,31 +504,31 @@ static void vc4_hdmi_encoder_enable(stru + if (vc4_encoder->hdmi_monitor) { + u32 drift; + +- WARN_ON(!(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) & ++ WARN_ON(!(HDMI_READ(HDMI_SCHEDULER_CONTROL) & + VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE)); +- HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL, +- HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) | ++ HDMI_WRITE(HDMI_SCHEDULER_CONTROL, ++ HDMI_READ(HDMI_SCHEDULER_CONTROL) | + VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT); + +- HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, ++ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, + VC4_HDMI_RAM_PACKET_ENABLE); + + vc4_hdmi_set_infoframes(encoder); + +- drift = HDMI_READ(VC4_HDMI_FIFO_CTL); ++ drift = HDMI_READ(HDMI_FIFO_CTL); + drift &= VC4_HDMI_FIFO_VALID_WRITE_MASK; + +- HDMI_WRITE(VC4_HDMI_FIFO_CTL, ++ HDMI_WRITE(HDMI_FIFO_CTL, + drift & ~VC4_HDMI_FIFO_CTL_RECENTER); +- HDMI_WRITE(VC4_HDMI_FIFO_CTL, ++ HDMI_WRITE(HDMI_FIFO_CTL, + drift | VC4_HDMI_FIFO_CTL_RECENTER); + usleep_range(1000, 1100); +- HDMI_WRITE(VC4_HDMI_FIFO_CTL, ++ HDMI_WRITE(HDMI_FIFO_CTL, + drift & ~VC4_HDMI_FIFO_CTL_RECENTER); +- HDMI_WRITE(VC4_HDMI_FIFO_CTL, ++ HDMI_WRITE(HDMI_FIFO_CTL, + drift | VC4_HDMI_FIFO_CTL_RECENTER); + +- ret = wait_for(HDMI_READ(VC4_HDMI_FIFO_CTL) & ++ ret = wait_for(HDMI_READ(HDMI_FIFO_CTL) & + VC4_HDMI_FIFO_CTL_RECENTER_DONE, 1); + WARN_ONCE(ret, "Timeout waiting for " + "VC4_HDMI_FIFO_CTL_RECENTER_DONE"); +@@ -625,7 +580,7 @@ static void vc4_hdmi_audio_set_mai_clock + VC4_HD_MAI_SMP_M_SHIFT) + 1, + &n, &m); + +- HD_WRITE(VC4_HD_MAI_SMP, ++ HDMI_WRITE(HDMI_MAI_SMP, + VC4_SET_FIELD(n, VC4_HD_MAI_SMP_N) | + VC4_SET_FIELD(m - 1, VC4_HD_MAI_SMP_M)); + } +@@ -644,7 +599,7 @@ static void vc4_hdmi_set_n_cts(struct vc + do_div(tmp, 128 * samplerate); + cts = tmp; + +- HDMI_WRITE(VC4_HDMI_CRP_CFG, ++ HDMI_WRITE(HDMI_CRP_CFG, + VC4_HDMI_CRP_CFG_EXTERNAL_CTS_EN | + VC4_SET_FIELD(n, VC4_HDMI_CRP_CFG_N)); + +@@ -653,8 +608,8 @@ static void vc4_hdmi_set_n_cts(struct vc + * providing a CTS_1 value. The two CTS values are alternated + * between based on the period fields + */ +- HDMI_WRITE(VC4_HDMI_CTS_0, cts); +- HDMI_WRITE(VC4_HDMI_CTS_1, cts); ++ HDMI_WRITE(HDMI_CTS_0, cts); ++ HDMI_WRITE(HDMI_CTS_1, cts); + } + + static inline struct vc4_hdmi *dai_to_hdmi(struct snd_soc_dai *dai) +@@ -681,7 +636,7 @@ static int vc4_hdmi_audio_startup(struct + * If the HDMI encoder hasn't probed, or the encoder is + * currently in DVI mode, treat the codec dai as missing. + */ +- if (!encoder->crtc || !(HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) & ++ if (!encoder->crtc || !(HDMI_READ(HDMI_RAM_PACKET_CONFIG) & + VC4_HDMI_RAM_PACKET_ENABLE)) + return -ENODEV; + +@@ -707,9 +662,9 @@ static void vc4_hdmi_audio_reset(struct + if (ret) + dev_err(dev, "Failed to stop audio infoframe: %d\n", ret); + +- HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_RESET); +- HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_ERRORF); +- HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_FLUSH); ++ HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_RESET); ++ HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_ERRORF); ++ HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_FLUSH); + } + + static void vc4_hdmi_audio_shutdown(struct snd_pcm_substream *substream, +@@ -745,7 +700,7 @@ static int vc4_hdmi_audio_hw_params(stru + vc4_hdmi->audio.channels = params_channels(params); + vc4_hdmi->audio.samplerate = params_rate(params); + +- HD_WRITE(VC4_HD_MAI_CTL, ++ HDMI_WRITE(HDMI_MAI_CTL, + VC4_HD_MAI_CTL_RESET | + VC4_HD_MAI_CTL_FLUSH | + VC4_HD_MAI_CTL_DLATE | +@@ -765,22 +720,22 @@ static int vc4_hdmi_audio_hw_params(stru + + /* Set the MAI threshold. This logic mimics the firmware's. */ + if (vc4_hdmi->audio.samplerate > 96000) { +- HD_WRITE(VC4_HD_MAI_THR, ++ HDMI_WRITE(HDMI_MAI_THR, + VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQHIGH) | + VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW)); + } else if (vc4_hdmi->audio.samplerate > 48000) { +- HD_WRITE(VC4_HD_MAI_THR, ++ HDMI_WRITE(HDMI_MAI_THR, + VC4_SET_FIELD(0x14, VC4_HD_MAI_THR_DREQHIGH) | + VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW)); + } else { +- HD_WRITE(VC4_HD_MAI_THR, ++ HDMI_WRITE(HDMI_MAI_THR, + VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICHIGH) | + VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICLOW) | + VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQHIGH) | + VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQLOW)); + } + +- HDMI_WRITE(VC4_HDMI_MAI_CONFIG, ++ HDMI_WRITE(HDMI_MAI_CONFIG, + VC4_HDMI_MAI_CONFIG_BIT_REVERSE | + VC4_SET_FIELD(channel_mask, VC4_HDMI_MAI_CHANNEL_MASK)); + +@@ -790,8 +745,8 @@ static int vc4_hdmi_audio_hw_params(stru + channel_map |= i << (3 * i); + } + +- HDMI_WRITE(VC4_HDMI_MAI_CHANNEL_MAP, channel_map); +- HDMI_WRITE(VC4_HDMI_AUDIO_PACKET_CONFIG, audio_packet_config); ++ HDMI_WRITE(HDMI_MAI_CHANNEL_MAP, channel_map); ++ HDMI_WRITE(HDMI_AUDIO_PACKET_CONFIG, audio_packet_config); + vc4_hdmi_set_n_cts(vc4_hdmi); + + return 0; +@@ -806,21 +761,22 @@ static int vc4_hdmi_audio_trigger(struct + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + vc4_hdmi_set_audio_infoframe(encoder); +- HDMI_WRITE(VC4_HDMI_TX_PHY_CTL0, +- HDMI_READ(VC4_HDMI_TX_PHY_CTL0) & ++ HDMI_WRITE(HDMI_TX_PHY_CTL_0, ++ HDMI_READ(HDMI_TX_PHY_CTL_0) & + ~VC4_HDMI_TX_PHY_RNG_PWRDN); +- HD_WRITE(VC4_HD_MAI_CTL, ++ ++ HDMI_WRITE(HDMI_MAI_CTL, + VC4_SET_FIELD(vc4_hdmi->audio.channels, + VC4_HD_MAI_CTL_CHNUM) | + VC4_HD_MAI_CTL_ENABLE); + break; + case SNDRV_PCM_TRIGGER_STOP: +- HD_WRITE(VC4_HD_MAI_CTL, ++ HDMI_WRITE(HDMI_MAI_CTL, + VC4_HD_MAI_CTL_DLATE | + VC4_HD_MAI_CTL_ERRORE | + VC4_HD_MAI_CTL_ERRORF); +- HDMI_WRITE(VC4_HDMI_TX_PHY_CTL0, +- HDMI_READ(VC4_HDMI_TX_PHY_CTL0) | ++ HDMI_WRITE(HDMI_TX_PHY_CTL_0, ++ HDMI_READ(HDMI_TX_PHY_CTL_0) | + VC4_HDMI_TX_PHY_RNG_PWRDN); + break; + default: +@@ -954,6 +910,8 @@ static const struct snd_dmaengine_pcm_co + + static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi) + { ++ const struct vc4_hdmi_register *mai_data = ++ &vc4_hdmi->variant->registers[HDMI_MAI_DATA]; + struct snd_soc_dai_link *dai_link = &vc4_hdmi->audio.link; + struct snd_soc_card *card = &vc4_hdmi->audio.card; + struct device *dev = &vc4_hdmi->pdev->dev; +@@ -968,6 +926,11 @@ static int vc4_hdmi_audio_init(struct vc + return 0; + } + ++ if (mai_data->reg != VC4_HD) { ++ WARN_ONCE(true, "MAI isn't in the HD block\n"); ++ return -EINVAL; ++ } ++ + /* + * Get the physical address of VC4_HD_MAI_DATA. We need to retrieve + * the bus address specified in the DT, because the physical address +@@ -976,7 +939,7 @@ static int vc4_hdmi_audio_init(struct vc + * This VC/MMU should probably be exposed to avoid this kind of hacks. + */ + addr = of_get_address(dev->of_node, 1, NULL, NULL); +- vc4_hdmi->audio.dma_data.addr = be32_to_cpup(addr) + VC4_HD_MAI_DATA; ++ vc4_hdmi->audio.dma_data.addr = be32_to_cpup(addr) + mai_data->offset; + vc4_hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + vc4_hdmi->audio.dma_data.maxburst = 2; + +@@ -1068,7 +1031,7 @@ static void vc4_cec_read_msg(struct vc4_ + msg->len = 1 + ((cntrl1 & VC4_HDMI_CEC_REC_WRD_CNT_MASK) >> + VC4_HDMI_CEC_REC_WRD_CNT_SHIFT); + for (i = 0; i < msg->len; i += 4) { +- u32 val = HDMI_READ(VC4_HDMI_CEC_RX_DATA_1 + i); ++ u32 val = HDMI_READ(HDMI_CEC_RX_DATA_1 + i); + + msg->msg[i] = val & 0xff; + msg->msg[i + 1] = (val >> 8) & 0xff; +@@ -1080,26 +1043,26 @@ static void vc4_cec_read_msg(struct vc4_ + static irqreturn_t vc4_cec_irq_handler(int irq, void *priv) + { + struct vc4_hdmi *vc4_hdmi = priv; +- u32 stat = HDMI_READ(VC4_HDMI_CPU_STATUS); ++ u32 stat = HDMI_READ(HDMI_CEC_CPU_STATUS); + u32 cntrl1, cntrl5; + + if (!(stat & VC4_HDMI_CPU_CEC)) + return IRQ_NONE; + vc4_hdmi->cec_rx_msg.len = 0; +- cntrl1 = HDMI_READ(VC4_HDMI_CEC_CNTRL_1); +- cntrl5 = HDMI_READ(VC4_HDMI_CEC_CNTRL_5); ++ cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1); ++ cntrl5 = HDMI_READ(HDMI_CEC_CNTRL_5); + vc4_hdmi->cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT; + if (vc4_hdmi->cec_irq_was_rx) { + vc4_cec_read_msg(vc4_hdmi, cntrl1); + cntrl1 |= VC4_HDMI_CEC_CLEAR_RECEIVE_OFF; +- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, cntrl1); ++ HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1); + cntrl1 &= ~VC4_HDMI_CEC_CLEAR_RECEIVE_OFF; + } else { + vc4_hdmi->cec_tx_ok = cntrl1 & VC4_HDMI_CEC_TX_STATUS_GOOD; + cntrl1 &= ~VC4_HDMI_CEC_START_XMIT_BEGIN; + } +- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, cntrl1); +- HDMI_WRITE(VC4_HDMI_CPU_CLEAR, VC4_HDMI_CPU_CEC); ++ HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1); ++ HDMI_WRITE(HDMI_CEC_CPU_CLEAR, VC4_HDMI_CPU_CEC); + + return IRQ_WAKE_THREAD; + } +@@ -1109,7 +1072,7 @@ static int vc4_hdmi_cec_adap_enable(stru + struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); + /* clock period in microseconds */ + const u32 usecs = 1000000 / CEC_CLOCK_FREQ; +- u32 val = HDMI_READ(VC4_HDMI_CEC_CNTRL_5); ++ u32 val = HDMI_READ(HDMI_CEC_CNTRL_5); + + val &= ~(VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET | + VC4_HDMI_CEC_CNT_TO_4700_US_MASK | +@@ -1118,30 +1081,30 @@ static int vc4_hdmi_cec_adap_enable(stru + ((4500 / usecs) << VC4_HDMI_CEC_CNT_TO_4500_US_SHIFT); + + if (enable) { +- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_5, val | ++ HDMI_WRITE(HDMI_CEC_CNTRL_5, val | + VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET); +- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_5, val); +- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_2, ++ HDMI_WRITE(HDMI_CEC_CNTRL_5, val); ++ HDMI_WRITE(HDMI_CEC_CNTRL_2, + ((1500 / usecs) << VC4_HDMI_CEC_CNT_TO_1500_US_SHIFT) | + ((1300 / usecs) << VC4_HDMI_CEC_CNT_TO_1300_US_SHIFT) | + ((800 / usecs) << VC4_HDMI_CEC_CNT_TO_800_US_SHIFT) | + ((600 / usecs) << VC4_HDMI_CEC_CNT_TO_600_US_SHIFT) | + ((400 / usecs) << VC4_HDMI_CEC_CNT_TO_400_US_SHIFT)); +- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_3, ++ HDMI_WRITE(HDMI_CEC_CNTRL_3, + ((2750 / usecs) << VC4_HDMI_CEC_CNT_TO_2750_US_SHIFT) | + ((2400 / usecs) << VC4_HDMI_CEC_CNT_TO_2400_US_SHIFT) | + ((2050 / usecs) << VC4_HDMI_CEC_CNT_TO_2050_US_SHIFT) | + ((1700 / usecs) << VC4_HDMI_CEC_CNT_TO_1700_US_SHIFT)); +- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_4, ++ HDMI_WRITE(HDMI_CEC_CNTRL_4, + ((4300 / usecs) << VC4_HDMI_CEC_CNT_TO_4300_US_SHIFT) | + ((3900 / usecs) << VC4_HDMI_CEC_CNT_TO_3900_US_SHIFT) | + ((3600 / usecs) << VC4_HDMI_CEC_CNT_TO_3600_US_SHIFT) | + ((3500 / usecs) << VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT)); + +- HDMI_WRITE(VC4_HDMI_CPU_MASK_CLEAR, VC4_HDMI_CPU_CEC); ++ HDMI_WRITE(HDMI_CEC_CPU_MASK_CLEAR, VC4_HDMI_CPU_CEC); + } else { +- HDMI_WRITE(VC4_HDMI_CPU_MASK_SET, VC4_HDMI_CPU_CEC); +- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_5, val | ++ HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, VC4_HDMI_CPU_CEC); ++ HDMI_WRITE(HDMI_CEC_CNTRL_5, val | + VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET); + } + return 0; +@@ -1151,8 +1114,8 @@ static int vc4_hdmi_cec_adap_log_addr(st + { + struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); + +- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, +- (HDMI_READ(VC4_HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) | ++ HDMI_WRITE(HDMI_CEC_CNTRL_1, ++ (HDMI_READ(HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) | + (log_addr & 0xf) << VC4_HDMI_CEC_ADDR_SHIFT); + return 0; + } +@@ -1165,20 +1128,20 @@ static int vc4_hdmi_cec_adap_transmit(st + unsigned int i; + + for (i = 0; i < msg->len; i += 4) +- HDMI_WRITE(VC4_HDMI_CEC_TX_DATA_1 + i, ++ HDMI_WRITE(HDMI_CEC_TX_DATA_1 + i, + (msg->msg[i]) | + (msg->msg[i + 1] << 8) | + (msg->msg[i + 2] << 16) | + (msg->msg[i + 3] << 24)); + +- val = HDMI_READ(VC4_HDMI_CEC_CNTRL_1); ++ val = HDMI_READ(HDMI_CEC_CNTRL_1); + val &= ~VC4_HDMI_CEC_START_XMIT_BEGIN; +- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, val); ++ HDMI_WRITE(HDMI_CEC_CNTRL_1, val); + val &= ~VC4_HDMI_CEC_MESSAGE_LENGTH_MASK; + val |= (msg->len - 1) << VC4_HDMI_CEC_MESSAGE_LENGTH_SHIFT; + val |= VC4_HDMI_CEC_START_XMIT_BEGIN; + +- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, val); ++ HDMI_WRITE(HDMI_CEC_CNTRL_1, val); + return 0; + } + +@@ -1189,26 +1152,63 @@ static const struct cec_adap_ops vc4_hdm + }; + #endif + ++static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi, ++ struct debugfs_regset32 *regset, ++ enum vc4_hdmi_regs reg) ++{ ++ const struct vc4_hdmi_variant *variant = vc4_hdmi->variant; ++ struct debugfs_reg32 *regs; ++ unsigned int count = 0; ++ unsigned int i; ++ ++ regs = kzalloc(variant->num_registers * sizeof(*regs), ++ GFP_KERNEL); ++ if (!regs) ++ return -ENOMEM; ++ ++ for (i = 0; i < variant->num_registers; i++) { ++ const struct vc4_hdmi_register *field = &variant->registers[i]; ++ ++ if (field->reg != reg) ++ continue; ++ ++ regs[count].name = field->name; ++ regs[count].offset = field->offset; ++ count++; ++ } ++ ++ regs = krealloc(regs, count * sizeof(*regs), GFP_KERNEL); ++ if (!regs) ++ return -ENOMEM; ++ ++ regset->base = __vc4_hdmi_get_field_base(vc4_hdmi, reg); ++ regset->regs = regs; ++ regset->nregs = count; ++ ++ return 0; ++} ++ + static int vc4_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi) + { + struct platform_device *pdev = vc4_hdmi->pdev; + struct device *dev = &pdev->dev; ++ int ret; + + vc4_hdmi->hdmicore_regs = vc4_ioremap_regs(pdev, 0); + if (IS_ERR(vc4_hdmi->hdmicore_regs)) + return PTR_ERR(vc4_hdmi->hdmicore_regs); + +- vc4_hdmi->hdmi_regset.base = vc4_hdmi->hdmicore_regs; +- vc4_hdmi->hdmi_regset.regs = hdmi_regs; +- vc4_hdmi->hdmi_regset.nregs = ARRAY_SIZE(hdmi_regs); ++ ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->hd_regset, VC4_HD); ++ if (ret) ++ return ret; + + vc4_hdmi->hd_regs = vc4_ioremap_regs(pdev, 1); + if (IS_ERR(vc4_hdmi->hd_regs)) + return PTR_ERR(vc4_hdmi->hd_regs); + +- vc4_hdmi->hd_regset.base = vc4_hdmi->hd_regs; +- vc4_hdmi->hd_regset.regs = hd_regs; +- vc4_hdmi->hd_regset.nregs = ARRAY_SIZE(hd_regs); ++ ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->hdmi_regset, VC4_HDMI); ++ if (ret) ++ return ret; + + vc4_hdmi->pixel_clock = devm_clk_get(dev, "pixel"); + if (IS_ERR(vc4_hdmi->pixel_clock)) { +@@ -1301,12 +1301,12 @@ static int vc4_hdmi_bind(struct device * + } + + /* HDMI core must be enabled. */ +- if (!(HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE)) { +- HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST); ++ if (!(HDMI_READ(HDMI_M_CTL) & VC4_HD_M_ENABLE)) { ++ HDMI_WRITE(HDMI_M_CTL, VC4_HD_M_SW_RST); + udelay(1); +- HD_WRITE(VC4_HD_M_CTL, 0); ++ HDMI_WRITE(HDMI_M_CTL, 0); + +- HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_ENABLE); ++ HDMI_WRITE(HDMI_M_CTL, VC4_HD_M_ENABLE); + } + pm_runtime_enable(dev); + +@@ -1330,8 +1330,8 @@ static int vc4_hdmi_bind(struct device * + cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector); + cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info); + +- HDMI_WRITE(VC4_HDMI_CPU_MASK_SET, 0xffffffff); +- value = HDMI_READ(VC4_HDMI_CEC_CNTRL_1); ++ HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff); ++ value = HDMI_READ(HDMI_CEC_CNTRL_1); + value &= ~VC4_HDMI_CEC_DIV_CLK_CNT_MASK; + /* + * Set the logical address to Unregistered and set the clock +@@ -1340,7 +1340,7 @@ static int vc4_hdmi_bind(struct device * + */ + value |= VC4_HDMI_CEC_ADDR_MASK | + (4091 << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT); +- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, value); ++ HDMI_WRITE(HDMI_CEC_CNTRL_1, value); + ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0), + vc4_cec_irq_handler, + vc4_cec_irq_handler_thread, 0, +@@ -1387,6 +1387,9 @@ static void vc4_hdmi_unbind(struct devic + struct snd_soc_card *card = dev_get_drvdata(dev); + struct vc4_hdmi *vc4_hdmi = snd_soc_card_get_drvdata(card); + ++ kfree(vc4_hdmi->hdmi_regset.regs); ++ kfree(vc4_hdmi->hd_regset.regs); ++ + cec_unregister_adapter(vc4_hdmi->cec_adap); + vc4_hdmi_connector_destroy(&vc4_hdmi->connector); + vc4_hdmi_encoder_destroy(&vc4_hdmi->encoder.base.base); +@@ -1414,6 +1417,9 @@ static int vc4_hdmi_dev_remove(struct pl + } + + static const struct vc4_hdmi_variant bcm2835_variant = { ++ .registers = vc4_hdmi_fields, ++ .num_registers = ARRAY_SIZE(vc4_hdmi_fields), ++ + .init_resources = vc4_hdmi_init_resources, + }; + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -22,8 +22,15 @@ to_vc4_hdmi_encoder(struct drm_encoder * + } + + struct vc4_hdmi; ++struct vc4_hdmi_register; + + struct vc4_hdmi_variant { ++ /* List of the registers available on that variant */ ++ const struct vc4_hdmi_register *registers; ++ ++ /* Number of registers on that variant */ ++ unsigned int num_registers; ++ + /* Callback to get the resources (memory region, interrupts, + * clocks, etc) for that variant. + */ +@@ -85,9 +92,4 @@ encoder_to_vc4_hdmi(struct drm_encoder * + return container_of(_encoder, struct vc4_hdmi, encoder); + } + +-#define HDMI_READ(offset) readl(vc4_hdmi->hdmicore_regs + offset) +-#define HDMI_WRITE(offset, val) writel(val, vc4_hdmi->hdmicore_regs + offset) +-#define HD_READ(offset) readl(vc4_hdmi->hd_regs + offset) +-#define HD_WRITE(offset, val) writel(val, vc4_hdmi->hd_regs + offset) +- + #endif /* _VC4_HDMI_H_ */ +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h +@@ -0,0 +1,250 @@ ++#ifndef _VC4_HDMI_REGS_H_ ++#define _VC4_HDMI_REGS_H_ ++ ++#include "vc4_hdmi.h" ++ ++#define VC4_MASK(high, low) ((u32)GENMASK(high, low)) ++/* Using the GNU statement expression extension */ ++#define VC4_SET_FIELD(value, field) \ ++ ({ \ ++ uint32_t fieldval = (value) << field##_SHIFT; \ ++ WARN_ON((fieldval & ~field##_MASK) != 0); \ ++ fieldval & field##_MASK; \ ++ }) ++ ++#define VC4_HDMI_PACKET_STRIDE 0x24 ++ ++enum vc4_hdmi_regs { ++ VC4_INVALID = 0, ++ VC4_HDMI, ++ VC4_HD, ++}; ++ ++enum vc4_hdmi_field { ++ HDMI_AUDIO_PACKET_CONFIG, ++ HDMI_CEC_CNTRL_1, ++ HDMI_CEC_CNTRL_2, ++ HDMI_CEC_CNTRL_3, ++ HDMI_CEC_CNTRL_4, ++ HDMI_CEC_CNTRL_5, ++ HDMI_CEC_CPU_CLEAR, ++ HDMI_CEC_CPU_MASK_CLEAR, ++ HDMI_CEC_CPU_MASK_SET, ++ HDMI_CEC_CPU_MASK_STATUS, ++ HDMI_CEC_CPU_STATUS, ++ ++ /* ++ * Transmit data, first byte is low byte of the 32-bit reg. ++ * MSB of each byte transmitted first. ++ */ ++ HDMI_CEC_RX_DATA_1, ++ HDMI_CEC_RX_DATA_2, ++ HDMI_CEC_RX_DATA_3, ++ HDMI_CEC_RX_DATA_4, ++ HDMI_CEC_TX_DATA_1, ++ HDMI_CEC_TX_DATA_2, ++ HDMI_CEC_TX_DATA_3, ++ HDMI_CEC_TX_DATA_4, ++ HDMI_CORE_REV, ++ HDMI_CRP_CFG, ++ HDMI_CSC_12_11, ++ HDMI_CSC_14_13, ++ HDMI_CSC_22_21, ++ HDMI_CSC_24_23, ++ HDMI_CSC_32_31, ++ HDMI_CSC_34_33, ++ HDMI_CSC_CTL, ++ ++ /* ++ * 20-bit fields containing CTS values to be transmitted if ++ * !EXTERNAL_CTS_EN ++ */ ++ HDMI_CTS_0, ++ HDMI_CTS_1, ++ HDMI_FIFO_CTL, ++ HDMI_FRAME_COUNT, ++ HDMI_HORZA, ++ HDMI_HORZB, ++ HDMI_HOTPLUG, ++ HDMI_HOTPLUG_INT, ++ ++ /* ++ * 3 bits per field, where each field maps from that ++ * corresponding MAI bus channel to the given HDMI channel. ++ */ ++ HDMI_MAI_CHANNEL_MAP, ++ HDMI_MAI_CONFIG, ++ HDMI_MAI_CTL, ++ ++ /* ++ * Register for DMAing in audio data to be transported over ++ * the MAI bus to the Falcon core. ++ */ ++ HDMI_MAI_DATA, ++ ++ /* Format header to be placed on the MAI data. Unused. */ ++ HDMI_MAI_FMT, ++ ++ /* Last received format word on the MAI bus. */ ++ HDMI_MAI_FORMAT, ++ HDMI_MAI_SMP, ++ HDMI_MAI_THR, ++ HDMI_M_CTL, ++ HDMI_RAM_PACKET_CONFIG, ++ HDMI_RAM_PACKET_START, ++ HDMI_RAM_PACKET_STATUS, ++ HDMI_SCHEDULER_CONTROL, ++ HDMI_SW_RESET_CONTROL, ++ HDMI_TX_PHY_CTL_0, ++ HDMI_TX_PHY_RESET_CTL, ++ HDMI_VERTA0, ++ HDMI_VERTA1, ++ HDMI_VERTB0, ++ HDMI_VERTB1, ++ HDMI_VID_CTL, ++}; ++ ++struct vc4_hdmi_register { ++ char *name; ++ enum vc4_hdmi_regs reg; ++ unsigned int offset; ++}; ++ ++#define _VC4_REG(_base, _reg, _offset) \ ++ [_reg] = { \ ++ .name = #_reg, \ ++ .reg = _base, \ ++ .offset = _offset, \ ++ } ++ ++#define VC4_HD_REG(reg, offset) _VC4_REG(VC4_HD, reg, offset) ++#define VC4_HDMI_REG(reg, offset) _VC4_REG(VC4_HDMI, reg, offset) ++ ++static const struct vc4_hdmi_register vc4_hdmi_fields[] = { ++ VC4_HD_REG(HDMI_M_CTL, 0x000c), ++ VC4_HD_REG(HDMI_MAI_CTL, 0x0014), ++ VC4_HD_REG(HDMI_MAI_THR, 0x0018), ++ VC4_HD_REG(HDMI_MAI_FMT, 0x001c), ++ VC4_HD_REG(HDMI_MAI_DATA, 0x0020), ++ VC4_HD_REG(HDMI_MAI_SMP, 0x002c), ++ VC4_HD_REG(HDMI_VID_CTL, 0x0038), ++ VC4_HD_REG(HDMI_CSC_CTL, 0x0040), ++ VC4_HD_REG(HDMI_CSC_12_11, 0x0044), ++ VC4_HD_REG(HDMI_CSC_14_13, 0x0048), ++ VC4_HD_REG(HDMI_CSC_22_21, 0x004c), ++ VC4_HD_REG(HDMI_CSC_24_23, 0x0050), ++ VC4_HD_REG(HDMI_CSC_32_31, 0x0054), ++ VC4_HD_REG(HDMI_CSC_34_33, 0x0058), ++ VC4_HD_REG(HDMI_FRAME_COUNT, 0x0068), ++ ++ VC4_HDMI_REG(HDMI_CORE_REV, 0x0000), ++ VC4_HDMI_REG(HDMI_SW_RESET_CONTROL, 0x0004), ++ VC4_HDMI_REG(HDMI_HOTPLUG_INT, 0x0008), ++ VC4_HDMI_REG(HDMI_HOTPLUG, 0x000c), ++ VC4_HDMI_REG(HDMI_FIFO_CTL, 0x005c), ++ VC4_HDMI_REG(HDMI_MAI_CHANNEL_MAP, 0x0090), ++ VC4_HDMI_REG(HDMI_MAI_CONFIG, 0x0094), ++ VC4_HDMI_REG(HDMI_MAI_FORMAT, 0x0098), ++ VC4_HDMI_REG(HDMI_AUDIO_PACKET_CONFIG, 0x009c), ++ VC4_HDMI_REG(HDMI_RAM_PACKET_CONFIG, 0x00a0), ++ VC4_HDMI_REG(HDMI_RAM_PACKET_STATUS, 0x00a4), ++ VC4_HDMI_REG(HDMI_CRP_CFG, 0x00a8), ++ VC4_HDMI_REG(HDMI_CTS_0, 0x00ac), ++ VC4_HDMI_REG(HDMI_CTS_1, 0x00b0), ++ VC4_HDMI_REG(HDMI_SCHEDULER_CONTROL, 0x00c0), ++ VC4_HDMI_REG(HDMI_HORZA, 0x00c4), ++ VC4_HDMI_REG(HDMI_HORZB, 0x00c8), ++ VC4_HDMI_REG(HDMI_VERTA0, 0x00cc), ++ VC4_HDMI_REG(HDMI_VERTB0, 0x00d0), ++ VC4_HDMI_REG(HDMI_VERTA1, 0x00d4), ++ VC4_HDMI_REG(HDMI_VERTB1, 0x00d8), ++ VC4_HDMI_REG(HDMI_CEC_CNTRL_1, 0x00e8), ++ VC4_HDMI_REG(HDMI_CEC_CNTRL_2, 0x00ec), ++ VC4_HDMI_REG(HDMI_CEC_CNTRL_3, 0x00f0), ++ VC4_HDMI_REG(HDMI_CEC_CNTRL_4, 0x00f4), ++ VC4_HDMI_REG(HDMI_CEC_CNTRL_5, 0x00f8), ++ VC4_HDMI_REG(HDMI_CEC_TX_DATA_1, 0x00fc), ++ VC4_HDMI_REG(HDMI_CEC_TX_DATA_2, 0x0100), ++ VC4_HDMI_REG(HDMI_CEC_TX_DATA_3, 0x0104), ++ VC4_HDMI_REG(HDMI_CEC_TX_DATA_4, 0x0108), ++ VC4_HDMI_REG(HDMI_CEC_RX_DATA_1, 0x010c), ++ VC4_HDMI_REG(HDMI_CEC_RX_DATA_2, 0x0110), ++ VC4_HDMI_REG(HDMI_CEC_RX_DATA_3, 0x0114), ++ VC4_HDMI_REG(HDMI_CEC_RX_DATA_4, 0x0118), ++ VC4_HDMI_REG(HDMI_TX_PHY_RESET_CTL, 0x02c0), ++ VC4_HDMI_REG(HDMI_TX_PHY_CTL_0, 0x02c4), ++ VC4_HDMI_REG(HDMI_CEC_CPU_STATUS, 0x0340), ++ VC4_HDMI_REG(HDMI_CEC_CPU_CLEAR, 0x0348), ++ VC4_HDMI_REG(HDMI_CEC_CPU_MASK_STATUS, 0x034c), ++ VC4_HDMI_REG(HDMI_CEC_CPU_MASK_SET, 0x034c), ++ VC4_HDMI_REG(HDMI_CEC_CPU_MASK_CLEAR, 0x0354), ++ VC4_HDMI_REG(HDMI_RAM_PACKET_START, 0x0400), ++}; ++ ++static inline ++void __iomem *__vc4_hdmi_get_field_base(struct vc4_hdmi *hdmi, ++ enum vc4_hdmi_regs reg) ++{ ++ switch (reg) { ++ case VC4_HD: ++ return hdmi->hd_regs; ++ ++ case VC4_HDMI: ++ return hdmi->hdmicore_regs; ++ ++ default: ++ return NULL; ++ } ++ ++ return NULL; ++} ++ ++static inline u32 vc4_hdmi_read(struct vc4_hdmi *hdmi, ++ enum vc4_hdmi_regs reg) ++{ ++ const struct vc4_hdmi_register *field; ++ const struct vc4_hdmi_variant *variant = hdmi->variant; ++ void __iomem *base; ++ ++ if (reg > variant->num_registers) { ++ dev_warn(&hdmi->pdev->dev, ++ "Invalid register ID %u\n", reg); ++ return 0; ++ } ++ ++ field = &variant->registers[reg]; ++ base = __vc4_hdmi_get_field_base(hdmi, field->reg); ++ if (!base) { ++ dev_warn(&hdmi->pdev->dev, ++ "Unknown register ID %u\n", reg); ++ return 0; ++ } ++ ++ return readl(base + field->offset); ++} ++#define HDMI_READ(reg) vc4_hdmi_read(vc4_hdmi, reg) ++ ++static inline void vc4_hdmi_write(struct vc4_hdmi *hdmi, ++ enum vc4_hdmi_regs reg, ++ u32 value) ++{ ++ const struct vc4_hdmi_register *field; ++ const struct vc4_hdmi_variant *variant = hdmi->variant; ++ void __iomem *base; ++ ++ if (reg > variant->num_registers) { ++ dev_warn(&hdmi->pdev->dev, ++ "Invalid register ID %u\n", reg); ++ return; ++ } ++ ++ field = &variant->registers[reg]; ++ base = __vc4_hdmi_get_field_base(hdmi, field->reg); ++ if (!base) ++ return; ++ ++ writel(value, base + field->offset); ++} ++#define HDMI_WRITE(reg, val) vc4_hdmi_write(vc4_hdmi, reg, val) ++ ++#endif /* _VC4_HDMI_REGS_H_ */ +--- a/drivers/gpu/drm/vc4/vc4_regs.h ++++ b/drivers/gpu/drm/vc4/vc4_regs.h +@@ -493,32 +493,16 @@ + + #define SCALER5_DLIST_START 0x00004000 + +-#define VC4_HDMI_CORE_REV 0x000 +- +-#define VC4_HDMI_SW_RESET_CONTROL 0x004 + # define VC4_HDMI_SW_RESET_FORMAT_DETECT BIT(1) + # define VC4_HDMI_SW_RESET_HDMI BIT(0) + +-#define VC4_HDMI_HOTPLUG_INT 0x008 +- +-#define VC4_HDMI_HOTPLUG 0x00c + # define VC4_HDMI_HOTPLUG_CONNECTED BIT(0) + +-/* 3 bits per field, where each field maps from that corresponding MAI +- * bus channel to the given HDMI channel. +- */ +-#define VC4_HDMI_MAI_CHANNEL_MAP 0x090 +- +-#define VC4_HDMI_MAI_CONFIG 0x094 + # define VC4_HDMI_MAI_CONFIG_FORMAT_REVERSE BIT(27) + # define VC4_HDMI_MAI_CONFIG_BIT_REVERSE BIT(26) + # define VC4_HDMI_MAI_CHANNEL_MASK_MASK VC4_MASK(15, 0) + # define VC4_HDMI_MAI_CHANNEL_MASK_SHIFT 0 + +-/* Last received format word on the MAI bus. */ +-#define VC4_HDMI_MAI_FORMAT 0x098 +- +-#define VC4_HDMI_AUDIO_PACKET_CONFIG 0x09c + # define VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_SAMPLE_FLAT BIT(29) + # define VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS BIT(24) + # define VC4_HDMI_AUDIO_PACKET_FORCE_SAMPLE_PRESENT BIT(19) +@@ -532,12 +516,8 @@ + # define VC4_HDMI_AUDIO_PACKET_CEA_MASK_MASK VC4_MASK(7, 0) + # define VC4_HDMI_AUDIO_PACKET_CEA_MASK_SHIFT 0 + +-#define VC4_HDMI_RAM_PACKET_CONFIG 0x0a0 + # define VC4_HDMI_RAM_PACKET_ENABLE BIT(16) + +-#define VC4_HDMI_RAM_PACKET_STATUS 0x0a4 +- +-#define VC4_HDMI_CRP_CFG 0x0a8 + /* When set, the CTS_PERIOD counts based on MAI bus sync pulse instead + * of pixel clock. + */ +@@ -551,23 +531,12 @@ + # define VC4_HDMI_CRP_CFG_N_MASK VC4_MASK(19, 0) + # define VC4_HDMI_CRP_CFG_N_SHIFT 0 + +-/* 20-bit fields containing CTS values to be transmitted if !EXTERNAL_CTS_EN */ +-#define VC4_HDMI_CTS_0 0x0ac +-#define VC4_HDMI_CTS_1 0x0b0 +-/* 20-bit fields containing number of clocks to send CTS0/1 before +- * switching to the other one. +- */ +-#define VC4_HDMI_CTS_PERIOD_0 0x0b4 +-#define VC4_HDMI_CTS_PERIOD_1 0x0b8 +- +-#define VC4_HDMI_HORZA 0x0c4 + # define VC4_HDMI_HORZA_VPOS BIT(14) + # define VC4_HDMI_HORZA_HPOS BIT(13) + /* Horizontal active pixels (hdisplay). */ + # define VC4_HDMI_HORZA_HAP_MASK VC4_MASK(12, 0) + # define VC4_HDMI_HORZA_HAP_SHIFT 0 + +-#define VC4_HDMI_HORZB 0x0c8 + /* Horizontal pack porch (htotal - hsync_end). */ + # define VC4_HDMI_HORZB_HBP_MASK VC4_MASK(29, 20) + # define VC4_HDMI_HORZB_HBP_SHIFT 20 +@@ -578,7 +547,6 @@ + # define VC4_HDMI_HORZB_HFP_MASK VC4_MASK(9, 0) + # define VC4_HDMI_HORZB_HFP_SHIFT 0 + +-#define VC4_HDMI_FIFO_CTL 0x05c + # define VC4_HDMI_FIFO_CTL_RECENTER_DONE BIT(14) + # define VC4_HDMI_FIFO_CTL_USE_EMPTY BIT(13) + # define VC4_HDMI_FIFO_CTL_ON_VB BIT(7) +@@ -591,15 +559,12 @@ + # define VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N BIT(0) + # define VC4_HDMI_FIFO_VALID_WRITE_MASK 0xefff + +-#define VC4_HDMI_SCHEDULER_CONTROL 0x0c0 + # define VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT BIT(15) + # define VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS BIT(5) + # define VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT BIT(3) + # define VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE BIT(1) + # define VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI BIT(0) + +-#define VC4_HDMI_VERTA0 0x0cc +-#define VC4_HDMI_VERTA1 0x0d4 + /* Vertical sync pulse (vsync_end - vsync_start). */ + # define VC4_HDMI_VERTA_VSP_MASK VC4_MASK(24, 20) + # define VC4_HDMI_VERTA_VSP_SHIFT 20 +@@ -610,8 +575,6 @@ + # define VC4_HDMI_VERTA_VAL_MASK VC4_MASK(12, 0) + # define VC4_HDMI_VERTA_VAL_SHIFT 0 + +-#define VC4_HDMI_VERTB0 0x0d0 +-#define VC4_HDMI_VERTB1 0x0d8 + /* Vertical sync pulse offset (for interlaced) */ + # define VC4_HDMI_VERTB_VSPO_MASK VC4_MASK(21, 9) + # define VC4_HDMI_VERTB_VSPO_SHIFT 9 +@@ -619,7 +582,6 @@ + # define VC4_HDMI_VERTB_VBP_MASK VC4_MASK(8, 0) + # define VC4_HDMI_VERTB_VBP_SHIFT 0 + +-#define VC4_HDMI_CEC_CNTRL_1 0x0e8 + /* Set when the transmission has ended. */ + # define VC4_HDMI_CEC_TX_EOM BIT(31) + /* If set, transmission was acked on the 1st or 2nd attempt (only one +@@ -660,7 +622,6 @@ + /* Set these fields to how many bit clock cycles get to that many + * microseconds. + */ +-#define VC4_HDMI_CEC_CNTRL_2 0x0ec + # define VC4_HDMI_CEC_CNT_TO_1500_US_MASK VC4_MASK(30, 24) + # define VC4_HDMI_CEC_CNT_TO_1500_US_SHIFT 24 + # define VC4_HDMI_CEC_CNT_TO_1300_US_MASK VC4_MASK(23, 17) +@@ -672,7 +633,6 @@ + # define VC4_HDMI_CEC_CNT_TO_400_US_MASK VC4_MASK(4, 0) + # define VC4_HDMI_CEC_CNT_TO_400_US_SHIFT 0 + +-#define VC4_HDMI_CEC_CNTRL_3 0x0f0 + # define VC4_HDMI_CEC_CNT_TO_2750_US_MASK VC4_MASK(31, 24) + # define VC4_HDMI_CEC_CNT_TO_2750_US_SHIFT 24 + # define VC4_HDMI_CEC_CNT_TO_2400_US_MASK VC4_MASK(23, 16) +@@ -682,7 +642,6 @@ + # define VC4_HDMI_CEC_CNT_TO_1700_US_MASK VC4_MASK(7, 0) + # define VC4_HDMI_CEC_CNT_TO_1700_US_SHIFT 0 + +-#define VC4_HDMI_CEC_CNTRL_4 0x0f4 + # define VC4_HDMI_CEC_CNT_TO_4300_US_MASK VC4_MASK(31, 24) + # define VC4_HDMI_CEC_CNT_TO_4300_US_SHIFT 24 + # define VC4_HDMI_CEC_CNT_TO_3900_US_MASK VC4_MASK(23, 16) +@@ -692,7 +651,6 @@ + # define VC4_HDMI_CEC_CNT_TO_3500_US_MASK VC4_MASK(7, 0) + # define VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT 0 + +-#define VC4_HDMI_CEC_CNTRL_5 0x0f8 + # define VC4_HDMI_CEC_TX_SW_RESET BIT(27) + # define VC4_HDMI_CEC_RX_SW_RESET BIT(26) + # define VC4_HDMI_CEC_PAD_SW_RESET BIT(25) +@@ -705,39 +663,11 @@ + # define VC4_HDMI_CEC_CNT_TO_4500_US_MASK VC4_MASK(7, 0) + # define VC4_HDMI_CEC_CNT_TO_4500_US_SHIFT 0 + +-/* Transmit data, first byte is low byte of the 32-bit reg. MSB of +- * each byte transmitted first. +- */ +-#define VC4_HDMI_CEC_TX_DATA_1 0x0fc +-#define VC4_HDMI_CEC_TX_DATA_2 0x100 +-#define VC4_HDMI_CEC_TX_DATA_3 0x104 +-#define VC4_HDMI_CEC_TX_DATA_4 0x108 +-#define VC4_HDMI_CEC_RX_DATA_1 0x10c +-#define VC4_HDMI_CEC_RX_DATA_2 0x110 +-#define VC4_HDMI_CEC_RX_DATA_3 0x114 +-#define VC4_HDMI_CEC_RX_DATA_4 0x118 +- +-#define VC4_HDMI_TX_PHY_RESET_CTL 0x2c0 +- +-#define VC4_HDMI_TX_PHY_CTL0 0x2c4 + # define VC4_HDMI_TX_PHY_RNG_PWRDN BIT(25) + +-/* Interrupt status bits */ +-#define VC4_HDMI_CPU_STATUS 0x340 +-#define VC4_HDMI_CPU_SET 0x344 +-#define VC4_HDMI_CPU_CLEAR 0x348 + # define VC4_HDMI_CPU_CEC BIT(6) + # define VC4_HDMI_CPU_HOTPLUG BIT(0) + +-#define VC4_HDMI_CPU_MASK_STATUS 0x34c +-#define VC4_HDMI_CPU_MASK_SET 0x350 +-#define VC4_HDMI_CPU_MASK_CLEAR 0x354 +- +-#define VC4_HDMI_GCP(x) (0x400 + ((x) * 0x4)) +-#define VC4_HDMI_RAM_PACKET(x) (0x400 + ((x) * 0x24)) +-#define VC4_HDMI_PACKET_STRIDE 0x24 +- +-#define VC4_HD_M_CTL 0x00c + /* Debug: Current receive value on the CEC pad. */ + # define VC4_HD_CECRXD BIT(9) + /* Debug: Override CEC output to 0. */ +@@ -747,7 +677,6 @@ + # define VC4_HD_M_SW_RST BIT(2) + # define VC4_HD_M_ENABLE BIT(0) + +-#define VC4_HD_MAI_CTL 0x014 + /* Set when audio stream is received at a slower rate than the + * sampling period, so MAI fifo goes empty. Write 1 to clear. + */ +@@ -772,7 +701,6 @@ + /* Single-shot reset bit. Read value is undefined. */ + # define VC4_HD_MAI_CTL_RESET BIT(0) + +-#define VC4_HD_MAI_THR 0x018 + # define VC4_HD_MAI_THR_PANICHIGH_MASK VC4_MASK(29, 24) + # define VC4_HD_MAI_THR_PANICHIGH_SHIFT 24 + # define VC4_HD_MAI_THR_PANICLOW_MASK VC4_MASK(21, 16) +@@ -782,31 +710,20 @@ + # define VC4_HD_MAI_THR_DREQLOW_MASK VC4_MASK(5, 0) + # define VC4_HD_MAI_THR_DREQLOW_SHIFT 0 + +-/* Format header to be placed on the MAI data. Unused. */ +-#define VC4_HD_MAI_FMT 0x01c +- +-/* Register for DMAing in audio data to be transported over the MAI +- * bus to the Falcon core. +- */ +-#define VC4_HD_MAI_DATA 0x020 +- + /* Divider from HDMI HSM clock to MAI serial clock. Sampling period + * converges to N / (M + 1) cycles. + */ +-#define VC4_HD_MAI_SMP 0x02c + # define VC4_HD_MAI_SMP_N_MASK VC4_MASK(31, 8) + # define VC4_HD_MAI_SMP_N_SHIFT 8 + # define VC4_HD_MAI_SMP_M_MASK VC4_MASK(7, 0) + # define VC4_HD_MAI_SMP_M_SHIFT 0 + +-#define VC4_HD_VID_CTL 0x038 + # define VC4_HD_VID_CTL_ENABLE BIT(31) + # define VC4_HD_VID_CTL_UNDERFLOW_ENABLE BIT(30) + # define VC4_HD_VID_CTL_FRAME_COUNTER_RESET BIT(29) + # define VC4_HD_VID_CTL_VSYNC_LOW BIT(28) + # define VC4_HD_VID_CTL_HSYNC_LOW BIT(27) + +-#define VC4_HD_CSC_CTL 0x040 + # define VC4_HD_CSC_CTL_ORDER_MASK VC4_MASK(7, 5) + # define VC4_HD_CSC_CTL_ORDER_SHIFT 5 + # define VC4_HD_CSC_CTL_ORDER_RGB 0 +@@ -824,15 +741,6 @@ + # define VC4_HD_CSC_CTL_RGB2YCC BIT(1) + # define VC4_HD_CSC_CTL_ENABLE BIT(0) + +-#define VC4_HD_CSC_12_11 0x044 +-#define VC4_HD_CSC_14_13 0x048 +-#define VC4_HD_CSC_22_21 0x04c +-#define VC4_HD_CSC_24_23 0x050 +-#define VC4_HD_CSC_32_31 0x054 +-#define VC4_HD_CSC_34_33 0x058 +- +-#define VC4_HD_FRAME_COUNT 0x068 +- + /* HVS display list information. */ + #define HVS_BOOTLOADER_DLIST_END 32 + diff --git a/target/linux/bcm27xx/patches-5.4/950-0584-drm-vc4-hdmi-Add-reset-callback.patch b/target/linux/bcm27xx/patches-5.4/950-0584-drm-vc4-hdmi-Add-reset-callback.patch new file mode 100644 index 0000000000..53dd3e4405 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0584-drm-vc4-hdmi-Add-reset-callback.patch @@ -0,0 +1,66 @@ +From 3cf4a365b833d7a2e7622ad5569b9d54aebbe593 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 19 Dec 2019 16:25:26 +0100 +Subject: [PATCH] drm/vc4: hdmi: Add reset callback + +The BCM2711 and BCM283x HDMI controllers use a slightly different reset +sequence, so let's add a callback to reset the controller. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 17 ++++++++++++----- + drivers/gpu/drm/vc4/vc4_hdmi.h | 3 +++ + 2 files changed, 15 insertions(+), 5 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -68,6 +68,15 @@ static int vc4_hdmi_debugfs_regs(struct + return 0; + } + ++static void vc4_hdmi_reset(struct vc4_hdmi *vc4_hdmi) ++{ ++ HDMI_WRITE(HDMI_SW_RESET_CONTROL, ++ VC4_HDMI_SW_RESET_HDMI | ++ VC4_HDMI_SW_RESET_FORMAT_DETECT); ++ ++ HDMI_WRITE(HDMI_SW_RESET_CONTROL, 0); ++} ++ + static enum drm_connector_status + vc4_hdmi_connector_detect(struct drm_connector *connector, bool force) + { +@@ -372,11 +381,8 @@ static void vc4_hdmi_encoder_enable(stru + return; + } + +- HDMI_WRITE(HDMI_SW_RESET_CONTROL, +- VC4_HDMI_SW_RESET_HDMI | +- VC4_HDMI_SW_RESET_FORMAT_DETECT); +- +- HDMI_WRITE(HDMI_SW_RESET_CONTROL, 0); ++ if (vc4_hdmi->variant->reset) ++ vc4_hdmi->variant->reset(vc4_hdmi); + + /* PHY should be in reset, like + * vc4_hdmi_encoder_disable() does. +@@ -1421,6 +1427,7 @@ static const struct vc4_hdmi_variant bcm + .num_registers = ARRAY_SIZE(vc4_hdmi_fields), + + .init_resources = vc4_hdmi_init_resources, ++ .reset = vc4_hdmi_reset, + }; + + static const struct of_device_id vc4_hdmi_dt_match[] = { +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -35,6 +35,9 @@ struct vc4_hdmi_variant { + * clocks, etc) for that variant. + */ + int (*init_resources)(struct vc4_hdmi *vc4_hdmi); ++ ++ /* Callback to reset the HDMI block */ ++ void (*reset)(struct vc4_hdmi *vc4_hdmi); + }; + + /* HDMI audio information */ diff --git a/target/linux/bcm27xx/patches-5.4/950-0585-drm-vc4-hdmi-Add-PHY-init-and-disable-function.patch b/target/linux/bcm27xx/patches-5.4/950-0585-drm-vc4-hdmi-Add-PHY-init-and-disable-function.patch new file mode 100644 index 0000000000..6db26d40aa --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0585-drm-vc4-hdmi-Add-PHY-init-and-disable-function.patch @@ -0,0 +1,128 @@ +From 9fe77147d40e0dc58e7297e79ba8b50e13b8269d Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 19 Dec 2019 16:53:33 +0100 +Subject: [PATCH] drm/vc4: hdmi: Add PHY init and disable function + +The HDMI PHY in the BCM2711 HDMI controller is significantly more +complicated to setup than in the older BCM283x SoCs. + +Let's add hooks to enable and disable the PHY. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/Makefile | 1 + + drivers/gpu/drm/vc4/vc4_hdmi.c | 14 +++++++------- + drivers/gpu/drm/vc4/vc4_hdmi.h | 13 +++++++++++++ + drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 25 +++++++++++++++++++++++++ + 4 files changed, 46 insertions(+), 7 deletions(-) + create mode 100644 drivers/gpu/drm/vc4/vc4_hdmi_phy.c + +--- a/drivers/gpu/drm/vc4/Makefile ++++ b/drivers/gpu/drm/vc4/Makefile +@@ -13,6 +13,7 @@ vc4-y := \ + vc4_kms.o \ + vc4_gem.o \ + vc4_hdmi.o \ ++ vc4_hdmi_phy.o \ + vc4_vec.o \ + vc4_hvs.o \ + vc4_irq.o \ +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -324,7 +324,9 @@ static void vc4_hdmi_encoder_disable(str + + HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0); + +- HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16); ++ if (vc4_hdmi->variant->phy_disable) ++ vc4_hdmi->variant->phy_disable(vc4_hdmi); ++ + HDMI_WRITE(HDMI_VID_CTL, + HDMI_READ(HDMI_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE); + +@@ -384,12 +386,8 @@ static void vc4_hdmi_encoder_enable(stru + if (vc4_hdmi->variant->reset) + vc4_hdmi->variant->reset(vc4_hdmi); + +- /* PHY should be in reset, like +- * vc4_hdmi_encoder_disable() does. +- */ +- HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16); +- +- HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0); ++ if (vc4_hdmi->variant->phy_init) ++ vc4_hdmi->variant->phy_init(vc4_hdmi, mode); + + if (debug_dump_regs) { + struct drm_printer p = drm_info_printer(&vc4_hdmi->pdev->dev); +@@ -1428,6 +1426,8 @@ static const struct vc4_hdmi_variant bcm + + .init_resources = vc4_hdmi_init_resources, + .reset = vc4_hdmi_reset, ++ .phy_init = vc4_hdmi_phy_init, ++ .phy_disable = vc4_hdmi_phy_disable, + }; + + static const struct of_device_id vc4_hdmi_dt_match[] = { +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -21,6 +21,8 @@ to_vc4_hdmi_encoder(struct drm_encoder * + return container_of(encoder, struct vc4_hdmi_encoder, base.base); + } + ++struct drm_display_mode; ++ + struct vc4_hdmi; + struct vc4_hdmi_register; + +@@ -38,6 +40,13 @@ struct vc4_hdmi_variant { + + /* Callback to reset the HDMI block */ + void (*reset)(struct vc4_hdmi *vc4_hdmi); ++ ++ /* Callback to initialize the PHY according to the mode */ ++ void (*phy_init)(struct vc4_hdmi *vc4_hdmi, ++ struct drm_display_mode *mode); ++ ++ /* Callback to disable the PHY */ ++ void (*phy_disable)(struct vc4_hdmi *vc4_hdmi); + }; + + /* HDMI audio information */ +@@ -95,4 +104,8 @@ encoder_to_vc4_hdmi(struct drm_encoder * + return container_of(_encoder, struct vc4_hdmi, encoder); + } + ++void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, ++ struct drm_display_mode *mode); ++void vc4_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi); ++ + #endif /* _VC4_HDMI_H_ */ +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c +@@ -0,0 +1,25 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2015 Broadcom ++ * Copyright (c) 2014 The Linux Foundation. All rights reserved. ++ * Copyright (C) 2013 Red Hat ++ * Author: Rob Clark <robdclark@gmail.com> ++ */ ++ ++#include "vc4_hdmi.h" ++#include "vc4_hdmi_regs.h" ++ ++void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, struct drm_display_mode *mode) ++{ ++ /* PHY should be in reset, like ++ * vc4_hdmi_encoder_disable() does. ++ */ ++ ++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16); ++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0); ++} ++ ++void vc4_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi) ++{ ++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16); ++} diff --git a/target/linux/bcm27xx/patches-5.4/950-0586-drm-vc4-hdmi-Add-PHY-RNG-enable-disable-function.patch b/target/linux/bcm27xx/patches-5.4/950-0586-drm-vc4-hdmi-Add-PHY-RNG-enable-disable-function.patch new file mode 100644 index 0000000000..cea17a621e --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0586-drm-vc4-hdmi-Add-PHY-RNG-enable-disable-function.patch @@ -0,0 +1,104 @@ +From ddf78df1db8752247e89a68231338a194e5dc52b Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 19 Dec 2019 17:22:24 +0100 +Subject: [PATCH] drm/vc4: hdmi: Add PHY RNG enable / disable function + +Let's continue the implementation of hooks for the parts that change in the +BCM2711 SoC with the PHY RNG setup. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 15 +++++++++------ + drivers/gpu/drm/vc4/vc4_hdmi.h | 8 ++++++++ + drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 15 +++++++++++++++ + 3 files changed, 32 insertions(+), 6 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -765,9 +765,9 @@ static int vc4_hdmi_audio_trigger(struct + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + vc4_hdmi_set_audio_infoframe(encoder); +- HDMI_WRITE(HDMI_TX_PHY_CTL_0, +- HDMI_READ(HDMI_TX_PHY_CTL_0) & +- ~VC4_HDMI_TX_PHY_RNG_PWRDN); ++ ++ if (vc4_hdmi->variant->phy_rng_enable) ++ vc4_hdmi->variant->phy_rng_enable(vc4_hdmi); + + HDMI_WRITE(HDMI_MAI_CTL, + VC4_SET_FIELD(vc4_hdmi->audio.channels, +@@ -779,9 +779,10 @@ static int vc4_hdmi_audio_trigger(struct + VC4_HD_MAI_CTL_DLATE | + VC4_HD_MAI_CTL_ERRORE | + VC4_HD_MAI_CTL_ERRORF); +- HDMI_WRITE(HDMI_TX_PHY_CTL_0, +- HDMI_READ(HDMI_TX_PHY_CTL_0) | +- VC4_HDMI_TX_PHY_RNG_PWRDN); ++ ++ if (vc4_hdmi->variant->phy_rng_disable) ++ vc4_hdmi->variant->phy_rng_disable(vc4_hdmi); ++ + break; + default: + break; +@@ -1428,6 +1429,8 @@ static const struct vc4_hdmi_variant bcm + .reset = vc4_hdmi_reset, + .phy_init = vc4_hdmi_phy_init, + .phy_disable = vc4_hdmi_phy_disable, ++ .phy_rng_enable = vc4_hdmi_phy_rng_enable, ++ .phy_rng_disable = vc4_hdmi_phy_rng_disable, + }; + + static const struct of_device_id vc4_hdmi_dt_match[] = { +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -47,6 +47,12 @@ struct vc4_hdmi_variant { + + /* Callback to disable the PHY */ + void (*phy_disable)(struct vc4_hdmi *vc4_hdmi); ++ ++ /* Callback to enable the RNG in the PHY */ ++ void (*phy_rng_enable)(struct vc4_hdmi *vc4_hdmi); ++ ++ /* Callback to disable the RNG in the PHY */ ++ void (*phy_rng_disable)(struct vc4_hdmi *vc4_hdmi); + }; + + /* HDMI audio information */ +@@ -107,5 +113,7 @@ encoder_to_vc4_hdmi(struct drm_encoder * + void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, + struct drm_display_mode *mode); + void vc4_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi); ++void vc4_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi); ++void vc4_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi); + + #endif /* _VC4_HDMI_H_ */ +--- a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c +@@ -7,6 +7,7 @@ + */ + + #include "vc4_hdmi.h" ++#include "vc4_regs.h" + #include "vc4_hdmi_regs.h" + + void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, struct drm_display_mode *mode) +@@ -23,3 +24,17 @@ void vc4_hdmi_phy_disable(struct vc4_hdm + { + HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16); + } ++ ++void vc4_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi) ++{ ++ HDMI_WRITE(HDMI_TX_PHY_CTL_0, ++ HDMI_READ(HDMI_TX_PHY_CTL_0) & ++ ~VC4_HDMI_TX_PHY_RNG_PWRDN); ++} ++ ++void vc4_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi) ++{ ++ HDMI_WRITE(HDMI_TX_PHY_CTL_0, ++ HDMI_READ(HDMI_TX_PHY_CTL_0) | ++ VC4_HDMI_TX_PHY_RNG_PWRDN); ++} diff --git a/target/linux/bcm27xx/patches-5.4/950-0587-drm-vc4-hdmi-Add-a-CSC-setup-callback.patch b/target/linux/bcm27xx/patches-5.4/950-0587-drm-vc4-hdmi-Add-a-CSC-setup-callback.patch new file mode 100644 index 0000000000..c9650bca08 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0587-drm-vc4-hdmi-Add-a-CSC-setup-callback.patch @@ -0,0 +1,134 @@ +From 110cf6bdc1d79f2ee7a435bc9d1ec900aba11ed5 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 26 Dec 2019 18:41:53 +0100 +Subject: [PATCH] drm/vc4: hdmi: Add a CSC setup callback + +Similarly to the previous patches, the CSC setup is slightly different in +the BCM2711 than in the previous generations. Let's add a callback for it. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 71 ++++++++++++++++++++-------------- + drivers/gpu/drm/vc4/vc4_hdmi.h | 3 ++ + 2 files changed, 45 insertions(+), 29 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -337,6 +337,41 @@ static void vc4_hdmi_encoder_disable(str + DRM_ERROR("Failed to release power domain: %d\n", ret); + } + ++static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, bool enable) ++{ ++ u32 csc_ctl; ++ ++ csc_ctl = VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR, ++ VC4_HD_CSC_CTL_ORDER); ++ ++ if (enable) { ++ /* CEA VICs other than #1 requre limited range RGB ++ * output unless overridden by an AVI infoframe. ++ * Apply a colorspace conversion to squash 0-255 down ++ * to 16-235. The matrix here is: ++ * ++ * [ 0 0 0.8594 16] ++ * [ 0 0.8594 0 16] ++ * [ 0.8594 0 0 16] ++ * [ 0 0 0 1] ++ */ ++ csc_ctl |= VC4_HD_CSC_CTL_ENABLE; ++ csc_ctl |= VC4_HD_CSC_CTL_RGB2YCC; ++ csc_ctl |= VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM, ++ VC4_HD_CSC_CTL_MODE); ++ ++ HDMI_WRITE(HDMI_CSC_12_11, (0x000 << 16) | 0x000); ++ HDMI_WRITE(HDMI_CSC_14_13, (0x100 << 16) | 0x6e0); ++ HDMI_WRITE(HDMI_CSC_22_21, (0x6e0 << 16) | 0x000); ++ HDMI_WRITE(HDMI_CSC_24_23, (0x100 << 16) | 0x000); ++ HDMI_WRITE(HDMI_CSC_32_31, (0x000 << 16) | 0x6e0); ++ HDMI_WRITE(HDMI_CSC_34_33, (0x100 << 16) | 0x000); ++ } ++ ++ /* The RGB order applies even when CSC is disabled. */ ++ HDMI_WRITE(HDMI_CSC_CTL, csc_ctl); ++} ++ + static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder) + { + struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; +@@ -360,7 +395,6 @@ static void vc4_hdmi_encoder_enable(stru + mode->crtc_vsync_end - + interlaced, + VC4_HDMI_VERTB_VBP)); +- u32 csc_ctl; + int ret; + + ret = pm_runtime_get_sync(&vc4_hdmi->pdev->dev); +@@ -431,41 +465,19 @@ static void vc4_hdmi_encoder_enable(stru + (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) | + (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW)); + +- csc_ctl = VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR, +- VC4_HD_CSC_CTL_ORDER); +- + if (vc4_encoder->hdmi_monitor && +- drm_default_rgb_quant_range(mode) == +- HDMI_QUANTIZATION_RANGE_LIMITED) { +- /* CEA VICs other than #1 requre limited range RGB +- * output unless overridden by an AVI infoframe. +- * Apply a colorspace conversion to squash 0-255 down +- * to 16-235. The matrix here is: +- * +- * [ 0 0 0.8594 16] +- * [ 0 0.8594 0 16] +- * [ 0.8594 0 0 16] +- * [ 0 0 0 1] +- */ +- csc_ctl |= VC4_HD_CSC_CTL_ENABLE; +- csc_ctl |= VC4_HD_CSC_CTL_RGB2YCC; +- csc_ctl |= VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM, +- VC4_HD_CSC_CTL_MODE); ++ drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED) { ++ if (vc4_hdmi->variant->csc_setup) ++ vc4_hdmi->variant->csc_setup(vc4_hdmi, true); + +- HDMI_WRITE(HDMI_CSC_12_11, (0x000 << 16) | 0x000); +- HDMI_WRITE(HDMI_CSC_14_13, (0x100 << 16) | 0x6e0); +- HDMI_WRITE(HDMI_CSC_22_21, (0x6e0 << 16) | 0x000); +- HDMI_WRITE(HDMI_CSC_24_23, (0x100 << 16) | 0x000); +- HDMI_WRITE(HDMI_CSC_32_31, (0x000 << 16) | 0x6e0); +- HDMI_WRITE(HDMI_CSC_34_33, (0x100 << 16) | 0x000); + vc4_encoder->limited_rgb_range = true; + } else { ++ if (vc4_hdmi->variant->csc_setup) ++ vc4_hdmi->variant->csc_setup(vc4_hdmi, false); ++ + vc4_encoder->limited_rgb_range = false; + } + +- /* The RGB order applies even when CSC is disabled. */ +- HDMI_WRITE(HDMI_CSC_CTL, csc_ctl); +- + HDMI_WRITE(HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N); + + if (debug_dump_regs) { +@@ -1426,6 +1438,7 @@ static const struct vc4_hdmi_variant bcm + .num_registers = ARRAY_SIZE(vc4_hdmi_fields), + + .init_resources = vc4_hdmi_init_resources, ++ .csc_setup = vc4_hdmi_csc_setup, + .reset = vc4_hdmi_reset, + .phy_init = vc4_hdmi_phy_init, + .phy_disable = vc4_hdmi_phy_disable, +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -41,6 +41,9 @@ struct vc4_hdmi_variant { + /* Callback to reset the HDMI block */ + void (*reset)(struct vc4_hdmi *vc4_hdmi); + ++ /* Callback to enable / disable the CSC */ ++ void (*csc_setup)(struct vc4_hdmi *vc4_hdmi, bool enable); ++ + /* Callback to initialize the PHY according to the mode */ + void (*phy_init)(struct vc4_hdmi *vc4_hdmi, + struct drm_display_mode *mode); diff --git a/target/linux/bcm27xx/patches-5.4/950-0588-drm-vc4-hdmi-Add-a-set_timings-callback.patch b/target/linux/bcm27xx/patches-5.4/950-0588-drm-vc4-hdmi-Add-a-set_timings-callback.patch new file mode 100644 index 0000000000..a39a8ac109 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0588-drm-vc4-hdmi-Add-a-set_timings-callback.patch @@ -0,0 +1,133 @@ +From b9c57901c600e09b100942b637c6bb01e52b7326 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Mon, 6 Jan 2020 13:43:27 +0100 +Subject: [PATCH] drm/vc4: hdmi: Add a set_timings callback + +Similarly to the previous patches, the timings setup in the HDMI controller +of the BCM2711 is slightly different, mostly because it supports higher +resolutions and thus needed more spaces for the various timings, resulting +in the register layout changing. + +Let's add a callback for that as well. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 71 +++++++++++++++++++--------------- + drivers/gpu/drm/vc4/vc4_hdmi.h | 4 ++ + 2 files changed, 44 insertions(+), 31 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -372,12 +372,9 @@ static void vc4_hdmi_csc_setup(struct vc + HDMI_WRITE(HDMI_CSC_CTL, csc_ctl); + } + +-static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder) ++static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, ++ struct drm_display_mode *mode) + { +- struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; +- struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); +- struct vc4_hdmi_encoder *vc4_encoder = &vc4_hdmi->encoder; +- bool debug_dump_regs = false; + bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; + bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC; + bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; +@@ -395,6 +392,41 @@ static void vc4_hdmi_encoder_enable(stru + mode->crtc_vsync_end - + interlaced, + VC4_HDMI_VERTB_VBP)); ++ ++ HDMI_WRITE(HDMI_HORZA, ++ (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) | ++ (hsync_pos ? VC4_HDMI_HORZA_HPOS : 0) | ++ VC4_SET_FIELD(mode->hdisplay * pixel_rep, ++ VC4_HDMI_HORZA_HAP)); ++ ++ HDMI_WRITE(HDMI_HORZB, ++ VC4_SET_FIELD((mode->htotal - ++ mode->hsync_end) * pixel_rep, ++ VC4_HDMI_HORZB_HBP) | ++ VC4_SET_FIELD((mode->hsync_end - ++ mode->hsync_start) * pixel_rep, ++ VC4_HDMI_HORZB_HSP) | ++ VC4_SET_FIELD((mode->hsync_start - ++ mode->hdisplay) * pixel_rep, ++ VC4_HDMI_HORZB_HFP)); ++ ++ HDMI_WRITE(HDMI_VERTA0, verta); ++ HDMI_WRITE(HDMI_VERTA1, verta); ++ ++ HDMI_WRITE(HDMI_VERTB0, vertb_even); ++ HDMI_WRITE(HDMI_VERTB1, vertb); ++ ++ HDMI_WRITE(HDMI_VID_CTL, ++ (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) | ++ (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW)); ++} ++ ++static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder) ++{ ++ struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; ++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); ++ struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); ++ bool debug_dump_regs = false; + int ret; + + ret = pm_runtime_get_sync(&vc4_hdmi->pdev->dev); +@@ -438,32 +470,8 @@ static void vc4_hdmi_encoder_enable(stru + VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT | + VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS); + +- HDMI_WRITE(HDMI_HORZA, +- (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) | +- (hsync_pos ? VC4_HDMI_HORZA_HPOS : 0) | +- VC4_SET_FIELD(mode->hdisplay * pixel_rep, +- VC4_HDMI_HORZA_HAP)); +- +- HDMI_WRITE(HDMI_HORZB, +- VC4_SET_FIELD((mode->htotal - +- mode->hsync_end) * pixel_rep, +- VC4_HDMI_HORZB_HBP) | +- VC4_SET_FIELD((mode->hsync_end - +- mode->hsync_start) * pixel_rep, +- VC4_HDMI_HORZB_HSP) | +- VC4_SET_FIELD((mode->hsync_start - +- mode->hdisplay) * pixel_rep, +- VC4_HDMI_HORZB_HFP)); +- +- HDMI_WRITE(HDMI_VERTA0, verta); +- HDMI_WRITE(HDMI_VERTA1, verta); +- +- HDMI_WRITE(HDMI_VERTB0, vertb_even); +- HDMI_WRITE(HDMI_VERTB1, vertb); +- +- HDMI_WRITE(HDMI_VID_CTL, +- (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) | +- (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW)); ++ if (vc4_hdmi->variant->set_timings) ++ vc4_hdmi->variant->set_timings(vc4_hdmi, mode); + + if (vc4_encoder->hdmi_monitor && + drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED) { +@@ -1440,6 +1448,7 @@ static const struct vc4_hdmi_variant bcm + .init_resources = vc4_hdmi_init_resources, + .csc_setup = vc4_hdmi_csc_setup, + .reset = vc4_hdmi_reset, ++ .set_timings = vc4_hdmi_set_timings, + .phy_init = vc4_hdmi_phy_init, + .phy_disable = vc4_hdmi_phy_disable, + .phy_rng_enable = vc4_hdmi_phy_rng_enable, +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -44,6 +44,10 @@ struct vc4_hdmi_variant { + /* Callback to enable / disable the CSC */ + void (*csc_setup)(struct vc4_hdmi *vc4_hdmi, bool enable); + ++ /* Callback to configure the video timings in the HDMI block */ ++ void (*set_timings)(struct vc4_hdmi *vc4_hdmi, ++ struct drm_display_mode *mode); ++ + /* Callback to initialize the PHY according to the mode */ + void (*phy_init)(struct vc4_hdmi *vc4_hdmi, + struct drm_display_mode *mode); diff --git a/target/linux/bcm27xx/patches-5.4/950-0589-drm-vc4-hdmi-Add-HDMI-ID.patch b/target/linux/bcm27xx/patches-5.4/950-0589-drm-vc4-hdmi-Add-HDMI-ID.patch new file mode 100644 index 0000000000..b6de732379 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0589-drm-vc4-hdmi-Add-HDMI-ID.patch @@ -0,0 +1,46 @@ +From 84c1a6034e361078d540c9b3bc672ccae623dc03 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Tue, 7 Jan 2020 13:14:07 +0100 +Subject: [PATCH] drm/vc4: hdmi: Add HDMI ID + +Some operations will need us to have the raw ID of the HDMI controller +in the BCM2711, such as the encoder type to register, the name of the +debugfs files, etc. + +Let's add it to our variant structure. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 3 +-- + drivers/gpu/drm/vc4/vc4_hdmi.h | 5 +++++ + 2 files changed, 6 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -1267,11 +1267,10 @@ static int vc4_hdmi_bind(struct device * + vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL); + if (!vc4_hdmi) + return -ENOMEM; +- + vc4_hdmi->pdev = pdev; + variant = of_device_get_match_data(dev); + vc4_hdmi->variant = variant; +- vc4_hdmi->encoder.base.type = VC4_ENCODER_TYPE_HDMI0; ++ vc4_hdmi->encoder.base.type = variant->id ? VC4_ENCODER_TYPE_HDMI1 : VC4_ENCODER_TYPE_HDMI0; + encoder = &vc4_hdmi->encoder.base.base; + + ret = variant->init_resources(vc4_hdmi); +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -27,6 +27,11 @@ struct vc4_hdmi; + struct vc4_hdmi_register; + + struct vc4_hdmi_variant { ++ /* On devices that have multiple, different instances (like ++ * the BCM2711), which instance is that variant useful for. ++ */ ++ unsigned int id; ++ + /* List of the registers available on that variant */ + const struct vc4_hdmi_register *registers; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0590-drm-vc4-hdmi-Deal-with-multiple-debugfs-files.patch b/target/linux/bcm27xx/patches-5.4/950-0590-drm-vc4-hdmi-Deal-with-multiple-debugfs-files.patch new file mode 100644 index 0000000000..746eff91d5 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0590-drm-vc4-hdmi-Deal-with-multiple-debugfs-files.patch @@ -0,0 +1,33 @@ +From a1f24e24c065b91f833e3f546c1507f69fb04bc7 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 16 Jan 2020 14:27:56 +0100 +Subject: [PATCH] drm/vc4: hdmi: Deal with multiple debugfs files + +The HDMI driver was registering a single debugfs file so far with the name +hdmi_regs. + +Obviously, this is not going to work anymore when will have multiple HDMI +controllers since we will end up trying to register two files with the same +name. + +Let's use the ID to avoid that name conflict. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -1380,7 +1380,10 @@ static int vc4_hdmi_bind(struct device * + if (ret) + goto err_destroy_encoder; + +- vc4_debugfs_add_file(drm, "hdmi_regs", vc4_hdmi_debugfs_regs, vc4_hdmi); ++ vc4_debugfs_add_file(drm, ++ variant->id ? "hdmi1_regs" : "hdmi_regs", ++ vc4_hdmi_debugfs_regs, ++ vc4_hdmi); + + return 0; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0591-drm-vc4-hdmi-Add-an-audio-support-flag.patch b/target/linux/bcm27xx/patches-5.4/950-0591-drm-vc4-hdmi-Add-an-audio-support-flag.patch new file mode 100644 index 0000000000..cc6cd86887 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0591-drm-vc4-hdmi-Add-an-audio-support-flag.patch @@ -0,0 +1,47 @@ +From 6154f7383e2defe48eea7fddb6ce646a0069828b Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 6 Feb 2020 16:21:45 +0100 +Subject: [PATCH] drm/vc4: hdmi: Add an audio support flag + +The BCM2711 audio support doesn't work yet, so let's add a boolean to +indicate whether or not it's supported, and only register a sound card if +that boolean is set. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 4 ++++ + drivers/gpu/drm/vc4/vc4_hdmi.h | 3 +++ + 2 files changed, 7 insertions(+) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -944,6 +944,9 @@ static int vc4_hdmi_audio_init(struct vc + int ret; + int len; + ++ if (!vc4_hdmi->variant->audio_available) ++ return 0; ++ + if (!of_find_property(dev->of_node, "dmas", &len) || + len == 0) { + dev_warn(dev, +@@ -1444,6 +1447,7 @@ static int vc4_hdmi_dev_remove(struct pl + } + + static const struct vc4_hdmi_variant bcm2835_variant = { ++ .audio_available = true, + .registers = vc4_hdmi_fields, + .num_registers = ARRAY_SIZE(vc4_hdmi_fields), + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -32,6 +32,9 @@ struct vc4_hdmi_variant { + */ + unsigned int id; + ++ /* Set to true when the audio support is available */ ++ bool audio_available; ++ + /* List of the registers available on that variant */ + const struct vc4_hdmi_register *registers; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0592-drm-vc4-hdmi-Move-CEC-init-to-its-own-function.patch b/target/linux/bcm27xx/patches-5.4/950-0592-drm-vc4-hdmi-Move-CEC-init-to-its-own-function.patch new file mode 100644 index 0000000000..533eb89e7a --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0592-drm-vc4-hdmi-Move-CEC-init-to-its-own-function.patch @@ -0,0 +1,165 @@ +From 9efd6edc4c7d01c74a92f2011ba285329ba956e4 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 6 Feb 2020 16:22:13 +0100 +Subject: [PATCH] drm/vc4: hdmi: Move CEC init to its own function + +The CEC init code was put directly into the bind function, which was quite +inconsistent with how the audio support was done, and would prevent us from +further changes to skip that initialisation entirely. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 108 ++++++++++++++++++++------------- + 1 file changed, 67 insertions(+), 41 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -1178,6 +1178,67 @@ static const struct cec_adap_ops vc4_hdm + .adap_log_addr = vc4_hdmi_cec_adap_log_addr, + .adap_transmit = vc4_hdmi_cec_adap_transmit, + }; ++ ++static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) ++{ ++ struct cec_connector_info conn_info; ++ struct platform_device *pdev = vc4_hdmi->pdev; ++ u32 value; ++ int ret; ++ ++ vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops, ++ vc4_hdmi, "vc4", ++ CEC_CAP_DEFAULTS | ++ CEC_CAP_CONNECTOR_INFO, 1); ++ ret = PTR_ERR_OR_ZERO(vc4_hdmi->cec_adap); ++ if (ret < 0) ++ return ret; ++ ++ cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector); ++ cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info); ++ ++ HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff); ++ value = HDMI_READ(HDMI_CEC_CNTRL_1); ++ value &= ~VC4_HDMI_CEC_DIV_CLK_CNT_MASK; ++ /* ++ * Set the logical address to Unregistered and set the clock ++ * divider: the hsm_clock rate and this divider setting will ++ * give a 40 kHz CEC clock. ++ */ ++ value |= VC4_HDMI_CEC_ADDR_MASK | ++ (4091 << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT); ++ HDMI_WRITE(HDMI_CEC_CNTRL_1, value); ++ ret = devm_request_threaded_irq(&pdev->dev, platform_get_irq(pdev, 0), ++ vc4_cec_irq_handler, ++ vc4_cec_irq_handler_thread, 0, ++ "vc4 hdmi cec", vc4_hdmi); ++ if (ret) ++ goto err_delete_cec_adap; ++ ++ ret = cec_register_adapter(vc4_hdmi->cec_adap, &pdev->dev); ++ if (ret < 0) ++ goto err_delete_cec_adap; ++ ++ return 0; ++ ++err_delete_cec_adap: ++ cec_delete_adapter(vc4_hdmi->cec_adap); ++ ++ return ret; ++} ++ ++static void vc4_hdmi_cec_exit(struct vc4_hdmi *vc4_hdmi) ++{ ++ cec_unregister_adapter(vc4_hdmi->cec_adap); ++} ++#else ++static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) ++{ ++ return 0; ++} ++ ++static void vc4_hdmi_cec_exit(struct vc4_hdmi *vc4_hdmi) {}; ++ + #endif + + static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi, +@@ -1255,9 +1316,6 @@ static int vc4_hdmi_init_resources(struc + + static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) + { +-#ifdef CONFIG_DRM_VC4_HDMI_CEC +- struct cec_connector_info conn_info; +-#endif + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = dev_get_drvdata(master); + const struct vc4_hdmi_variant *variant; +@@ -1345,43 +1403,13 @@ static int vc4_hdmi_bind(struct device * + if (ret) + goto err_destroy_encoder; + +-#ifdef CONFIG_DRM_VC4_HDMI_CEC +- vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops, +- vc4_hdmi, "vc4", +- CEC_CAP_DEFAULTS | +- CEC_CAP_CONNECTOR_INFO, 1); +- ret = PTR_ERR_OR_ZERO(vc4_hdmi->cec_adap); +- if (ret < 0) +- goto err_destroy_conn; +- +- cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector); +- cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info); +- +- HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff); +- value = HDMI_READ(HDMI_CEC_CNTRL_1); +- value &= ~VC4_HDMI_CEC_DIV_CLK_CNT_MASK; +- /* +- * Set the logical address to Unregistered and set the clock +- * divider: the hsm_clock rate and this divider setting will +- * give a 40 kHz CEC clock. +- */ +- value |= VC4_HDMI_CEC_ADDR_MASK | +- (4091 << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT); +- HDMI_WRITE(HDMI_CEC_CNTRL_1, value); +- ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0), +- vc4_cec_irq_handler, +- vc4_cec_irq_handler_thread, 0, +- "vc4 hdmi cec", vc4_hdmi); ++ ret = vc4_hdmi_cec_init(vc4_hdmi); + if (ret) +- goto err_delete_cec_adap; +- ret = cec_register_adapter(vc4_hdmi->cec_adap, dev); +- if (ret < 0) +- goto err_delete_cec_adap; +-#endif ++ goto err_destroy_conn; + + ret = vc4_hdmi_audio_init(vc4_hdmi); + if (ret) +- goto err_destroy_encoder; ++ goto err_free_cec; + + vc4_debugfs_add_file(drm, + variant->id ? "hdmi1_regs" : "hdmi_regs", +@@ -1390,12 +1418,10 @@ static int vc4_hdmi_bind(struct device * + + return 0; + +-#ifdef CONFIG_DRM_VC4_HDMI_CEC +-err_delete_cec_adap: +- cec_delete_adapter(vc4_hdmi->cec_adap); ++err_free_cec: ++ vc4_hdmi_cec_exit(vc4_hdmi); + err_destroy_conn: + vc4_hdmi_connector_destroy(&vc4_hdmi->connector); +-#endif + err_destroy_encoder: + vc4_hdmi_encoder_destroy(encoder); + err_unprepare_hsm: +@@ -1420,7 +1446,7 @@ static void vc4_hdmi_unbind(struct devic + kfree(vc4_hdmi->hdmi_regset.regs); + kfree(vc4_hdmi->hd_regset.regs); + +- cec_unregister_adapter(vc4_hdmi->cec_adap); ++ vc4_hdmi_cec_exit(vc4_hdmi); + vc4_hdmi_connector_destroy(&vc4_hdmi->connector); + vc4_hdmi_encoder_destroy(&vc4_hdmi->encoder.base.base); + diff --git a/target/linux/bcm27xx/patches-5.4/950-0593-drm-vc4-hdmi-Add-CEC-support-flag.patch b/target/linux/bcm27xx/patches-5.4/950-0593-drm-vc4-hdmi-Add-CEC-support-flag.patch new file mode 100644 index 0000000000..33ecf44a27 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0593-drm-vc4-hdmi-Add-CEC-support-flag.patch @@ -0,0 +1,47 @@ +From 0f626dc8443a93138806b4a3f351bac346036358 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 6 Feb 2020 16:22:50 +0100 +Subject: [PATCH] drm/vc4: hdmi: Add CEC support flag + +Similarly to the audio support, CEC support is not there yet for the +BCM2711, so let's skip entirely the CEC initialization through a variant +flag. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 4 ++++ + drivers/gpu/drm/vc4/vc4_hdmi.h | 3 +++ + 2 files changed, 7 insertions(+) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -1186,6 +1186,9 @@ static int vc4_hdmi_cec_init(struct vc4_ + u32 value; + int ret; + ++ if (!vc4_hdmi->variant->cec_available) ++ return 0; ++ + vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops, + vc4_hdmi, "vc4", + CEC_CAP_DEFAULTS | +@@ -1474,6 +1477,7 @@ static int vc4_hdmi_dev_remove(struct pl + + static const struct vc4_hdmi_variant bcm2835_variant = { + .audio_available = true, ++ .cec_available = true, + .registers = vc4_hdmi_fields, + .num_registers = ARRAY_SIZE(vc4_hdmi_fields), + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -35,6 +35,9 @@ struct vc4_hdmi_variant { + /* Set to true when the audio support is available */ + bool audio_available; + ++ /* Set to true when the CEC support is available */ ++ bool cec_available; ++ + /* List of the registers available on that variant */ + const struct vc4_hdmi_register *registers; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0594-drm-vc4-hdmi-Remove-unused-CEC_CLOCK_DIV-define.patch b/target/linux/bcm27xx/patches-5.4/950-0594-drm-vc4-hdmi-Remove-unused-CEC_CLOCK_DIV-define.patch new file mode 100644 index 0000000000..4befec6c05 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0594-drm-vc4-hdmi-Remove-unused-CEC_CLOCK_DIV-define.patch @@ -0,0 +1,23 @@ +From a1a87ba39e7fad93cbbb5ea178a12d0c669a9812 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Mon, 10 Feb 2020 15:15:47 +0100 +Subject: [PATCH] drm/vc4: hdmi: Remove unused CEC_CLOCK_DIV define + +The CEC_CLOCK_DIV define is not used anywhere in the driver, let's remove +it. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -54,7 +54,6 @@ + + #define HSM_CLOCK_FREQ 163682864 + #define CEC_CLOCK_FREQ 40000 +-#define CEC_CLOCK_DIV (HSM_CLOCK_FREQ / CEC_CLOCK_FREQ) + + static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) + { diff --git a/target/linux/bcm27xx/patches-5.4/950-0595-drm-vc4-hdmi-Rename-drm_encoder-pointer-in-mode_vali.patch b/target/linux/bcm27xx/patches-5.4/950-0595-drm-vc4-hdmi-Rename-drm_encoder-pointer-in-mode_vali.patch new file mode 100644 index 0000000000..5c0ab9768b --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0595-drm-vc4-hdmi-Rename-drm_encoder-pointer-in-mode_vali.patch @@ -0,0 +1,26 @@ +From dfc6e670144207251dc0902d1756bc89ef6cd1dc Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 13 Feb 2020 12:31:09 +0100 +Subject: [PATCH] drm/vc4: hdmi: Rename drm_encoder pointer in + mode_valid + +The mode_valid hook on the encoder uses a pointer to a drm_encoder called +crtc, which is pretty confusing. Let's rename it to encoder to make it +clear what it is. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -559,7 +559,7 @@ static void vc4_hdmi_encoder_enable(stru + } + + static enum drm_mode_status +-vc4_hdmi_encoder_mode_valid(struct drm_encoder *crtc, ++vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder, + const struct drm_display_mode *mode) + { + /* diff --git a/target/linux/bcm27xx/patches-5.4/950-0596-drm-vc4-hdmi-Adjust-HSM-clock-rate-depending-on-pixe.patch b/target/linux/bcm27xx/patches-5.4/950-0596-drm-vc4-hdmi-Adjust-HSM-clock-rate-depending-on-pixe.patch new file mode 100644 index 0000000000..4dba81b035 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0596-drm-vc4-hdmi-Adjust-HSM-clock-rate-depending-on-pixe.patch @@ -0,0 +1,163 @@ +From 3c33724058852d7c58d77d03e11ca545fb04256a Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Mon, 10 Feb 2020 15:23:06 +0100 +Subject: [PATCH] drm/vc4: hdmi: Adjust HSM clock rate depending on + pixel rate + +The HSM clock needs to be setup at around 110% of the pixel rate. This +was done previously by setting the clock rate to 148.5MHz * 108% at +probe time and only check in mode_valid whether the mode pixel clock was +under 148.5MHz or not. + +However, with 4k we need to change that frequency to a higher frequency +than 148.5MHz. + +Let's change that logic a bit by setting the clock rate of the HSM clock +to the pixel rate at encoder_enable time. This would work for the +BCM2711 that support 4k resolutions and has a clock that can provide it, +but we still have to take care of a 4k panel plugged on a BCM283x SoCs +that wouldn't be able to use those modes, so let's define the limit in +the variant. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 51 +++++++++++++++++----------------- + drivers/gpu/drm/vc4/vc4_hdmi.h | 3 ++ + 2 files changed, 29 insertions(+), 25 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -52,7 +52,6 @@ + #include "vc4_hdmi_regs.h" + #include "vc4_regs.h" + +-#define HSM_CLOCK_FREQ 163682864 + #define CEC_CLOCK_FREQ 40000 + + static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) +@@ -329,6 +328,7 @@ static void vc4_hdmi_encoder_disable(str + HDMI_WRITE(HDMI_VID_CTL, + HDMI_READ(HDMI_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE); + ++ clk_disable_unprepare(vc4_hdmi->hsm_clock); + clk_disable_unprepare(vc4_hdmi->pixel_clock); + + ret = pm_runtime_put(&vc4_hdmi->pdev->dev); +@@ -426,6 +426,7 @@ static void vc4_hdmi_encoder_enable(stru + struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); + bool debug_dump_regs = false; ++ unsigned long pixel_rate, hsm_rate; + int ret; + + ret = pm_runtime_get_sync(&vc4_hdmi->pdev->dev); +@@ -434,9 +435,8 @@ static void vc4_hdmi_encoder_enable(stru + return; + } + +- ret = clk_set_rate(vc4_hdmi->pixel_clock, +- mode->clock * 1000 * +- ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1)); ++ pixel_rate = mode->clock * 1000 * ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1); ++ ret = clk_set_rate(vc4_hdmi->pixel_clock, pixel_rate); + if (ret) { + DRM_ERROR("Failed to set pixel clock rate: %d\n", ret); + return; +@@ -448,6 +448,24 @@ static void vc4_hdmi_encoder_enable(stru + return; + } + ++ /* ++ * The HSM rate needs to be at 108% of the pixel clock, with a ++ * minimum of 108MHz. ++ */ ++ hsm_rate = max_t(unsigned long, 108000000, (pixel_rate / 100) * 108); ++ ret = clk_set_rate(vc4_hdmi->hsm_clock, hsm_rate); ++ if (ret) { ++ DRM_ERROR("Failed to set HSM clock rate: %d\n", ret); ++ return; ++ } ++ ++ ret = clk_prepare_enable(vc4_hdmi->hsm_clock); ++ if (ret) { ++ DRM_ERROR("Failed to turn on HSM clock: %d\n", ret); ++ clk_disable_unprepare(vc4_hdmi->pixel_clock); ++ return; ++ } ++ + if (vc4_hdmi->variant->reset) + vc4_hdmi->variant->reset(vc4_hdmi); + +@@ -578,7 +596,9 @@ vc4_hdmi_encoder_mode_valid(struct drm_e + * Additionally, the AXI clock needs to be at least 25% of + * pixel clock, but HSM ends up being the limiting factor. + */ +- if (mode->clock > HSM_CLOCK_FREQ / (1000 * 101 / 100)) ++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); ++ ++ if ((mode->clock * 1000) > vc4_hdmi->variant->max_pixel_clock) + return MODE_CLOCK_HIGH; + + return MODE_OK; +@@ -1353,23 +1373,6 @@ static int vc4_hdmi_bind(struct device * + return -EPROBE_DEFER; + } + +- /* This is the rate that is set by the firmware. The number +- * needs to be a bit higher than the pixel clock rate +- * (generally 148.5Mhz). +- */ +- ret = clk_set_rate(vc4_hdmi->hsm_clock, HSM_CLOCK_FREQ); +- if (ret) { +- DRM_ERROR("Failed to set HSM clock rate: %d\n", ret); +- goto err_put_i2c; +- } +- +- ret = clk_prepare_enable(vc4_hdmi->hsm_clock); +- if (ret) { +- DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n", +- ret); +- goto err_put_i2c; +- } +- + /* Only use the GPIO HPD pin if present in the DT, otherwise + * we'll use the HDMI core's register. + */ +@@ -1427,9 +1430,7 @@ err_destroy_conn: + err_destroy_encoder: + vc4_hdmi_encoder_destroy(encoder); + err_unprepare_hsm: +- clk_disable_unprepare(vc4_hdmi->hsm_clock); + pm_runtime_disable(dev); +-err_put_i2c: + put_device(&vc4_hdmi->ddc->dev); + + return ret; +@@ -1452,7 +1453,6 @@ static void vc4_hdmi_unbind(struct devic + vc4_hdmi_connector_destroy(&vc4_hdmi->connector); + vc4_hdmi_encoder_destroy(&vc4_hdmi->encoder.base.base); + +- clk_disable_unprepare(vc4_hdmi->hsm_clock); + pm_runtime_disable(dev); + + put_device(&vc4_hdmi->ddc->dev); +@@ -1475,6 +1475,7 @@ static int vc4_hdmi_dev_remove(struct pl + } + + static const struct vc4_hdmi_variant bcm2835_variant = { ++ .max_pixel_clock = 148500000, + .audio_available = true, + .cec_available = true, + .registers = vc4_hdmi_fields, +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -38,6 +38,9 @@ struct vc4_hdmi_variant { + /* Set to true when the CEC support is available */ + bool cec_available; + ++ /* Maximum pixel clock supported by the controller (in Hz) */ ++ unsigned long long max_pixel_clock; ++ + /* List of the registers available on that variant */ + const struct vc4_hdmi_register *registers; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0597-drm-vc4-hdmi-Support-the-BCM2711-HDMI-controllers.patch b/target/linux/bcm27xx/patches-5.4/950-0597-drm-vc4-hdmi-Support-the-BCM2711-HDMI-controllers.patch new file mode 100644 index 0000000000..d511f1e212 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0597-drm-vc4-hdmi-Support-the-BCM2711-HDMI-controllers.patch @@ -0,0 +1,1131 @@ +From d0931317c51f14bf65af65e7c3f2df6bb26d7c97 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Tue, 17 Dec 2019 11:48:37 +0100 +Subject: [PATCH] drm/vc4: hdmi: Support the BCM2711 HDMI controllers + +Now that the driver is ready for it, let's bring in the HDMI controllers +variants for the BCM2711. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 254 +++++++++++++++ + drivers/gpu/drm/vc4/vc4_hdmi.h | 35 +++ + drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 469 ++++++++++++++++++++++++++++ + drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 201 ++++++++++++ + 4 files changed, 959 insertions(+) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -42,6 +42,7 @@ + #include <linux/of_platform.h> + #include <linux/pm_runtime.h> + #include <linux/rational.h> ++#include <linux/reset.h> + #include <sound/dmaengine_pcm.h> + #include <sound/pcm_drm_eld.h> + #include <sound/pcm_params.h> +@@ -52,6 +53,31 @@ + #include "vc4_hdmi_regs.h" + #include "vc4_regs.h" + ++#define VC5_HDMI_HORZA_HFP_SHIFT 16 ++#define VC5_HDMI_HORZA_HFP_MASK VC4_MASK(28, 16) ++#define VC5_HDMI_HORZA_VPOS BIT(15) ++#define VC5_HDMI_HORZA_HPOS BIT(14) ++#define VC5_HDMI_HORZA_HAP_SHIFT 0 ++#define VC5_HDMI_HORZA_HAP_MASK VC4_MASK(13, 0) ++ ++#define VC5_HDMI_HORZB_HBP_SHIFT 16 ++#define VC5_HDMI_HORZB_HBP_MASK VC4_MASK(26, 16) ++#define VC5_HDMI_HORZB_HSP_SHIFT 0 ++#define VC5_HDMI_HORZB_HSP_MASK VC4_MASK(10, 0) ++ ++#define VC5_HDMI_VERTA_VSP_SHIFT 24 ++#define VC5_HDMI_VERTA_VSP_MASK VC4_MASK(28, 24) ++#define VC5_HDMI_VERTA_VFP_SHIFT 16 ++#define VC5_HDMI_VERTA_VFP_MASK VC4_MASK(22, 16) ++#define VC5_HDMI_VERTA_VAL_SHIFT 0 ++#define VC5_HDMI_VERTA_VAL_MASK VC4_MASK(12, 0) ++ ++#define VC5_HDMI_VERTB_VSPO_SHIFT 16 ++#define VC5_HDMI_VERTB_VSPO_MASK VC4_MASK(29, 16) ++ ++# define VC4_HD_M_SW_RST BIT(2) ++# define VC4_HD_M_ENABLE BIT(0) ++ + #define CEC_CLOCK_FREQ 40000 + + static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) +@@ -75,6 +101,13 @@ static void vc4_hdmi_reset(struct vc4_hd + HDMI_WRITE(HDMI_SW_RESET_CONTROL, 0); + } + ++static void vc5_hdmi_reset(struct vc4_hdmi *vc4_hdmi) ++{ ++ reset_control_reset(vc4_hdmi->reset); ++ ++ HDMI_WRITE(HDMI_DVP_CTL, 0); ++} ++ + static enum drm_connector_status + vc4_hdmi_connector_detect(struct drm_connector *connector, bool force) + { +@@ -371,6 +404,45 @@ static void vc4_hdmi_csc_setup(struct vc + HDMI_WRITE(HDMI_CSC_CTL, csc_ctl); + } + ++static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, bool enable) ++{ ++ u32 csc_ctl; ++ ++ csc_ctl = 0x07; /* RGB_CONVERT_MODE = custom matrix, || USE_RGB_TO_YCBCR */ ++ ++ if (enable) { ++ /* CEA VICs other than #1 requre limited range RGB ++ * output unless overridden by an AVI infoframe. ++ * Apply a colorspace conversion to squash 0-255 down ++ * to 16-235. The matrix here is: ++ * ++ * [ 0.8594 0 0 16] ++ * [ 0 0.8594 0 16] ++ * [ 0 0 0.8594 16] ++ * [ 0 0 0 1] ++ * Matrix is signed 2p13 fixed point, with signed 9p6 offsets ++ */ ++ HDMI_WRITE(HDMI_CSC_12_11, (0x0000 << 16) | 0x1b80); ++ HDMI_WRITE(HDMI_CSC_14_13, (0x0400 << 16) | 0x0000); ++ HDMI_WRITE(HDMI_CSC_22_21, (0x1b80 << 16) | 0x0000); ++ HDMI_WRITE(HDMI_CSC_24_23, (0x0400 << 16) | 0x0000); ++ HDMI_WRITE(HDMI_CSC_32_31, (0x0000 << 16) | 0x0000); ++ HDMI_WRITE(HDMI_CSC_34_33, (0x0400 << 16) | 0x1b80); ++ } else { ++ /* Still use the matrix for full range, but make it unity. ++ * Matrix is signed 2p13 fixed point, with signed 9p6 offsets ++ */ ++ HDMI_WRITE(HDMI_CSC_12_11, (0x0000 << 16) | 0x2000); ++ HDMI_WRITE(HDMI_CSC_14_13, (0x0000 << 16) | 0x0000); ++ HDMI_WRITE(HDMI_CSC_22_21, (0x2000 << 16) | 0x0000); ++ HDMI_WRITE(HDMI_CSC_24_23, (0x0000 << 16) | 0x0000); ++ HDMI_WRITE(HDMI_CSC_32_31, (0x0000 << 16) | 0x0000); ++ HDMI_WRITE(HDMI_CSC_34_33, (0x0000 << 16) | 0x2000); ++ } ++ ++ HDMI_WRITE(HDMI_CSC_CTL, csc_ctl); ++} ++ + static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, + struct drm_display_mode *mode) + { +@@ -420,6 +492,58 @@ static void vc4_hdmi_set_timings(struct + (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW)); + } + ++static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, ++ struct drm_display_mode *mode) ++{ ++ bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; ++ bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC; ++ bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; ++ u32 pixel_rep = (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1; ++ u32 verta = (VC4_SET_FIELD(mode->crtc_vsync_end - mode->crtc_vsync_start, ++ VC5_HDMI_VERTA_VSP) | ++ VC4_SET_FIELD(mode->crtc_vsync_start - mode->crtc_vdisplay, ++ VC5_HDMI_VERTA_VFP) | ++ VC4_SET_FIELD(mode->crtc_vdisplay, VC5_HDMI_VERTA_VAL)); ++ u32 vertb = (VC4_SET_FIELD(0, VC5_HDMI_VERTB_VSPO) | ++ VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end, ++ VC4_HDMI_VERTB_VBP)); ++ u32 vertb_even = (VC4_SET_FIELD(0, VC5_HDMI_VERTB_VSPO) | ++ VC4_SET_FIELD(mode->crtc_vtotal - ++ mode->crtc_vsync_end - ++ interlaced, ++ VC4_HDMI_VERTB_VBP)); ++ ++ HDMI_WRITE(HDMI_VEC_INTERFACE_XBAR, 0x354021); ++ HDMI_WRITE(HDMI_HORZA, ++ (vsync_pos ? VC5_HDMI_HORZA_VPOS : 0) | ++ (hsync_pos ? VC5_HDMI_HORZA_HPOS : 0) | ++ VC4_SET_FIELD(mode->hdisplay * pixel_rep, ++ VC5_HDMI_HORZA_HAP) | ++ VC4_SET_FIELD((mode->hsync_start - ++ mode->hdisplay) * pixel_rep, ++ VC5_HDMI_HORZA_HFP)); ++ ++ HDMI_WRITE(HDMI_HORZB, ++ VC4_SET_FIELD((mode->htotal - ++ mode->hsync_end) * pixel_rep, ++ VC5_HDMI_HORZB_HBP) | ++ VC4_SET_FIELD((mode->hsync_end - ++ mode->hsync_start) * pixel_rep, ++ VC5_HDMI_HORZB_HSP)); ++ ++ HDMI_WRITE(HDMI_VERTA0, verta); ++ HDMI_WRITE(HDMI_VERTA1, verta); ++ ++ HDMI_WRITE(HDMI_VERTB0, vertb_even); ++ HDMI_WRITE(HDMI_VERTB1, vertb); ++ ++ HDMI_WRITE(HDMI_VID_CTL, ++ (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) | ++ (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW)); ++ ++ HDMI_WRITE(HDMI_CLOCK_STOP, 0); ++} ++ + static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder) + { + struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; +@@ -1336,6 +1460,92 @@ static int vc4_hdmi_init_resources(struc + return 0; + } + ++static int vc5_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi) ++{ ++ struct platform_device *pdev = vc4_hdmi->pdev; ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi"); ++ if (!res) ++ return -ENODEV; ++ ++ vc4_hdmi->hdmicore_regs = devm_ioremap(dev, res->start, ++ resource_size(res)); ++ if (IS_ERR(vc4_hdmi->hdmicore_regs)) ++ return PTR_ERR(vc4_hdmi->hdmicore_regs); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hd"); ++ if (!res) ++ return -ENODEV; ++ ++ vc4_hdmi->hd_regs = devm_ioremap(dev, res->start, resource_size(res)); ++ if (IS_ERR(vc4_hdmi->hd_regs)) ++ return PTR_ERR(vc4_hdmi->hd_regs); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cec"); ++ if (!res) ++ return -ENODEV; ++ ++ vc4_hdmi->cec_regs = devm_ioremap(dev, res->start, resource_size(res)); ++ if (IS_ERR(vc4_hdmi->cec_regs)) ++ return PTR_ERR(vc4_hdmi->cec_regs); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csc"); ++ if (!res) ++ return -ENODEV; ++ ++ vc4_hdmi->csc_regs = devm_ioremap(dev, res->start, resource_size(res)); ++ if (IS_ERR(vc4_hdmi->csc_regs)) ++ return PTR_ERR(vc4_hdmi->csc_regs); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dvp"); ++ if (!res) ++ return -ENODEV; ++ ++ vc4_hdmi->dvp_regs = devm_ioremap(dev, res->start, resource_size(res)); ++ if (IS_ERR(vc4_hdmi->dvp_regs)) ++ return PTR_ERR(vc4_hdmi->dvp_regs); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); ++ if (!res) ++ return -ENODEV; ++ ++ vc4_hdmi->phy_regs = devm_ioremap(dev, res->start, resource_size(res)); ++ if (IS_ERR(vc4_hdmi->phy_regs)) ++ return PTR_ERR(vc4_hdmi->phy_regs); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "packet"); ++ if (!res) ++ return -ENODEV; ++ ++ vc4_hdmi->ram_regs = devm_ioremap(dev, res->start, resource_size(res)); ++ if (IS_ERR(vc4_hdmi->ram_regs)) ++ return PTR_ERR(vc4_hdmi->ram_regs); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rm"); ++ if (!res) ++ return -ENODEV; ++ ++ vc4_hdmi->rm_regs = devm_ioremap(dev, res->start, resource_size(res)); ++ if (IS_ERR(vc4_hdmi->rm_regs)) ++ return PTR_ERR(vc4_hdmi->rm_regs); ++ ++ vc4_hdmi->hsm_clock = devm_clk_get(dev, "hdmi"); ++ if (IS_ERR(vc4_hdmi->hsm_clock)) { ++ DRM_ERROR("Failed to get HDMI state machine clock\n"); ++ return PTR_ERR(vc4_hdmi->hsm_clock); ++ } ++ ++ vc4_hdmi->reset = devm_reset_control_get(dev, NULL); ++ if (IS_ERR(vc4_hdmi->reset)) { ++ DRM_ERROR("Failed to get HDMI reset line\n"); ++ return PTR_ERR(vc4_hdmi->reset); ++ } ++ ++ return 0; ++} ++ + static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) + { + struct platform_device *pdev = to_platform_device(dev); +@@ -1491,8 +1701,52 @@ static const struct vc4_hdmi_variant bcm + .phy_rng_disable = vc4_hdmi_phy_rng_disable, + }; + ++static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = { ++ .id = 0, ++ .max_pixel_clock = 297000000, ++ .registers = vc5_hdmi_hdmi0_fields, ++ .num_registers = ARRAY_SIZE(vc5_hdmi_hdmi0_fields), ++ .phy_lane_mapping = { ++ PHY_LANE_0, ++ PHY_LANE_1, ++ PHY_LANE_2, ++ PHY_LANE_CK, ++ }, ++ ++ .init_resources = vc5_hdmi_init_resources, ++ .csc_setup = vc5_hdmi_csc_setup, ++ .reset = vc5_hdmi_reset, ++ .set_timings = vc5_hdmi_set_timings, ++ .phy_init = vc5_hdmi_phy_init, ++ .phy_rng_enable = vc5_hdmi_phy_rng_enable, ++ .phy_rng_disable = vc5_hdmi_phy_rng_disable, ++}; ++ ++static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = { ++ .id = 1, ++ .max_pixel_clock = 297000000, ++ .registers = vc5_hdmi_hdmi1_fields, ++ .num_registers = ARRAY_SIZE(vc5_hdmi_hdmi1_fields), ++ .phy_lane_mapping = { ++ PHY_LANE_1, ++ PHY_LANE_0, ++ PHY_LANE_CK, ++ PHY_LANE_2, ++ }, ++ ++ .init_resources = vc5_hdmi_init_resources, ++ .csc_setup = vc5_hdmi_csc_setup, ++ .reset = vc5_hdmi_reset, ++ .set_timings = vc5_hdmi_set_timings, ++ .phy_init = vc5_hdmi_phy_init, ++ .phy_rng_enable = vc5_hdmi_phy_rng_enable, ++ .phy_rng_disable = vc5_hdmi_phy_rng_disable, ++}; ++ + static const struct of_device_id vc4_hdmi_dt_match[] = { + { .compatible = "brcm,bcm2835-hdmi", .data = &bcm2835_variant }, ++ { .compatible = "brcm,bcm2711-hdmi0", .data = &bcm2711_hdmi0_variant }, ++ { .compatible = "brcm,bcm2711-hdmi1", .data = &bcm2711_hdmi1_variant }, + {} + }; + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -26,6 +26,13 @@ struct drm_display_mode; + struct vc4_hdmi; + struct vc4_hdmi_register; + ++enum vc4_hdmi_phy_channel { ++ PHY_LANE_0 = 0, ++ PHY_LANE_1, ++ PHY_LANE_2, ++ PHY_LANE_CK, ++}; ++ + struct vc4_hdmi_variant { + /* On devices that have multiple, different instances (like + * the BCM2711), which instance is that variant useful for. +@@ -47,6 +54,13 @@ struct vc4_hdmi_variant { + /* Number of registers on that variant */ + unsigned int num_registers; + ++ /* BCM2711 Only. ++ * The variants don't map the lane in the same order in the ++ * PHY, so this is an array mapping the HDMI channel (index) ++ * to the PHY lane (value). ++ */ ++ enum vc4_hdmi_phy_channel phy_lane_mapping[4]; ++ + /* Callback to get the resources (memory region, interrupts, + * clocks, etc) for that variant. + */ +@@ -102,6 +116,20 @@ struct vc4_hdmi { + struct i2c_adapter *ddc; + void __iomem *hdmicore_regs; + void __iomem *hd_regs; ++ ++ /* VC5 Only */ ++ void __iomem *cec_regs; ++ /* VC5 Only */ ++ void __iomem *csc_regs; ++ /* VC5 Only */ ++ void __iomem *dvp_regs; ++ /* VC5 Only */ ++ void __iomem *phy_regs; ++ /* VC5 Only */ ++ void __iomem *ram_regs; ++ /* VC5 Only */ ++ void __iomem *rm_regs; ++ + int hpd_gpio; + bool hpd_active_low; + +@@ -113,6 +141,8 @@ struct vc4_hdmi { + struct clk *pixel_clock; + struct clk *hsm_clock; + ++ struct reset_control *reset; ++ + struct debugfs_regset32 hdmi_regset; + struct debugfs_regset32 hd_regset; + }; +@@ -137,4 +167,9 @@ void vc4_hdmi_phy_disable(struct vc4_hdm + void vc4_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi); + void vc4_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi); + ++void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, ++ struct drm_display_mode *mode); ++void vc5_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi); ++void vc5_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi); ++ + #endif /* _VC4_HDMI_H_ */ +--- a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c +@@ -10,6 +10,123 @@ + #include "vc4_regs.h" + #include "vc4_hdmi_regs.h" + ++#define VC4_HDMI_TX_PHY_RESET_CTL_PLL_RESETB BIT(5) ++#define VC4_HDMI_TX_PHY_RESET_CTL_PLLDIV_RESETB BIT(4) ++#define VC4_HDMI_TX_PHY_RESET_CTL_TX_CK_RESET BIT(3) ++#define VC4_HDMI_TX_PHY_RESET_CTL_TX_2_RESET BIT(2) ++#define VC4_HDMI_TX_PHY_RESET_CTL_TX_1_RESET BIT(1) ++#define VC4_HDMI_TX_PHY_RESET_CTL_TX_0_RESET BIT(0) ++ ++#define VC4_HDMI_TX_PHY_POWERDOWN_CTL_RNDGEN_PWRDN BIT(4) ++ ++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_2_PREEMP_SHIFT 29 ++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_2_PREEMP_MASK VC4_MASK(31, 29) ++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_2_MAINDRV_SHIFT 24 ++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_2_MAINDRV_MASK VC4_MASK(28, 24) ++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_1_PREEMP_SHIFT 21 ++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_1_PREEMP_MASK VC4_MASK(23, 21) ++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_1_MAINDRV_SHIFT 16 ++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_1_MAINDRV_MASK VC4_MASK(20, 16) ++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_0_PREEMP_SHIFT 13 ++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_0_PREEMP_MASK VC4_MASK(15, 13) ++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_0_MAINDRV_SHIFT 8 ++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_0_MAINDRV_MASK VC4_MASK(12, 8) ++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_CK_PREEMP_SHIFT 5 ++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_CK_PREEMP_MASK VC4_MASK(7, 5) ++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_CK_MAINDRV_SHIFT 0 ++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_CK_MAINDRV_MASK VC4_MASK(4, 0) ++ ++#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA2_SHIFT 15 ++#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA2_MASK VC4_MASK(19, 15) ++#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA1_SHIFT 10 ++#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA1_MASK VC4_MASK(14, 10) ++#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA0_SHIFT 5 ++#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA0_MASK VC4_MASK(9, 5) ++#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_CK_SHIFT 0 ++#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_CK_MASK VC4_MASK(4, 0) ++ ++#define VC4_HDMI_TX_PHY_CTL_2_VCO_GAIN_SHIFT 16 ++#define VC4_HDMI_TX_PHY_CTL_2_VCO_GAIN_MASK VC4_MASK(19, 16) ++#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA2_SHIFT 12 ++#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA2_MASK VC4_MASK(15, 12) ++#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA1_SHIFT 8 ++#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA1_MASK VC4_MASK(11, 8) ++#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA0_SHIFT 4 ++#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA0_MASK VC4_MASK(7, 4) ++#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELCK_SHIFT 0 ++#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELCK_MASK VC4_MASK(3, 0) ++ ++#define VC4_HDMI_TX_PHY_CTL_3_RP_SHIFT 17 ++#define VC4_HDMI_TX_PHY_CTL_3_RP_MASK VC4_MASK(19, 17) ++#define VC4_HDMI_TX_PHY_CTL_3_RZ_SHIFT 12 ++#define VC4_HDMI_TX_PHY_CTL_3_RZ_MASK VC4_MASK(16, 12) ++#define VC4_HDMI_TX_PHY_CTL_3_CP1_SHIFT 10 ++#define VC4_HDMI_TX_PHY_CTL_3_CP1_MASK VC4_MASK(11, 10) ++#define VC4_HDMI_TX_PHY_CTL_3_CP_SHIFT 8 ++#define VC4_HDMI_TX_PHY_CTL_3_CP_MASK VC4_MASK(9, 8) ++#define VC4_HDMI_TX_PHY_CTL_3_CZ_SHIFT 6 ++#define VC4_HDMI_TX_PHY_CTL_3_CZ_MASK VC4_MASK(7, 6) ++#define VC4_HDMI_TX_PHY_CTL_3_ICP_SHIFT 0 ++#define VC4_HDMI_TX_PHY_CTL_3_ICP_MASK VC4_MASK(5, 0) ++ ++#define VC4_HDMI_TX_PHY_PLL_CTL_0_MASH11_MODE BIT(13) ++#define VC4_HDMI_TX_PHY_PLL_CTL_0_VC_RANGE_EN BIT(12) ++#define VC4_HDMI_TX_PHY_PLL_CTL_0_EMULATE_VC_LOW BIT(11) ++#define VC4_HDMI_TX_PHY_PLL_CTL_0_EMULATE_VC_HIGH BIT(10) ++#define VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_SEL_SHIFT 9 ++#define VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_SEL_MASK VC4_MASK(9, 9) ++#define VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_FB_DIV2 BIT(8) ++#define VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_POST_DIV2 BIT(7) ++#define VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_CONT_EN BIT(6) ++#define VC4_HDMI_TX_PHY_PLL_CTL_0_ENA_VCO_CLK BIT(5) ++ ++#define VC4_HDMI_TX_PHY_PLL_CTL_1_CPP_SHIFT 16 ++#define VC4_HDMI_TX_PHY_PLL_CTL_1_CPP_MASK VC4_MASK(27, 16) ++#define VC4_HDMI_TX_PHY_PLL_CTL_1_FREQ_DOUBLER_DELAY_SHIFT 14 ++#define VC4_HDMI_TX_PHY_PLL_CTL_1_FREQ_DOUBLER_DELAY_MASK VC4_MASK(15, 14) ++#define VC4_HDMI_TX_PHY_PLL_CTL_1_FREQ_DOUBLER_ENABLE BIT(13) ++#define VC4_HDMI_TX_PHY_PLL_CTL_1_POST_RST_SEL_SHIFT 11 ++#define VC4_HDMI_TX_PHY_PLL_CTL_1_POST_RST_SEL_MASK VC4_MASK(12, 11) ++ ++#define VC4_HDMI_TX_PHY_CLK_DIV_VCO_SHIFT 8 ++#define VC4_HDMI_TX_PHY_CLK_DIV_VCO_MASK VC4_MASK(15, 8) ++ ++#define VC4_HDMI_TX_PHY_PLL_CFG_PDIV_SHIFT 0 ++#define VC4_HDMI_TX_PHY_PLL_CFG_PDIV_MASK VC4_MASK(3, 0) ++ ++#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TXCK_OUT_SEL_MASK VC4_MASK(13, 12) ++#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TXCK_OUT_SEL_SHIFT 12 ++#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX2_OUT_SEL_MASK VC4_MASK(9, 8) ++#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX2_OUT_SEL_SHIFT 8 ++#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX1_OUT_SEL_MASK VC4_MASK(5, 4) ++#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX1_OUT_SEL_SHIFT 4 ++#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX0_OUT_SEL_MASK VC4_MASK(1, 0) ++#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX0_OUT_SEL_SHIFT 0 ++ ++#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1_MIN_LIMIT_MASK VC4_MASK(27, 0) ++#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1_MIN_LIMIT_SHIFT 0 ++ ++#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2_MAX_LIMIT_MASK VC4_MASK(27, 0) ++#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2_MAX_LIMIT_SHIFT 0 ++ ++#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4_STABLE_THRESHOLD_MASK VC4_MASK(31, 16) ++#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4_STABLE_THRESHOLD_SHIFT 16 ++#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4_HOLD_THRESHOLD_MASK VC4_MASK(15, 0) ++#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4_HOLD_THRESHOLD_SHIFT 0 ++ ++#define VC4_HDMI_RM_CONTROL_EN_FREEZE_COUNTERS BIT(19) ++#define VC4_HDMI_RM_CONTROL_EN_LOAD_INTEGRATOR BIT(17) ++#define VC4_HDMI_RM_CONTROL_FREE_RUN BIT(4) ++ ++#define VC4_HDMI_RM_OFFSET_ONLY BIT(31) ++#define VC4_HDMI_RM_OFFSET_OFFSET_SHIFT 0 ++#define VC4_HDMI_RM_OFFSET_OFFSET_MASK VC4_MASK(30, 0) ++ ++#define VC4_HDMI_RM_FORMAT_SHIFT_SHIFT 24 ++#define VC4_HDMI_RM_FORMAT_SHIFT_MASK VC4_MASK(25, 24) ++ ++#define OSCILLATOR_FREQUENCY 54000000 ++ + void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, struct drm_display_mode *mode) + { + /* PHY should be in reset, like +@@ -38,3 +155,355 @@ void vc4_hdmi_phy_rng_disable(struct vc4 + HDMI_READ(HDMI_TX_PHY_CTL_0) | + VC4_HDMI_TX_PHY_RNG_PWRDN); + } ++ ++static unsigned long long ++phy_get_vco_freq(unsigned long long clock, u8 *vco_sel, u8 *vco_div) ++{ ++ unsigned long long vco_freq = clock; ++ unsigned int _vco_div = 0; ++ unsigned int _vco_sel = 0; ++ ++ while (vco_freq < 3000000000ULL) { ++ _vco_div++; ++ vco_freq = clock * _vco_div * 10; ++ } ++ ++ if (vco_freq > 4500000000ULL) ++ _vco_sel = 1; ++ ++ *vco_sel = _vco_sel; ++ *vco_div = _vco_div; ++ ++ return vco_freq; ++} ++ ++static u8 phy_get_cp_current(unsigned long vco_freq) ++{ ++ if (vco_freq < 3700000000ULL) ++ return 0x1c; ++ ++ return 0xc8; ++} ++ ++static u32 phy_get_rm_offset(unsigned long long vco_freq) ++{ ++ unsigned long long fref = OSCILLATOR_FREQUENCY; ++ uint64_t offset = 0; ++ ++ /* RM offset is stored as 9.22 format */ ++ offset = vco_freq * 2; ++ do_div(offset, fref); ++ offset = offset << 22; ++ offset >>= 2; ++ ++ return offset; ++} ++ ++static u8 phy_get_vco_gain(unsigned long long vco_freq) ++{ ++ if (vco_freq < 3350000000ULL) ++ return 0xf; ++ ++ if (vco_freq < 3700000000ULL) ++ return 0xc; ++ ++ if (vco_freq < 4050000000ULL) ++ return 0x6; ++ ++ if (vco_freq < 4800000000ULL) ++ return 0x5; ++ ++ if (vco_freq < 5200000000ULL) ++ return 0x7; ++ ++ return 0x2; ++} ++ ++struct phy_lane_settings { ++ struct { ++ u8 preemphasis; ++ u8 main_driver; ++ } amplitude; ++ ++ u8 res_sel_data; ++ u8 term_res_sel_data; ++}; ++ ++struct phy_settings { ++ unsigned long long min_rate; ++ unsigned long long max_rate; ++ struct phy_lane_settings channel[3]; ++ struct phy_lane_settings clock; ++}; ++ ++static const struct phy_settings vc5_hdmi_phy_settings[] = ++{ ++ { ++ 0, 50000000, ++ { ++ {{0x0, 0x0A}, 0x12, 0x0}, ++ {{0x0, 0x0A}, 0x12, 0x0}, ++ {{0x0, 0x0A}, 0x12, 0x0} ++ }, ++ {{0x0, 0x0A}, 0x18, 0x0}, ++ }, ++ { ++ 50000001, 75000000, ++ { ++ {{0x0, 0x09}, 0x12, 0x0}, ++ {{0x0, 0x09}, 0x12, 0x0}, ++ {{0x0, 0x09}, 0x12, 0x0} ++ }, ++ {{0x0, 0x0C}, 0x18, 0x3}, ++ }, ++ { ++ 75000001, 165000000, ++ { ++ {{0x0, 0x09}, 0x12, 0x0}, ++ {{0x0, 0x09}, 0x12, 0x0}, ++ {{0x0, 0x09}, 0x12, 0x0} ++ }, ++ {{0x0, 0x0C}, 0x18, 0x3}, ++ }, ++ { ++ 165000001, 250000000, ++ { ++ {{0x0, 0x0F}, 0x12, 0x1}, ++ {{0x0, 0x0F}, 0x12, 0x1}, ++ {{0x0, 0x0F}, 0x12, 0x1} ++ }, ++ {{0x0, 0x0C}, 0x18, 0x3}, ++ }, ++ { ++ 250000001, 340000000, ++ { ++ {{0x2, 0x0D}, 0x12, 0x1}, ++ {{0x2, 0x0D}, 0x12, 0x1}, ++ {{0x2, 0x0D}, 0x12, 0x1} ++ }, ++ {{0x0, 0x0C}, 0x18, 0xF}, ++ }, ++ { ++ 340000001, 450000000, ++ { ++ {{0x0, 0x1B}, 0x12, 0xF}, ++ {{0x0, 0x1B}, 0x12, 0xF}, ++ {{0x0, 0x1B}, 0x12, 0xF} ++ }, ++ {{0x0, 0x0A}, 0x12, 0xF}, ++ }, ++ { ++ 450000001, 600000000, ++ { ++ {{0x0, 0x1C}, 0x12, 0xF}, ++ {{0x0, 0x1C}, 0x12, 0xF}, ++ {{0x0, 0x1C}, 0x12, 0xF} ++ }, ++ {{0x0, 0x0B}, 0x13, 0xF}, ++ }, ++}; ++ ++static const struct phy_settings *phy_get_settings(unsigned long long tmds_rate) ++{ ++ unsigned int count = ARRAY_SIZE(vc5_hdmi_phy_settings); ++ unsigned int i; ++ ++ for (i = 0; i < count; i++) { ++ const struct phy_settings *s = &vc5_hdmi_phy_settings[i]; ++ ++ if (tmds_rate >= s->min_rate && tmds_rate <= s->max_rate) ++ return s; ++ } ++ ++ /* ++ * If the pixel clock exceeds our max setting, try the max ++ * setting anyway. ++ */ ++ return &vc5_hdmi_phy_settings[count - 1]; ++} ++ ++static const struct phy_lane_settings * ++phy_get_channel_settings(enum vc4_hdmi_phy_channel chan, ++ unsigned long long tmds_rate) ++{ ++ const struct phy_settings *settings = phy_get_settings(tmds_rate); ++ ++ if (chan == PHY_LANE_CK) ++ return &settings->clock; ++ ++ return &settings->channel[chan]; ++} ++ ++void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, struct drm_display_mode *mode) ++{ ++ const struct phy_lane_settings *chan0_settings, *chan1_settings, *chan2_settings, *clock_settings; ++ const struct vc4_hdmi_variant *variant = vc4_hdmi->variant; ++ unsigned long long pixel_freq = mode->clock * 1000; ++ unsigned long long vco_freq; ++ unsigned char word_sel; ++ u8 vco_sel, vco_div; ++ ++ vco_freq = phy_get_vco_freq(pixel_freq, &vco_sel, &vco_div); ++ ++ HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL, ++ VC4_HDMI_TX_PHY_POWERDOWN_CTL_RNDGEN_PWRDN); ++ ++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, ++ HDMI_READ(HDMI_TX_PHY_RESET_CTL) & ++ ~VC4_HDMI_TX_PHY_RESET_CTL_TX_0_RESET & ++ ~VC4_HDMI_TX_PHY_RESET_CTL_TX_1_RESET & ++ ~VC4_HDMI_TX_PHY_RESET_CTL_TX_2_RESET & ++ ~VC4_HDMI_TX_PHY_RESET_CTL_TX_CK_RESET); ++ ++ HDMI_WRITE(HDMI_RM_CONTROL, ++ HDMI_READ(HDMI_RM_CONTROL) | ++ VC4_HDMI_RM_CONTROL_EN_FREEZE_COUNTERS | ++ VC4_HDMI_RM_CONTROL_EN_LOAD_INTEGRATOR | ++ VC4_HDMI_RM_CONTROL_FREE_RUN); ++ ++ HDMI_WRITE(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1, ++ (HDMI_READ(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1) & ++ ~VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1_MIN_LIMIT_MASK) | ++ VC4_SET_FIELD(0, VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1_MIN_LIMIT)); ++ ++ HDMI_WRITE(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2, ++ (HDMI_READ(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2) & ++ ~VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2_MAX_LIMIT_MASK) | ++ VC4_SET_FIELD(0, VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2_MAX_LIMIT)); ++ ++ HDMI_WRITE(HDMI_RM_OFFSET, ++ VC4_SET_FIELD(phy_get_rm_offset(vco_freq), ++ VC4_HDMI_RM_OFFSET_OFFSET) | ++ VC4_HDMI_RM_OFFSET_ONLY); ++ ++ HDMI_WRITE(HDMI_TX_PHY_CLK_DIV, ++ VC4_SET_FIELD(vco_div, VC4_HDMI_TX_PHY_CLK_DIV_VCO)); ++ ++ HDMI_WRITE(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4, ++ VC4_SET_FIELD(0xe147, VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4_HOLD_THRESHOLD) | ++ VC4_SET_FIELD(0xe14, VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4_STABLE_THRESHOLD)); ++ ++ HDMI_WRITE(HDMI_TX_PHY_PLL_CTL_0, ++ VC4_HDMI_TX_PHY_PLL_CTL_0_ENA_VCO_CLK | ++ VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_CONT_EN | ++ VC4_HDMI_TX_PHY_PLL_CTL_0_MASH11_MODE | ++ VC4_SET_FIELD(vco_sel, VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_SEL)); ++ ++ HDMI_WRITE(HDMI_TX_PHY_PLL_CTL_1, ++ HDMI_READ(HDMI_TX_PHY_PLL_CTL_1) | ++ VC4_HDMI_TX_PHY_PLL_CTL_1_FREQ_DOUBLER_ENABLE | ++ VC4_SET_FIELD(3, VC4_HDMI_TX_PHY_PLL_CTL_1_POST_RST_SEL) | ++ VC4_SET_FIELD(1, VC4_HDMI_TX_PHY_PLL_CTL_1_FREQ_DOUBLER_DELAY) | ++ VC4_SET_FIELD(0x8a, VC4_HDMI_TX_PHY_PLL_CTL_1_CPP)); ++ ++ HDMI_WRITE(HDMI_RM_FORMAT, ++ HDMI_READ(HDMI_RM_FORMAT) | ++ VC4_SET_FIELD(2, VC4_HDMI_RM_FORMAT_SHIFT)); ++ ++ HDMI_WRITE(HDMI_TX_PHY_PLL_CFG, ++ HDMI_READ(HDMI_TX_PHY_PLL_CFG) | ++ VC4_SET_FIELD(1, VC4_HDMI_TX_PHY_PLL_CFG_PDIV)); ++ ++ if (pixel_freq >= 340000000) ++ word_sel = 3; ++ else ++ word_sel = 0; ++ HDMI_WRITE(HDMI_TX_PHY_TMDS_CLK_WORD_SEL, word_sel); ++ ++ HDMI_WRITE(HDMI_TX_PHY_CTL_3, ++ VC4_SET_FIELD(phy_get_cp_current(vco_freq), ++ VC4_HDMI_TX_PHY_CTL_3_ICP) | ++ VC4_SET_FIELD(1, VC4_HDMI_TX_PHY_CTL_3_CP) | ++ VC4_SET_FIELD(1, VC4_HDMI_TX_PHY_CTL_3_CP1) | ++ VC4_SET_FIELD(3, VC4_HDMI_TX_PHY_CTL_3_CZ) | ++ VC4_SET_FIELD(4, VC4_HDMI_TX_PHY_CTL_3_RP) | ++ VC4_SET_FIELD(6, VC4_HDMI_TX_PHY_CTL_3_RZ)); ++ ++ chan0_settings = ++ phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_0], ++ pixel_freq); ++ chan1_settings = ++ phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_1], ++ pixel_freq); ++ chan2_settings = ++ phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_2], ++ pixel_freq); ++ clock_settings = ++ phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_CK], ++ pixel_freq); ++ ++ HDMI_WRITE(HDMI_TX_PHY_CTL_0, ++ VC4_SET_FIELD(chan0_settings->amplitude.preemphasis, ++ VC4_HDMI_TX_PHY_CTL_0_PREEMP_0_PREEMP) | ++ VC4_SET_FIELD(chan0_settings->amplitude.main_driver, ++ VC4_HDMI_TX_PHY_CTL_0_PREEMP_0_MAINDRV) | ++ VC4_SET_FIELD(chan1_settings->amplitude.preemphasis, ++ VC4_HDMI_TX_PHY_CTL_0_PREEMP_1_PREEMP) | ++ VC4_SET_FIELD(chan1_settings->amplitude.main_driver, ++ VC4_HDMI_TX_PHY_CTL_0_PREEMP_1_MAINDRV) | ++ VC4_SET_FIELD(chan2_settings->amplitude.preemphasis, ++ VC4_HDMI_TX_PHY_CTL_0_PREEMP_2_PREEMP) | ++ VC4_SET_FIELD(chan2_settings->amplitude.main_driver, ++ VC4_HDMI_TX_PHY_CTL_0_PREEMP_2_MAINDRV) | ++ VC4_SET_FIELD(clock_settings->amplitude.preemphasis, ++ VC4_HDMI_TX_PHY_CTL_0_PREEMP_CK_PREEMP) | ++ VC4_SET_FIELD(clock_settings->amplitude.main_driver, ++ VC4_HDMI_TX_PHY_CTL_0_PREEMP_CK_MAINDRV)); ++ ++ HDMI_WRITE(HDMI_TX_PHY_CTL_1, ++ HDMI_READ(HDMI_TX_PHY_CTL_1) | ++ VC4_SET_FIELD(chan0_settings->res_sel_data, ++ VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA0) | ++ VC4_SET_FIELD(chan1_settings->res_sel_data, ++ VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA1) | ++ VC4_SET_FIELD(chan2_settings->res_sel_data, ++ VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA2) | ++ VC4_SET_FIELD(clock_settings->res_sel_data, ++ VC4_HDMI_TX_PHY_CTL_1_RES_SEL_CK)); ++ ++ HDMI_WRITE(HDMI_TX_PHY_CTL_2, ++ VC4_SET_FIELD(chan0_settings->term_res_sel_data, ++ VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA0) | ++ VC4_SET_FIELD(chan1_settings->term_res_sel_data, ++ VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA1) | ++ VC4_SET_FIELD(chan2_settings->term_res_sel_data, ++ VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA2) | ++ VC4_SET_FIELD(clock_settings->term_res_sel_data, ++ VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELCK) | ++ VC4_SET_FIELD(phy_get_vco_gain(vco_freq), ++ VC4_HDMI_TX_PHY_CTL_2_VCO_GAIN)); ++ ++ HDMI_WRITE(HDMI_TX_PHY_CHANNEL_SWAP, ++ VC4_SET_FIELD(variant->phy_lane_mapping[PHY_LANE_0], ++ VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX0_OUT_SEL) | ++ VC4_SET_FIELD(variant->phy_lane_mapping[PHY_LANE_1], ++ VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX1_OUT_SEL) | ++ VC4_SET_FIELD(variant->phy_lane_mapping[PHY_LANE_2], ++ VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX2_OUT_SEL) | ++ VC4_SET_FIELD(variant->phy_lane_mapping[PHY_LANE_CK], ++ VC4_HDMI_TX_PHY_CHANNEL_SWAP_TXCK_OUT_SEL)); ++ ++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, ++ HDMI_READ(HDMI_TX_PHY_RESET_CTL) & ++ ~(VC4_HDMI_TX_PHY_RESET_CTL_PLL_RESETB | ++ VC4_HDMI_TX_PHY_RESET_CTL_PLLDIV_RESETB)); ++ ++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, ++ HDMI_READ(HDMI_TX_PHY_RESET_CTL) | ++ VC4_HDMI_TX_PHY_RESET_CTL_PLL_RESETB | ++ VC4_HDMI_TX_PHY_RESET_CTL_PLLDIV_RESETB); ++} ++ ++void vc5_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi) ++{ ++ HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL, ++ HDMI_READ(HDMI_TX_PHY_POWERDOWN_CTL) & ++ ~VC4_HDMI_TX_PHY_POWERDOWN_CTL_RNDGEN_PWRDN); ++} ++ ++void vc5_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi) ++{ ++ HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL, ++ HDMI_READ(HDMI_TX_PHY_POWERDOWN_CTL) | ++ VC4_HDMI_TX_PHY_POWERDOWN_CTL_RNDGEN_PWRDN); ++} +--- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h +@@ -18,6 +18,12 @@ enum vc4_hdmi_regs { + VC4_INVALID = 0, + VC4_HDMI, + VC4_HD, ++ VC5_CEC, ++ VC5_CSC, ++ VC5_DVP, ++ VC5_PHY, ++ VC5_RAM, ++ VC5_RM, + }; + + enum vc4_hdmi_field { +@@ -45,6 +51,7 @@ enum vc4_hdmi_field { + HDMI_CEC_TX_DATA_2, + HDMI_CEC_TX_DATA_3, + HDMI_CEC_TX_DATA_4, ++ HDMI_CLOCK_STOP, + HDMI_CORE_REV, + HDMI_CRP_CFG, + HDMI_CSC_12_11, +@@ -61,6 +68,7 @@ enum vc4_hdmi_field { + */ + HDMI_CTS_0, + HDMI_CTS_1, ++ HDMI_DVP_CTL, + HDMI_FIFO_CTL, + HDMI_FRAME_COUNT, + HDMI_HORZA, +@@ -93,10 +101,27 @@ enum vc4_hdmi_field { + HDMI_RAM_PACKET_CONFIG, + HDMI_RAM_PACKET_START, + HDMI_RAM_PACKET_STATUS, ++ HDMI_RM_CONTROL, ++ HDMI_RM_FORMAT, ++ HDMI_RM_OFFSET, + HDMI_SCHEDULER_CONTROL, + HDMI_SW_RESET_CONTROL, ++ HDMI_TX_PHY_CHANNEL_SWAP, ++ HDMI_TX_PHY_CLK_DIV, + HDMI_TX_PHY_CTL_0, ++ HDMI_TX_PHY_CTL_1, ++ HDMI_TX_PHY_CTL_2, ++ HDMI_TX_PHY_CTL_3, ++ HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1, ++ HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2, ++ HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4, ++ HDMI_TX_PHY_PLL_CFG, ++ HDMI_TX_PHY_PLL_CTL_0, ++ HDMI_TX_PHY_PLL_CTL_1, ++ HDMI_TX_PHY_POWERDOWN_CTL, + HDMI_TX_PHY_RESET_CTL, ++ HDMI_TX_PHY_TMDS_CLK_WORD_SEL, ++ HDMI_VEC_INTERFACE_XBAR, + HDMI_VERTA0, + HDMI_VERTA1, + HDMI_VERTB0, +@@ -119,6 +144,12 @@ struct vc4_hdmi_register { + + #define VC4_HD_REG(reg, offset) _VC4_REG(VC4_HD, reg, offset) + #define VC4_HDMI_REG(reg, offset) _VC4_REG(VC4_HDMI, reg, offset) ++#define VC5_CEC_REG(reg, offset) _VC4_REG(VC5_CEC, reg, offset) ++#define VC5_CSC_REG(reg, offset) _VC4_REG(VC5_CSC, reg, offset) ++#define VC5_DVP_REG(reg, offset) _VC4_REG(VC5_DVP, reg, offset) ++#define VC5_PHY_REG(reg, offset) _VC4_REG(VC5_PHY, reg, offset) ++#define VC5_RAM_REG(reg, offset) _VC4_REG(VC5_RAM, reg, offset) ++#define VC5_RM_REG(reg, offset) _VC4_REG(VC5_RM, reg, offset) + + static const struct vc4_hdmi_register vc4_hdmi_fields[] = { + VC4_HD_REG(HDMI_M_CTL, 0x000c), +@@ -181,6 +212,158 @@ static const struct vc4_hdmi_register vc + VC4_HDMI_REG(HDMI_RAM_PACKET_START, 0x0400), + }; + ++static const struct vc4_hdmi_register vc5_hdmi_hdmi0_fields[] = { ++ VC4_HD_REG(HDMI_DVP_CTL, 0x0000), ++ VC4_HD_REG(HDMI_MAI_CTL, 0x0010), ++ VC4_HD_REG(HDMI_MAI_THR, 0x0014), ++ VC4_HD_REG(HDMI_MAI_FMT, 0x0018), ++ VC4_HD_REG(HDMI_MAI_DATA, 0x001c), ++ VC4_HD_REG(HDMI_MAI_SMP, 0x0020), ++ VC4_HD_REG(HDMI_VID_CTL, 0x0044), ++ VC4_HD_REG(HDMI_FRAME_COUNT, 0x0060), ++ ++ VC4_HDMI_REG(HDMI_FIFO_CTL, 0x074), ++ VC4_HDMI_REG(HDMI_AUDIO_PACKET_CONFIG, 0x0b8), ++ VC4_HDMI_REG(HDMI_RAM_PACKET_CONFIG, 0x0bc), ++ VC4_HDMI_REG(HDMI_RAM_PACKET_STATUS, 0x0c4), ++ VC4_HDMI_REG(HDMI_CRP_CFG, 0x0c8), ++ VC4_HDMI_REG(HDMI_CTS_0, 0x0cc), ++ VC4_HDMI_REG(HDMI_CTS_1, 0x0d0), ++ VC4_HDMI_REG(HDMI_SCHEDULER_CONTROL, 0x0e0), ++ VC4_HDMI_REG(HDMI_HORZA, 0x0e4), ++ VC4_HDMI_REG(HDMI_HORZB, 0x0e8), ++ VC4_HDMI_REG(HDMI_VERTA0, 0x0ec), ++ VC4_HDMI_REG(HDMI_VERTB0, 0x0f0), ++ VC4_HDMI_REG(HDMI_VERTA1, 0x0f4), ++ VC4_HDMI_REG(HDMI_VERTB1, 0x0f8), ++ VC4_HDMI_REG(HDMI_MAI_CHANNEL_MAP, 0x09c), ++ VC4_HDMI_REG(HDMI_MAI_CONFIG, 0x0a0), ++ VC4_HDMI_REG(HDMI_HOTPLUG, 0x1a8), ++ ++ VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc), ++ VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f0), ++ ++ VC5_PHY_REG(HDMI_TX_PHY_RESET_CTL, 0x000), ++ VC5_PHY_REG(HDMI_TX_PHY_POWERDOWN_CTL, 0x004), ++ VC5_PHY_REG(HDMI_TX_PHY_CTL_0, 0x008), ++ VC5_PHY_REG(HDMI_TX_PHY_CTL_1, 0x00c), ++ VC5_PHY_REG(HDMI_TX_PHY_CTL_2, 0x010), ++ VC5_PHY_REG(HDMI_TX_PHY_CTL_3, 0x014), ++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CTL_0, 0x01c), ++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CTL_1, 0x020), ++ VC5_PHY_REG(HDMI_TX_PHY_CLK_DIV, 0x028), ++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CFG, 0x034), ++ VC5_PHY_REG(HDMI_TX_PHY_TMDS_CLK_WORD_SEL, 0x044), ++ VC5_PHY_REG(HDMI_TX_PHY_CHANNEL_SWAP, 0x04c), ++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1, 0x050), ++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2, 0x054), ++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4, 0x05c), ++ ++ VC5_RM_REG(HDMI_RM_CONTROL, 0x000), ++ VC5_RM_REG(HDMI_RM_OFFSET, 0x018), ++ VC5_RM_REG(HDMI_RM_FORMAT, 0x01c), ++ ++ VC5_RAM_REG(HDMI_RAM_PACKET_START, 0x000), ++ ++ VC5_CEC_REG(HDMI_CEC_CNTRL_1, 0x010), ++ VC5_CEC_REG(HDMI_CEC_CNTRL_2, 0x014), ++ VC5_CEC_REG(HDMI_CEC_CNTRL_3, 0x018), ++ VC5_CEC_REG(HDMI_CEC_CNTRL_4, 0x01c), ++ VC5_CEC_REG(HDMI_CEC_CNTRL_5, 0x020), ++ VC5_CEC_REG(HDMI_CEC_TX_DATA_1, 0x028), ++ VC5_CEC_REG(HDMI_CEC_TX_DATA_2, 0x02c), ++ VC5_CEC_REG(HDMI_CEC_TX_DATA_3, 0x030), ++ VC5_CEC_REG(HDMI_CEC_TX_DATA_4, 0x034), ++ VC5_CEC_REG(HDMI_CEC_RX_DATA_1, 0x038), ++ VC5_CEC_REG(HDMI_CEC_RX_DATA_2, 0x03c), ++ VC5_CEC_REG(HDMI_CEC_RX_DATA_3, 0x040), ++ VC5_CEC_REG(HDMI_CEC_RX_DATA_4, 0x044), ++ ++ VC5_CSC_REG(HDMI_CSC_CTL, 0x000), ++ VC5_CSC_REG(HDMI_CSC_12_11, 0x004), ++ VC5_CSC_REG(HDMI_CSC_14_13, 0x008), ++ VC5_CSC_REG(HDMI_CSC_22_21, 0x00c), ++ VC5_CSC_REG(HDMI_CSC_24_23, 0x010), ++ VC5_CSC_REG(HDMI_CSC_32_31, 0x014), ++ VC5_CSC_REG(HDMI_CSC_34_33, 0x018), ++}; ++ ++static const struct vc4_hdmi_register vc5_hdmi_hdmi1_fields[] = { ++ VC4_HD_REG(HDMI_DVP_CTL, 0x0000), ++ VC4_HD_REG(HDMI_MAI_CTL, 0x0030), ++ VC4_HD_REG(HDMI_MAI_THR, 0x0034), ++ VC4_HD_REG(HDMI_MAI_FMT, 0x0038), ++ VC4_HD_REG(HDMI_MAI_DATA, 0x003c), ++ VC4_HD_REG(HDMI_MAI_SMP, 0x0040), ++ VC4_HD_REG(HDMI_VID_CTL, 0x0048), ++ VC4_HD_REG(HDMI_FRAME_COUNT, 0x0064), ++ ++ VC4_HDMI_REG(HDMI_FIFO_CTL, 0x074), ++ VC4_HDMI_REG(HDMI_AUDIO_PACKET_CONFIG, 0x0b8), ++ VC4_HDMI_REG(HDMI_RAM_PACKET_CONFIG, 0x0bc), ++ VC4_HDMI_REG(HDMI_RAM_PACKET_STATUS, 0x0c4), ++ VC4_HDMI_REG(HDMI_CRP_CFG, 0x0c8), ++ VC4_HDMI_REG(HDMI_CTS_0, 0x0cc), ++ VC4_HDMI_REG(HDMI_CTS_1, 0x0d0), ++ VC4_HDMI_REG(HDMI_SCHEDULER_CONTROL, 0x0e0), ++ VC4_HDMI_REG(HDMI_HORZA, 0x0e4), ++ VC4_HDMI_REG(HDMI_HORZB, 0x0e8), ++ VC4_HDMI_REG(HDMI_VERTA0, 0x0ec), ++ VC4_HDMI_REG(HDMI_VERTB0, 0x0f0), ++ VC4_HDMI_REG(HDMI_VERTA1, 0x0f4), ++ VC4_HDMI_REG(HDMI_VERTB1, 0x0f8), ++ VC4_HDMI_REG(HDMI_MAI_CHANNEL_MAP, 0x09c), ++ VC4_HDMI_REG(HDMI_MAI_CONFIG, 0x0a0), ++ VC4_HDMI_REG(HDMI_HOTPLUG, 0x1a8), ++ ++ VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc), ++ VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f0), ++ ++ VC5_PHY_REG(HDMI_TX_PHY_RESET_CTL, 0x000), ++ VC5_PHY_REG(HDMI_TX_PHY_POWERDOWN_CTL, 0x004), ++ VC5_PHY_REG(HDMI_TX_PHY_CTL_0, 0x008), ++ VC5_PHY_REG(HDMI_TX_PHY_CTL_1, 0x00c), ++ VC5_PHY_REG(HDMI_TX_PHY_CTL_2, 0x010), ++ VC5_PHY_REG(HDMI_TX_PHY_CTL_3, 0x014), ++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CTL_0, 0x01c), ++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CTL_1, 0x020), ++ VC5_PHY_REG(HDMI_TX_PHY_CLK_DIV, 0x028), ++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CFG, 0x034), ++ VC5_PHY_REG(HDMI_TX_PHY_CHANNEL_SWAP, 0x04c), ++ VC5_PHY_REG(HDMI_TX_PHY_TMDS_CLK_WORD_SEL, 0x044), ++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1, 0x050), ++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2, 0x054), ++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4, 0x05c), ++ ++ VC5_RM_REG(HDMI_RM_CONTROL, 0x000), ++ VC5_RM_REG(HDMI_RM_OFFSET, 0x018), ++ VC5_RM_REG(HDMI_RM_FORMAT, 0x01c), ++ ++ VC5_RAM_REG(HDMI_RAM_PACKET_START, 0x000), ++ ++ VC5_CEC_REG(HDMI_CEC_CNTRL_1, 0x010), ++ VC5_CEC_REG(HDMI_CEC_CNTRL_2, 0x014), ++ VC5_CEC_REG(HDMI_CEC_CNTRL_3, 0x018), ++ VC5_CEC_REG(HDMI_CEC_CNTRL_4, 0x01c), ++ VC5_CEC_REG(HDMI_CEC_CNTRL_5, 0x020), ++ VC5_CEC_REG(HDMI_CEC_TX_DATA_1, 0x028), ++ VC5_CEC_REG(HDMI_CEC_TX_DATA_2, 0x02c), ++ VC5_CEC_REG(HDMI_CEC_TX_DATA_3, 0x030), ++ VC5_CEC_REG(HDMI_CEC_TX_DATA_4, 0x034), ++ VC5_CEC_REG(HDMI_CEC_RX_DATA_1, 0x038), ++ VC5_CEC_REG(HDMI_CEC_RX_DATA_2, 0x03c), ++ VC5_CEC_REG(HDMI_CEC_RX_DATA_3, 0x040), ++ VC5_CEC_REG(HDMI_CEC_RX_DATA_4, 0x044), ++ ++ VC5_CSC_REG(HDMI_CSC_CTL, 0x000), ++ VC5_CSC_REG(HDMI_CSC_12_11, 0x004), ++ VC5_CSC_REG(HDMI_CSC_14_13, 0x008), ++ VC5_CSC_REG(HDMI_CSC_22_21, 0x00c), ++ VC5_CSC_REG(HDMI_CSC_24_23, 0x010), ++ VC5_CSC_REG(HDMI_CSC_32_31, 0x014), ++ VC5_CSC_REG(HDMI_CSC_34_33, 0x018), ++}; ++ + static inline + void __iomem *__vc4_hdmi_get_field_base(struct vc4_hdmi *hdmi, + enum vc4_hdmi_regs reg) +@@ -192,6 +375,24 @@ void __iomem *__vc4_hdmi_get_field_base( + case VC4_HDMI: + return hdmi->hdmicore_regs; + ++ case VC5_CSC: ++ return hdmi->csc_regs; ++ ++ case VC5_CEC: ++ return hdmi->cec_regs; ++ ++ case VC5_DVP: ++ return hdmi->dvp_regs; ++ ++ case VC5_PHY: ++ return hdmi->phy_regs; ++ ++ case VC5_RAM: ++ return hdmi->ram_regs; ++ ++ case VC5_RM: ++ return hdmi->rm_regs; ++ + default: + return NULL; + } diff --git a/target/linux/bcm27xx/patches-5.4/950-0598-dt-bindings-display-vc4-hdmi-Add-BCM2711-HDMI-contro.patch b/target/linux/bcm27xx/patches-5.4/950-0598-dt-bindings-display-vc4-hdmi-Add-BCM2711-HDMI-contro.patch new file mode 100644 index 0000000000..c41e063fde --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0598-dt-bindings-display-vc4-hdmi-Add-BCM2711-HDMI-contro.patch @@ -0,0 +1,174 @@ +From 965351ba5a271c0a4a7776193b7af78871370f7a Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Thu, 13 Feb 2020 16:45:24 +0100 +Subject: [PATCH] dt-bindings: display: vc4: hdmi: Add BCM2711 HDMI + controllers bindings + +The HDMI controllers found in the BCM2711 SoC need some adjustments to the +bindings, especially since the registers have been shuffled around in more +register ranges. + +Cc: Rob Herring <robh+dt@kernel.org> +Cc: devicetree@vger.kernel.org +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + .../bindings/display/brcm,bcm2835-hdmi.yaml | 118 ++++++++++++++++-- + 1 file changed, 109 insertions(+), 9 deletions(-) + +--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-hdmi.yaml ++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-hdmi.yaml +@@ -11,24 +11,58 @@ maintainers: + + properties: + compatible: +- const: brcm,bcm2835-hdmi ++ enum: ++ - brcm,bcm2835-hdmi ++ - brcm,bcm2711-hdmi0 ++ - brcm,bcm2711-hdmi1 + + reg: ++ oneOf: ++ - items: ++ - description: HDMI register range ++ - description: HD register range ++ ++ - items: ++ - description: HDMI controller register range ++ - description: DVP register range ++ - description: HDMI PHY register range ++ - description: Rate Manager register range ++ - description: Packet RAM register range ++ - description: Metadata RAM register range ++ - description: CSC register range ++ - description: CEC register range ++ - description: HD register range ++ ++ reg-names: + items: +- - description: HDMI register range +- - description: HD register range ++ - const: hdmi ++ - const: dvp ++ - const: phy ++ - const: rm ++ - const: packet ++ - const: metadata ++ - const: csc ++ - const: cec ++ - const: hd + + interrupts: + minItems: 2 + + clocks: +- items: +- - description: The pixel clock +- - description: The HDMI state machine clock ++ oneOf: ++ - items: ++ - description: The pixel clock ++ - description: The HDMI state machine clock ++ ++ - items: ++ - description: The HDMI state machine clock + + clock-names: +- items: +- - const: pixel ++ oneOf: ++ - items: ++ - const: pixel ++ - const: hdmi ++ + - const: hdmi + + ddc: +@@ -51,15 +85,54 @@ properties: + dma-names: + const: audio-rx + ++ resets: ++ maxItems: 1 ++ + required: + - compatible + - reg +- - interrupts + - clocks + - ddc + + additionalProperties: false + ++if: ++ properties: ++ compatible: ++ contains: ++ enum: ++ - brcm,bcm2711-hdmi0 ++ - brcm,bcm2711-hdmi1 ++ ++then: ++ properties: ++ reg: ++ minItems: 9 ++ ++ clocks: ++ maxItems: 1 ++ ++ clock-names: ++ maxItems: 1 ++ ++ required: ++ - reg-names ++ - resets ++ ++else: ++ properties: ++ reg: ++ maxItems: 2 ++ ++ clocks: ++ minItems: 2 ++ ++ clock-names: ++ minItems: 2 ++ ++ required: ++ - interrupts ++ + examples: + - | + #include <dt-bindings/clock/bcm2835.h> +@@ -77,4 +150,31 @@ examples: + clock-names = "pixel", "hdmi"; + }; + ++ - | ++ hdmi0: hdmi@7ef00700 { ++ compatible = "brcm,bcm2711-hdmi0"; ++ reg = <0x7ef00700 0x300>, ++ <0x7ef00300 0x200>, ++ <0x7ef00f00 0x80>, ++ <0x7ef00f80 0x80>, ++ <0x7ef01b00 0x200>, ++ <0x7ef01f00 0x400>, ++ <0x7ef00200 0x80>, ++ <0x7ef04300 0x100>, ++ <0x7ef20000 0x100>; ++ reg-names = "hdmi", ++ "dvp", ++ "phy", ++ "rm", ++ "packet", ++ "metadata", ++ "csc", ++ "cec", ++ "hd"; ++ clocks = <&firmware_clocks 13>; ++ clock-names = "hdmi"; ++ resets = <&dvp 0>; ++ ddc = <&ddc0>; ++ }; ++ + ... diff --git a/target/linux/bcm27xx/patches-5.4/950-0599-ARM-dts-bcm2711-Enable-the-display-pipeline.patch b/target/linux/bcm27xx/patches-5.4/950-0599-ARM-dts-bcm2711-Enable-the-display-pipeline.patch new file mode 100644 index 0000000000..aae9b9e00d --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0599-ARM-dts-bcm2711-Enable-the-display-pipeline.patch @@ -0,0 +1,210 @@ +From 661edd663841d94bded4e95acfd0a4947cb079b5 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Wed, 12 Feb 2020 12:26:40 +0100 +Subject: [PATCH] ARM: dts: bcm2711: Enable the display pipeline + +Now that all the drivers have been adjusted for it, let's bring in the +necessary device tree changes. + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 40 ++++++++++ + arch/arm/boot/dts/bcm2711.dtsi | 110 ++++++++++++++++++++++++++ + 2 files changed, 150 insertions(+) + +--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts ++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts +@@ -138,6 +138,46 @@ + interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>; + }; + ++&vc4 { ++ status = "okay"; ++}; ++ ++&pixelvalve0 { ++ status = "okay"; ++}; ++ ++&pixelvalve1 { ++ status = "okay"; ++}; ++ ++&pixelvalve2 { ++ status = "okay"; ++}; ++ ++&pixelvalve3 { ++ status = "okay"; ++}; ++ ++&pixelvalve4 { ++ status = "okay"; ++}; ++ ++&hdmi0 { ++ status = "okay"; ++}; ++ ++&ddc0 { ++ status = "okay"; ++}; ++ ++&hdmi1 { ++ status = "okay"; ++}; ++ ++&ddc1 { ++ status = "okay"; ++}; ++ + // ============================================= + // Downstream rpi- changes + +--- a/arch/arm/boot/dts/bcm2711.dtsi ++++ b/arch/arm/boot/dts/bcm2711.dtsi +@@ -31,6 +31,11 @@ + }; + }; + ++ vc4: gpu { ++ compatible = "brcm,bcm2711-vc5"; ++ status = "disabled"; ++ }; ++ + clk_108MHz: clk-108M { + #clock-cells = <0>; + compatible = "fixed-clock"; +@@ -254,6 +259,27 @@ + status = "disabled"; + }; + ++ pixelvalve0: pixelvalve@7e206000 { ++ compatible = "brcm,bcm2711-pixelvalve0"; ++ reg = <0x7e206000 0x100>; ++ interrupts = <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>; ++ status = "disabled"; ++ }; ++ ++ pixelvalve1: pixelvalve@7e207000 { ++ compatible = "brcm,bcm2711-pixelvalve1"; ++ reg = <0x7e207000 0x100>; ++ interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>; ++ status = "disabled"; ++ }; ++ ++ pixelvalve2: pixelvalve@7e20a000 { ++ compatible = "brcm,bcm2711-pixelvalve2"; ++ reg = <0x7e20a000 0x100>; ++ interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>; ++ status = "disabled"; ++ }; ++ + pwm1: pwm@7e20c800 { + compatible = "brcm,bcm2835-pwm"; + reg = <0x7e20c800 0x28>; +@@ -264,6 +290,13 @@ + status = "disabled"; + }; + ++ pixelvalve4: pixelvalve@7e216000 { ++ compatible = "brcm,bcm2711-pixelvalve4"; ++ reg = <0x7e216000 0x100>; ++ interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>; ++ status = "disabled"; ++ }; ++ + emmc2: emmc2@7e340000 { + compatible = "brcm,bcm2711-emmc2"; + reg = <0x7e340000 0x100>; +@@ -276,6 +309,13 @@ + interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>; + }; + ++ pixelvalve3: pixelvalve@7ec12000 { ++ compatible = "brcm,bcm2711-pixelvalve3"; ++ reg = <0x7ec12000 0x100>; ++ interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>; ++ status = "disabled"; ++ }; ++ + dvp: clock@7ef00000 { + compatible = "brcm,brcm2711-dvp"; + reg = <0x7ef00000 0x10>; +@@ -283,6 +323,76 @@ + #clock-cells = <1>; + #reset-cells = <1>; + }; ++ ++ hdmi0: hdmi@7ef00700 { ++ compatible = "brcm,bcm2711-hdmi0"; ++ reg = <0x7ef00700 0x300>, ++ <0x7ef00300 0x200>, ++ <0x7ef00f00 0x80>, ++ <0x7ef00f80 0x80>, ++ <0x7ef01b00 0x200>, ++ <0x7ef01f00 0x400>, ++ <0x7ef00200 0x80>, ++ <0x7ef04300 0x100>, ++ <0x7ef20000 0x100>; ++ reg-names = "hdmi", ++ "dvp", ++ "phy", ++ "rm", ++ "packet", ++ "metadata", ++ "csc", ++ "cec", ++ "hd"; ++ clocks = <&firmware_clocks 13>; ++ clock-names = "hdmi"; ++ resets = <&dvp 0>; ++ ddc = <&ddc0>; ++ status = "disabled"; ++ }; ++ ++ ddc0: i2c@7ef04500 { ++ compatible = "brcm,bcm2711-hdmi-i2c"; ++ reg = <0x7ef04500 0x100>, <0x7ef00b00 0x300>; ++ reg-names = "bsc", "auto-i2c"; ++ clock-frequency = <390000>; ++ status = "disabled"; ++ }; ++ ++ hdmi1: hdmi@7ef05700 { ++ compatible = "brcm,bcm2711-hdmi1"; ++ reg = <0x7ef05700 0x300>, ++ <0x7ef05300 0x200>, ++ <0x7ef05f00 0x80>, ++ <0x7ef05f80 0x80>, ++ <0x7ef06b00 0x200>, ++ <0x7ef06f00 0x400>, ++ <0x7ef00280 0x80>, ++ <0x7ef09300 0x100>, ++ <0x7ef20000 0x100>; ++ reg-names = "hdmi", ++ "dvp", ++ "phy", ++ "rm", ++ "packet", ++ "metadata", ++ "csc", ++ "cec", ++ "hd"; ++ ddc = <&ddc1>; ++ clocks = <&firmware_clocks 13>; ++ clock-names = "hdmi"; ++ resets = <&dvp 1>; ++ status = "disabled"; ++ }; ++ ++ ddc1: i2c@7ef09500 { ++ compatible = "brcm,bcm2711-hdmi-i2c"; ++ reg = <0x7ef09500 0x100>, <0x7ef05b00 0x300>; ++ reg-names = "bsc", "auto-i2c"; ++ clock-frequency = <390000>; ++ status = "disabled"; ++ }; + }; + + arm-pmu { diff --git a/target/linux/bcm27xx/patches-5.4/950-0600-DOWNSTREAM-ARM-dts-rpi4-Disable-KMS-driver-by-defaul.patch b/target/linux/bcm27xx/patches-5.4/950-0600-DOWNSTREAM-ARM-dts-rpi4-Disable-KMS-driver-by-defaul.patch new file mode 100644 index 0000000000..b27d355340 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0600-DOWNSTREAM-ARM-dts-rpi4-Disable-KMS-driver-by-defaul.patch @@ -0,0 +1,90 @@ +From 46369abfb7dd4c33637da4340fa47a5f76f7f1c2 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime@cerno.tech> +Date: Fri, 21 Feb 2020 17:10:45 +0100 +Subject: [PATCH] ARM: dts: rpi4: Disable KMS driver by + default + +Signed-off-by: Maxime Ripard <maxime@cerno.tech> +--- + arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 48 +++++++++++++++++++++++++++ + arch/arm/boot/dts/bcm2711-rpi.dtsi | 5 --- + 2 files changed, 48 insertions(+), 5 deletions(-) + +--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts ++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts +@@ -182,6 +182,14 @@ + // Downstream rpi- changes + + #include "bcm270x.dtsi" ++ ++/ { ++ soc { ++ /delete-node/ pixelvalve@7e807000; ++ /delete-node/ hdmi@7e902000; ++ }; ++}; ++ + #include "bcm2711-rpi.dtsi" + #include "bcm283x-rpi-csi1-2lane.dtsi" + +@@ -476,6 +484,46 @@ + pinctrl-0 = <&audio_pins>; + }; + ++&vc4 { ++ status = "disabled"; ++}; ++ ++&pixelvalve0 { ++ status = "disabled"; ++}; ++ ++&pixelvalve1 { ++ status = "disabled"; ++}; ++ ++&pixelvalve2 { ++ status = "disabled"; ++}; ++ ++&pixelvalve3 { ++ status = "disabled"; ++}; ++ ++&pixelvalve4 { ++ status = "disabled"; ++}; ++ ++&hdmi0 { ++ status = "disabled"; ++}; ++ ++&ddc0 { ++ status = "disabled"; ++}; ++ ++&hdmi1 { ++ status = "disabled"; ++}; ++ ++&ddc1 { ++ status = "disabled"; ++}; ++ + / { + __overrides__ { + act_led_gpio = <&act_led>,"gpios:4"; +--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi ++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi +@@ -55,11 +55,6 @@ + status = "okay"; + }; + +- vc4: gpu { +- compatible = "brcm,bcm2835-vc4"; +- status = "disabled"; +- }; +- + /delete-node/ audio; + }; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0601-dtoverlays-Add-Pi4-version-of-vc4-kms-v3d.patch b/target/linux/bcm27xx/patches-5.4/950-0601-dtoverlays-Add-Pi4-version-of-vc4-kms-v3d.patch new file mode 100644 index 0000000000..1c46fa4485 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0601-dtoverlays-Add-Pi4-version-of-vc4-kms-v3d.patch @@ -0,0 +1,241 @@ +From 7f9f7a113e9c5d6efd997de7de93af31ec286174 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.org> +Date: Fri, 20 Sep 2019 17:20:01 +0100 +Subject: [PATCH] dtoverlays: Add Pi4 version of vc4-kms-v3d + +The Pi4 version of the KMS drivers is a work in progress, some +blocks need alternate configuration, and some blocks currently +need to remain disabled (eg the VEC). + +Add a new overlay (vc4-kms-v3d-pi4) that loads the parts of +vc4-kms that do work on Pi4. +This has been tested with DPI and HDMI (not 100% reliable on mode +switching) + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org> +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 14 ++ + .../dts/overlays/vc4-kms-v3d-pi4-overlay.dts | 183 ++++++++++++++++++ + 3 files changed, 198 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -191,6 +191,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + vc4-fkms-v3d.dtbo \ + vc4-kms-kippah-7inch.dtbo \ + vc4-kms-v3d.dtbo \ ++ vc4-kms-v3d-pi4.dtbo \ + vga666.dtbo \ + w1-gpio.dtbo \ + w1-gpio-pullup.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -2684,6 +2684,20 @@ Params: cma-256 CMA is 2 + audio Enable or disable audio over HDMI (default "on") + + ++Name: vc4-kms-v3d-pi4 ++Info: Enable Eric Anholt's DRM VC4 HDMI/HVS/V3D driver for Pi4. ++Load: dtoverlay=vc4-kms-v3d-pi4,<param> ++Params: cma-256 CMA is 256MB ++ cma-192 CMA is 192MB ++ cma-128 CMA is 128MB ++ cma-96 CMA is 96MB ++ cma-64 CMA is 64MB ++ audio Enable or disable audio over HDMI0 (default ++ "on") ++ audio1 Enable or disable audio over HDMI1 (default ++ "on") ++ ++ + Name: vga666 + Info: Overlay for the Fen Logic VGA666 board + This uses GPIOs 2-21 (so no I2C), and activates the output 2-3 seconds +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts +@@ -0,0 +1,183 @@ ++/* ++ * vc4-kms-v3d-pi4-overlay.dts ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++#include <dt-bindings/clock/bcm2835.h> ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target-path = "/chosen"; ++ __overlay__ { ++ bootargs = "cma=256M"; ++ }; ++ }; ++ ++ fragment@1 { ++ target-path = "/chosen"; ++ __dormant__ { ++ bootargs = "cma=192M"; ++ }; ++ }; ++ ++ fragment@2 { ++ target-path = "/chosen"; ++ __dormant__ { ++ bootargs = "cma=128M"; ++ }; ++ }; ++ ++ fragment@3 { ++ target-path = "/chosen"; ++ __dormant__ { ++ bootargs = "cma=96M"; ++ }; ++ }; ++ ++ fragment@4 { ++ target-path = "/chosen"; ++ __dormant__ { ++ bootargs = "cma=64M"; ++ }; ++ }; ++ ++ fragment@5 { ++ target = <&ddc0>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@6 { ++ target = <&ddc1>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@7 { ++ target = <&hdmi0>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@8 { ++ target = <&hdmi1>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@9 { ++ target = <&hvs>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@10 { ++ target = <&pixelvalve0>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@11 { ++ target = <&pixelvalve1>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@12 { ++ target = <&pixelvalve2>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@13 { ++ target = <&pixelvalve3>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@14 { ++ target = <&pixelvalve4>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@15 { ++ target = <&v3d>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@16 { ++ target = <&vc4>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@17 { ++ target = <&txp>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@18 { ++ target = <&fb>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ ++ fragment@19 { ++ target = <&firmwarekms>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ ++ fragment@20 { ++ target = <&vec>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ ++ fragment@21 { ++ target = <&hdmi0>; ++ __dormant__ { ++ dmas; ++ }; ++ }; ++ ++ fragment@22 { ++ target = <&hdmi1>; ++ __dormant__ { ++ dmas; ++ }; ++ }; ++ ++ __overrides__ { ++ cma-256 = <0>,"+0-1-2-3-4"; ++ cma-192 = <0>,"-0+1-2-3-4"; ++ cma-128 = <0>,"-0-1+2-3-4"; ++ cma-96 = <0>,"-0-1-2+3-4"; ++ cma-64 = <0>,"-0-1-2-3+4"; ++ audio = <0>,"!21"; ++ audio1 = <0>,"!22"; ++ }; ++}; diff --git a/target/linux/bcm27xx/patches-5.4/950-0602-drm-Checking-of-the-pitch-is-only-valid-for-linear-f.patch b/target/linux/bcm27xx/patches-5.4/950-0602-drm-Checking-of-the-pitch-is-only-valid-for-linear-f.patch new file mode 100644 index 0000000000..3f0aa4aa02 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0602-drm-Checking-of-the-pitch-is-only-valid-for-linear-f.patch @@ -0,0 +1,39 @@ +From 60ef8af4bc2d5f8643adbcb69bb1f52e491a96ae Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Mon, 27 Jan 2020 10:22:44 +0000 +Subject: [PATCH] drm: Checking of the pitch is only valid for linear + formats + +framebuffer_check was computing a minimum pitch value and ensuring +that the provided value was greater than this. +That check is only valid if the format is linear. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/gpu/drm/drm_framebuffer.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +--- a/drivers/gpu/drm/drm_framebuffer.c ++++ b/drivers/gpu/drm/drm_framebuffer.c +@@ -217,12 +217,16 @@ static int framebuffer_check(struct drm_ + if (min_pitch > UINT_MAX) + return -ERANGE; + +- if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX) +- return -ERANGE; ++ if (r->modifier[i] == DRM_FORMAT_MOD_LINEAR) { ++ if ((uint64_t)height * r->pitches[i] + r->offsets[i] > ++ UINT_MAX) ++ return -ERANGE; + +- if (block_size && r->pitches[i] < min_pitch) { +- DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i); +- return -EINVAL; ++ if (block_size && r->pitches[i] < min_pitch) { ++ DRM_DEBUG_KMS("bad pitch %u for plane %d\n", ++ r->pitches[i], i); ++ return -EINVAL; ++ } + } + + if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) { diff --git a/target/linux/bcm27xx/patches-5.4/950-0603-drm-vc4-Add-support-for-DRM_FORMAT_P030-to-vc4-plane.patch b/target/linux/bcm27xx/patches-5.4/950-0603-drm-vc4-Add-support-for-DRM_FORMAT_P030-to-vc4-plane.patch new file mode 100644 index 0000000000..9d83d8a3dd --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0603-drm-vc4-Add-support-for-DRM_FORMAT_P030-to-vc4-plane.patch @@ -0,0 +1,174 @@ +From 87c4b03b9d1180c2f878b19363ec0609b5f24c75 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Fri, 24 Jan 2020 14:25:41 +0000 +Subject: [PATCH] drm/vc4: Add support for DRM_FORMAT_P030 to vc4 + planes + +This currently doesn't handle non-zero source rectangles correctly, +but add support for DRM_FORMAT_P030 with DRM_FORMAT_MOD_BROADCOM_SAND128 +modifier to planes when running on HVS5. + +WIP still for source cropping SAND/P030 formats + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/gpu/drm/vc4/vc4_plane.c | 83 +++++++++++++++++++++++---------- + 1 file changed, 59 insertions(+), 24 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -33,6 +33,7 @@ static const struct hvs_format { + u32 hvs; /* HVS_FORMAT_* */ + u32 pixel_order; + u32 pixel_order_hvs5; ++ bool hvs5_only; + } hvs_formats[] = { + { + .drm = DRM_FORMAT_XRGB8888, +@@ -128,6 +129,12 @@ static const struct hvs_format { + .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE, + .pixel_order = HVS_PIXEL_ORDER_XYCRCB, + }, ++ { ++ .drm = DRM_FORMAT_P030, ++ .hvs = HVS_PIXEL_FORMAT_YCBCR_10BIT, ++ .pixel_order = HVS_PIXEL_ORDER_XYCBCR, ++ .hvs5_only = true, ++ }, + }; + + static const struct hvs_format *vc4_get_hvs_format(u32 drm_format) +@@ -801,27 +808,33 @@ static int vc4_plane_mode_set(struct drm + uint32_t param = fourcc_mod_broadcom_param(fb->modifier); + u32 tile_w, tile, x_off, pix_per_tile; + +- hvs_format = HVS_PIXEL_FORMAT_H264; +- +- switch (base_format_mod) { +- case DRM_FORMAT_MOD_BROADCOM_SAND64: +- tiling = SCALER_CTL0_TILING_64B; +- tile_w = 64; +- break; +- case DRM_FORMAT_MOD_BROADCOM_SAND128: ++ if (fb->format->format == DRM_FORMAT_P030) { ++ hvs_format = HVS_PIXEL_FORMAT_YCBCR_10BIT; + tiling = SCALER_CTL0_TILING_128B; +- tile_w = 128; +- break; +- case DRM_FORMAT_MOD_BROADCOM_SAND256: +- tiling = SCALER_CTL0_TILING_256B_OR_T; +- tile_w = 256; +- break; +- default: +- break; +- } ++ tile_w = 96; ++ } else { ++ hvs_format = HVS_PIXEL_FORMAT_H264; + ++ switch (base_format_mod) { ++ case DRM_FORMAT_MOD_BROADCOM_SAND64: ++ tiling = SCALER_CTL0_TILING_64B; ++ tile_w = 64; ++ break; ++ case DRM_FORMAT_MOD_BROADCOM_SAND128: ++ tiling = SCALER_CTL0_TILING_128B; ++ tile_w = 128; ++ break; ++ case DRM_FORMAT_MOD_BROADCOM_SAND256: ++ tiling = SCALER_CTL0_TILING_256B_OR_T; ++ tile_w = 256; ++ break; ++ default: ++ break; ++ } ++ } + if (param > SCALER_TILE_HEIGHT_MASK) { +- DRM_DEBUG_KMS("SAND height too large (%d)\n", param); ++ DRM_DEBUG_KMS("SAND height too large (%d)\n", ++ param); + return -EINVAL; + } + +@@ -831,6 +844,13 @@ static int vc4_plane_mode_set(struct drm + + /* Adjust the base pointer to the first pixel to be scanned + * out. ++ * ++ * For P030, y_ptr [31:4] is the 128bit word for the start pixel ++ * y_ptr [3:0] is the pixel (0-11) contained within that 128bit ++ * word that should be taken as the first pixel. ++ * Ditto uv_ptr [31:4] vs [3:0], however [3:0] contains the ++ * element within the 128bit word, eg for pixel 3 the value ++ * should be 6. + */ + for (i = 0; i < num_planes; i++) { + vc4_state->offsets[i] += param * tile_w * tile; +@@ -943,8 +963,8 @@ static int vc4_plane_mode_set(struct drm + vc4_dlist_write(vc4_state, + VC4_SET_FIELD(state->alpha >> 4, + SCALER5_CTL2_ALPHA) | +- fb->format->has_alpha ? +- SCALER5_CTL2_ALPHA_PREMULT : 0 | ++ (fb->format->has_alpha ? ++ SCALER5_CTL2_ALPHA_PREMULT : 0) | + (mix_plane_alpha ? + SCALER5_CTL2_ALPHA_MIX : 0) | + VC4_SET_FIELD(fb->format->has_alpha ? +@@ -992,7 +1012,8 @@ static int vc4_plane_mode_set(struct drm + + /* Pitch word 1/2 */ + for (i = 1; i < num_planes; i++) { +- if (hvs_format != HVS_PIXEL_FORMAT_H264) { ++ if (hvs_format != HVS_PIXEL_FORMAT_H264 && ++ hvs_format != HVS_PIXEL_FORMAT_YCBCR_10BIT) { + vc4_dlist_write(vc4_state, + VC4_SET_FIELD(fb->pitches[i], + SCALER_SRC_PITCH)); +@@ -1361,6 +1382,13 @@ static bool vc4_format_mod_supported(str + 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_RGBX1010102: + case DRM_FORMAT_BGRX1010102: + case DRM_FORMAT_RGBA1010102: +@@ -1393,8 +1421,11 @@ struct drm_plane *vc4_plane_init(struct + struct drm_plane *plane = NULL; + struct vc4_plane *vc4_plane; + u32 formats[ARRAY_SIZE(hvs_formats)]; ++ int num_formats = 0; + int ret = 0; + unsigned i; ++ bool hvs5 = of_device_is_compatible(dev->dev->of_node, ++ "brcm,bcm2711-vc5"); + static const uint64_t modifiers[] = { + DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED, + DRM_FORMAT_MOD_BROADCOM_SAND128, +@@ -1409,13 +1440,17 @@ struct drm_plane *vc4_plane_init(struct + if (!vc4_plane) + return ERR_PTR(-ENOMEM); + +- for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) +- formats[i] = hvs_formats[i].drm; ++ for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) { ++ if (hvs_formats[i].hvs5_only || hvs5) { ++ formats[num_formats] = hvs_formats[i].drm; ++ num_formats++; ++ } ++ } + + plane = &vc4_plane->base; + ret = drm_universal_plane_init(dev, plane, 0, + &vc4_plane_funcs, +- formats, ARRAY_SIZE(formats), ++ formats, num_formats, + modifiers, type, NULL); + + drm_plane_helper_add(plane, &vc4_plane_helper_funcs); diff --git a/target/linux/bcm27xx/patches-5.4/950-0604-Fixup-P030-support.patch b/target/linux/bcm27xx/patches-5.4/950-0604-Fixup-P030-support.patch new file mode 100644 index 0000000000..a24127e6f2 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0604-Fixup-P030-support.patch @@ -0,0 +1,26 @@ +From 63423f4f48afc96949a63c53203faa904a85670b Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Tue, 25 Feb 2020 17:35:10 +0000 +Subject: [PATCH] Fixup P030 support + +I got the logic wrong for enabling pixel formats, resulting in +Pi0-3 only getting a single, invalid, format (P030 SAND). + +Fixes: e07ef1d drm/vc4: Add support for DRM_FORMAT_P030 to vc4 planes + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/gpu/drm/vc4/vc4_plane.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -1441,7 +1441,7 @@ struct drm_plane *vc4_plane_init(struct + return ERR_PTR(-ENOMEM); + + for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) { +- if (hvs_formats[i].hvs5_only || hvs5) { ++ if (!hvs_formats[i].hvs5_only || hvs5) { + formats[num_formats] = hvs_formats[i].drm; + num_formats++; + } diff --git a/target/linux/bcm27xx/patches-5.4/950-0605-drm-vc4-The-check-for-assigned-HVS-channels-is-not-a.patch b/target/linux/bcm27xx/patches-5.4/950-0605-drm-vc4-The-check-for-assigned-HVS-channels-is-not-a.patch new file mode 100644 index 0000000000..00a65a9b11 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0605-drm-vc4-The-check-for-assigned-HVS-channels-is-not-a.patch @@ -0,0 +1,33 @@ +From 76534156ad6e835ad89135210d565dd5f58e91ab Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Tue, 11 Feb 2020 15:36:59 +0000 +Subject: [PATCH] drm/vc4: The check for assigned HVS channels is not + applicable firmware_kms + +Channel assignments is only in full KMS, so skip the check +if in firmware kms mode. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/gpu/drm/vc4/vc4_kms.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/gpu/drm/vc4/vc4_kms.c ++++ b/drivers/gpu/drm/vc4/vc4_kms.c +@@ -579,6 +579,7 @@ static int + vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) + { + unsigned long unassigned_channels = GENMASK(NUM_CHANNELS - 1, 0); ++ struct vc4_dev *vc4 = to_vc4_dev(state->dev); + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + int i, ret; +@@ -590,7 +591,7 @@ vc4_atomic_check(struct drm_device *dev, + bool is_assigned = false; + unsigned int channel; + +- if (!crtc_state->active) ++ if (!crtc_state->active || vc4->firmware_kms) + continue; + + /* diff --git a/target/linux/bcm27xx/patches-5.4/950-0606-dt-Update-v3d-to-use-firmware_clocks.patch b/target/linux/bcm27xx/patches-5.4/950-0606-dt-Update-v3d-to-use-firmware_clocks.patch new file mode 100644 index 0000000000..b67f38782f --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0606-dt-Update-v3d-to-use-firmware_clocks.patch @@ -0,0 +1,23 @@ +From 310d91d120b672d13d83fd4ab7cfb9cff485a1de Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Mon, 17 Feb 2020 11:37:21 +0000 +Subject: [PATCH] dt: Update v3d to use firmware_clocks. + +Use the updated DT clock-names property to map the v3d clock +to the firmware_clocks driver, instead of the older clkdev API. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + arch/arm/boot/dts/bcm2711-rpi.dtsi | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi ++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi +@@ -34,6 +34,7 @@ + power-domains = <&pm BCM2835_POWER_DOMAIN_GRAFX_V3D>; + resets = <&pm BCM2835_RESET_V3D>; + clocks = <&firmware_clocks 5>; ++ clocks-names = "v3d"; + interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; diff --git a/target/linux/bcm27xx/patches-5.4/950-0607-drm-vc4-Reset-audio-infoframe-on-encoder_enable-if-p.patch b/target/linux/bcm27xx/patches-5.4/950-0607-drm-vc4-Reset-audio-infoframe-on-encoder_enable-if-p.patch new file mode 100644 index 0000000000..56106a4274 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0607-drm-vc4-Reset-audio-infoframe-on-encoder_enable-if-p.patch @@ -0,0 +1,72 @@ +From 3e45488069e20b07b83d8cbba88c7fa2b205e559 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 25 Mar 2020 18:01:04 +0000 +Subject: [PATCH] drm/vc4: Reset audio infoframe on encoder_enable if + previously streaming + +If the encoder is disabled and re-enabled (eg mode change) all infoframes +are reset, whilst the audio subsystem know nothing about this change. +The driver therefore needs to reinstate the audio infoframe for +itself. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 12 ++++++++++++ + drivers/gpu/drm/vc4/vc4_hdmi.h | 2 ++ + 2 files changed, 14 insertions(+) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -344,8 +344,16 @@ static void vc4_hdmi_set_audio_infoframe + + static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder) + { ++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); ++ + vc4_hdmi_set_avi_infoframe(encoder); + vc4_hdmi_set_spd_infoframe(encoder); ++ /* ++ * If audio was streaming, then we need to reenabled the audio ++ * infoframe here during encoder_enable. ++ */ ++ if (vc4_hdmi->audio.streaming) ++ vc4_hdmi_set_audio_infoframe(encoder); + } + + static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder) +@@ -825,6 +833,7 @@ static void vc4_hdmi_audio_reset(struct + struct device *dev = &vc4_hdmi->pdev->dev; + int ret; + ++ vc4_hdmi->audio.streaming = false; + ret = vc4_hdmi_stop_packet(encoder, HDMI_INFOFRAME_TYPE_AUDIO); + if (ret) + dev_err(dev, "Failed to stop audio infoframe: %d\n", ret); +@@ -928,6 +937,7 @@ static int vc4_hdmi_audio_trigger(struct + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + vc4_hdmi_set_audio_infoframe(encoder); ++ vc4_hdmi->audio.streaming = true; + + if (vc4_hdmi->variant->phy_rng_enable) + vc4_hdmi->variant->phy_rng_enable(vc4_hdmi); +@@ -946,6 +956,8 @@ static int vc4_hdmi_audio_trigger(struct + if (vc4_hdmi->variant->phy_rng_disable) + vc4_hdmi->variant->phy_rng_disable(vc4_hdmi); + ++ vc4_hdmi->audio.streaming = false; ++ + break; + default: + break; +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -101,6 +101,8 @@ struct vc4_hdmi_audio { + int channels; + struct snd_dmaengine_dai_dma_data dma_data; + struct snd_pcm_substream *substream; ++ ++ bool streaming; + }; + + /* General HDMI hardware state. */ diff --git a/target/linux/bcm27xx/patches-5.4/950-0608-drm-vc4-Set-the-b-frame-marker-to-the-match-ALSA-s-d.patch b/target/linux/bcm27xx/patches-5.4/950-0608-drm-vc4-Set-the-b-frame-marker-to-the-match-ALSA-s-d.patch new file mode 100644 index 0000000000..4827f96023 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0608-drm-vc4-Set-the-b-frame-marker-to-the-match-ALSA-s-d.patch @@ -0,0 +1,31 @@ +From 1cf3e20f13378430cd1fc929548bca9f5e517afe Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 25 Mar 2020 18:03:42 +0000 +Subject: [PATCH] drm/vc4: Set the b-frame marker to the match ALSA's + default. + +ALSA's iec958 plugin by default sets the block start preamble +to 8, whilst this driver was programming the hardware to expect +0xF. +Amend the hardware config to match ALSA. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -885,10 +885,11 @@ static int vc4_hdmi_audio_hw_params(stru + + vc4_hdmi_audio_set_mai_clock(vc4_hdmi); + ++ /* The B frame identifier should match the value used by alsa-lib (8) */ + audio_packet_config = + VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_SAMPLE_FLAT | + VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS | +- VC4_SET_FIELD(0xf, VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER); ++ VC4_SET_FIELD(0x8, VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER); + + channel_mask = GENMASK(vc4_hdmi->audio.channels - 1, 0); + audio_packet_config |= VC4_SET_FIELD(channel_mask, diff --git a/target/linux/bcm27xx/patches-5.4/950-0609-dts-Add-reg-names-for-the-HDMI-registers-on-bcm2835.patch b/target/linux/bcm27xx/patches-5.4/950-0609-dts-Add-reg-names-for-the-HDMI-registers-on-bcm2835.patch new file mode 100644 index 0000000000..65e206c181 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0609-dts-Add-reg-names-for-the-HDMI-registers-on-bcm2835.patch @@ -0,0 +1,27 @@ +From 9f7718ae7edcf5feab81d3c8561e6c5112e0b462 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 25 Mar 2020 18:07:19 +0000 +Subject: [PATCH] dts: Add reg-names for the HDMI registers on bcm2835 + +Pi4 is requiring many more register configs in the HDMI +block, and has switched to using reg-names instead of fixed index +values. + +Switch bc2835-common to match. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + arch/arm/boot/dts/bcm2835-common.dtsi | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/arch/arm/boot/dts/bcm2835-common.dtsi ++++ b/arch/arm/boot/dts/bcm2835-common.dtsi +@@ -110,6 +110,8 @@ + compatible = "brcm,bcm2835-hdmi"; + reg = <0x7e902000 0x600>, + <0x7e808000 0x100>; ++ reg-names = "hdmi", ++ "hd"; + interrupts = <2 8>, <2 9>; + ddc = <&i2c2>; + clocks = <&clocks BCM2835_PLLH_PIX>, diff --git a/target/linux/bcm27xx/patches-5.4/950-0610-dt-Add-HDMI-audio-dma-values-to-bcm2711.dtsi.patch b/target/linux/bcm27xx/patches-5.4/950-0610-dt-Add-HDMI-audio-dma-values-to-bcm2711.dtsi.patch new file mode 100644 index 0000000000..9a9fd06eeb --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0610-dt-Add-HDMI-audio-dma-values-to-bcm2711.dtsi.patch @@ -0,0 +1,32 @@ +From 4a3b5d7018f3b0d66f412b0b1500b76ab089a2c9 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 25 Mar 2020 18:08:39 +0000 +Subject: [PATCH] dt: Add HDMI audio dma values to bcm2711.dtsi + +Adds the relevant DMA settings for HDMI audio to work. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + arch/arm/boot/dts/bcm2711.dtsi | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/arch/arm/boot/dts/bcm2711.dtsi ++++ b/arch/arm/boot/dts/bcm2711.dtsi +@@ -348,6 +348,8 @@ + clock-names = "hdmi"; + resets = <&dvp 0>; + ddc = <&ddc0>; ++ dmas = <&dma 10>; ++ dma-names = "audio-rx"; + status = "disabled"; + }; + +@@ -383,6 +385,8 @@ + clocks = <&firmware_clocks 13>; + clock-names = "hdmi"; + resets = <&dvp 1>; ++ dmas = <&dma 17>; ++ dma-names = "audio-rx"; + status = "disabled"; + }; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0611-drm-vc4-Use-reg-names-to-configure-HDMI-audio.patch b/target/linux/bcm27xx/patches-5.4/950-0611-drm-vc4-Use-reg-names-to-configure-HDMI-audio.patch new file mode 100644 index 0000000000..2504a07083 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0611-drm-vc4-Use-reg-names-to-configure-HDMI-audio.patch @@ -0,0 +1,35 @@ +From 7a463d59a0539cdf79ee6f1fe6c52f0a487ee63e Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 25 Mar 2020 18:11:41 +0000 +Subject: [PATCH] drm/vc4: Use reg-names to configure HDMI audio. + +HDMI audio configuration was using fixed index numbers to +load in DT register settings. +Switch to using reg-names for flexibility and to match Pi4. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -1097,6 +1097,7 @@ static int vc4_hdmi_audio_init(struct vc + struct snd_soc_card *card = &vc4_hdmi->audio.card; + struct device *dev = &vc4_hdmi->pdev->dev; + const __be32 *addr; ++ int index; + int ret; + int len; + +@@ -1122,7 +1123,9 @@ static int vc4_hdmi_audio_init(struct vc + * for DMA transfers. + * This VC/MMU should probably be exposed to avoid this kind of hacks. + */ +- addr = of_get_address(dev->of_node, 1, NULL, NULL); ++ index = of_property_match_string(dev->of_node, "reg-names", "hd"); ++ addr = of_get_address(dev->of_node, index, NULL, NULL); ++ + vc4_hdmi->audio.dma_data.addr = be32_to_cpup(addr) + mai_data->offset; + vc4_hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + vc4_hdmi->audio.dma_data.maxburst = 2; diff --git a/target/linux/bcm27xx/patches-5.4/950-0612-drm-vc4-Add-audio-initialisation-for-Pi4.patch b/target/linux/bcm27xx/patches-5.4/950-0612-drm-vc4-Add-audio-initialisation-for-Pi4.patch new file mode 100644 index 0000000000..6ec6850866 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0612-drm-vc4-Add-audio-initialisation-for-Pi4.patch @@ -0,0 +1,127 @@ +From 727b5180ec09faab313d7e2517e225001c967bb0 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 25 Mar 2020 18:16:14 +0000 +Subject: [PATCH] drm/vc4: Add audio initialisation for Pi4. + +The audio configuration has changed for Pi4, so support the +configuration functions via the variant tables. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 51 ++++++++++++++++++++++++++++------ + drivers/gpu/drm/vc4/vc4_hdmi.h | 6 ++++ + 2 files changed, 49 insertions(+), 8 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -742,10 +742,44 @@ static const struct drm_encoder_helper_f + .enable = vc4_hdmi_encoder_enable, + }; + ++static u32 vc4_hdmi_get_hsm_clock(struct vc4_hdmi *vc4_hdmi) ++{ ++ return clk_get_rate(vc4_hdmi->hsm_clock); ++} ++ ++static u32 vc5_hdmi_get_hsm_clock(struct vc4_hdmi *vc4_hdmi) ++{ ++ return 108000000; ++} ++ ++static u32 vc4_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask) ++{ ++ int i; ++ u32 channel_map = 0; ++ ++ for (i = 0; i < 8; i++) { ++ if (channel_mask & BIT(i)) ++ channel_map |= i << (3 * i); ++ } ++ return channel_map; ++} ++ ++static u32 vc5_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask) ++{ ++ int i; ++ u32 channel_map = 0; ++ ++ for (i = 0; i < 8; i++) { ++ if (channel_mask & BIT(i)) ++ channel_map |= i << (4 * i); ++ } ++ return channel_map; ++} ++ + /* HDMI audio codec callbacks */ + static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *vc4_hdmi) + { +- u32 hsm_clock = clk_get_rate(vc4_hdmi->hsm_clock); ++ u32 hsm_clock = vc4_hdmi->variant->get_hsm_clock(vc4_hdmi); + unsigned long n, m; + + rational_best_approximation(hsm_clock, vc4_hdmi->audio.samplerate, +@@ -864,7 +898,7 @@ static int vc4_hdmi_audio_hw_params(stru + struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai); + struct device *dev = &vc4_hdmi->pdev->dev; + u32 audio_packet_config, channel_mask; +- u32 channel_map, i; ++ u32 channel_map; + + if (substream != vc4_hdmi->audio.substream) + return -EINVAL; +@@ -916,12 +950,7 @@ static int vc4_hdmi_audio_hw_params(stru + VC4_HDMI_MAI_CONFIG_BIT_REVERSE | + VC4_SET_FIELD(channel_mask, VC4_HDMI_MAI_CHANNEL_MASK)); + +- channel_map = 0; +- for (i = 0; i < 8; i++) { +- if (channel_mask & BIT(i)) +- channel_map |= i << (3 * i); +- } +- ++ channel_map = vc4_hdmi->variant->channel_map(vc4_hdmi, channel_mask); + HDMI_WRITE(HDMI_MAI_CHANNEL_MAP, channel_map); + HDMI_WRITE(HDMI_AUDIO_PACKET_CONFIG, audio_packet_config); + vc4_hdmi_set_n_cts(vc4_hdmi); +@@ -1715,6 +1744,8 @@ static const struct vc4_hdmi_variant bcm + .phy_disable = vc4_hdmi_phy_disable, + .phy_rng_enable = vc4_hdmi_phy_rng_enable, + .phy_rng_disable = vc4_hdmi_phy_rng_disable, ++ .get_hsm_clock = vc4_hdmi_get_hsm_clock, ++ .channel_map = vc4_hdmi_channel_map, + }; + + static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = { +@@ -1736,6 +1767,8 @@ static const struct vc4_hdmi_variant bcm + .phy_init = vc5_hdmi_phy_init, + .phy_rng_enable = vc5_hdmi_phy_rng_enable, + .phy_rng_disable = vc5_hdmi_phy_rng_disable, ++ .get_hsm_clock = vc5_hdmi_get_hsm_clock, ++ .channel_map = vc5_hdmi_channel_map, + }; + + static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = { +@@ -1757,6 +1790,8 @@ static const struct vc4_hdmi_variant bcm + .phy_init = vc5_hdmi_phy_init, + .phy_rng_enable = vc5_hdmi_phy_rng_enable, + .phy_rng_disable = vc5_hdmi_phy_rng_disable, ++ .get_hsm_clock = vc5_hdmi_get_hsm_clock, ++ .channel_map = vc5_hdmi_channel_map, + }; + + static const struct of_device_id vc4_hdmi_dt_match[] = { +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -88,6 +88,12 @@ struct vc4_hdmi_variant { + + /* Callback to disable the RNG in the PHY */ + void (*phy_rng_disable)(struct vc4_hdmi *vc4_hdmi); ++ ++ /* Callback to get hsm clock */ ++ u32 (*get_hsm_clock)(struct vc4_hdmi *vc4_hdmi); ++ ++ /* Callback to get channel map */ ++ u32 (*channel_map)(struct vc4_hdmi *vc4_hdmi, u32 channel_mask); + }; + + /* HDMI audio information */ diff --git a/target/linux/bcm27xx/patches-5.4/950-0613-drm-vc4-Enable-audio-on-Pi4.patch b/target/linux/bcm27xx/patches-5.4/950-0613-drm-vc4-Enable-audio-on-Pi4.patch new file mode 100644 index 0000000000..78340ced70 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0613-drm-vc4-Enable-audio-on-Pi4.patch @@ -0,0 +1,31 @@ +From 446b19807781d73f214f959a8f4dab7662eed337 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 25 Mar 2020 18:18:45 +0000 +Subject: [PATCH] drm/vc4: Enable audio on Pi4. + +This could be a revert of "drm/vc4: hdmi: Add an audio support flag" +as it is no longer needed. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -1750,6 +1750,7 @@ static const struct vc4_hdmi_variant bcm + + static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = { + .id = 0, ++ .audio_available = true, + .max_pixel_clock = 297000000, + .registers = vc5_hdmi_hdmi0_fields, + .num_registers = ARRAY_SIZE(vc5_hdmi_hdmi0_fields), +@@ -1773,6 +1774,7 @@ static const struct vc4_hdmi_variant bcm + + static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = { + .id = 1, ++ .audio_available = true, + .max_pixel_clock = 297000000, + .registers = vc5_hdmi_hdmi1_fields, + .num_registers = ARRAY_SIZE(vc5_hdmi_hdmi1_fields), diff --git a/target/linux/bcm27xx/patches-5.4/950-0614-drm-vc4-Alter-the-HDMI-state-machine-clock-calc-to-a.patch b/target/linux/bcm27xx/patches-5.4/950-0614-drm-vc4-Alter-the-HDMI-state-machine-clock-calc-to-a.patch new file mode 100644 index 0000000000..f576b8624e --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0614-drm-vc4-Alter-the-HDMI-state-machine-clock-calc-to-a.patch @@ -0,0 +1,46 @@ +From 37b204f22778f51cad7bdf678d7574ff6d7508a6 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 25 Mar 2020 18:22:40 +0000 +Subject: [PATCH] drm/vc4: Alter the HDMI state machine clock calc to + allow for 1920x1200 + +Whilst the documentation for BCM2835 states that the HDMI state machine +clock needs to be 108% of the pixel clock, other documentation says +that it only has to be greater than the pixel clock. The firmware +uses 101%, and that allows 1920x1200@60Hz to work within the +constraint of the HSM clock being < 163.68MHz. + +Adopt 101%, and increase the maximum pixel clock for vc4 to 162MHz +so that it too supports 1920x1200@60. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -581,10 +581,11 @@ static void vc4_hdmi_encoder_enable(stru + } + + /* +- * The HSM rate needs to be at 108% of the pixel clock, with a +- * minimum of 108MHz. ++ * The HSM rate needs to be slightly greater than the pixel clock, with ++ * a minimum of 108MHz. ++ * Use 101% as this is what the firmware uses. + */ +- hsm_rate = max_t(unsigned long, 108000000, (pixel_rate / 100) * 108); ++ hsm_rate = max_t(unsigned long, 108000000, (pixel_rate / 100) * 101); + ret = clk_set_rate(vc4_hdmi->hsm_clock, hsm_rate); + if (ret) { + DRM_ERROR("Failed to set HSM clock rate: %d\n", ret); +@@ -1730,7 +1731,7 @@ static int vc4_hdmi_dev_remove(struct pl + } + + static const struct vc4_hdmi_variant bcm2835_variant = { +- .max_pixel_clock = 148500000, ++ .max_pixel_clock = 162000000, + .audio_available = true, + .cec_available = true, + .registers = vc4_hdmi_fields, diff --git a/target/linux/bcm27xx/patches-5.4/950-0615-dtoverlays-Remove-comment-about-vc4-kms-v3d-locking-.patch b/target/linux/bcm27xx/patches-5.4/950-0615-dtoverlays-Remove-comment-about-vc4-kms-v3d-locking-.patch new file mode 100644 index 0000000000..a05bf055ff --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0615-dtoverlays-Remove-comment-about-vc4-kms-v3d-locking-.patch @@ -0,0 +1,28 @@ +From 11d932df7f39124645cd017eb00853b4a70c28b4 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Thu, 26 Mar 2020 11:51:55 +0000 +Subject: [PATCH] dtoverlays: Remove comment about vc4-kms-v3d locking + up X from README + +Using vc4-kms-v3d with X has worked for quite a while, and essentially +required not using fbturbo and having an up to date MESA library. +Remove the comment that says otherwise. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + arch/arm/boot/dts/overlays/README | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -2672,9 +2672,7 @@ Params: <None> + + + Name: vc4-kms-v3d +-Info: Enable Eric Anholt's DRM VC4 HDMI/HVS/V3D driver. Running startx or +- booting to GUI while this overlay is in use will cause interesting +- lockups. ++Info: Enable Eric Anholt's DRM VC4 HDMI/HVS/V3D driver. + Load: dtoverlay=vc4-kms-v3d,<param> + Params: cma-256 CMA is 256MB (needs 1GB) + cma-192 CMA is 192MB (needs 1GB) diff --git a/target/linux/bcm27xx/patches-5.4/950-0616-drm-vc4-Kick-the-core-clock-up-during-a-mode-change.patch b/target/linux/bcm27xx/patches-5.4/950-0616-drm-vc4-Kick-the-core-clock-up-during-a-mode-change.patch new file mode 100644 index 0000000000..871d86a7a9 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0616-drm-vc4-Kick-the-core-clock-up-during-a-mode-change.patch @@ -0,0 +1,97 @@ +From 289c29b96fcc6cbe5c966fb0cc9e1bb8efbdd9dc Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Thu, 26 Mar 2020 15:32:19 +0000 +Subject: [PATCH] drm/vc4: Kick the core clock up during a mode change + +Experimental commit to kick the core clock up during mode +switching. This makes mode switching far more reliable, and +mimics what the firmware does. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + arch/arm/boot/dts/bcm2711.dtsi | 1 + + drivers/gpu/drm/vc4/vc4_drv.h | 2 ++ + drivers/gpu/drm/vc4/vc4_hvs.c | 7 +++++++ + drivers/gpu/drm/vc4/vc4_kms.c | 6 ++++++ + 4 files changed, 16 insertions(+) + +--- a/arch/arm/boot/dts/bcm2711.dtsi ++++ b/arch/arm/boot/dts/bcm2711.dtsi +@@ -306,6 +306,7 @@ + }; + + hvs@7e400000 { ++ clocks = <&firmware_clocks 4>; + interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>; + }; + +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -326,6 +326,8 @@ struct vc4_hvs { + void __iomem *regs; + u32 __iomem *dlist; + ++ struct clk *core_clk; ++ + /* Memory manager for CRTCs to allocate space in the display + * list. Units are dwords. + */ +--- a/drivers/gpu/drm/vc4/vc4_hvs.c ++++ b/drivers/gpu/drm/vc4/vc4_hvs.c +@@ -19,6 +19,7 @@ + * each CRTC. + */ + ++#include <linux/clk.h> + #include <linux/component.h> + #include <linux/platform_device.h> + +@@ -239,6 +240,12 @@ static int vc4_hvs_bind(struct device *d + hvs->regset.regs = hvs_regs; + hvs->regset.nregs = ARRAY_SIZE(hvs_regs); + ++ hvs->core_clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(hvs->core_clk)) { ++ dev_err(&pdev->dev, "Couldn't get core clock\n"); ++ return PTR_ERR(hvs->regs); ++ } ++ + hvs_version = readl(hvs->regs + SCALER_DISPLSTAT) >> 24; + if (hvs_version >= 0x40) + hvs->hvs5 = true; +--- a/drivers/gpu/drm/vc4/vc4_kms.c ++++ b/drivers/gpu/drm/vc4/vc4_kms.c +@@ -13,6 +13,7 @@ + + #include <linux/bitfield.h> + #include <linux/bitops.h> ++#include <linux/clk.h> + + #include <drm/drm_atomic.h> + #include <drm/drm_atomic_helper.h> +@@ -222,6 +223,7 @@ vc4_atomic_complete_commit(struct drm_at + { + struct drm_device *dev = state->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct vc4_hvs *hvs = vc4->hvs; + struct vc4_crtc *vc4_crtc; + int i; + +@@ -237,6 +239,8 @@ vc4_atomic_complete_commit(struct drm_at + vc4_hvs_mask_underrun(dev, vc4_crtc_state->assigned_channel); + } + ++ clk_set_rate(hvs->core_clk, 500000000); ++ + drm_atomic_helper_wait_for_fences(dev, state, false); + + drm_atomic_helper_wait_for_dependencies(state); +@@ -262,6 +266,8 @@ vc4_atomic_complete_commit(struct drm_at + + drm_atomic_helper_commit_cleanup_done(state); + ++ clk_set_rate(hvs->core_clk, 200000000); ++ + drm_atomic_state_put(state); + + up(&vc4->async_modeset); diff --git a/target/linux/bcm27xx/patches-5.4/950-0617-drm-vc4-Fixup-for-firmware-KMS.patch b/target/linux/bcm27xx/patches-5.4/950-0617-drm-vc4-Fixup-for-firmware-KMS.patch new file mode 100644 index 0000000000..62080bffec --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0617-drm-vc4-Fixup-for-firmware-KMS.patch @@ -0,0 +1,36 @@ +From 3c10a82ae5ce60ee8b4dbd1d1f8436efaa4593c3 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Mon, 30 Mar 2020 12:52:26 +0100 +Subject: [PATCH] drm/vc4: Fixup for firmware KMS + +Fix up "drm/vc4: Kick the core clock up during a mode change" for +firmware KMS mode where we don't have the HVS or core clock +configured. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/gpu/drm/vc4/vc4_kms.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_kms.c ++++ b/drivers/gpu/drm/vc4/vc4_kms.c +@@ -239,7 +239,8 @@ vc4_atomic_complete_commit(struct drm_at + vc4_hvs_mask_underrun(dev, vc4_crtc_state->assigned_channel); + } + +- clk_set_rate(hvs->core_clk, 500000000); ++ if (!vc4->firmware_kms) ++ clk_set_rate(hvs->core_clk, 500000000); + + drm_atomic_helper_wait_for_fences(dev, state, false); + +@@ -266,7 +267,8 @@ vc4_atomic_complete_commit(struct drm_at + + drm_atomic_helper_commit_cleanup_done(state); + +- clk_set_rate(hvs->core_clk, 200000000); ++ if (!vc4->firmware_kms) ++ clk_set_rate(hvs->core_clk, 200000000); + + drm_atomic_state_put(state); + diff --git a/target/linux/bcm27xx/patches-5.4/950-0618-drm-vc4-Fixup-plane-init-within-firmware-kms.patch b/target/linux/bcm27xx/patches-5.4/950-0618-drm-vc4-Fixup-plane-init-within-firmware-kms.patch new file mode 100644 index 0000000000..8edba988f2 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0618-drm-vc4-Fixup-plane-init-within-firmware-kms.patch @@ -0,0 +1,31 @@ +From d39fee7763d49f3b0bfd57e6cdc014467415d56a Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Mon, 30 Mar 2020 18:25:10 +0100 +Subject: [PATCH] drm/vc4: Fixup plane init within firmware-kms + +"drm/vc4: plane: Move additional planes creation to driver" moved +overlay and cursor plane creation to a global function thata was +unconditionally run, when it is not wanted in firmware KMS mode. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/gpu/drm/vc4/vc4_drv.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_drv.c ++++ b/drivers/gpu/drm/vc4/vc4_drv.c +@@ -291,9 +291,11 @@ static int vc4_drm_bind(struct device *d + if (ret) + goto gem_destroy; + +- ret = vc4_plane_create_additional_planes(drm); +- if (ret) +- goto unbind_all; ++ if (!vc4->firmware_kms) { ++ ret = vc4_plane_create_additional_planes(drm); ++ if (ret) ++ goto unbind_all; ++ } + + drm_fb_helper_remove_conflicting_framebuffers(NULL, "vc4drmfb", false); + diff --git a/target/linux/bcm27xx/patches-5.4/950-0619-drm-vc4-hdmi-Give-the-HDMI-audio-instances-different.patch b/target/linux/bcm27xx/patches-5.4/950-0619-drm-vc4-hdmi-Give-the-HDMI-audio-instances-different.patch new file mode 100644 index 0000000000..a7b5b5fad3 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0619-drm-vc4-hdmi-Give-the-HDMI-audio-instances-different.patch @@ -0,0 +1,26 @@ +From 992513ac2ec9245cb8f0fdd9c0e2ce4add07beb2 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Tue, 31 Mar 2020 16:21:45 +0100 +Subject: [PATCH] drm/vc4-hdmi: Give the HDMI audio instances different + names + +The debugfs usage within asoc gets confused if multiple interfaces +have the same card name, therefore use unique names when +initialising them. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -1198,7 +1198,7 @@ static int vc4_hdmi_audio_init(struct vc + + card->dai_link = dai_link; + card->num_links = 1; +- card->name = "vc4-hdmi"; ++ card->name = vc4_hdmi->variant->id ? "vc4-hdmi1" : "vc4-hdmi"; + card->dev = dev; + + /* diff --git a/target/linux/bcm27xx/patches-5.4/950-0620-i2c-brcmstb-The-interrupt-line-is-optional-so-use-pl.patch b/target/linux/bcm27xx/patches-5.4/950-0620-i2c-brcmstb-The-interrupt-line-is-optional-so-use-pl.patch new file mode 100644 index 0000000000..89285ad25c --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0620-i2c-brcmstb-The-interrupt-line-is-optional-so-use-pl.patch @@ -0,0 +1,49 @@ +From 8aa48c2a3fa470d348104e8f8aa558a661b724e5 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Tue, 31 Mar 2020 16:23:11 +0100 +Subject: [PATCH] i2c: brcmstb: The interrupt line is optional, so use + platform_get_irq_optional + +If there is no interrupt defined then an error is logged due +to the use of platform_get_irq. The driver handles not having +the interrupt by falling back to polling, therefore make +the appropriate call when claiming it. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/i2c/busses/i2c-brcmstb.c | 20 +++++++++++--------- + 1 file changed, 11 insertions(+), 9 deletions(-) + +--- a/drivers/i2c/busses/i2c-brcmstb.c ++++ b/drivers/i2c/busses/i2c-brcmstb.c +@@ -647,20 +647,22 @@ static int brcmstb_i2c_probe(struct plat + int_name = NULL; + + /* Get the interrupt number */ +- dev->irq = platform_get_irq(pdev, 0); ++ dev->irq = platform_get_irq_optional(pdev, 0); + + /* disable the bsc interrupt line */ + brcmstb_i2c_enable_disable_irq(dev, INT_DISABLE); + + /* register the ISR handler */ +- rc = devm_request_irq(&pdev->dev, dev->irq, brcmstb_i2c_isr, +- IRQF_SHARED, +- int_name ? int_name : pdev->name, +- dev); ++ if (dev->irq >= 0) { ++ rc = devm_request_irq(&pdev->dev, dev->irq, brcmstb_i2c_isr, ++ IRQF_SHARED, ++ int_name ? int_name : pdev->name, ++ dev); + +- if (rc) { +- dev_dbg(dev->device, "falling back to polling mode"); +- dev->irq = -1; ++ if (rc) { ++ dev_dbg(dev->device, "falling back to polling mode"); ++ dev->irq = -1; ++ } + } + + if (of_property_read_u32(dev->device->of_node, diff --git a/target/linux/bcm27xx/patches-5.4/950-0621-dt-Drop-I2C-for-Pi4-HDMI-interfaces-to-97.5kHz.patch b/target/linux/bcm27xx/patches-5.4/950-0621-dt-Drop-I2C-for-Pi4-HDMI-interfaces-to-97.5kHz.patch new file mode 100644 index 0000000000..cd18cd1309 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0621-dt-Drop-I2C-for-Pi4-HDMI-interfaces-to-97.5kHz.patch @@ -0,0 +1,35 @@ +From 460ddd2729f0dbb2723e48f3a22ffccbb78b42c7 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Tue, 31 Mar 2020 17:54:08 +0100 +Subject: [PATCH] dt: Drop I2C for Pi4 HDMI interfaces to 97.5kHz. + +It was set to 390kHz, which is outside of the required spec for +reading HDMI (max 100kHz). The i2c-brcmstb driver only supports +a number of fixed bus speeds, of which 97.5kHz is the closest to +100kHz without exceeding it. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + arch/arm/boot/dts/bcm2711.dtsi | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/arch/arm/boot/dts/bcm2711.dtsi ++++ b/arch/arm/boot/dts/bcm2711.dtsi +@@ -358,7 +358,7 @@ + compatible = "brcm,bcm2711-hdmi-i2c"; + reg = <0x7ef04500 0x100>, <0x7ef00b00 0x300>; + reg-names = "bsc", "auto-i2c"; +- clock-frequency = <390000>; ++ clock-frequency = <97500>; + status = "disabled"; + }; + +@@ -395,7 +395,7 @@ + compatible = "brcm,bcm2711-hdmi-i2c"; + reg = <0x7ef09500 0x100>, <0x7ef05b00 0x300>; + reg-names = "bsc", "auto-i2c"; +- clock-frequency = <390000>; ++ clock-frequency = <97500>; + status = "disabled"; + }; + }; diff --git a/target/linux/bcm27xx/patches-5.4/950-0622-overlays-Add-missing-rpi-poe-parameters.patch b/target/linux/bcm27xx/patches-5.4/950-0622-overlays-Add-missing-rpi-poe-parameters.patch new file mode 100644 index 0000000000..716678f078 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0622-overlays-Add-missing-rpi-poe-parameters.patch @@ -0,0 +1,39 @@ +From bb766b0401a49f4a824dd116b9befe8542fe3cd6 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Fri, 27 Mar 2020 13:49:25 +0000 +Subject: [PATCH] overlays: Add missing rpi-poe parameters + +The rpi-poe fan overlay has gained two more fan speeds and adjusted +the thresholds and hystereses. + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/overlays/README | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -2037,12 +2037,20 @@ Name: rpi-poe + Info: Raspberry Pi PoE HAT fan + Load: dtoverlay=rpi-poe,<param>[=<val>] + Params: poe_fan_temp0 Temperature (in millicelcius) at which the fan +- turns on (default 50000) ++ turns on (default 40000) + poe_fan_temp0_hyst Temperature delta (in millicelcius) at which +- the fan turns off (default 5000) ++ the fan turns off (default 2000) + poe_fan_temp1 Temperature (in millicelcius) at which the fan +- speeds up (default 55000) ++ speeds up (default 45000) + poe_fan_temp1_hyst Temperature delta (in millicelcius) at which ++ the fan slows down (default 2000) ++ poe_fan_temp2 Temperature (in millicelcius) at which the fan ++ speeds up (default 50000) ++ poe_fan_temp2_hyst Temperature delta (in millicelcius) at which ++ the fan slows down (default 2000) ++ poe_fan_temp3 Temperature (in millicelcius) at which the fan ++ speeds up (default 55000) ++ poe_fan_temp3_hyst Temperature delta (in millicelcius) at which + the fan slows down (default 5000) + + diff --git a/target/linux/bcm27xx/patches-5.4/950-0623-vc4_hdmi_phy-Fix-offset-calculation.patch b/target/linux/bcm27xx/patches-5.4/950-0623-vc4_hdmi_phy-Fix-offset-calculation.patch new file mode 100644 index 0000000000..83d8d25e29 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0623-vc4_hdmi_phy-Fix-offset-calculation.patch @@ -0,0 +1,30 @@ +From 9da2d153eb010d3e92083c322e87de9ae066d93e Mon Sep 17 00:00:00 2001 +From: popcornmix <popcornmix@gmail.com> +Date: Thu, 2 Apr 2020 16:46:31 +0100 +Subject: [PATCH] vc4_hdmi_phy: Fix offset calculation + +The original firmware code worked with float and did + offset = ((vco_freq / fref * 2) * (1 << 22)); + offset >>= 2; + +In this code it's all integer so doing the integer divide before the shift loses lots of precision + +This fixes the issue of 1080p59.94 mode having 59.64 fps + +Signed-off-by: popcornmix <popcornmix@gmail.com> +--- + drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c +@@ -192,8 +192,8 @@ static u32 phy_get_rm_offset(unsigned lo + + /* RM offset is stored as 9.22 format */ + offset = vco_freq * 2; +- do_div(offset, fref); + offset = offset << 22; ++ do_div(offset, fref); + offset >>= 2; + + return offset; diff --git a/target/linux/bcm27xx/patches-5.4/950-0624-overlays-Add-overlay_map.patch b/target/linux/bcm27xx/patches-5.4/950-0624-overlays-Add-overlay_map.patch new file mode 100644 index 0000000000..20f548328c --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0624-overlays-Add-overlay_map.patch @@ -0,0 +1,101 @@ +From b9b7a84463d95fd912406e377a58af8b1fb81d21 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Wed, 1 Apr 2020 15:09:42 +0100 +Subject: [PATCH] overlays: Add overlay_map + +The overlay map permits platform-specific overlays, with deprecation +and renaming. + +See: https://github.com/raspberrypi/linux/issues/3520 + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/overlays/Makefile | 2 + + arch/arm/boot/dts/overlays/overlay_map.dts | 71 ++++++++++++++++++++++ + 2 files changed, 73 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/overlay_map.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -1,5 +1,7 @@ + # Overlays for the Raspberry Pi platform + ++dtb-$(CONFIG_ARCH_BCM2835) += overlay_map.dtb ++ + dtbo-$(CONFIG_ARCH_BCM2835) += \ + act-led.dtbo \ + adau1977-adc.dtbo \ +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/overlay_map.dts +@@ -0,0 +1,71 @@ ++/dts-v1/; ++ ++/ { ++ i2c3 { ++ bcm2711; ++ }; ++ ++ i2c4 { ++ bcm2711; ++ }; ++ ++ i2c5 { ++ bcm2711; ++ }; ++ ++ i2c6 { ++ bcm2711; ++ }; ++ ++ rpivid-v4l2 { ++ bcm2711; ++ }; ++ ++ spi3-1cs { ++ bcm2711; ++ }; ++ ++ spi3-2cs { ++ bcm2711; ++ }; ++ ++ spi4-1cs { ++ bcm2711; ++ }; ++ ++ spi4-2cs { ++ bcm2711; ++ }; ++ ++ spi5-1cs { ++ bcm2711; ++ }; ++ ++ spi5-2cs { ++ bcm2711; ++ }; ++ ++ spi6-1cs { ++ bcm2711; ++ }; ++ ++ spi6-2cs { ++ bcm2711; ++ }; ++ ++ uart2 { ++ bcm2711; ++ }; ++ ++ uart3 { ++ bcm2711; ++ }; ++ ++ uart4 { ++ bcm2711; ++ }; ++ ++ uart5 { ++ bcm2711; ++ }; ++}; diff --git a/target/linux/bcm27xx/patches-5.4/950-0625-overlays-Formally-rename-deprecate-old-overlays.patch b/target/linux/bcm27xx/patches-5.4/950-0625-overlays-Formally-rename-deprecate-old-overlays.patch new file mode 100644 index 0000000000..edcb3792af --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0625-overlays-Formally-rename-deprecate-old-overlays.patch @@ -0,0 +1,226 @@ +From c9d7d2eb73f2c6024e3f94765fc830bce0203f2b Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Wed, 1 Apr 2020 17:24:15 +0100 +Subject: [PATCH] overlays: Formally rename/deprecate old overlays + +Take advantage of the overlay_map to rename or deprecate some obsolete +overlays. + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/overlays/Makefile | 7 ---- + arch/arm/boot/dts/overlays/README | 12 +----- + .../overlays/bmp085_i2c-sensor-overlay.dts | 23 ----------- + .../dts/overlays/i2c0-bcm2708-overlay.dts | 14 ------- + .../dts/overlays/i2c1-bcm2708-overlay.dts | 9 ----- + arch/arm/boot/dts/overlays/overlay_map.dts | 40 +++++++++++++++++++ + .../boot/dts/overlays/pi3-act-led-overlay.dts | 1 - + .../dts/overlays/pi3-disable-bt-overlay.dts | 1 - + .../dts/overlays/pi3-disable-wifi-overlay.dts | 1 - + .../dts/overlays/pi3-miniuart-bt-overlay.dts | 1 - + 10 files changed, 42 insertions(+), 67 deletions(-) + delete mode 100644 arch/arm/boot/dts/overlays/bmp085_i2c-sensor-overlay.dts + delete mode 100644 arch/arm/boot/dts/overlays/i2c0-bcm2708-overlay.dts + delete mode 100644 arch/arm/boot/dts/overlays/i2c1-bcm2708-overlay.dts + delete mode 100644 arch/arm/boot/dts/overlays/pi3-act-led-overlay.dts + delete mode 100644 arch/arm/boot/dts/overlays/pi3-disable-bt-overlay.dts + delete mode 100644 arch/arm/boot/dts/overlays/pi3-disable-wifi-overlay.dts + delete mode 100644 arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -27,7 +27,6 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + audiosense-pi.dtbo \ + audremap.dtbo \ + balena-fin.dtbo \ +- bmp085_i2c-sensor.dtbo \ + dht11.dtbo \ + dionaudio-loco.dtbo \ + dionaudio-loco-v2.dtbo \ +@@ -75,9 +74,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + i2c-rtc-gpio.dtbo \ + i2c-sensor.dtbo \ + i2c0.dtbo \ +- i2c0-bcm2708.dtbo \ + i2c1.dtbo \ +- i2c1-bcm2708.dtbo \ + i2c3.dtbo \ + i2c4.dtbo \ + i2c5.dtbo \ +@@ -114,10 +111,6 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + mz61581.dtbo \ + ov5647.dtbo \ + papirus.dtbo \ +- pi3-act-led.dtbo \ +- pi3-disable-bt.dtbo \ +- pi3-disable-wifi.dtbo \ +- pi3-miniuart-bt.dtbo \ + pibell.dtbo \ + piglow.dtbo \ + piscreen.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -1288,11 +1288,8 @@ Params: pins_0_1 Use pins + + + Name: i2c0-bcm2708 +-Info: Deprecated, legacy version of i2c0, from which it inherits its +- parameters, just adding the explicit individual pin specifiers. ++Info: Deprecated, legacy version of i2c0. + Load: <Deprecated> +-Params: sda0_pin GPIO pin for SDA0 (deprecated - use pins_*) +- scl0_pin GPIO pin for SCL0 (deprecated - use pins_*) + + + Name: i2c1 +@@ -1307,13 +1304,8 @@ Params: pins_2_3 Use pins + + + Name: i2c1-bcm2708 +-Info: Deprecated, legacy version of i2c1, from which it inherits its +- parameters, just adding the explicit individual pin specifiers. ++Info: Deprecated, legacy version of i2c1. + Load: <Deprecated> +-Params: sda1_pin GPIO pin for SDA1 (2 or 44 - default 2) +- scl1_pin GPIO pin for SCL1 (3 or 45 - default 3) +- pin_func Alternative pin function (4 (alt0), 6 (alt2) - +- default 4) + + + Name: i2c3 +--- a/arch/arm/boot/dts/overlays/bmp085_i2c-sensor-overlay.dts ++++ /dev/null +@@ -1,23 +0,0 @@ +-// Definitions for BMP085/BMP180 digital barometric pressure and temperature sensors from Bosch Sensortec +-/dts-v1/; +-/plugin/; +- +-/ { +- compatible = "brcm,bcm2835"; +- +- fragment@0 { +- target = <&i2c_arm>; +- __overlay__ { +- #address-cells = <1>; +- #size-cells = <0>; +- status = "okay"; +- +- bmp085@77 { +- compatible = "bosch,bmp085"; +- reg = <0x77>; +- default-oversampling = <3>; +- status = "okay"; +- }; +- }; +- }; +-}; +--- a/arch/arm/boot/dts/overlays/i2c0-bcm2708-overlay.dts ++++ /dev/null +@@ -1,14 +0,0 @@ +-#include "i2c0-overlay.dts" +- +-/{ +- __overrides__ { +- sda0_pin = <&pins1>,"brcm,pins:0", +- <&pins2>,"brcm,pins:0", +- <&pins3>,"brcm,pins:0", +- <&pins4>,"brcm,pins:0"; +- scl0_pin = <&pins1>,"brcm,pins:4", +- <&pins2>,"brcm,pins:4", +- <&pins3>,"brcm,pins:4", +- <&pins4>,"brcm,pins:4"; +- }; +-}; +--- a/arch/arm/boot/dts/overlays/i2c1-bcm2708-overlay.dts ++++ /dev/null +@@ -1,9 +0,0 @@ +-#include "i2c1-overlay.dts" +- +-/{ +- __overrides__ { +- sda1_pin = <&pins1>,"brcm,pins:0", <&pins2>,"brcm,pins:0"; +- scl1_pin = <&pins1>,"brcm,pins:4", <&pins1>,"brcm,pins:4"; +- pin_func = <&pins1>,"brcm,function:0", <&pins2>,"brcm,function:0"; +- }; +-}; +--- a/arch/arm/boot/dts/overlays/overlay_map.dts ++++ b/arch/arm/boot/dts/overlays/overlay_map.dts +@@ -1,6 +1,18 @@ + /dts-v1/; + + / { ++ bmp085_i2c-sensor { ++ deprecated = "use i2c-sensor,bmp085"; ++ }; ++ ++ i2c0-bcm2708 { ++ deprecated = "use i2c0"; ++ }; ++ ++ i2c1-bcm2708 { ++ deprecated = "use i2c1"; ++ }; ++ + i2c3 { + bcm2711; + }; +@@ -17,10 +29,34 @@ + bcm2711; + }; + ++ lirc-rpi { ++ deprecated = "use gpio-ir"; ++ }; ++ ++ pi3-act-led { ++ renamed = "act-led"; ++ }; ++ ++ pi3-disable-bt { ++ renamed = "disable-bt"; ++ }; ++ ++ pi3-disable-wifi { ++ renamed = "disable-wifi"; ++ }; ++ ++ pi3-miniuart-bt { ++ renamed = "miniuart-bt"; ++ }; ++ + rpivid-v4l2 { + bcm2711; + }; + ++ sdio-1bit { ++ deprecated = "use sdio,bus_width=1,gpios_22_25"; ++ }; ++ + spi3-1cs { + bcm2711; + }; +@@ -68,4 +104,8 @@ + uart5 { + bcm2711; + }; ++ ++ upstream-aux-interrupt { ++ deprecated = "no longer necessary"; ++ }; + }; +--- a/arch/arm/boot/dts/overlays/pi3-act-led-overlay.dts ++++ /dev/null +@@ -1 +0,0 @@ +-#include "act-led-overlay.dts" +--- a/arch/arm/boot/dts/overlays/pi3-disable-bt-overlay.dts ++++ /dev/null +@@ -1 +0,0 @@ +-#include "disable-bt-overlay.dts" +--- a/arch/arm/boot/dts/overlays/pi3-disable-wifi-overlay.dts ++++ /dev/null +@@ -1 +0,0 @@ +-#include "disable-wifi-overlay.dts" +--- a/arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts ++++ /dev/null +@@ -1 +0,0 @@ +-#include "miniuart-bt-overlay.dts" diff --git a/target/linux/bcm27xx/patches-5.4/950-0626-overlays-Add-vc4-kms-v3d-pi4-to-overlay_map.patch b/target/linux/bcm27xx/patches-5.4/950-0626-overlays-Add-vc4-kms-v3d-pi4-to-overlay_map.patch new file mode 100644 index 0000000000..4fd5879ec8 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0626-overlays-Add-vc4-kms-v3d-pi4-to-overlay_map.patch @@ -0,0 +1,26 @@ +From ed5f6f5d1077e849c0762595069e79a7749951bf Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Wed, 1 Apr 2020 15:51:56 +0100 +Subject: [PATCH] overlays: Add vc4-kms-v3d-pi4 to overlay_map + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/overlays/overlay_map.dts | 9 +++++++++ + 1 file changed, 9 insertions(+) + +--- a/arch/arm/boot/dts/overlays/overlay_map.dts ++++ b/arch/arm/boot/dts/overlays/overlay_map.dts +@@ -108,4 +108,13 @@ + upstream-aux-interrupt { + deprecated = "no longer necessary"; + }; ++ ++ vc4-kms-v3d { ++ bcm2835; ++ bcm2711 = "vc4-kms-v3d-pi4"; ++ }; ++ ++ vc4-kms-v3d-pi4 { ++ bcm2711; ++ }; + }; diff --git a/target/linux/bcm27xx/patches-5.4/950-0627-Add-upstream-and-upstream-pi4-to-overlay_map.patch b/target/linux/bcm27xx/patches-5.4/950-0627-Add-upstream-and-upstream-pi4-to-overlay_map.patch new file mode 100644 index 0000000000..e76367b9da --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0627-Add-upstream-and-upstream-pi4-to-overlay_map.patch @@ -0,0 +1,229 @@ +From 0a65f76d99bce7685e57ae506eedc499c551ac83 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Mon, 6 Apr 2020 09:47:42 +0100 +Subject: [PATCH] Add upstream and upstream-pi4 to overlay_map + +Because the upstream overlay applies vc4-kms-v3d, of which Pi 4 has its +own version, there also needs to be a Pi 4 version - vc4-kms-v3d-pi4. + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 7 + + arch/arm/boot/dts/overlays/overlay_map.dts | 9 + + .../dts/overlays/upstream-pi4-overlay.dts | 161 ++++++++++++++++++ + 4 files changed, 178 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/upstream-pi4-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -183,6 +183,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + uart5.dtbo \ + udrc.dtbo \ + upstream.dtbo \ ++ upstream-pi4.dtbo \ + vc4-fkms-v3d.dtbo \ + vc4-kms-kippah-7inch.dtbo \ + vc4-kms-v3d.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -2653,6 +2653,13 @@ Info: This overlay has been deprecated + Load: <Deprecated> + + ++Name: upstream-pi4 ++Info: Allow usage of downstream .dtb with upstream kernel on Pi 4. Comprises ++ the vc4-kms-v3d-pi4 and dwc2 overlays. ++Load: dtoverlay=upstream-pi4 ++Params: <None> ++ ++ + Name: vc4-fkms-v3d + Info: Enable Eric Anholt's DRM VC4 V3D driver on top of the dispmanx + display stack. +--- a/arch/arm/boot/dts/overlays/overlay_map.dts ++++ b/arch/arm/boot/dts/overlays/overlay_map.dts +@@ -105,10 +105,19 @@ + bcm2711; + }; + ++ upstream { ++ bcm2835; ++ bcm2711 = "upstream-pi4"; ++ }; ++ + upstream-aux-interrupt { + deprecated = "no longer necessary"; + }; + ++ upstream-pi4 { ++ bcm2711; ++ }; ++ + vc4-kms-v3d { + bcm2835; + bcm2711 = "vc4-kms-v3d-pi4"; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/upstream-pi4-overlay.dts +@@ -0,0 +1,161 @@ ++// redo: ovmerge -c vc4-kms-v3d-pi4-overlay.dts,cma-96 dwc2-overlay.dts,dr_mode=otg ++ ++/dts-v1/; ++/plugin/; ++ ++#include <dt-bindings/clock/bcm2835.h> ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ fragment@0 { ++ target-path = "/chosen"; ++ __dormant__ { ++ bootargs = "cma=256M"; ++ }; ++ }; ++ fragment@1 { ++ target-path = "/chosen"; ++ __dormant__ { ++ bootargs = "cma=192M"; ++ }; ++ }; ++ fragment@2 { ++ target-path = "/chosen"; ++ __dormant__ { ++ bootargs = "cma=128M"; ++ }; ++ }; ++ fragment@3 { ++ target-path = "/chosen"; ++ __overlay__ { ++ bootargs = "cma=96M"; ++ }; ++ }; ++ fragment@4 { ++ target-path = "/chosen"; ++ __dormant__ { ++ bootargs = "cma=64M"; ++ }; ++ }; ++ fragment@5 { ++ target = <&ddc0>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ fragment@6 { ++ target = <&ddc1>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ fragment@7 { ++ target = <&hdmi0>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ fragment@8 { ++ target = <&hdmi1>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ fragment@9 { ++ target = <&hvs>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ fragment@10 { ++ target = <&pixelvalve0>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ fragment@11 { ++ target = <&pixelvalve1>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ fragment@12 { ++ target = <&pixelvalve2>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ fragment@13 { ++ target = <&pixelvalve3>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ fragment@14 { ++ target = <&pixelvalve4>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ fragment@15 { ++ target = <&v3d>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ fragment@16 { ++ target = <&vc4>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ fragment@17 { ++ target = <&txp>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ fragment@18 { ++ target = <&fb>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ fragment@19 { ++ target = <&firmwarekms>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ fragment@20 { ++ target = <&vec>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ fragment@21 { ++ target = <&hdmi0>; ++ __dormant__ { ++ dmas; ++ }; ++ }; ++ fragment@22 { ++ target = <&hdmi1>; ++ __dormant__ { ++ dmas; ++ }; ++ }; ++ fragment@23 { ++ target = <&usb>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ __overlay__ { ++ compatible = "brcm,bcm2835-usb"; ++ dr_mode = "otg"; ++ g-np-tx-fifo-size = <32>; ++ g-rx-fifo-size = <558>; ++ g-tx-fifo-size = <512 512 512 512 512 256 256>; ++ status = "okay"; ++ }; ++ }; ++}; diff --git a/target/linux/bcm27xx/patches-5.4/950-0353-clk-raspberrypi-Allow-cpufreq-driver-to-also-adjust-.patch b/target/linux/bcm27xx/patches-5.4/950-0628-clk-raspberrypi-Allow-cpufreq-driver-to-also-adjust-.patch index 31978c761a..c0422f4ed3 100644 --- a/target/linux/bcm27xx/patches-5.4/950-0353-clk-raspberrypi-Allow-cpufreq-driver-to-also-adjust-.patch +++ b/target/linux/bcm27xx/patches-5.4/950-0628-clk-raspberrypi-Allow-cpufreq-driver-to-also-adjust-.patch @@ -1,4 +1,4 @@ -From 3e2eb77ba8d0c6913138382512309e7892907a1c Mon Sep 17 00:00:00 2001 +From a2e39f36678626f5d7883c5a1dc8c476134c5e0b Mon Sep 17 00:00:00 2001 From: popcornmix <popcornmix@gmail.com> Date: Mon, 9 Sep 2019 15:49:56 +0100 Subject: [PATCH] clk-raspberrypi: Allow cpufreq driver to also adjust @@ -14,9 +14,9 @@ Signed-off-by: popcornmix <popcornmix@gmail.com> --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c -@@ -70,7 +70,7 @@ static int raspberrypi_clock_property(st +@@ -76,7 +76,7 @@ static int raspberrypi_clock_property(st struct raspberrypi_firmware_prop msg = { - .id = cpu_to_le32(clk), + .id = cpu_to_le32(data->id), .val = cpu_to_le32(*val), - .disable_turbo = cpu_to_le32(1), + .disable_turbo = cpu_to_le32(0), diff --git a/target/linux/bcm27xx/patches-5.4/950-0629-Add-support-for-the-AudioInjector.net-Isolated-sound.patch b/target/linux/bcm27xx/patches-5.4/950-0629-Add-support-for-the-AudioInjector.net-Isolated-sound.patch new file mode 100644 index 0000000000..e35806962e --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0629-Add-support-for-the-AudioInjector.net-Isolated-sound.patch @@ -0,0 +1,323 @@ +From 5b37b08ff1c29e7386eb8a29b168e94e33cf82c3 Mon Sep 17 00:00:00 2001 +From: Matt Flax <flatmax@flatmax.org> +Date: Wed, 8 Apr 2020 20:00:30 +1000 +Subject: [PATCH] Add support for the AudioInjector.net Isolated sound + card + +This patch adds support for the Audio Injector Isolated sound card. + +Signed-off-by: Matt Flax <flatmax@flatmax.org> +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 6 + + ...dioinjector-isolated-soundcard-overlay.dts | 55 ++++++ + sound/soc/bcm/Kconfig | 7 + + sound/soc/bcm/Makefile | 2 + + .../bcm/audioinjector-isolated-soundcard.c | 183 ++++++++++++++++++ + 11 files changed, 259 insertions(+), 5 deletions(-) + create mode 100644 arch/arm/boot/dts/overlays/audioinjector-isolated-soundcard-overlay.dts + create mode 100644 sound/soc/bcm/audioinjector-isolated-soundcard.c + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -22,6 +22,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + applepi-dac.dtbo \ + at86rf233.dtbo \ + audioinjector-addons.dtbo \ ++ audioinjector-isolated-soundcard.dtbo \ + audioinjector-ultra.dtbo \ + audioinjector-wm8731-audio.dtbo \ + audiosense-pi.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -505,6 +505,12 @@ Params: non-stop-clocks Keeps th + is paused or stopped (default off) + + ++Name: audioinjector-isolated-soundcard ++Info: Configures the audioinjector.net isolated soundcard ++Load: dtoverlay=audioinjector-isolated-soundcard ++Params: <None> ++ ++ + Name: audioinjector-ultra + Info: Configures the audioinjector.net ultra soundcard + Load: dtoverlay=audioinjector-ultra +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/audioinjector-isolated-soundcard-overlay.dts +@@ -0,0 +1,55 @@ ++// Definitions for audioinjector.net audio isolated soundcard ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target = <&i2s>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@1 { ++ target-path = "/"; ++ __overlay__ { ++ cs4272_mclk: codec-mclk { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24576000>; ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&i2c1>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ cs4272: cs4271@10 { ++ #sound-dai-cells = <0>; ++ compatible = "cirrus,cs4271"; ++ reg = <0x10>; ++ reset-gpio = <&gpio 5 0>; ++ clocks = <&cs4272_mclk>; ++ clock-names = "mclk"; ++ status = "okay"; ++ }; ++ }; ++ }; ++ ++ fragment@3 { ++ target = <&sound>; ++ snd: __overlay__ { ++ compatible = "ai,audioinjector-isolated-soundcard"; ++ mute-gpios = <&gpio 17 0>; ++ i2s-controller = <&i2s>; ++ codec = <&cs4272>; ++ status = "okay"; ++ }; ++ }; ++}; +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -192,6 +192,13 @@ config SND_AUDIOINJECTOR_OCTO_SOUNDCARD + help + Say Y or M if you want to add support for audioinjector.net octo add on + ++config SND_AUDIOINJECTOR_ISOLATED_SOUNDCARD ++ tristate "Support for audioinjector.net isolated DAC and ADC soundcard" ++ depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S ++ select SND_SOC_CS4271_I2C ++ help ++ Say Y or M if you want to add support for audioinjector.net isolated soundcard ++ + config SND_AUDIOSENSE_PI + tristate "Support for AudioSense Add-On Soundcard" + depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -27,6 +27,7 @@ snd-soc-iqaudio-dac-objs := iqaudio-dac. + snd-soc-i-sabre-q2m-objs := i-sabre-q2m.o + snd-soc-audioinjector-pi-soundcard-objs := audioinjector-pi-soundcard.o + snd-soc-audioinjector-octo-soundcard-objs := audioinjector-octo-soundcard.o ++snd-soc-audioinjector-isolated-soundcard-objs := audioinjector-isolated-soundcard.o + snd-soc-audiosense-pi-objs := audiosense-pi.o + snd-soc-digidac1-soundcard-objs := digidac1-soundcard.o + snd-soc-dionaudio-loco-objs := dionaudio_loco.o +@@ -55,6 +56,7 @@ obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC + obj-$(CONFIG_SND_BCM2708_SOC_I_SABRE_Q2M) += snd-soc-i-sabre-q2m.o + obj-$(CONFIG_SND_AUDIOINJECTOR_PI_SOUNDCARD) += snd-soc-audioinjector-pi-soundcard.o + obj-$(CONFIG_SND_AUDIOINJECTOR_OCTO_SOUNDCARD) += snd-soc-audioinjector-octo-soundcard.o ++obj-$(CONFIG_SND_AUDIOINJECTOR_ISOLATED_SOUNDCARD) += snd-soc-audioinjector-isolated-soundcard.o + obj-$(CONFIG_SND_AUDIOSENSE_PI) += snd-soc-audiosense-pi.o + obj-$(CONFIG_SND_DIGIDAC1_SOUNDCARD) += snd-soc-digidac1-soundcard.o + obj-$(CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO) += snd-soc-dionaudio-loco.o +--- /dev/null ++++ b/sound/soc/bcm/audioinjector-isolated-soundcard.c +@@ -0,0 +1,183 @@ ++/* ++ * ASoC Driver for AudioInjector.net isolated soundcard ++ * ++ * Created on: 20-February-2020 ++ * Author: flatmax@flatmax.org ++ * based on audioinjector-octo-soundcard.c ++ * ++ * Copyright (C) 2020 Flatmax Pty. Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ */ ++ ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/gpio/consumer.h> ++ ++#include <sound/core.h> ++#include <sound/soc.h> ++#include <sound/pcm_params.h> ++#include <sound/control.h> ++ ++static struct gpio_desc *mute_gpio; ++ ++static const unsigned int audioinjector_isolated_rates[] = { ++ 192000, 96000, 48000, 32000, 24000, 16000, 8000 ++}; ++ ++static struct snd_pcm_hw_constraint_list audioinjector_isolated_constraints = { ++ .list = audioinjector_isolated_rates, ++ .count = ARRAY_SIZE(audioinjector_isolated_rates), ++}; ++ ++static int audioinjector_isolated_dai_init(struct snd_soc_pcm_runtime *rtd) ++{ ++ int ret=snd_soc_dai_set_sysclk(rtd->codec_dai, 0, 24576000, 0); ++ if (ret) ++ return ret; ++ ++ return snd_soc_dai_set_bclk_ratio(rtd->cpu_dai, 64); ++} ++ ++static int audioinjector_isolated_startup(struct snd_pcm_substream *substream) ++{ ++ snd_pcm_hw_constraint_list(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_RATE, &audioinjector_isolated_constraints); ++ ++ return 0; ++} ++ ++static int audioinjector_isolated_trigger(struct snd_pcm_substream *substream, ++ int cmd){ ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ gpiod_set_value(mute_gpio, 0); ++ break; ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ gpiod_set_value(mute_gpio, 1); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static struct snd_soc_ops audioinjector_isolated_ops = { ++ .startup = audioinjector_isolated_startup, ++ .trigger = audioinjector_isolated_trigger, ++}; ++ ++SND_SOC_DAILINK_DEFS(audioinjector_isolated, ++ DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")), ++ DAILINK_COMP_ARRAY(COMP_CODEC("cs4271.1-0010", "cs4271-hifi")), ++ DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0"))); ++ ++static struct snd_soc_dai_link audioinjector_isolated_dai[] = { ++ { ++ .name = "AudioInjector ISO", ++ .stream_name = "AI-HIFI", ++ .ops = &audioinjector_isolated_ops, ++ .init = audioinjector_isolated_dai_init, ++ .symmetric_rates = 1, ++ .symmetric_channels = 1, ++ .dai_fmt = SND_SOC_DAIFMT_CBM_CFM|SND_SOC_DAIFMT_I2S|SND_SOC_DAIFMT_NB_NF, ++ SND_SOC_DAILINK_REG(audioinjector_isolated), ++ } ++}; ++ ++static const struct snd_soc_dapm_widget audioinjector_isolated_widgets[] = { ++ SND_SOC_DAPM_OUTPUT("OUTPUTS"), ++ SND_SOC_DAPM_INPUT("INPUTS"), ++}; ++ ++static const struct snd_soc_dapm_route audioinjector_isolated_route[] = { ++ /* Balanced outputs */ ++ {"OUTPUTS", NULL, "AOUTA+"}, ++ {"OUTPUTS", NULL, "AOUTA-"}, ++ {"OUTPUTS", NULL, "AOUTB+"}, ++ {"OUTPUTS", NULL, "AOUTB-"}, ++ ++ /* Balanced inputs */ ++ {"AINA", NULL, "INPUTS"}, ++ {"AINB", NULL, "INPUTS"}, ++}; ++ ++static struct snd_soc_card snd_soc_audioinjector_isolated = { ++ .name = "audioinjector-isolated-soundcard", ++ .dai_link = audioinjector_isolated_dai, ++ .num_links = ARRAY_SIZE(audioinjector_isolated_dai), ++ ++ .dapm_widgets = audioinjector_isolated_widgets, ++ .num_dapm_widgets = ARRAY_SIZE(audioinjector_isolated_widgets), ++ .dapm_routes = audioinjector_isolated_route, ++ .num_dapm_routes = ARRAY_SIZE(audioinjector_isolated_route), ++}; ++ ++static int audioinjector_isolated_probe(struct platform_device *pdev) ++{ ++ struct snd_soc_card *card = &snd_soc_audioinjector_isolated; ++ int ret; ++ ++ card->dev = &pdev->dev; ++ ++ if (pdev->dev.of_node) { ++ struct snd_soc_dai_link *dai = &audioinjector_isolated_dai[0]; ++ struct device_node *i2s_node = ++ of_parse_phandle(pdev->dev.of_node, "i2s-controller", 0); ++ ++ if (i2s_node) { ++ dai->cpus->dai_name = NULL; ++ dai->cpus->of_node = i2s_node; ++ dai->platforms->name = NULL; ++ dai->platforms->of_node = i2s_node; ++ } else { ++ dev_err(&pdev->dev, ++ "i2s-controller missing or invalid in DT\n"); ++ return -EINVAL; ++ } ++ ++ mute_gpio = devm_gpiod_get_optional(&pdev->dev, "mute", GPIOD_OUT_LOW); ++ if (IS_ERR(mute_gpio)){ ++ dev_err(&pdev->dev, "mute gpio not found in dt overlay\n"); ++ return PTR_ERR(mute_gpio); ++ } ++ } ++ ++ ret = devm_snd_soc_register_card(&pdev->dev, card); ++ if (ret && ret != -EPROBE_DEFER) ++ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); ++ return ret; ++} ++ ++static const struct of_device_id audioinjector_isolated_of_match[] = { ++ { .compatible = "ai,audioinjector-isolated-soundcard", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, audioinjector_isolated_of_match); ++ ++static struct platform_driver audioinjector_isolated_driver = { ++ .driver = { ++ .name = "audioinjector-isolated", ++ .owner = THIS_MODULE, ++ .of_match_table = audioinjector_isolated_of_match, ++ }, ++ .probe = audioinjector_isolated_probe, ++}; ++ ++module_platform_driver(audioinjector_isolated_driver); ++MODULE_AUTHOR("Matt Flax <flatmax@flatmax.org>"); ++MODULE_DESCRIPTION("AudioInjector.net isolated Soundcard"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:audioinjector-isolated-soundcard"); diff --git a/target/linux/bcm27xx/patches-5.4/950-0630-overlays-Fix-dtc-warnings-in-i2c-gpio.patch b/target/linux/bcm27xx/patches-5.4/950-0630-overlays-Fix-dtc-warnings-in-i2c-gpio.patch new file mode 100644 index 0000000000..2da57b81de --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0630-overlays-Fix-dtc-warnings-in-i2c-gpio.patch @@ -0,0 +1,24 @@ +From 1231481bdb45114abe7b0348c78a943642fde717 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Wed, 8 Apr 2020 11:59:39 +0100 +Subject: [PATCH] overlays: Fix dtc warnings in i2c-gpio + +Better late than never. + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts +@@ -9,6 +9,9 @@ + target-path = "/"; + + __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ + i2c_gpio: i2c@0 { + reg = <0xffffffff>; + compatible = "i2c-gpio"; diff --git a/target/linux/bcm27xx/patches-5.4/950-0631-kbuild-Disable-gcc-plugins.patch b/target/linux/bcm27xx/patches-5.4/950-0631-kbuild-Disable-gcc-plugins.patch new file mode 100644 index 0000000000..66091ca591 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0631-kbuild-Disable-gcc-plugins.patch @@ -0,0 +1,28 @@ +From 31b68a380e7649f0cbc7209c465bf747c072a7ce Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Wed, 8 Apr 2020 15:23:56 +0100 +Subject: [PATCH] kbuild: Disable gcc plugins + +The GCC plugin feature leads to different kernel configurations on what +ought to be equivalent build systems because they depend on the build +hosts native compilers rather than the cross compilers needed for the +target. This causes problems with module symbol version mismatches. + +Disable GCC plugins for all build hosts. + +Advanced build script hackery borrowed from a patch by milhouse. + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + scripts/gcc-plugin.sh | 1 + + 1 file changed, 1 insertion(+) + +--- a/scripts/gcc-plugin.sh ++++ b/scripts/gcc-plugin.sh +@@ -1,5 +1,6 @@ + #!/bin/sh + # SPDX-License-Identifier: GPL-2.0 ++exit 0 # Disable plugins + srctree=$(dirname "$0") + + SHOW_ERROR= diff --git a/target/linux/bcm27xx/patches-5.4/950-0632-ASoC-ma120x0p-Add-96KHz-rate-support.patch b/target/linux/bcm27xx/patches-5.4/950-0632-ASoC-ma120x0p-Add-96KHz-rate-support.patch new file mode 100644 index 0000000000..a5aee43aef --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0632-ASoC-ma120x0p-Add-96KHz-rate-support.patch @@ -0,0 +1,42 @@ +From 9554903fc8c15828d8f6cc9bd8c5444433c56cae Mon Sep 17 00:00:00 2001 +From: AMuszkat <ariel.muszkat@gmail.com> +Date: Wed, 8 Apr 2020 10:04:49 +0200 +Subject: [PATCH] ASoC: ma120x0p: Add 96KHz rate support + +Add 96KHz rate support to MA120X0P codec and make enable and mute gpio +pins optional. + +Signed-off-by: AMuszkat <ariel.muszkat@gmail.com> +--- + sound/soc/codecs/ma120x0p.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/sound/soc/codecs/ma120x0p.c ++++ b/sound/soc/codecs/ma120x0p.c +@@ -1002,7 +1002,7 @@ static struct snd_soc_dai_driver ma120x0 + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 44100, +- .rate_max = 48000, ++ .rate_max = 96000, + .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE + }, + .ops = &ma120x0p_dai_ops, +@@ -1235,7 +1235,7 @@ static int ma120x0p_i2c_probe(struct i2c + //Startup sequence + + //Make sure the device is muted +- priv_data->mute_gpio = devm_gpiod_get(&i2c->dev, "mute_gp", ++ priv_data->mute_gpio = devm_gpiod_get_optional(&i2c->dev, "mute_gp", + GPIOD_OUT_LOW); + if (IS_ERR(priv_data->mute_gpio)) { + ret = PTR_ERR(priv_data->mute_gpio); +@@ -1262,7 +1262,7 @@ static int ma120x0p_i2c_probe(struct i2c + msleep(200); + + //Enable ma120x0pp +- priv_data->enable_gpio = devm_gpiod_get(&i2c->dev, ++ priv_data->enable_gpio = devm_gpiod_get_optional(&i2c->dev, + "enable_gp", GPIOD_OUT_LOW); + if (IS_ERR(priv_data->enable_gpio)) { + ret = PTR_ERR(priv_data->enable_gpio); diff --git a/target/linux/bcm27xx/patches-5.4/950-0633-arm64-mm-reserve-CMA-and-crashkernel-in-ZONE_DMA32.patch b/target/linux/bcm27xx/patches-5.4/950-0633-arm64-mm-reserve-CMA-and-crashkernel-in-ZONE_DMA32.patch new file mode 100644 index 0000000000..137a2fa4a0 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0633-arm64-mm-reserve-CMA-and-crashkernel-in-ZONE_DMA32.patch @@ -0,0 +1,44 @@ +From d4cf092a0e923361f521e1bc7d1fbfb1907958b3 Mon Sep 17 00:00:00 2001 +From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Date: Thu, 7 Nov 2019 10:56:11 +0100 +Subject: [PATCH] arm64: mm: reserve CMA and crashkernel in ZONE_DMA32 + +commit bff3b04460a80f425442fe8e5c6ee8c3ebef611f upstream. + +With the introduction of ZONE_DMA in arm64 we moved the default CMA and +crashkernel reservation into that area. This caused a regression on big +machines that need big CMA and crashkernel reservations. Note that +ZONE_DMA is only 1GB big. + +Restore the previous behavior as the wide majority of devices are OK +with reserving these in ZONE_DMA32. The ones that need them in ZONE_DMA +will configure it explicitly. + +Fixes: 1a8e1cef7603 ("arm64: use both ZONE_DMA and ZONE_DMA32") +Reported-by: Qian Cai <cai@lca.pw> +Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> +--- + arch/arm64/mm/init.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/arch/arm64/mm/init.c ++++ b/arch/arm64/mm/init.c +@@ -91,7 +91,7 @@ static void __init reserve_crashkernel(v + + if (crash_base == 0) { + /* Current arm64 boot protocol requires 2MB alignment */ +- crash_base = memblock_find_in_range(0, ARCH_LOW_ADDRESS_LIMIT, ++ crash_base = memblock_find_in_range(0, arm64_dma32_phys_limit, + crash_size, SZ_2M); + if (crash_base == 0) { + pr_warn("cannot allocate crashkernel (size:0x%llx)\n", +@@ -459,7 +459,7 @@ void __init arm64_memblock_init(void) + + high_memory = __va(memblock_end_of_DRAM() - 1) + 1; + +- dma_contiguous_reserve(arm64_dma_phys_limit ? : arm64_dma32_phys_limit); ++ dma_contiguous_reserve(arm64_dma32_phys_limit); + } + + void __init bootmem_init(void) diff --git a/target/linux/bcm27xx/patches-5.4/950-0634-arm64-mm-Fix-initialisation-of-DMA-zones-on-non-NUMA.patch b/target/linux/bcm27xx/patches-5.4/950-0634-arm64-mm-Fix-initialisation-of-DMA-zones-on-non-NUMA.patch new file mode 100644 index 0000000000..654864d52d --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0634-arm64-mm-Fix-initialisation-of-DMA-zones-on-non-NUMA.patch @@ -0,0 +1,105 @@ +From cd2d09e995bc72711b48b5c271b72e3ea3e99cdf Mon Sep 17 00:00:00 2001 +From: Will Deacon <will@kernel.org> +Date: Tue, 3 Dec 2019 12:10:13 +0000 +Subject: [PATCH] arm64: mm: Fix initialisation of DMA zones on + non-NUMA systems + +commit 93b90414c33f59b7960bc8d607da0ce83377e021 upstream. + +John reports that the recently merged commit 1a8e1cef7603 ("arm64: use +both ZONE_DMA and ZONE_DMA32") breaks the boot on his DB845C board: + + | Booting Linux on physical CPU 0x0000000000 [0x517f803c] + | Linux version 5.4.0-mainline-10675-g957a03b9e38f + | Machine model: Thundercomm Dragonboard 845c + | [...] + | Built 1 zonelists, mobility grouping on. Total pages: -188245 + | Kernel command line: earlycon + | firmware_class.path=/vendor/firmware/ androidboot.hardware=db845c + | init=/init androidboot.boot_devices=soc/1d84000.ufshc + | printk.devkmsg=on buildvariant=userdebug root=/dev/sda2 + | androidboot.bootdevice=1d84000.ufshc androidboot.serialno=c4e1189c + | androidboot.baseband=sda + | msm_drm.dsi_display0=dsi_lt9611_1080_video_display: + | androidboot.slot_suffix=_a skip_initramfs rootwait ro init=/init + | + | <hangs indefinitely here> + +This is because, when CONFIG_NUMA=n, zone_sizes_init() fails to handle +memblocks that fall entirely within the ZONE_DMA region and erroneously ends up +trying to add a negatively-sized region into the following ZONE_DMA32, which is +later interpreted as a large unsigned region by the core MM code. + +Rework the non-NUMA implementation of zone_sizes_init() so that the start +address of the memblock being processed is adjusted according to the end of the +previous zone, which is then range-checked before updating the hole information +of subsequent zones. + +Cc: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Cc: Christoph Hellwig <hch@lst.de> +Cc: Bjorn Andersson <bjorn.andersson@linaro.org> +Link: https://lore.kernel.org/lkml/CALAqxLVVcsmFrDKLRGRq7GewcW405yTOxG=KR3csVzQ6bXutkA@mail.gmail.com +Fixes: 1a8e1cef7603 ("arm64: use both ZONE_DMA and ZONE_DMA32") +Reported-by: John Stultz <john.stultz@linaro.org> +Tested-by: John Stultz <john.stultz@linaro.org> +Signed-off-by: Will Deacon <will@kernel.org> +Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> +--- + arch/arm64/mm/init.c | 25 +++++++++++-------------- + 1 file changed, 11 insertions(+), 14 deletions(-) + +--- a/arch/arm64/mm/init.c ++++ b/arch/arm64/mm/init.c +@@ -214,15 +214,14 @@ 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; ++ unsigned long __maybe_unused max_dma, max_dma32; + + memset(zone_size, 0, sizeof(zone_size)); + ++ max_dma = max_dma32 = min; + #ifdef CONFIG_ZONE_DMA +- max_dma = PFN_DOWN(arm64_dma_phys_limit); ++ max_dma = max_dma32 = 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); +@@ -236,25 +235,23 @@ static void __init zone_sizes_init(unsig + unsigned long start = memblock_region_memory_base_pfn(reg); + unsigned long end = memblock_region_memory_end_pfn(reg); + +- if (start >= max) +- continue; + #ifdef CONFIG_ZONE_DMA +- if (start < max_dma) { +- unsigned long dma_end = min_not_zero(end, max_dma); ++ if (start >= min && start < max_dma) { ++ unsigned long dma_end = min(end, max_dma); + zhole_size[ZONE_DMA] -= dma_end - start; ++ start = dma_end; + } + #endif + #ifdef CONFIG_ZONE_DMA32 +- if (start < max_dma32) { ++ if (start >= max_dma && start < max_dma32) { + unsigned long dma32_end = min(end, max_dma32); +- unsigned long dma32_start = max(start, max_dma); +- zhole_size[ZONE_DMA32] -= dma32_end - dma32_start; ++ zhole_size[ZONE_DMA32] -= dma32_end - start; ++ start = dma32_end; + } + #endif +- if (end > max_dma32) { ++ if (start >= max_dma32 && start < max) { + unsigned long normal_end = min(end, max); +- unsigned long normal_start = max(start, max_dma32); +- zhole_size[ZONE_NORMAL] -= normal_end - normal_start; ++ zhole_size[ZONE_NORMAL] -= normal_end - start; + } + } + diff --git a/target/linux/bcm27xx/patches-5.4/950-0635-ARM-dts-bcm283x-Unify-CMA-configuration.patch b/target/linux/bcm27xx/patches-5.4/950-0635-ARM-dts-bcm283x-Unify-CMA-configuration.patch new file mode 100644 index 0000000000..a0dfcb3325 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0635-ARM-dts-bcm283x-Unify-CMA-configuration.patch @@ -0,0 +1,95 @@ +From a2e6d1c03908eccf76b9305c4a493230a36035c0 Mon Sep 17 00:00:00 2001 +From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Date: Fri, 10 Jan 2020 18:29:35 +0100 +Subject: [PATCH] ARM: dts: bcm283x: Unify CMA configuration + +commit c5a1e5375d19bd4001c59dc5d482ac5b1ba51cbf upstream. + +With the introduction of the Raspberry Pi 4 we were forced to explicitly +configure CMA's location, since arm64 defaults it into the ZONE_DMA32 +memory area, which is not good enough to perform DMA operations on that +device. To bypass this limitation a dedicated CMA DT node was created, +explicitly indicating the acceptable memory range and size. + +That said, compatibility between boards is a must on the Raspberry Pi +ecosystem so this creates a common CMA DT node so as for DT overlays to +be able to update CMA's properties regardless of the board being used. + +Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Reviewed-by: Phil Elwell <phil@raspberrypi.org> +Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> +--- + arch/arm/boot/dts/bcm2711.dtsi | 32 +++++++++++++------------------- + arch/arm/boot/dts/bcm283x.dtsi | 13 +++++++++++++ + 2 files changed, 26 insertions(+), 19 deletions(-) + +--- a/arch/arm/boot/dts/bcm2711.dtsi ++++ b/arch/arm/boot/dts/bcm2711.dtsi +@@ -12,25 +12,6 @@ + + interrupt-parent = <&gicv2>; + +- reserved-memory { +- #address-cells = <2>; +- #size-cells = <1>; +- ranges; +- +- /* +- * arm64 reserves the CMA by default somewhere in ZONE_DMA32, +- * that's not good enough for the BCM2711 as some devices can +- * only address the lower 1G of memory (ZONE_DMA). +- */ +- linux,cma { +- compatible = "shared-dma-pool"; +- size = <0x2000000>; /* 32MB */ +- alloc-ranges = <0x0 0x00000000 0x40000000>; +- reusable; +- linux,cma-default; +- }; +- }; +- + vc4: gpu { + compatible = "brcm,bcm2711-vc5"; + status = "disabled"; +@@ -992,6 +973,19 @@ + }; + }; + ++&rmem { ++ #address-cells = <2>; ++}; ++ ++&cma { ++ /* ++ * arm64 reserves the CMA by default somewhere in ZONE_DMA32, ++ * that's not good enough for the BCM2711 as some devices can ++ * only address the lower 1G of memory (ZONE_DMA). ++ */ ++ alloc-ranges = <0x0 0x00000000 0x40000000>; ++}; ++ + &i2c0 { + compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c"; + interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>; +--- a/arch/arm/boot/dts/bcm283x.dtsi ++++ b/arch/arm/boot/dts/bcm283x.dtsi +@@ -30,6 +30,19 @@ + stdout-path = "serial0:115200n8"; + }; + ++ rmem: reserved-memory { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ cma: linux,cma { ++ compatible = "shared-dma-pool"; ++ size = <0x4000000>; /* 64MB */ ++ reusable; ++ linux,cma-default; ++ }; ++ }; ++ + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <0>; diff --git a/target/linux/bcm27xx/patches-5.4/950-0636-dma-contiguous-CMA-give-precedence-to-cmdline.patch b/target/linux/bcm27xx/patches-5.4/950-0636-dma-contiguous-CMA-give-precedence-to-cmdline.patch new file mode 100644 index 0000000000..d3354bb9e1 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0636-dma-contiguous-CMA-give-precedence-to-cmdline.patch @@ -0,0 +1,49 @@ +From cf40e83d2b6fb6857b13df4c8d69cc4c45395ea2 Mon Sep 17 00:00:00 2001 +From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Date: Fri, 10 Jan 2020 18:19:33 +0100 +Subject: [PATCH] dma-contiguous: CMA: give precedence to cmdline + +commit 8c8c5a4994a306c217fd061cbfc5903399fd4c1c upstream. + +Although the device tree might contain a reserved-memory DT node +dedicated as the default CMA pool, users might want to change CMA's +parameters using the kernel command line for debugging purposes and +whatnot. Honor this by bypassing the reserved memory CMA setup, which +will ultimately end up freeing the memblock and allow the command line +CMA configuration routine to run. + +Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Reviewed-by: Phil Elwell <phil@raspberrypi.org> +Signed-off-by: Christoph Hellwig <hch@lst.de> +--- + kernel/dma/contiguous.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +--- a/kernel/dma/contiguous.c ++++ b/kernel/dma/contiguous.c +@@ -301,9 +301,16 @@ static int __init rmem_cma_setup(struct + phys_addr_t align = PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order); + phys_addr_t mask = align - 1; + unsigned long node = rmem->fdt_node; ++ bool default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL); + struct cma *cma; + int err; + ++ if (size_cmdline != -1 && default_cma) { ++ pr_info("Reserved memory: bypass %s node, using cmdline CMA params instead\n", ++ rmem->name); ++ return -EBUSY; ++ } ++ + if (!of_get_flat_dt_prop(node, "reusable", NULL) || + of_get_flat_dt_prop(node, "no-map", NULL)) + return -EINVAL; +@@ -321,7 +328,7 @@ static int __init rmem_cma_setup(struct + /* Architecture specific contiguous memory fixup. */ + dma_contiguous_early_fixup(rmem->base, rmem->size); + +- if (of_get_flat_dt_prop(node, "linux,cma-default", NULL)) ++ if (default_cma) + dma_contiguous_set_default(cma); + + rmem->ops = &rmem_cma_ops; diff --git a/target/linux/bcm27xx/patches-5.4/950-0637-ARM-dts-Use-upstream-CMA-configuration.patch b/target/linux/bcm27xx/patches-5.4/950-0637-ARM-dts-Use-upstream-CMA-configuration.patch new file mode 100644 index 0000000000..fb4fe893c0 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0637-ARM-dts-Use-upstream-CMA-configuration.patch @@ -0,0 +1,36 @@ +From 4d0f0dfc57f6a8652c624575ea34f04bddea629b Mon Sep 17 00:00:00 2001 +From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Date: Thu, 2 Apr 2020 19:22:46 +0200 +Subject: [PATCH] ARM: dts: Use upstream CMA configuration + +Now that the kernel command line has precedence over the device tree, +we can use the upstream CMA setup without breaking backward +compatibility. + +Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +--- + arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts ++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts +@@ -195,7 +195,7 @@ + + / { + chosen { +- bootargs = "coherent_pool=1M 8250.nr_uarts=1 cma=64M"; ++ bootargs = "coherent_pool=1M 8250.nr_uarts=1"; + }; + + aliases { +@@ -215,10 +215,6 @@ + }; + + /delete-node/ wifi-pwrseq; +- +- reserved-memory { +- /delete-node/ linux,cma; +- }; + }; + + &mmcnr { diff --git a/target/linux/bcm27xx/patches-5.4/950-0638-ARM-dts-overlays-Unify-overlay-CMA-handling.patch b/target/linux/bcm27xx/patches-5.4/950-0638-ARM-dts-overlays-Unify-overlay-CMA-handling.patch new file mode 100644 index 0000000000..3c0f1e4388 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0638-ARM-dts-overlays-Unify-overlay-CMA-handling.patch @@ -0,0 +1,871 @@ +From 3cd31a44e61e2219d730d6b1a4a13c8e15d6e395 Mon Sep 17 00:00:00 2001 +From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Date: Thu, 2 Apr 2020 19:54:33 +0200 +Subject: [PATCH] ARM: dts: overlays: Unify overlay CMA handling + +Now that we don't have to abuse the kernel command line to change CMA's +size we can clean-up and centralize CMA usage in overlays. + +A new file, cma-overlay.dts is created to be used as a standalone +overlay or included on other overlays. All CMA users are converted to +this scheme. Ultimately upstream-overlay.dts is also updated to use the +default CMA size provided by upstream. + +Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 19 +++++ + arch/arm/boot/dts/overlays/cma-overlay.dts | 32 ++++++++ + .../boot/dts/overlays/upstream-overlay.dts | 56 ++++--------- + .../dts/overlays/upstream-pi4-overlay.dts | 66 +++++---------- + .../dts/overlays/vc4-fkms-v3d-overlay.dts | 51 ++---------- + .../boot/dts/overlays/vc4-kms-v3d-overlay.dts | 66 ++++----------- + .../dts/overlays/vc4-kms-v3d-pi4-overlay.dts | 80 +++++-------------- + 8 files changed, 129 insertions(+), 242 deletions(-) + create mode 100644 arch/arm/boot/dts/overlays/cma-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -28,6 +28,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + audiosense-pi.dtbo \ + audremap.dtbo \ + balena-fin.dtbo \ ++ cma.dtbo \ + dht11.dtbo \ + dionaudio-loco.dtbo \ + dionaudio-loco-v2.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -554,6 +554,19 @@ Info: This overlay is now deprecated - + Load: <Deprecated> + + ++Name: cma ++Info: Set custom CMA sizes, only use if you know what you are doing, might ++ clash with other overlays like vc4-fkms-v3d and vc4-kms-v3d. ++Load: dtoverlay=cma,<param>=<val> ++Params: cma-256 CMA is 256MB (needs 1GB) ++ cma-192 CMA is 192MB (needs 1GB) ++ cma-128 CMA is 128MB ++ cma-96 CMA is 96MB ++ cma-64 CMA is 64MB ++ cma-size CMA size in bytes, 4MB aligned ++ cma-default Use upstream's default value ++ ++ + Name: dht11 + Info: Overlay for the DHT11/DHT21/DHT22 humidity/temperature sensors + Also sometimes found with the part number(s) AM230x. +@@ -2675,6 +2688,8 @@ Params: cma-256 CMA is 2 + cma-128 CMA is 128MB + cma-96 CMA is 96MB + cma-64 CMA is 64MB ++ cma-size CMA size in bytes, 4MB aligned ++ cma-default Use upstream's default value + + + Name: vc4-kms-kippah-7inch +@@ -2692,6 +2707,8 @@ Params: cma-256 CMA is 2 + cma-128 CMA is 128MB + cma-96 CMA is 96MB + cma-64 CMA is 64MB ++ cma-size CMA size in bytes, 4MB aligned ++ cma-default Use upstream's default value + audio Enable or disable audio over HDMI (default "on") + + +@@ -2703,6 +2720,8 @@ Params: cma-256 CMA is 2 + cma-128 CMA is 128MB + cma-96 CMA is 96MB + cma-64 CMA is 64MB ++ cma-size CMA size in bytes, 4MB aligned ++ cma-default Use upstream's default value + audio Enable or disable audio over HDMI0 (default + "on") + audio1 Enable or disable audio over HDMI1 (default +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/cma-overlay.dts +@@ -0,0 +1,32 @@ ++/* ++ * cma.dts ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target = <&cma>; ++ frag0: __overlay__ { ++ /* ++ * The default size when using this overlay is 256 MB ++ * and should be kept as is for backwards ++ * compatibility. ++ */ ++ size = <0x10000000>; ++ }; ++ }; ++ ++ __overrides__ { ++ cma-256 = <&frag0>,"size:0=",<0x10000000>; ++ cma-192 = <&frag0>,"size:0=",<0xC000000>; ++ cma-128 = <&frag0>,"size:0=",<0x8000000>; ++ cma-96 = <&frag0>,"size:0=",<0x6000000>; ++ cma-64 = <&frag0>,"size:0=",<0x4000000>; ++ cma-size = <&frag0>,"size:0"; /* in bytes, 4MB aligned */ ++ cma-default = <0>,"-0"; ++ }; ++}; +--- a/arch/arm/boot/dts/overlays/upstream-overlay.dts ++++ b/arch/arm/boot/dts/overlays/upstream-overlay.dts +@@ -1,4 +1,4 @@ +-// redo: ovmerge -c vc4-kms-v3d-overlay.dts,cma-96 dwc2-overlay.dts,dr_mode=otg ++// redo: ovmerge -c vc4-kms-v3d-overlay.dts,cma-default dwc2-overlay.dts,dr_mode=otg + + /dts-v1/; + /plugin/; +@@ -8,114 +8,90 @@ + / { + compatible = "brcm,bcm2835"; + fragment@0 { +- target-path = "/chosen"; ++ target = <&cma>; + __dormant__ { +- bootargs = "cma=256M"; ++ size = <0x10000000>; + }; + }; + fragment@1 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=192M"; +- }; +- }; +- fragment@2 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=128M"; +- }; +- }; +- fragment@3 { +- target-path = "/chosen"; +- __overlay__ { +- bootargs = "cma=96M"; +- }; +- }; +- fragment@4 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=64M"; +- }; +- }; +- fragment@5 { + target = <&i2c2>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@6 { ++ fragment@2 { + target = <&fb>; + __overlay__ { + status = "disabled"; + }; + }; +- fragment@7 { ++ fragment@3 { + target = <&pixelvalve0>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@8 { ++ fragment@4 { + target = <&pixelvalve1>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@9 { ++ fragment@5 { + target = <&pixelvalve2>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@10 { ++ fragment@6 { + target = <&hvs>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@11 { ++ fragment@7 { + target = <&hdmi>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@12 { ++ fragment@8 { + target = <&v3d>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@13 { ++ fragment@9 { + target = <&vc4>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@14 { ++ fragment@10 { + target = <&clocks>; + __overlay__ { + claim-clocks = <BCM2835_PLLD_DSI0 BCM2835_PLLD_DSI1 BCM2835_PLLH_AUX BCM2835_PLLH_PIX>; + }; + }; +- fragment@15 { ++ fragment@11 { + target = <&vec>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@16 { ++ fragment@12 { + target = <&txp>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@17 { ++ fragment@13 { + target = <&hdmi>; + __dormant__ { + dmas; + }; + }; +- fragment@18 { ++ fragment@14 { + target = <&usb>; + #address-cells = <1>; + #size-cells = <1>; +--- a/arch/arm/boot/dts/overlays/upstream-pi4-overlay.dts ++++ b/arch/arm/boot/dts/overlays/upstream-pi4-overlay.dts +@@ -8,144 +8,120 @@ + / { + compatible = "brcm,bcm2835"; + fragment@0 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=256M"; +- }; +- }; +- fragment@1 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=192M"; +- }; +- }; +- fragment@2 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=128M"; +- }; +- }; +- fragment@3 { +- target-path = "/chosen"; ++ target = <&cma>; + __overlay__ { +- bootargs = "cma=96M"; ++ size = <100663296>; + }; + }; +- fragment@4 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=64M"; +- }; +- }; +- fragment@5 { ++ fragment@1 { + target = <&ddc0>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@6 { ++ fragment@2 { + target = <&ddc1>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@7 { ++ fragment@3 { + target = <&hdmi0>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@8 { ++ fragment@4 { + target = <&hdmi1>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@9 { ++ fragment@5 { + target = <&hvs>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@10 { ++ fragment@6 { + target = <&pixelvalve0>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@11 { ++ fragment@7 { + target = <&pixelvalve1>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@12 { ++ fragment@8 { + target = <&pixelvalve2>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@13 { ++ fragment@9 { + target = <&pixelvalve3>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@14 { ++ fragment@10 { + target = <&pixelvalve4>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@15 { ++ fragment@11 { + target = <&v3d>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@16 { ++ fragment@12 { + target = <&vc4>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@17 { ++ fragment@13 { + target = <&txp>; + __overlay__ { + status = "okay"; + }; + }; +- fragment@18 { ++ fragment@14 { + target = <&fb>; + __overlay__ { + status = "disabled"; + }; + }; +- fragment@19 { ++ fragment@15 { + target = <&firmwarekms>; + __overlay__ { + status = "disabled"; + }; + }; +- fragment@20 { ++ fragment@16 { + target = <&vec>; + __overlay__ { + status = "disabled"; + }; + }; +- fragment@21 { ++ fragment@17 { + target = <&hdmi0>; + __dormant__ { + dmas; + }; + }; +- fragment@22 { ++ fragment@18 { + target = <&hdmi1>; + __dormant__ { + dmas; + }; + }; +- fragment@23 { ++ fragment@19 { + target = <&usb>; + #address-cells = <1>; + #size-cells = <1>; +--- a/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts +@@ -5,77 +5,36 @@ + /dts-v1/; + /plugin/; + ++#include "cma-overlay.dts" ++ + / { + compatible = "brcm,bcm2835"; + +- fragment@0 { +- target-path = "/chosen"; +- __overlay__ { +- bootargs = "cma=256M"; +- }; +- }; +- + fragment@1 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=192M"; +- }; +- }; +- +- fragment@2 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=128M"; +- }; +- }; +- +- fragment@3 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=96M"; +- }; +- }; +- +- fragment@4 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=64M"; +- }; +- }; +- +- fragment@5 { + target = <&fb>; + __overlay__ { + status = "disabled"; + }; + }; + +- fragment@6 { ++ fragment@2 { + target = <&firmwarekms>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@7 { ++ fragment@3 { + target = <&v3d>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@8 { ++ fragment@4 { + target = <&vc4>; + __overlay__ { + status = "okay"; + }; + }; +- +- __overrides__ { +- cma-256 = <0>,"+0-1-2-3-4"; +- cma-192 = <0>,"-0+1-2-3-4"; +- cma-128 = <0>,"-0-1+2-3-4"; +- cma-96 = <0>,"-0-1-2+3-4"; +- cma-64 = <0>,"-0-1-2-3+4"; +- }; + }; +--- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts +@@ -7,108 +7,75 @@ + + #include <dt-bindings/clock/bcm2835.h> + ++#include "cma-overlay.dts" ++ + / { + compatible = "brcm,bcm2835"; + +- fragment@0 { +- target-path = "/chosen"; +- __overlay__ { +- bootargs = "cma=256M"; +- }; +- }; +- + fragment@1 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=192M"; +- }; +- }; +- +- fragment@2 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=128M"; +- }; +- }; +- +- fragment@3 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=96M"; +- }; +- }; +- +- fragment@4 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=64M"; +- }; +- }; +- +- fragment@5 { + target = <&i2c2>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@6 { ++ fragment@2 { + target = <&fb>; + __overlay__ { + status = "disabled"; + }; + }; + +- fragment@7 { ++ fragment@3 { + target = <&pixelvalve0>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@8 { ++ fragment@4 { + target = <&pixelvalve1>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@9 { ++ fragment@5 { + target = <&pixelvalve2>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@10 { ++ fragment@6 { + target = <&hvs>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@11 { ++ fragment@7 { + target = <&hdmi>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@12 { ++ fragment@8 { + target = <&v3d>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@13 { ++ fragment@9 { + target = <&vc4>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@14 { ++ fragment@10 { + target = <&clocks>; + __overlay__ { + claim-clocks = < +@@ -120,21 +87,21 @@ + }; + }; + +- fragment@15 { ++ fragment@11 { + target = <&vec>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@16 { ++ fragment@12 { + target = <&txp>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@17 { ++ fragment@13 { + target = <&hdmi>; + __dormant__ { + dmas; +@@ -142,11 +109,6 @@ + }; + + __overrides__ { +- cma-256 = <0>,"+0-1-2-3-4"; +- cma-192 = <0>,"-0+1-2-3-4"; +- cma-128 = <0>,"-0-1+2-3-4"; +- cma-96 = <0>,"-0-1-2+3-4"; +- cma-64 = <0>,"-0-1-2-3+4"; + audio = <0>,"!17"; + }; + }; +--- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts +@@ -7,164 +7,131 @@ + + #include <dt-bindings/clock/bcm2835.h> + ++#include "cma-overlay.dts" ++ + / { + compatible = "brcm,bcm2835"; + +- fragment@0 { +- target-path = "/chosen"; +- __overlay__ { +- bootargs = "cma=256M"; +- }; +- }; +- + fragment@1 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=192M"; +- }; +- }; +- +- fragment@2 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=128M"; +- }; +- }; +- +- fragment@3 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=96M"; +- }; +- }; +- +- fragment@4 { +- target-path = "/chosen"; +- __dormant__ { +- bootargs = "cma=64M"; +- }; +- }; +- +- fragment@5 { + target = <&ddc0>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@6 { ++ fragment@2 { + target = <&ddc1>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@7 { ++ fragment@3 { + target = <&hdmi0>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@8 { ++ fragment@4 { + target = <&hdmi1>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@9 { ++ fragment@5 { + target = <&hvs>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@10 { ++ fragment@6 { + target = <&pixelvalve0>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@11 { ++ fragment@7 { + target = <&pixelvalve1>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@12 { ++ fragment@8 { + target = <&pixelvalve2>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@13 { ++ fragment@9 { + target = <&pixelvalve3>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@14 { ++ fragment@10 { + target = <&pixelvalve4>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@15 { ++ fragment@11 { + target = <&v3d>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@16 { ++ fragment@12 { + target = <&vc4>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@17 { ++ fragment@13 { + target = <&txp>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@18 { ++ fragment@14 { + target = <&fb>; + __overlay__ { + status = "disabled"; + }; + }; + +- fragment@19 { ++ fragment@15 { + target = <&firmwarekms>; + __overlay__ { + status = "disabled"; + }; + }; + +- fragment@20 { ++ fragment@16 { + target = <&vec>; + __overlay__ { + status = "disabled"; + }; + }; + +- fragment@21 { ++ fragment@17 { + target = <&hdmi0>; + __dormant__ { + dmas; + }; + }; + +- fragment@22 { ++ fragment@18 { + target = <&hdmi1>; + __dormant__ { + dmas; +@@ -172,12 +139,7 @@ + }; + + __overrides__ { +- cma-256 = <0>,"+0-1-2-3-4"; +- cma-192 = <0>,"-0+1-2-3-4"; +- cma-128 = <0>,"-0-1+2-3-4"; +- cma-96 = <0>,"-0-1-2+3-4"; +- cma-64 = <0>,"-0-1-2-3+4"; +- audio = <0>,"!21"; +- audio1 = <0>,"!22"; ++ audio = <0>,"!17"; ++ audio1 = <0>,"!18"; + }; + }; diff --git a/target/linux/bcm27xx/patches-5.4/950-0639-ARM-dts-bcm283x-Fix-vc4-s-firmware-bus-DMA-limitatio.patch b/target/linux/bcm27xx/patches-5.4/950-0639-ARM-dts-bcm283x-Fix-vc4-s-firmware-bus-DMA-limitatio.patch new file mode 100644 index 0000000000..31840e5437 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0639-ARM-dts-bcm283x-Fix-vc4-s-firmware-bus-DMA-limitatio.patch @@ -0,0 +1,28 @@ +From 142ad0b8433d6beb87070bd39a6b2d23ca9cae30 Mon Sep 17 00:00:00 2001 +From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Date: Thu, 19 Mar 2020 20:00:13 +0100 +Subject: [PATCH] ARM: dts: bcm283x: Fix vc4's firmware bus DMA + limitations + +The bus is virtual and devices have to inherit their DMA constraints +from the underlying interconnect. So add an empty dma-ranges property to +the bus node, implying the firmware bus' DMA constraints are identical to +its parent's. + +Fixes: 7dbe8c62ceeb ("ARM: dts: Add minimal Raspberry Pi 4 support") +Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> +Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> +--- + arch/arm/boot/dts/bcm2835-rpi.dtsi | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi ++++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi +@@ -15,6 +15,7 @@ + firmware: firmware { + compatible = "raspberrypi,bcm2835-firmware", "simple-bus"; + mboxes = <&mailbox>; ++ dma-ranges; + }; + + power: power { diff --git a/target/linux/bcm27xx/patches-5.4/950-0640-ARM-dts-bcm2711-Restrict-CMA-to-first-768MB.patch b/target/linux/bcm27xx/patches-5.4/950-0640-ARM-dts-bcm2711-Restrict-CMA-to-first-768MB.patch new file mode 100644 index 0000000000..9a1e29e7d1 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0640-ARM-dts-bcm2711-Restrict-CMA-to-first-768MB.patch @@ -0,0 +1,33 @@ +From 7b307016ed13cbb65e08b6a704912e5c9e5b81ac Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Tue, 14 Apr 2020 15:25:02 +0100 +Subject: [PATCH] ARM: dts: bcm2711: Restrict CMA to first 768MB + +The downstream 32-bit 2711 kernel configuration enables HIGHMEM for +access to more physical RAM. The HIGHMEM zone starts at 0x30000000 +(768MB), and allowing the CMA zone to overlap that area causes a +failure during CMA activation. + +Avoid the overlap by limiting CMA to the first 768MB. This is overly +restrictive on a 64-bit kernel, but shouldn't cause any practical +problems. + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/bcm2711-rpi.dtsi | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi ++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi +@@ -45,6 +45,11 @@ + }; + }; + ++&cma { ++ /* Limit cma to the lower 768MB to allow room for HIGHMEM on 32-bit */ ++ alloc-ranges = <0x0 0x00000000 0x30000000>; ++}; ++ + &soc { + thermal: thermal@7d5d2200 { + compatible = "brcm,avs-tmon-bcm2711"; diff --git a/target/linux/bcm27xx/patches-5.4/950-0641-ARM-dts-Extend-SCB-bus-address-range.patch b/target/linux/bcm27xx/patches-5.4/950-0641-ARM-dts-Extend-SCB-bus-address-range.patch new file mode 100644 index 0000000000..8eb2b16b10 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0641-ARM-dts-Extend-SCB-bus-address-range.patch @@ -0,0 +1,23 @@ +From 9773c8c521f45f34631bcb147d638ba0252bbdd4 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Tue, 4 Feb 2020 12:51:56 +0000 +Subject: [PATCH] ARM: dts: Extend SCB bus address range + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/bcm2711-rpi.dtsi | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi ++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi +@@ -69,7 +69,9 @@ + <0x0 0x40000000 0x0 0xff800000 0x00800000>, + <0x6 0x00000000 0x6 0x00000000 0x40000000>, + <0x0 0x00000000 0x0 0x00000000 0xfc000000>; +- dma-ranges = <0x0 0x00000000 0x0 0x00000000 0xfc000000>; ++ dma-ranges = <0x0 0x00000000 0x0 0x00000000 0xfc000000>, ++ <0x1 0x00000000 0x1 0x00000000 0x80000000>, ++ <0x1 0x80000000 0x1 0x80000000 0x80000000>; + + dma40: dma@7e007b00 { + compatible = "brcm,bcm2711-dma"; diff --git a/target/linux/bcm27xx/patches-5.4/950-0642-dts-bcm2711-Move-emmc2-to-its-own-bus.patch b/target/linux/bcm27xx/patches-5.4/950-0642-dts-bcm2711-Move-emmc2-to-its-own-bus.patch new file mode 100644 index 0000000000..52cf03a382 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0642-dts-bcm2711-Move-emmc2-to-its-own-bus.patch @@ -0,0 +1,52 @@ +From f97ba33547711d727fbbcb10eb046f8ac605a966 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.org> +Date: Thu, 5 Dec 2019 18:02:08 +0000 +Subject: [PATCH] dts: bcm2711: Move emmc2 to its own "bus" + +Moving the EMMC2 controller under a dedicated bus allows the firmware +to patch the dma-ranges property for different memory sizes without +affecting anything else. + +Signed-off-by: Phil Elwell <phil@raspberrypi.org> +--- + arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts ++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts +@@ -193,6 +193,8 @@ + #include "bcm2711-rpi.dtsi" + #include "bcm283x-rpi-csi1-2lane.dtsi" + ++/delete-node/ &emmc2; ++ + / { + chosen { + bootargs = "coherent_pool=1M 8250.nr_uarts=1"; +@@ -212,6 +214,26 @@ + /delete-property/ ethernet; + /delete-property/ intc; + pcie0 = &pcie0; ++ emmc2bus = &emmc2bus; ++ }; ++ ++ emmc2bus: emmc2bus { ++ compatible = "simple-bus"; ++ #address-cells = <2>; ++ #size-cells = <1>; ++ ++ ranges = <0x0 0x7e000000 0x0 0xfe000000 0x01800000>; ++ dma-ranges = <0x0 0xc0000000 0x0 0x00000000 0x3c000000>; ++ ++ emmc2: emmc2@7e340000 { ++ compatible = "brcm,bcm2711-emmc2"; ++ status = "okay"; ++ interrupts = <GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clocks BCM2711_CLOCK_EMMC2>; ++ reg = <0x0 0x7e340000 0x100>; ++ vqmmc-supply = <&sd_io_1v8_reg>; ++ broken-cd; ++ }; + }; + + /delete-node/ wifi-pwrseq; diff --git a/target/linux/bcm27xx/patches-5.4/950-0643-drm-vc4-hdmi-Silence-pixel-clock-error-on-EPROBE_DEF.patch b/target/linux/bcm27xx/patches-5.4/950-0643-drm-vc4-hdmi-Silence-pixel-clock-error-on-EPROBE_DEF.patch new file mode 100644 index 0000000000..1b1cb143ae --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0643-drm-vc4-hdmi-Silence-pixel-clock-error-on-EPROBE_DEF.patch @@ -0,0 +1,29 @@ +From ba875ce27cd407bc61502517671623df07bb6c1a Mon Sep 17 00:00:00 2001 +From: James Hilliard <james.hilliard1@gmail.com> +Date: Fri, 10 Apr 2020 19:24:40 -0600 +Subject: [PATCH] drm/vc4: hdmi: Silence pixel clock error on + -EPROBE_DEFER + +If the vc4 hdmi driver loads before the pixel clock is available we +see a spurious "*ERROR* Failed to get pixel clock" error. + +Signed-off-by: James Hilliard <james.hilliard1@gmail.com> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -1493,8 +1493,10 @@ static int vc4_hdmi_init_resources(struc + + vc4_hdmi->pixel_clock = devm_clk_get(dev, "pixel"); + if (IS_ERR(vc4_hdmi->pixel_clock)) { +- DRM_ERROR("Failed to get pixel clock\n"); +- return PTR_ERR(vc4_hdmi->pixel_clock); ++ ret = PTR_ERR(vc4_hdmi->pixel_clock); ++ if (ret != -EPROBE_DEFER) ++ DRM_ERROR("Failed to get pixel clock\n"); ++ return ret; + } + + vc4_hdmi->hsm_clock = devm_clk_get(dev, "hdmi"); diff --git a/target/linux/bcm27xx/patches-5.4/950-0644-component-Silence-bind-error-on-EPROBE_DEFER.patch b/target/linux/bcm27xx/patches-5.4/950-0644-component-Silence-bind-error-on-EPROBE_DEFER.patch new file mode 100644 index 0000000000..f7b1217527 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0644-component-Silence-bind-error-on-EPROBE_DEFER.patch @@ -0,0 +1,41 @@ +From 8695764265eb6e749c9fdc901e98c2e7b4d2adfc Mon Sep 17 00:00:00 2001 +From: James Hilliard <james.hilliard1@gmail.com> +Date: Fri, 10 Apr 2020 20:23:13 -0600 +Subject: [PATCH] component: Silence bind error on -EPROBE_DEFER + +If a component fails to bind due to -EPROBE_DEFER we should not log an +error as this is not a real failure. + +Fixes: +vc4-drm soc:gpu: failed to bind 3f902000.hdmi (ops vc4_hdmi_ops): -517 +vc4-drm soc:gpu: master bind failed: -517 + +Signed-off-by: James Hilliard <james.hilliard1@gmail.com> +--- + drivers/base/component.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/drivers/base/component.c ++++ b/drivers/base/component.c +@@ -257,7 +257,8 @@ static int try_to_bring_up_master(struct + ret = master->ops->bind(master->dev); + if (ret < 0) { + devres_release_group(master->dev, NULL); +- dev_info(master->dev, "master bind failed: %d\n", ret); ++ if (ret != -EPROBE_DEFER) ++ dev_info(master->dev, "master bind failed: %d\n", ret); + return ret; + } + +@@ -611,8 +612,9 @@ static int component_bind(struct compone + devres_release_group(component->dev, NULL); + devres_release_group(master->dev, NULL); + +- dev_err(master->dev, "failed to bind %s (ops %ps): %d\n", +- dev_name(component->dev), component->ops, ret); ++ if (ret != -EPROBE_DEFER) ++ dev_err(master->dev, "failed to bind %s (ops %ps): %d\n", ++ dev_name(component->dev), component->ops, ret); + } + + return ret; diff --git a/target/linux/bcm27xx/patches-5.4/950-0645-Fixes-a-problem-with-clock-settings-of-HiFiBerry-DAC.patch b/target/linux/bcm27xx/patches-5.4/950-0645-Fixes-a-problem-with-clock-settings-of-HiFiBerry-DAC.patch new file mode 100644 index 0000000000..f1427ae71f --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0645-Fixes-a-problem-with-clock-settings-of-HiFiBerry-DAC.patch @@ -0,0 +1,42 @@ +From f303194bc24925d3efd965ccfae40974ea437240 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=B6rg=20Schambacher?= + <j-schambacher@users.noreply.github.com> +Date: Wed, 15 Apr 2020 11:48:29 +0200 +Subject: [PATCH] Fixes a problem with clock settings of HiFiBerry + DAC+ADC PRO (#3545) + +This patch fixes a problem of the re-calculation of +i2s-clock and -parameter settings when only the ADC is activated. + +Signed-off-by: Joerg Schambacher <joerg@i2audio.com> +--- + sound/soc/bcm/hifiberry_dacplusadcpro.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +--- a/sound/soc/bcm/hifiberry_dacplusadcpro.c ++++ b/sound/soc/bcm/hifiberry_dacplusadcpro.c +@@ -390,9 +390,11 @@ static int snd_rpi_hifiberry_dacplusadcp + int channels = params_channels(params); + int width = 32; + struct snd_soc_component *dac = rtd->codec_dais[0]->component; ++ struct snd_soc_dai *dai = rtd->codec_dais[0]; ++ struct snd_soc_dai_driver *drv = dai->driver; ++ const struct snd_soc_dai_ops *ops = drv->ops; + + if (snd_rpi_hifiberry_is_dacpro) { +- + width = snd_pcm_format_physical_width(params_format(params)); + + snd_rpi_hifiberry_dacplusadcpro_set_sclk(dac, +@@ -414,6 +416,11 @@ static int snd_rpi_hifiberry_dacplusadcp + return ret; + ret = snd_soc_dai_set_tdm_slot(rtd->codec_dais[1], 0x03, 0x03, + channels, width); ++ if (ret) ++ return ret; ++ ++ if (snd_rpi_hifiberry_is_dacpro && ops->hw_params) ++ ret = ops->hw_params(substream, params, dai); + return ret; + } + diff --git a/target/linux/bcm27xx/patches-5.4/950-0646-Documentation-media-Update-sub-device-API-intro.patch b/target/linux/bcm27xx/patches-5.4/950-0646-Documentation-media-Update-sub-device-API-intro.patch new file mode 100644 index 0000000000..f843a83a7d --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0646-Documentation-media-Update-sub-device-API-intro.patch @@ -0,0 +1,34 @@ +From afd6952c4dfb0d4945b496ef08b2f478d6f40097 Mon Sep 17 00:00:00 2001 +From: Jacopo Mondi <jacopo@jmondi.org> +Date: Tue, 7 Apr 2020 17:21:55 +0200 +Subject: [PATCH] Documentation: media: Update sub-device API intro + +Update the V4L2 sub-device userspace API introduction to provide more +details on why complex devices might want to register devnodes for the +connected subdevices. + +Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com> +Suggested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> +Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> +--- + Documentation/media/kapi/v4l2-subdev.rst | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +--- a/Documentation/media/kapi/v4l2-subdev.rst ++++ b/Documentation/media/kapi/v4l2-subdev.rst +@@ -275,8 +275,13 @@ system the .unbind() method is called. A + V4L2 sub-device userspace API + ----------------------------- + +-Beside exposing a kernel API through the :c:type:`v4l2_subdev_ops` structure, +-V4L2 sub-devices can also be controlled directly by userspace applications. ++Bridge drivers traditionally expose one or multiple video nodes to userspace, ++and control subdevices through the :c:type:`v4l2_subdev_ops` operations in ++response to video node operations. This hides the complexity of the underlying ++hardware from applications. For complex devices, finer-grained control of the ++device than what the video nodes offer may be required. In those cases, bridge ++drivers that implement :ref:`the media controller API <media_controller>` may ++opt for making the subdevice operations directly accessible from userpace. + + Device nodes named ``v4l-subdev``\ *X* can be created in ``/dev`` to access + sub-devices directly. If a sub-device supports direct userspace configuration diff --git a/target/linux/bcm27xx/patches-5.4/950-0647-Documentation-media-Document-read-only-subdevice.patch b/target/linux/bcm27xx/patches-5.4/950-0647-Documentation-media-Document-read-only-subdevice.patch new file mode 100644 index 0000000000..1983334ae1 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0647-Documentation-media-Document-read-only-subdevice.patch @@ -0,0 +1,217 @@ +From c1a630e792140b4791bad84974e31b4a1cf09b1b Mon Sep 17 00:00:00 2001 +From: Jacopo Mondi <jacopo@jmondi.org> +Date: Tue, 7 Apr 2020 17:21:56 +0200 +Subject: [PATCH] Documentation: media: Document read-only subdevice + +Document a new kAPI function to register subdev device nodes in read only +mode and for each affected ioctl report how access is restricted. + +Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com> +Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> +--- + Documentation/media/kapi/v4l2-subdev.rst | 44 +++++++++++++++++++ + Documentation/media/uapi/v4l/dev-subdev.rst | 5 +++ + .../media/uapi/v4l/vidioc-g-dv-timings.rst | 6 +++ + Documentation/media/uapi/v4l/vidioc-g-std.rst | 6 +++ + .../media/uapi/v4l/vidioc-subdev-g-crop.rst | 9 ++++ + .../media/uapi/v4l/vidioc-subdev-g-fmt.rst | 8 ++++ + .../v4l/vidioc-subdev-g-frame-interval.rst | 8 ++++ + .../uapi/v4l/vidioc-subdev-g-selection.rst | 8 ++++ + 8 files changed, 94 insertions(+) + +--- a/Documentation/media/kapi/v4l2-subdev.rst ++++ b/Documentation/media/kapi/v4l2-subdev.rst +@@ -332,6 +332,50 @@ Private ioctls + All ioctls not in the above list are passed directly to the sub-device + driver through the core::ioctl operation. + ++Read-only sub-device userspace API ++---------------------------------- ++ ++Bridge drivers that control their connected subdevices through direct calls to ++the kernel API realized by :c:type:`v4l2_subdev_ops` structure do not usually ++want userspace to be able to change the same parameters through the subdevice ++device node and thus do not usually register any. ++ ++It is sometimes useful to report to userspace the current subdevice ++configuration through a read-only API, that does not permit applications to ++change to the device parameters but allows interfacing to the subdevice device ++node to inspect them. ++ ++For instance, to implement cameras based on computational photography, userspace ++needs to know the detailed camera sensor configuration (in terms of skipping, ++binning, cropping and scaling) for each supported output resolution. To support ++such use cases, bridge drivers may expose the subdevice operations to userspace ++through a read-only API. ++ ++To create a read-only device node for all the subdevices registered with the ++``V4L2_SUBDEV_FL_HAS_DEVNODE`` set, the :c:type:`v4l2_device` driver should call ++:c:func:`v4l2_device_register_ro_subdev_nodes`. ++ ++Access to the following ioctls for userspace applications is restricted on ++sub-device device nodes registered with ++:c:func:`v4l2_device_register_ro_subdev_nodes`. ++ ++``VIDIOC_SUBDEV_S_FMT``, ++``VIDIOC_SUBDEV_S_CROP``, ++``VIDIOC_SUBDEV_S_SELECTION``: ++ ++ These ioctls are only allowed on a read-only subdevice device node ++ for the :ref:`V4L2_SUBDEV_FORMAT_TRY <v4l2-subdev-format-whence>` ++ formats and selection rectangles. ++ ++``VIDIOC_SUBDEV_S_FRAME_INTERVAL``, ++``VIDIOC_SUBDEV_S_DV_TIMINGS``, ++``VIDIOC_SUBDEV_S_STD``: ++ ++ These ioctls are not allowed on a read-only subdevice node. ++ ++In case the ioctl is not allowed, or the format to modify is set to ++``V4L2_SUBDEV_FORMAT_ACTIVE``, the core returns a negative error code and ++the errno variable is set to ``-EPERM``. + + I2C sub-device drivers + ---------------------- +--- a/Documentation/media/uapi/v4l/dev-subdev.rst ++++ b/Documentation/media/uapi/v4l/dev-subdev.rst +@@ -39,6 +39,11 @@ will feature a character device node on + Sub-device character device nodes, conventionally named + ``/dev/v4l-subdev*``, use major number 81. + ++Drivers may opt to limit the sub-device character devices to only expose ++operations that do not modify the device state. In such a case the sub-devices ++are referred to as ``read-only`` in the rest of this documentation, and the ++related restrictions are documented in individual ioctls. ++ + + Controls + ======== +--- a/Documentation/media/uapi/v4l/vidioc-g-dv-timings.rst ++++ b/Documentation/media/uapi/v4l/vidioc-g-dv-timings.rst +@@ -57,6 +57,10 @@ pointer to the struct :c:type:`v4l2_dv_t + structure as argument. If the ioctl is not supported or the timing + values are not correct, the driver returns ``EINVAL`` error code. + ++Calling ``VIDIOC_SUBDEV_S_DV_TIMINGS`` on a subdev device node that has been ++registered in read-only mode is not allowed. An error is returned and the errno ++variable is set to ``-EPERM``. ++ + The ``linux/v4l2-dv-timings.h`` header can be used to get the timings of + the formats in the :ref:`cea861` and :ref:`vesadmt` standards. If + the current input or output does not support DV timings (e.g. if +@@ -81,6 +85,8 @@ ENODATA + EBUSY + The device is busy and therefore can not change the timings. + ++EPERM ++ ``VIDIOC_SUBDEV_S_DV_TIMINGS`` has been called on a read-only subdevice. + + .. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}| + +--- a/Documentation/media/uapi/v4l/vidioc-g-std.rst ++++ b/Documentation/media/uapi/v4l/vidioc-g-std.rst +@@ -66,6 +66,9 @@ video timings (e.g. if :ref:`VIDIOC_ENUM + does not set the ``V4L2_IN_CAP_STD`` flag), then ``ENODATA`` error code is + returned. + ++Calling ``VIDIOC_SUBDEV_S_STD`` on a subdev device node that has been registered ++in read-only mode is not allowed. An error is returned and the errno variable is ++set to ``-EPERM``. + + Return Value + ============ +@@ -79,3 +82,6 @@ EINVAL + + ENODATA + Standard video timings are not supported for this input or output. ++ ++EPERM ++ ``VIDIOC_SUBDEV_S_STD`` has been called on a read-only subdevice. +--- a/Documentation/media/uapi/v4l/vidioc-subdev-g-crop.rst ++++ b/Documentation/media/uapi/v4l/vidioc-subdev-g-crop.rst +@@ -73,6 +73,11 @@ crop rectangles and stored in the sub-de + applications querying the same sub-device would thus not interact with + each other. + ++If the subdev device node has been registered in read-only mode, calls to ++``VIDIOC_SUBDEV_S_CROP`` are only valid if the ``which`` field is set to ++``V4L2_SUBDEV_FORMAT_TRY``, otherwise an error is returned and the errno ++variable is set to ``-EPERM``. ++ + Drivers must not return an error solely because the requested crop + rectangle doesn't match the device capabilities. They must instead + modify the rectangle to match what the hardware can provide. The +@@ -123,3 +128,7 @@ EINVAL + references a non-existing pad, the ``which`` field references a + non-existing format, or cropping is not supported on the given + subdev pad. ++ ++EPERM ++ The ``VIDIOC_SUBDEV_S_CROP`` ioctl has been called on a read-only subdevice ++ and the ``which`` field is set to ``V4L2_SUBDEV_FORMAT_ACTIVE``. +--- a/Documentation/media/uapi/v4l/vidioc-subdev-g-fmt.rst ++++ b/Documentation/media/uapi/v4l/vidioc-subdev-g-fmt.rst +@@ -78,6 +78,11 @@ current links configuration or sub-devic + a low-pass noise filter might crop pixels at the frame boundaries, + modifying its output frame size. + ++If the subdev device node has been registered in read-only mode, calls to ++``VIDIOC_SUBDEV_S_FMT`` are only valid if the ``which`` field is set to ++``V4L2_SUBDEV_FORMAT_TRY``, otherwise an error is returned and the errno ++variable is set to ``-EPERM``. ++ + Drivers must not return an error solely because the requested format + doesn't match the device capabilities. They must instead modify the + format to match what the hardware can provide. The modified format +@@ -146,6 +151,9 @@ EINVAL + ``pad`` references a non-existing pad, or the ``which`` field + references a non-existing format. + ++EPERM ++ The ``VIDIOC_SUBDEV_S_FMT`` ioctl has been called on a read-only subdevice ++ and the ``which`` field is set to ``V4L2_SUBDEV_FORMAT_ACTIVE``. + + ============ + +--- a/Documentation/media/uapi/v4l/vidioc-subdev-g-frame-interval.rst ++++ b/Documentation/media/uapi/v4l/vidioc-subdev-g-frame-interval.rst +@@ -65,6 +65,10 @@ struct + contains the current frame interval as would be returned by a + ``VIDIOC_SUBDEV_G_FRAME_INTERVAL`` call. + ++Calling ``VIDIOC_SUBDEV_S_FRAME_INTERVAL`` on a subdev device node that has been ++registered in read-only mode is not allowed. An error is returned and the errno ++variable is set to ``-EPERM``. ++ + Drivers must not return an error solely because the requested interval + doesn't match the device capabilities. They must instead modify the + interval to match what the hardware can provide. The modified interval +@@ -118,3 +122,7 @@ EINVAL + :c:type:`v4l2_subdev_frame_interval` + ``pad`` references a non-existing pad, or the pad doesn't support + frame intervals. ++ ++EPERM ++ The ``VIDIOC_SUBDEV_S_FRAME_INTERVAL`` ioctl has been called on a read-only ++ subdevice. +--- a/Documentation/media/uapi/v4l/vidioc-subdev-g-selection.rst ++++ b/Documentation/media/uapi/v4l/vidioc-subdev-g-selection.rst +@@ -53,6 +53,10 @@ function of the crop API, and more, are + See :ref:`subdev` for more information on how each selection target + affects the image processing pipeline inside the subdevice. + ++If the subdev device node has been registered in read-only mode, calls to ++``VIDIOC_SUBDEV_S_SELECTION`` are only valid if the ``which`` field is set to ++``V4L2_SUBDEV_FORMAT_TRY``, otherwise an error is returned and the errno ++variable is set to ``-EPERM``. + + Types of selection targets + -------------------------- +@@ -123,3 +127,7 @@ EINVAL + ``pad`` references a non-existing pad, the ``which`` field + references a non-existing format, or the selection target is not + supported on the given subdev pad. ++ ++EPERM ++ The ``VIDIOC_SUBDEV_S_SELECTION`` ioctl has been called on a read-only ++ subdevice and the ``which`` field is set to ``V4L2_SUBDEV_FORMAT_ACTIVE``. diff --git a/target/linux/bcm27xx/patches-5.4/950-0648-media-v4l2-dev-Add-v4l2_device_register_ro_subdev_no.patch b/target/linux/bcm27xx/patches-5.4/950-0648-media-v4l2-dev-Add-v4l2_device_register_ro_subdev_no.patch new file mode 100644 index 0000000000..47f2551ec7 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0648-media-v4l2-dev-Add-v4l2_device_register_ro_subdev_no.patch @@ -0,0 +1,206 @@ +From 90712c1a495c2aa4b10dd8127fdd7f1a0cd9ef00 Mon Sep 17 00:00:00 2001 +From: Jacopo Mondi <jacopo@jmondi.org> +Date: Tue, 7 Apr 2020 17:21:57 +0200 +Subject: [PATCH] media: v4l2-dev: Add + v4l2_device_register_ro_subdev_node() + +Add to the V4L2 core a function to register device nodes for video +subdevices in read-only mode. + +Registering a device node in read-only mode is useful to expose to +userspace the current sub-device configuration, without allowing +application to change it by using the V4L2 subdevice ioctls. + +Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com> +Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> +--- + drivers/media/v4l2-core/v4l2-device.c | 7 ++-- + drivers/media/v4l2-core/v4l2-subdev.c | 19 ++++++++++ + include/media/v4l2-dev.h | 7 ++++ + include/media/v4l2-device.h | 50 ++++++++++++++++++++++++--- + 4 files changed, 77 insertions(+), 6 deletions(-) + +--- a/drivers/media/v4l2-core/v4l2-device.c ++++ b/drivers/media/v4l2-core/v4l2-device.c +@@ -189,7 +189,8 @@ static void v4l2_device_release_subdev_n + kfree(vdev); + } + +-int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) ++int __v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev, ++ bool read_only) + { + struct video_device *vdev; + struct v4l2_subdev *sd; +@@ -218,6 +219,8 @@ int v4l2_device_register_subdev_nodes(st + vdev->fops = &v4l2_subdev_fops; + vdev->release = v4l2_device_release_subdev_node; + vdev->ctrl_handler = sd->ctrl_handler; ++ if (read_only) ++ set_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags); + err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1, + sd->owner); + if (err < 0) { +@@ -255,7 +258,7 @@ clean_up: + + return err; + } +-EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes); ++EXPORT_SYMBOL_GPL(__v4l2_device_register_subdev_nodes); + + void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) + { +--- a/drivers/media/v4l2-core/v4l2-subdev.c ++++ b/drivers/media/v4l2-core/v4l2-subdev.c +@@ -331,6 +331,7 @@ static long subdev_do_ioctl(struct file + struct v4l2_fh *vfh = file->private_data; + #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); ++ bool ro_subdev = test_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags); + int rval; + #endif + +@@ -453,6 +454,9 @@ static long subdev_do_ioctl(struct file + case VIDIOC_SUBDEV_S_FMT: { + struct v4l2_subdev_format *format = arg; + ++ if (format->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) ++ return -EPERM; ++ + memset(format->reserved, 0, sizeof(format->reserved)); + memset(format->format.reserved, 0, sizeof(format->format.reserved)); + return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh->pad, format); +@@ -480,6 +484,9 @@ static long subdev_do_ioctl(struct file + struct v4l2_subdev_crop *crop = arg; + struct v4l2_subdev_selection sel; + ++ if (crop->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) ++ return -EPERM; ++ + memset(crop->reserved, 0, sizeof(crop->reserved)); + memset(&sel, 0, sizeof(sel)); + sel.which = crop->which; +@@ -521,6 +528,9 @@ static long subdev_do_ioctl(struct file + case VIDIOC_SUBDEV_S_FRAME_INTERVAL: { + struct v4l2_subdev_frame_interval *fi = arg; + ++ if (ro_subdev) ++ return -EPERM; ++ + memset(fi->reserved, 0, sizeof(fi->reserved)); + return v4l2_subdev_call(sd, video, s_frame_interval, arg); + } +@@ -544,6 +554,9 @@ static long subdev_do_ioctl(struct file + case VIDIOC_SUBDEV_S_SELECTION: { + struct v4l2_subdev_selection *sel = arg; + ++ if (sel->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) ++ return -EPERM; ++ + memset(sel->reserved, 0, sizeof(sel->reserved)); + return v4l2_subdev_call( + sd, pad, set_selection, subdev_fh->pad, sel); +@@ -580,6 +593,9 @@ static long subdev_do_ioctl(struct file + return v4l2_subdev_call(sd, video, g_dv_timings, arg); + + case VIDIOC_SUBDEV_S_DV_TIMINGS: ++ if (ro_subdev) ++ return -EPERM; ++ + return v4l2_subdev_call(sd, video, s_dv_timings, arg); + + case VIDIOC_SUBDEV_G_STD: +@@ -588,6 +604,9 @@ static long subdev_do_ioctl(struct file + case VIDIOC_SUBDEV_S_STD: { + v4l2_std_id *std = arg; + ++ if (ro_subdev) ++ return -EPERM; ++ + return v4l2_subdev_call(sd, video, s_std, *std); + } + +--- a/include/media/v4l2-dev.h ++++ b/include/media/v4l2-dev.h +@@ -82,11 +82,18 @@ struct v4l2_ctrl_handler; + * but the old crop API will still work as expected in order to preserve + * backwards compatibility. + * Never set this flag for new drivers. ++ * @V4L2_FL_SUBDEV_RO_DEVNODE: ++ * indicates that the video device node is registered in read-only mode. ++ * The flag only applies to device nodes registered for sub-devices, it is ++ * set by the core when the sub-devices device nodes are registered with ++ * v4l2_device_register_ro_subdev_nodes() and used by the sub-device ioctl ++ * handler to restrict access to some ioctl calls. + */ + enum v4l2_video_device_flags { + V4L2_FL_REGISTERED = 0, + V4L2_FL_USES_V4L2_FH = 1, + V4L2_FL_QUIRK_INVERTED_CROP = 2, ++ V4L2_FL_SUBDEV_RO_DEVNODE = 3, + }; + + /* Priority helper functions */ +--- a/include/media/v4l2-device.h ++++ b/include/media/v4l2-device.h +@@ -174,14 +174,56 @@ int __must_check v4l2_device_register_su + void v4l2_device_unregister_subdev(struct v4l2_subdev *sd); + + /** +- * v4l2_device_register_subdev_nodes - Registers device nodes for all subdevs +- * of the v4l2 device that are marked with +- * the %V4L2_SUBDEV_FL_HAS_DEVNODE flag. ++ * __v4l2_device_register_ro_subdev_nodes - Registers device nodes for ++ * all subdevs of the v4l2 device that are marked with the ++ * %V4L2_SUBDEV_FL_HAS_DEVNODE flag. + * + * @v4l2_dev: pointer to struct v4l2_device ++ * @read_only: subdevices read-only flag. True to register the subdevices ++ * device nodes in read-only mode, false to allow full access to the ++ * subdevice userspace API. + */ + int __must_check +-v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev); ++__v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev, ++ bool read_only); ++ ++/** ++ * v4l2_device_register_subdev_nodes - Registers subdevices device nodes with ++ * unrestricted access to the subdevice userspace operations ++ * ++ * Internally calls __v4l2_device_register_subdev_nodes(). See its documentation ++ * for more details. ++ * ++ * @v4l2_dev: pointer to struct v4l2_device ++ */ ++static inline int __must_check ++v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) ++{ ++#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) ++ return __v4l2_device_register_subdev_nodes(v4l2_dev, false); ++#else ++ return 0; ++#endif ++} ++ ++/** ++ * v4l2_device_register_ro_subdev_nodes - Registers subdevices device nodes ++ * in read-only mode ++ * ++ * Internally calls __v4l2_device_register_subdev_nodes(). See its documentation ++ * for more details. ++ * ++ * @v4l2_dev: pointer to struct v4l2_device ++ */ ++static inline int __must_check ++v4l2_device_register_ro_subdev_nodes(struct v4l2_device *v4l2_dev) ++{ ++#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) ++ return __v4l2_device_register_subdev_nodes(v4l2_dev, true); ++#else ++ return 0; ++#endif ++} + + /** + * v4l2_subdev_notify - Sends a notification to v4l2_device. diff --git a/target/linux/bcm27xx/patches-5.4/950-0649-media-bcm2835-unicam-Driver-for-CCP2-CSI2-camera-int.patch b/target/linux/bcm27xx/patches-5.4/950-0649-media-bcm2835-unicam-Driver-for-CCP2-CSI2-camera-int.patch new file mode 100644 index 0000000000..e142f598ad --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0649-media-bcm2835-unicam-Driver-for-CCP2-CSI2-camera-int.patch @@ -0,0 +1,2709 @@ +From 33a897162230dfc35b854aae2bec1ce8c2996642 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Wed, 1 Apr 2020 08:39:49 +0100 +Subject: [PATCH] media: bcm2835-unicam: Driver for CCP2/CSI2 camera + interface + +Add driver for the Unicam camera receiver block on +BCM283x processors. + +This commit is made up of a series of changes cherry-picked from the +rpi-4.19.y branch. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + MAINTAINERS | 2 +- + drivers/media/platform/Kconfig | 1 + + drivers/media/platform/Makefile | 2 + + drivers/media/platform/bcm2835/Kconfig | 14 + + drivers/media/platform/bcm2835/Makefile | 3 + + .../media/platform/bcm2835/bcm2835-unicam.c | 2369 +++++++++++++++++ + .../media/platform/bcm2835/vc4-regs-unicam.h | 253 ++ + 7 files changed, 2643 insertions(+), 1 deletion(-) + create mode 100644 drivers/media/platform/bcm2835/Kconfig + create mode 100644 drivers/media/platform/bcm2835/Makefile + create mode 100644 drivers/media/platform/bcm2835/bcm2835-unicam.c + create mode 100644 drivers/media/platform/bcm2835/vc4-regs-unicam.h + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -3206,7 +3206,7 @@ F: Documentation/devicetree/bindings/med + F: drivers/staging/media/rpivid + + BROADCOM BCM2835 CAMERA DRIVER +-M: Dave Stevenson <dave.stevenson@raspberrypi.org> ++M: Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com> + L: linux-media@vger.kernel.org + S: Maintained + F: drivers/media/platform/bcm2835/ +--- a/drivers/media/platform/Kconfig ++++ b/drivers/media/platform/Kconfig +@@ -146,6 +146,7 @@ source "drivers/media/platform/am437x/Kc + source "drivers/media/platform/xilinx/Kconfig" + source "drivers/media/platform/rcar-vin/Kconfig" + source "drivers/media/platform/atmel/Kconfig" ++source "drivers/media/platform/bcm2835/Kconfig" + source "drivers/media/platform/sunxi/Kconfig" + + config VIDEO_TI_CAL +--- a/drivers/media/platform/Makefile ++++ b/drivers/media/platform/Makefile +@@ -100,4 +100,6 @@ obj-y += meson/ + + obj-y += cros-ec-cec/ + ++obj-y += bcm2835/ ++ + obj-y += sunxi/ +--- /dev/null ++++ b/drivers/media/platform/bcm2835/Kconfig +@@ -0,0 +1,14 @@ ++# Broadcom VideoCore4 V4L2 camera support ++ ++config VIDEO_BCM2835_UNICAM ++ tristate "Broadcom BCM2835 Unicam video capture driver" ++ depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER ++ depends on ARCH_BCM2835 || COMPILE_TEST ++ select VIDEOBUF2_DMA_CONTIG ++ select V4L2_FWNODE ++ help ++ Say Y here to enable V4L2 subdevice for CSI2 receiver. ++ This is a V4L2 subdevice that interfaces directly to the VC4 peripheral. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called bcm2835-unicam. +--- /dev/null ++++ b/drivers/media/platform/bcm2835/Makefile +@@ -0,0 +1,3 @@ ++# Makefile for BCM2835 Unicam driver ++ ++obj-$(CONFIG_VIDEO_BCM2835_UNICAM) += bcm2835-unicam.o +--- /dev/null ++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c +@@ -0,0 +1,2369 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * BCM2835 Unicam Capture Driver ++ * ++ * Copyright (C) 2017-2020 - Raspberry Pi (Trading) Ltd. ++ * ++ * Dave Stevenson <dave.stevenson@raspberrypi.com> ++ * ++ * Based on TI am437x driver by ++ * Benoit Parrot <bparrot@ti.com> ++ * Lad, Prabhakar <prabhakar.csengg@gmail.com> ++ * ++ * and TI CAL camera interface driver by ++ * Benoit Parrot <bparrot@ti.com> ++ * ++ * ++ * There are two camera drivers in the kernel for BCM283x - this one ++ * and bcm2835-camera (currently in staging). ++ * ++ * This driver directly controls the Unicam peripheral - there is no ++ * involvement with the VideoCore firmware. Unicam receives CSI-2 or ++ * CCP2 data and writes it into SDRAM. ++ * The only potential processing options are to repack Bayer data into an ++ * alternate format, and applying windowing. ++ * The repacking does not shift the data, so can repack V4L2_PIX_FMT_Sxxxx10P ++ * to V4L2_PIX_FMT_Sxxxx10, or V4L2_PIX_FMT_Sxxxx12P to V4L2_PIX_FMT_Sxxxx12, ++ * but not generically up to V4L2_PIX_FMT_Sxxxx16. The driver will add both ++ * formats where the relevant formats are defined, and will automatically ++ * configure the repacking as required. ++ * Support for windowing may be added later. ++ * ++ * It should be possible to connect this driver to any sensor with a ++ * suitable output interface and V4L2 subdevice driver. ++ * ++ * bcm2835-camera uses the VideoCore firmware to control the sensor, ++ * Unicam, ISP, and all tuner control loops. Fully processed frames are ++ * delivered to the driver by the firmware. It only has sensor drivers ++ * for Omnivision OV5647, and Sony IMX219 sensors. ++ * ++ * The two drivers are mutually exclusive for the same Unicam instance. ++ * The VideoCore firmware checks the device tree configuration during boot. ++ * If it finds device tree nodes called csi0 or csi1 it will block the ++ * firmware from accessing the peripheral, and bcm2835-camera will ++ * not be able to stream data. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <linux/device.h> ++#include <linux/err.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of_device.h> ++#include <linux/of_graph.h> ++#include <linux/pinctrl/consumer.h> ++#include <linux/platform_device.h> ++#include <linux/pm_runtime.h> ++#include <linux/slab.h> ++#include <linux/uaccess.h> ++#include <linux/videodev2.h> ++ ++#include <media/v4l2-common.h> ++#include <media/v4l2-ctrls.h> ++#include <media/v4l2-dev.h> ++#include <media/v4l2-device.h> ++#include <media/v4l2-dv-timings.h> ++#include <media/v4l2-event.h> ++#include <media/v4l2-ioctl.h> ++#include <media/v4l2-fwnode.h> ++#include <media/videobuf2-dma-contig.h> ++ ++#include "vc4-regs-unicam.h" ++ ++#define UNICAM_MODULE_NAME "unicam" ++#define UNICAM_VERSION "0.1.0" ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Debug level 0-3"); ++ ++#define unicam_dbg(level, dev, fmt, arg...) \ ++ v4l2_dbg(level, debug, &(dev)->v4l2_dev, fmt, ##arg) ++#define unicam_info(dev, fmt, arg...) \ ++ v4l2_info(&(dev)->v4l2_dev, fmt, ##arg) ++#define unicam_err(dev, fmt, arg...) \ ++ v4l2_err(&(dev)->v4l2_dev, fmt, ##arg) ++ ++/* To protect against a dodgy sensor driver never returning an error from ++ * enum_mbus_code, set a maximum index value to be used. ++ */ ++#define MAX_ENUM_MBUS_CODE 128 ++ ++/* ++ * Stride is a 16 bit register, but also has to be a multiple of 32. ++ */ ++#define BPL_ALIGNMENT 32 ++#define MAX_BYTESPERLINE ((1 << 16) - BPL_ALIGNMENT) ++/* ++ * Max width is therefore determined by the max stride divided by ++ * the number of bits per pixel. Take 32bpp as a ++ * worst case. ++ * No imposed limit on the height, so adopt a square image for want ++ * of anything better. ++ */ ++#define MAX_WIDTH (MAX_BYTESPERLINE / 4) ++#define MAX_HEIGHT MAX_WIDTH ++/* Define a nominal minimum image size */ ++#define MIN_WIDTH 16 ++#define MIN_HEIGHT 16 ++ ++/* ++ * struct unicam_fmt - Unicam media bus format information ++ * @pixelformat: V4L2 pixel format FCC identifier. 0 if n/a. ++ * @repacked_fourcc: V4L2 pixel format FCC identifier if the data is expanded ++ * out to 16bpp. 0 if n/a. ++ * @code: V4L2 media bus format code. ++ * @depth: Bits per pixel as delivered from the source. ++ * @csi_dt: CSI data type. ++ * @check_variants: Flag to denote that there are multiple mediabus formats ++ * still in the list that could match this V4L2 format. ++ */ ++struct unicam_fmt { ++ u32 fourcc; ++ u32 repacked_fourcc; ++ u32 code; ++ u8 depth; ++ u8 csi_dt; ++ u8 check_variants; ++}; ++ ++static const struct unicam_fmt formats[] = { ++ /* YUV Formats */ ++ { ++ .fourcc = V4L2_PIX_FMT_YUYV, ++ .code = MEDIA_BUS_FMT_YUYV8_2X8, ++ .depth = 16, ++ .csi_dt = 0x1e, ++ .check_variants = 1, ++ }, { ++ .fourcc = V4L2_PIX_FMT_UYVY, ++ .code = MEDIA_BUS_FMT_UYVY8_2X8, ++ .depth = 16, ++ .csi_dt = 0x1e, ++ .check_variants = 1, ++ }, { ++ .fourcc = V4L2_PIX_FMT_YVYU, ++ .code = MEDIA_BUS_FMT_YVYU8_2X8, ++ .depth = 16, ++ .csi_dt = 0x1e, ++ .check_variants = 1, ++ }, { ++ .fourcc = V4L2_PIX_FMT_VYUY, ++ .code = MEDIA_BUS_FMT_VYUY8_2X8, ++ .depth = 16, ++ .csi_dt = 0x1e, ++ .check_variants = 1, ++ }, { ++ .fourcc = V4L2_PIX_FMT_YUYV, ++ .code = MEDIA_BUS_FMT_YUYV8_1X16, ++ .depth = 16, ++ .csi_dt = 0x1e, ++ }, { ++ .fourcc = V4L2_PIX_FMT_UYVY, ++ .code = MEDIA_BUS_FMT_UYVY8_1X16, ++ .depth = 16, ++ .csi_dt = 0x1e, ++ }, { ++ .fourcc = V4L2_PIX_FMT_YVYU, ++ .code = MEDIA_BUS_FMT_YVYU8_1X16, ++ .depth = 16, ++ .csi_dt = 0x1e, ++ }, { ++ .fourcc = V4L2_PIX_FMT_VYUY, ++ .code = MEDIA_BUS_FMT_VYUY8_1X16, ++ .depth = 16, ++ .csi_dt = 0x1e, ++ }, { ++ /* RGB Formats */ ++ .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ ++ .code = MEDIA_BUS_FMT_RGB565_2X8_LE, ++ .depth = 16, ++ .csi_dt = 0x22, ++ }, { ++ .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ ++ .code = MEDIA_BUS_FMT_RGB565_2X8_BE, ++ .depth = 16, ++ .csi_dt = 0x22 ++ }, { ++ .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ ++ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, ++ .depth = 16, ++ .csi_dt = 0x21, ++ }, { ++ .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ ++ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, ++ .depth = 16, ++ .csi_dt = 0x21, ++ }, { ++ .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ ++ .code = MEDIA_BUS_FMT_RGB888_1X24, ++ .depth = 24, ++ .csi_dt = 0x24, ++ }, { ++ .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ ++ .code = MEDIA_BUS_FMT_BGR888_1X24, ++ .depth = 24, ++ .csi_dt = 0x24, ++ }, { ++ .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ ++ .code = MEDIA_BUS_FMT_ARGB8888_1X32, ++ .depth = 32, ++ .csi_dt = 0x0, ++ }, { ++ /* Bayer Formats */ ++ .fourcc = V4L2_PIX_FMT_SBGGR8, ++ .code = MEDIA_BUS_FMT_SBGGR8_1X8, ++ .depth = 8, ++ .csi_dt = 0x2a, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SGBRG8, ++ .code = MEDIA_BUS_FMT_SGBRG8_1X8, ++ .depth = 8, ++ .csi_dt = 0x2a, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SGRBG8, ++ .code = MEDIA_BUS_FMT_SGRBG8_1X8, ++ .depth = 8, ++ .csi_dt = 0x2a, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SRGGB8, ++ .code = MEDIA_BUS_FMT_SRGGB8_1X8, ++ .depth = 8, ++ .csi_dt = 0x2a, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SBGGR10P, ++ .repacked_fourcc = V4L2_PIX_FMT_SBGGR10, ++ .code = MEDIA_BUS_FMT_SBGGR10_1X10, ++ .depth = 10, ++ .csi_dt = 0x2b, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SGBRG10P, ++ .repacked_fourcc = V4L2_PIX_FMT_SGBRG10, ++ .code = MEDIA_BUS_FMT_SGBRG10_1X10, ++ .depth = 10, ++ .csi_dt = 0x2b, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SGRBG10P, ++ .repacked_fourcc = V4L2_PIX_FMT_SGRBG10, ++ .code = MEDIA_BUS_FMT_SGRBG10_1X10, ++ .depth = 10, ++ .csi_dt = 0x2b, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SRGGB10P, ++ .repacked_fourcc = V4L2_PIX_FMT_SRGGB10, ++ .code = MEDIA_BUS_FMT_SRGGB10_1X10, ++ .depth = 10, ++ .csi_dt = 0x2b, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SBGGR12P, ++ .repacked_fourcc = V4L2_PIX_FMT_SBGGR12, ++ .code = MEDIA_BUS_FMT_SBGGR12_1X12, ++ .depth = 12, ++ .csi_dt = 0x2c, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SGBRG12P, ++ .repacked_fourcc = V4L2_PIX_FMT_SGBRG12, ++ .code = MEDIA_BUS_FMT_SGBRG12_1X12, ++ .depth = 12, ++ .csi_dt = 0x2c, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SGRBG12P, ++ .repacked_fourcc = V4L2_PIX_FMT_SGRBG12, ++ .code = MEDIA_BUS_FMT_SGRBG12_1X12, ++ .depth = 12, ++ .csi_dt = 0x2c, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SRGGB12P, ++ .repacked_fourcc = V4L2_PIX_FMT_SRGGB12, ++ .code = MEDIA_BUS_FMT_SRGGB12_1X12, ++ .depth = 12, ++ .csi_dt = 0x2c, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SBGGR14P, ++ .code = MEDIA_BUS_FMT_SBGGR14_1X14, ++ .depth = 14, ++ .csi_dt = 0x2d, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SGBRG14P, ++ .code = MEDIA_BUS_FMT_SGBRG14_1X14, ++ .depth = 14, ++ .csi_dt = 0x2d, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SGRBG14P, ++ .code = MEDIA_BUS_FMT_SGRBG14_1X14, ++ .depth = 14, ++ .csi_dt = 0x2d, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SRGGB14P, ++ .code = MEDIA_BUS_FMT_SRGGB14_1X14, ++ .depth = 14, ++ .csi_dt = 0x2d, ++ }, { ++ /* ++ * 16 bit Bayer formats could be supported, but there is no CSI2 ++ * data_type defined for raw 16, and no sensors that produce it at ++ * present. ++ */ ++ ++ /* Greyscale formats */ ++ .fourcc = V4L2_PIX_FMT_GREY, ++ .code = MEDIA_BUS_FMT_Y8_1X8, ++ .depth = 8, ++ .csi_dt = 0x2a, ++ }, { ++ .fourcc = V4L2_PIX_FMT_Y10P, ++ .repacked_fourcc = V4L2_PIX_FMT_Y10, ++ .code = MEDIA_BUS_FMT_Y10_1X10, ++ .depth = 10, ++ .csi_dt = 0x2b, ++ }, { ++ /* NB There is no packed V4L2 fourcc for this format. */ ++ .repacked_fourcc = V4L2_PIX_FMT_Y12, ++ .code = MEDIA_BUS_FMT_Y12_1X12, ++ .depth = 12, ++ .csi_dt = 0x2c, ++ }, ++}; ++ ++struct unicam_dmaqueue { ++ struct list_head active; ++}; ++ ++struct unicam_buffer { ++ struct vb2_v4l2_buffer vb; ++ struct list_head list; ++}; ++ ++struct unicam_cfg { ++ /* peripheral base address */ ++ void __iomem *base; ++ /* clock gating base address */ ++ void __iomem *clk_gate_base; ++}; ++ ++#define MAX_POSSIBLE_PIX_FMTS (ARRAY_SIZE(formats)) ++ ++struct unicam_device { ++ /* V4l2 specific parameters */ ++ /* Identifies video device for this channel */ ++ struct video_device video_dev; ++ struct v4l2_ctrl_handler ctrl_handler; ++ ++ struct v4l2_fwnode_endpoint endpoint; ++ ++ struct v4l2_async_subdev asd; ++ ++ /* unicam cfg */ ++ struct unicam_cfg cfg; ++ /* clock handle */ ++ struct clk *clock; ++ /* V4l2 device */ ++ struct v4l2_device v4l2_dev; ++ struct media_device mdev; ++ struct media_pad pad; ++ ++ /* parent device */ ++ struct platform_device *pdev; ++ /* subdevice async Notifier */ ++ struct v4l2_async_notifier notifier; ++ unsigned int sequence; ++ ++ /* ptr to sub device */ ++ struct v4l2_subdev *sensor; ++ /* Pad config for the sensor */ ++ struct v4l2_subdev_pad_config *sensor_config; ++ /* current input at the sub device */ ++ int current_input; ++ ++ /* Pointer pointing to current v4l2_buffer */ ++ struct unicam_buffer *cur_frm; ++ /* Pointer pointing to next v4l2_buffer */ ++ struct unicam_buffer *next_frm; ++ ++ /* video capture */ ++ const struct unicam_fmt *fmt; ++ /* Used to store current pixel format */ ++ struct v4l2_format v_fmt; ++ /* Used to store current mbus frame format */ ++ struct v4l2_mbus_framefmt m_fmt; ++ ++ unsigned int virtual_channel; ++ enum v4l2_mbus_type bus_type; ++ /* ++ * Stores bus.mipi_csi2.flags for CSI2 sensors, or ++ * bus.mipi_csi1.strobe for CCP2. ++ */ ++ unsigned int bus_flags; ++ unsigned int max_data_lanes; ++ unsigned int active_data_lanes; ++ ++ struct v4l2_rect crop; ++ ++ /* Currently selected input on subdev */ ++ int input; ++ ++ /* Buffer queue used in video-buf */ ++ struct vb2_queue buffer_queue; ++ /* Queue of filled frames */ ++ struct unicam_dmaqueue dma_queue; ++ /* IRQ lock for DMA queue */ ++ spinlock_t dma_queue_lock; ++ /* lock used to access this structure */ ++ struct mutex lock; ++ /* Flag to denote that we are processing buffers */ ++ int streaming; ++}; ++ ++/* Hardware access */ ++#define clk_write(dev, val) writel((val) | 0x5a000000, (dev)->clk_gate_base) ++#define clk_read(dev) readl((dev)->clk_gate_base) ++ ++#define reg_read(dev, offset) readl((dev)->base + (offset)) ++#define reg_write(dev, offset, val) writel(val, (dev)->base + (offset)) ++ ++#define reg_read_field(dev, offset, mask) get_field(reg_read((dev), (offset), \ ++ mask)) ++ ++static inline int get_field(u32 value, u32 mask) ++{ ++ return (value & mask) >> __ffs(mask); ++} ++ ++static inline void set_field(u32 *valp, u32 field, u32 mask) ++{ ++ u32 val = *valp; ++ ++ val &= ~mask; ++ val |= (field << __ffs(mask)) & mask; ++ *valp = val; ++} ++ ++static inline void reg_write_field(struct unicam_cfg *dev, u32 offset, ++ u32 field, u32 mask) ++{ ++ u32 val = reg_read((dev), (offset)); ++ ++ set_field(&val, field, mask); ++ reg_write((dev), (offset), val); ++} ++ ++/* Power management functions */ ++static inline int unicam_runtime_get(struct unicam_device *dev) ++{ ++ return pm_runtime_get_sync(&dev->pdev->dev); ++} ++ ++static inline void unicam_runtime_put(struct unicam_device *dev) ++{ ++ pm_runtime_put_sync(&dev->pdev->dev); ++} ++ ++/* Format setup functions */ ++static const struct unicam_fmt *find_format_by_code(u32 code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(formats); i++) { ++ if (formats[i].code == code) ++ return &formats[i]; ++ } ++ ++ return NULL; ++} ++ ++static int check_mbus_format(struct unicam_device *dev, ++ const struct unicam_fmt *format) ++{ ++ struct v4l2_subdev_mbus_code_enum mbus_code; ++ int ret = 0; ++ int i; ++ ++ for (i = 0; !ret && i < MAX_ENUM_MBUS_CODE; i++) { ++ memset(&mbus_code, 0, sizeof(mbus_code)); ++ mbus_code.index = i; ++ mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; ++ ++ ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, ++ NULL, &mbus_code); ++ ++ if (!ret && mbus_code.code == format->code) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static const struct unicam_fmt *find_format_by_pix(struct unicam_device *dev, ++ u32 pixelformat) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(formats); i++) { ++ if (formats[i].fourcc == pixelformat || ++ formats[i].repacked_fourcc == pixelformat) { ++ if (formats[i].check_variants && ++ !check_mbus_format(dev, &formats[i])) ++ continue; ++ return &formats[i]; ++ } ++ } ++ ++ return NULL; ++} ++ ++static inline unsigned int bytes_per_line(u32 width, ++ const struct unicam_fmt *fmt, ++ u32 v4l2_fourcc) ++{ ++ if (v4l2_fourcc == fmt->repacked_fourcc) ++ /* Repacking always goes to 16bpp */ ++ return ALIGN(width << 1, BPL_ALIGNMENT); ++ else ++ return ALIGN((width * fmt->depth) >> 3, BPL_ALIGNMENT); ++} ++ ++static int __subdev_get_format(struct unicam_device *dev, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ struct v4l2_subdev_format sd_fmt = { ++ .which = V4L2_SUBDEV_FORMAT_ACTIVE, ++ }; ++ int ret; ++ ++ ret = v4l2_subdev_call(dev->sensor, pad, get_fmt, dev->sensor_config, ++ &sd_fmt); ++ if (ret < 0) ++ return ret; ++ ++ *fmt = sd_fmt.format; ++ ++ unicam_dbg(1, dev, "%s %dx%d code:%04x\n", __func__, ++ fmt->width, fmt->height, fmt->code); ++ ++ return 0; ++} ++ ++static int __subdev_set_format(struct unicam_device *dev, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ struct v4l2_subdev_format sd_fmt = { ++ .which = V4L2_SUBDEV_FORMAT_ACTIVE, ++ }; ++ int ret; ++ ++ sd_fmt.format = *fmt; ++ ++ ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_config, ++ &sd_fmt); ++ if (ret < 0) ++ return ret; ++ ++ unicam_dbg(1, dev, "%s %dx%d code:%04x\n", __func__, ++ fmt->width, fmt->height, fmt->code); ++ ++ return 0; ++} ++ ++static int unicam_calc_format_size_bpl(struct unicam_device *dev, ++ const struct unicam_fmt *fmt, ++ struct v4l2_format *f) ++{ ++ unsigned int min_bytesperline; ++ ++ v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 2, ++ &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 0, ++ 0); ++ ++ min_bytesperline = bytes_per_line(f->fmt.pix.width, fmt, ++ f->fmt.pix.pixelformat); ++ ++ if (f->fmt.pix.bytesperline > min_bytesperline && ++ f->fmt.pix.bytesperline <= MAX_BYTESPERLINE) ++ f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.bytesperline, ++ BPL_ALIGNMENT); ++ else ++ f->fmt.pix.bytesperline = min_bytesperline; ++ ++ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; ++ ++ unicam_dbg(3, dev, "%s: fourcc: %08X size: %dx%d bpl:%d img_size:%d\n", ++ __func__, ++ f->fmt.pix.pixelformat, ++ f->fmt.pix.width, f->fmt.pix.height, ++ f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); ++ ++ return 0; ++} ++ ++static int unicam_reset_format(struct unicam_device *dev) ++{ ++ struct v4l2_mbus_framefmt mbus_fmt; ++ int ret; ++ ++ ret = __subdev_get_format(dev, &mbus_fmt); ++ if (ret) { ++ unicam_err(dev, "Failed to get_format - ret %d\n", ret); ++ return ret; ++ } ++ ++ if (mbus_fmt.code != dev->fmt->code) { ++ unicam_err(dev, "code mismatch - fmt->code %08x, mbus_fmt.code %08x\n", ++ dev->fmt->code, mbus_fmt.code); ++ return ret; ++ } ++ ++ v4l2_fill_pix_format(&dev->v_fmt.fmt.pix, &mbus_fmt); ++ dev->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ unicam_calc_format_size_bpl(dev, dev->fmt, &dev->v_fmt); ++ ++ dev->m_fmt = mbus_fmt; ++ ++ return 0; ++} ++ ++static void unicam_wr_dma_addr(struct unicam_device *dev, dma_addr_t dmaaddr) ++{ ++ /* ++ * dmaaddr should be a 32-bit address with the top two bits set to 0x3 ++ * to signify uncached access through the Videocore memory controller. ++ */ ++ BUG_ON((dmaaddr >> 30) != 0x3); ++ ++ reg_write(&dev->cfg, UNICAM_IBSA0, dmaaddr); ++ reg_write(&dev->cfg, UNICAM_IBEA0, ++ dmaaddr + dev->v_fmt.fmt.pix.sizeimage); ++} ++ ++static inline unsigned int unicam_get_lines_done(struct unicam_device *dev) ++{ ++ dma_addr_t start_addr, cur_addr; ++ unsigned int stride = dev->v_fmt.fmt.pix.bytesperline; ++ struct unicam_buffer *frm = dev->cur_frm; ++ ++ if (!frm) ++ return 0; ++ ++ start_addr = vb2_dma_contig_plane_dma_addr(&frm->vb.vb2_buf, 0); ++ cur_addr = reg_read(&dev->cfg, UNICAM_IBWP); ++ return (unsigned int)(cur_addr - start_addr) / stride; ++} ++ ++static inline void unicam_schedule_next_buffer(struct unicam_device *dev) ++{ ++ struct unicam_dmaqueue *dma_q = &dev->dma_queue; ++ struct unicam_buffer *buf; ++ dma_addr_t addr; ++ ++ buf = list_entry(dma_q->active.next, struct unicam_buffer, list); ++ dev->next_frm = buf; ++ list_del(&buf->list); ++ ++ addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); ++ unicam_wr_dma_addr(dev, addr); ++} ++ ++static inline void unicam_process_buffer_complete(struct unicam_device *dev) ++{ ++ dev->cur_frm->vb.field = dev->m_fmt.field; ++ dev->cur_frm->vb.sequence = dev->sequence++; ++ ++ vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); ++ dev->cur_frm = dev->next_frm; ++} ++ ++/* ++ * unicam_isr : ISR handler for unicam capture ++ * @irq: irq number ++ * @dev_id: dev_id ptr ++ * ++ * It changes status of the captured buffer, takes next buffer from the queue ++ * and sets its address in unicam registers ++ */ ++static irqreturn_t unicam_isr(int irq, void *dev) ++{ ++ struct unicam_device *unicam = (struct unicam_device *)dev; ++ struct unicam_cfg *cfg = &unicam->cfg; ++ struct unicam_dmaqueue *dma_q = &unicam->dma_queue; ++ unsigned int lines_done = unicam_get_lines_done(dev); ++ unsigned int sequence = unicam->sequence; ++ int ista, sta; ++ ++ /* ++ * Don't service interrupts if not streaming. ++ * Avoids issues if the VPU should enable the ++ * peripheral without the kernel knowing (that ++ * shouldn't happen, but causes issues if it does). ++ */ ++ if (!unicam->streaming) ++ return IRQ_HANDLED; ++ ++ sta = reg_read(cfg, UNICAM_STA); ++ /* Write value back to clear the interrupts */ ++ reg_write(cfg, UNICAM_STA, sta); ++ ++ ista = reg_read(cfg, UNICAM_ISTA); ++ /* Write value back to clear the interrupts */ ++ reg_write(cfg, UNICAM_ISTA, ista); ++ ++ unicam_dbg(3, unicam, "ISR: ISTA: 0x%X, STA: 0x%X, sequence %d, lines done %d", ++ ista, sta, sequence, lines_done); ++ ++ if (!(sta && (UNICAM_IS | UNICAM_PI0))) ++ return IRQ_HANDLED; ++ ++ if (ista & UNICAM_FSI) { ++ /* ++ * Timestamp is to be when the first data byte was captured, ++ * aka frame start. ++ */ ++ if (unicam->cur_frm) ++ unicam->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns(); ++ } ++ if (ista & UNICAM_FEI || sta & UNICAM_PI0) { ++ /* ++ * Ensure we have swapped buffers already as we can't ++ * stop the peripheral. Overwrite the frame we've just ++ * captured instead. ++ */ ++ if (unicam->cur_frm && unicam->cur_frm != unicam->next_frm) ++ unicam_process_buffer_complete(unicam); ++ } ++ ++ /* Cannot swap buffer at frame end, there may be a race condition ++ * where the HW does not actually swap it if the new frame has ++ * already started. ++ */ ++ if (ista & (UNICAM_FSI | UNICAM_LCI) && !(ista & UNICAM_FEI)) { ++ spin_lock(&unicam->dma_queue_lock); ++ if (!list_empty(&dma_q->active) && ++ unicam->cur_frm == unicam->next_frm) ++ unicam_schedule_next_buffer(unicam); ++ spin_unlock(&unicam->dma_queue_lock); ++ } ++ ++ if (reg_read(&unicam->cfg, UNICAM_ICTL) & UNICAM_FCM) { ++ /* Switch out of trigger mode if selected */ ++ reg_write_field(&unicam->cfg, UNICAM_ICTL, 1, UNICAM_TFC); ++ reg_write_field(&unicam->cfg, UNICAM_ICTL, 0, UNICAM_FCM); ++ } ++ return IRQ_HANDLED; ++} ++ ++static int unicam_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ ++ strlcpy(cap->driver, UNICAM_MODULE_NAME, sizeof(cap->driver)); ++ strlcpy(cap->card, UNICAM_MODULE_NAME, sizeof(cap->card)); ++ ++ snprintf(cap->bus_info, sizeof(cap->bus_info), ++ "platform:%s", dev->v4l2_dev.name); ++ ++ return 0; ++} ++ ++static int unicam_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ struct v4l2_subdev_mbus_code_enum mbus_code; ++ const struct unicam_fmt *fmt = NULL; ++ int index = 0; ++ int ret = 0; ++ int i; ++ ++ for (i = 0; !ret && i < MAX_ENUM_MBUS_CODE; i++) { ++ memset(&mbus_code, 0, sizeof(mbus_code)); ++ mbus_code.index = i; ++ ++ ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, ++ NULL, &mbus_code); ++ if (ret < 0) { ++ unicam_dbg(2, dev, ++ "subdev->enum_mbus_code idx %d returned %d - index invalid\n", ++ i, ret); ++ return -EINVAL; ++ } ++ ++ fmt = find_format_by_code(mbus_code.code); ++ if (fmt) { ++ if (fmt->fourcc) { ++ if (index == f->index) { ++ f->pixelformat = fmt->fourcc; ++ break; ++ } ++ index++; ++ } ++ if (fmt->repacked_fourcc) { ++ if (index == f->index) { ++ f->pixelformat = fmt->repacked_fourcc; ++ break; ++ } ++ index++; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static int unicam_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ ++ *f = dev->v_fmt; ++ ++ return 0; ++} ++ ++static ++const struct unicam_fmt *get_first_supported_format(struct unicam_device *dev) ++{ ++ struct v4l2_subdev_mbus_code_enum mbus_code; ++ const struct unicam_fmt *fmt = NULL; ++ int ret; ++ int j; ++ ++ for (j = 0; ret != -EINVAL && ret != -ENOIOCTLCMD; ++j) { ++ memset(&mbus_code, 0, sizeof(mbus_code)); ++ mbus_code.index = j; ++ ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, NULL, ++ &mbus_code); ++ if (ret < 0) { ++ unicam_dbg(2, dev, ++ "subdev->enum_mbus_code idx %d returned %d - continue\n", ++ j, ret); ++ continue; ++ } ++ ++ unicam_dbg(2, dev, "subdev %s: code: 0x%08x idx: %d\n", ++ dev->sensor->name, mbus_code.code, j); ++ ++ fmt = find_format_by_code(mbus_code.code); ++ unicam_dbg(2, dev, "fmt 0x%08x returned as %p, V4L2 FOURCC 0x%08x, csi_dt 0x%02x\n", ++ mbus_code.code, fmt, fmt ? fmt->fourcc : 0, ++ fmt ? fmt->csi_dt : 0); ++ if (fmt) ++ return fmt; ++ } ++ ++ return NULL; ++} ++ ++static int unicam_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ struct v4l2_subdev_format sd_fmt = { ++ .which = V4L2_SUBDEV_FORMAT_TRY, ++ }; ++ struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; ++ const struct unicam_fmt *fmt; ++ int ret; ++ ++ fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat); ++ if (!fmt) { ++ /* Pixel format not supported by unicam. Choose the first ++ * supported format, and let the sensor choose something else. ++ */ ++ unicam_dbg(3, dev, "Fourcc format (0x%08x) not found. Use first format.\n", ++ f->fmt.pix.pixelformat); ++ ++ fmt = &formats[0]; ++ f->fmt.pix.pixelformat = fmt->fourcc; ++ } ++ ++ v4l2_fill_mbus_format(mbus_fmt, &f->fmt.pix, fmt->code); ++ /* ++ * No support for receiving interlaced video, so never ++ * request it from the sensor subdev. ++ */ ++ mbus_fmt->field = V4L2_FIELD_NONE; ++ ++ ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_config, ++ &sd_fmt); ++ if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) ++ return ret; ++ ++ if (mbus_fmt->field != V4L2_FIELD_NONE) ++ unicam_info(dev, "Sensor trying to send interlaced video - results may be unpredictable\n"); ++ ++ v4l2_fill_pix_format(&f->fmt.pix, &sd_fmt.format); ++ if (mbus_fmt->code != fmt->code) { ++ /* Sensor has returned an alternate format */ ++ fmt = find_format_by_code(mbus_fmt->code); ++ if (!fmt) { ++ /* The alternate format is one unicam can't support. ++ * Find the first format that is supported by both, and ++ * then set that. ++ */ ++ fmt = get_first_supported_format(dev); ++ mbus_fmt->code = fmt->code; ++ ++ ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, ++ dev->sensor_config, &sd_fmt); ++ if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) ++ return ret; ++ ++ if (mbus_fmt->field != V4L2_FIELD_NONE) ++ unicam_info(dev, "Sensor trying to send interlaced video - results may be unpredictable\n"); ++ ++ v4l2_fill_pix_format(&f->fmt.pix, &sd_fmt.format); ++ ++ if (mbus_fmt->code != fmt->code) { ++ /* We've set a format that the sensor reports ++ * as being supported, but it refuses to set it. ++ * Not much else we can do. ++ * Assume that the sensor driver may accept the ++ * format when it is set (rather than tried). ++ */ ++ unicam_err(dev, "Sensor won't accept default format, and Unicam can't support sensor default\n"); ++ } ++ } ++ ++ if (fmt->fourcc) ++ f->fmt.pix.pixelformat = fmt->fourcc; ++ else ++ f->fmt.pix.pixelformat = fmt->repacked_fourcc; ++ } ++ ++ return unicam_calc_format_size_bpl(dev, fmt, f); ++} ++ ++static int unicam_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ struct vb2_queue *q = &dev->buffer_queue; ++ struct v4l2_mbus_framefmt mbus_fmt = {0}; ++ const struct unicam_fmt *fmt; ++ int ret; ++ ++ if (vb2_is_busy(q)) ++ return -EBUSY; ++ ++ ret = unicam_try_fmt_vid_cap(file, priv, f); ++ if (ret < 0) ++ return ret; ++ ++ fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat); ++ if (!fmt) { ++ /* Unknown pixel format - adopt a default. ++ * This shouldn't happen as try_fmt should have resolved any ++ * issues first. ++ */ ++ fmt = get_first_supported_format(dev); ++ if (!fmt) ++ /* It shouldn't be possible to get here with no ++ * supported formats ++ */ ++ return -EINVAL; ++ f->fmt.pix.pixelformat = fmt->fourcc; ++ return -EINVAL; ++ } ++ ++ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); ++ ++ ret = __subdev_set_format(dev, &mbus_fmt); ++ if (ret) { ++ unicam_dbg(3, dev, "%s __subdev_set_format failed %d\n", ++ __func__, ret); ++ return ret; ++ } ++ ++ /* Just double check nothing has gone wrong */ ++ if (mbus_fmt.code != fmt->code) { ++ unicam_dbg(3, dev, ++ "%s subdev changed format on us, this should not happen\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ dev->fmt = fmt; ++ dev->v_fmt.fmt.pix.pixelformat = f->fmt.pix.pixelformat; ++ dev->v_fmt.fmt.pix.bytesperline = f->fmt.pix.bytesperline; ++ unicam_reset_format(dev); ++ ++ unicam_dbg(3, dev, "%s %dx%d, mbus_fmt 0x%08X, V4L2 pix 0x%08X.\n", ++ __func__, dev->v_fmt.fmt.pix.width, ++ dev->v_fmt.fmt.pix.height, mbus_fmt.code, ++ dev->v_fmt.fmt.pix.pixelformat); ++ ++ *f = dev->v_fmt; ++ ++ return 0; ++} ++ ++static int unicam_queue_setup(struct vb2_queue *vq, ++ unsigned int *nbuffers, ++ unsigned int *nplanes, ++ unsigned int sizes[], ++ struct device *alloc_devs[]) ++{ ++ struct unicam_device *dev = vb2_get_drv_priv(vq); ++ unsigned int size = dev->v_fmt.fmt.pix.sizeimage; ++ ++ if (vq->num_buffers + *nbuffers < 3) ++ *nbuffers = 3 - vq->num_buffers; ++ ++ if (*nplanes) { ++ if (sizes[0] < size) { ++ unicam_err(dev, "sizes[0] %i < size %u\n", sizes[0], ++ size); ++ return -EINVAL; ++ } ++ size = sizes[0]; ++ } ++ ++ *nplanes = 1; ++ sizes[0] = size; ++ ++ return 0; ++} ++ ++static int unicam_buffer_prepare(struct vb2_buffer *vb) ++{ ++ struct unicam_device *dev = vb2_get_drv_priv(vb->vb2_queue); ++ struct unicam_buffer *buf = container_of(vb, struct unicam_buffer, ++ vb.vb2_buf); ++ unsigned long size; ++ ++ if (WARN_ON(!dev->fmt)) ++ return -EINVAL; ++ ++ size = dev->v_fmt.fmt.pix.sizeimage; ++ if (vb2_plane_size(vb, 0) < size) { ++ unicam_err(dev, "data will not fit into plane (%lu < %lu)\n", ++ vb2_plane_size(vb, 0), size); ++ return -EINVAL; ++ } ++ ++ vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); ++ return 0; ++} ++ ++static void unicam_buffer_queue(struct vb2_buffer *vb) ++{ ++ struct unicam_device *dev = vb2_get_drv_priv(vb->vb2_queue); ++ struct unicam_buffer *buf = container_of(vb, struct unicam_buffer, ++ vb.vb2_buf); ++ struct unicam_dmaqueue *dma_queue = &dev->dma_queue; ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&dev->dma_queue_lock, flags); ++ list_add_tail(&buf->list, &dma_queue->active); ++ spin_unlock_irqrestore(&dev->dma_queue_lock, flags); ++} ++ ++static void unicam_set_packing_config(struct unicam_device *dev) ++{ ++ int pack, unpack; ++ u32 val; ++ ++ if (dev->v_fmt.fmt.pix.pixelformat == dev->fmt->fourcc) { ++ unpack = UNICAM_PUM_NONE; ++ pack = UNICAM_PPM_NONE; ++ } else { ++ switch (dev->fmt->depth) { ++ case 8: ++ unpack = UNICAM_PUM_UNPACK8; ++ break; ++ case 10: ++ unpack = UNICAM_PUM_UNPACK10; ++ break; ++ case 12: ++ unpack = UNICAM_PUM_UNPACK12; ++ break; ++ case 14: ++ unpack = UNICAM_PUM_UNPACK14; ++ break; ++ case 16: ++ unpack = UNICAM_PUM_UNPACK16; ++ break; ++ default: ++ unpack = UNICAM_PUM_NONE; ++ break; ++ } ++ ++ /* Repacking is always to 16bpp */ ++ pack = UNICAM_PPM_PACK16; ++ } ++ ++ val = 0; ++ set_field(&val, unpack, UNICAM_PUM_MASK); ++ set_field(&val, pack, UNICAM_PPM_MASK); ++ reg_write(&dev->cfg, UNICAM_IPIPE, val); ++} ++ ++static void unicam_cfg_image_id(struct unicam_device *dev) ++{ ++ struct unicam_cfg *cfg = &dev->cfg; ++ ++ if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { ++ /* CSI2 mode */ ++ reg_write(cfg, UNICAM_IDI0, ++ (dev->virtual_channel << 6) | dev->fmt->csi_dt); ++ } else { ++ /* CCP2 mode */ ++ reg_write(cfg, UNICAM_IDI0, (0x80 | dev->fmt->csi_dt)); ++ } ++} ++ ++static void unicam_start_rx(struct unicam_device *dev, unsigned long addr) ++{ ++ struct unicam_cfg *cfg = &dev->cfg; ++ int line_int_freq = dev->v_fmt.fmt.pix.height >> 2; ++ unsigned int i; ++ u32 val; ++ ++ if (line_int_freq < 128) ++ line_int_freq = 128; ++ ++ /* Enable lane clocks */ ++ val = 1; ++ for (i = 0; i < dev->active_data_lanes; i++) ++ val = val << 2 | 1; ++ clk_write(cfg, val); ++ ++ /* Basic init */ ++ reg_write(cfg, UNICAM_CTRL, UNICAM_MEM); ++ ++ /* Enable analogue control, and leave in reset. */ ++ val = UNICAM_AR; ++ set_field(&val, 7, UNICAM_CTATADJ_MASK); ++ set_field(&val, 7, UNICAM_PTATADJ_MASK); ++ reg_write(cfg, UNICAM_ANA, val); ++ usleep_range(1000, 2000); ++ ++ /* Come out of reset */ ++ reg_write_field(cfg, UNICAM_ANA, 0, UNICAM_AR); ++ ++ /* Peripheral reset */ ++ reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_CPR); ++ reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPR); ++ ++ reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPE); ++ ++ /* Enable Rx control. */ ++ val = reg_read(cfg, UNICAM_CTRL); ++ if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { ++ set_field(&val, UNICAM_CPM_CSI2, UNICAM_CPM_MASK); ++ set_field(&val, UNICAM_DCM_STROBE, UNICAM_DCM_MASK); ++ } else { ++ set_field(&val, UNICAM_CPM_CCP2, UNICAM_CPM_MASK); ++ set_field(&val, dev->bus_flags, UNICAM_DCM_MASK); ++ } ++ /* Packet framer timeout */ ++ set_field(&val, 0xf, UNICAM_PFT_MASK); ++ set_field(&val, 128, UNICAM_OET_MASK); ++ reg_write(cfg, UNICAM_CTRL, val); ++ ++ reg_write(cfg, UNICAM_IHWIN, 0); ++ reg_write(cfg, UNICAM_IVWIN, 0); ++ ++ /* AXI bus access QoS setup */ ++ val = reg_read(&dev->cfg, UNICAM_PRI); ++ set_field(&val, 0, UNICAM_BL_MASK); ++ set_field(&val, 0, UNICAM_BS_MASK); ++ set_field(&val, 0xe, UNICAM_PP_MASK); ++ set_field(&val, 8, UNICAM_NP_MASK); ++ set_field(&val, 2, UNICAM_PT_MASK); ++ set_field(&val, 1, UNICAM_PE); ++ reg_write(cfg, UNICAM_PRI, val); ++ ++ reg_write_field(cfg, UNICAM_ANA, 0, UNICAM_DDL); ++ ++ /* Always start in trigger frame capture mode (UNICAM_FCM set) */ ++ val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_FCM; ++ set_field(&val, line_int_freq, UNICAM_LCIE_MASK); ++ reg_write(cfg, UNICAM_ICTL, val); ++ reg_write(cfg, UNICAM_STA, UNICAM_STA_MASK_ALL); ++ reg_write(cfg, UNICAM_ISTA, UNICAM_ISTA_MASK_ALL); ++ ++ /* tclk_term_en */ ++ reg_write_field(cfg, UNICAM_CLT, 2, UNICAM_CLT1_MASK); ++ /* tclk_settle */ ++ reg_write_field(cfg, UNICAM_CLT, 6, UNICAM_CLT2_MASK); ++ /* td_term_en */ ++ reg_write_field(cfg, UNICAM_DLT, 2, UNICAM_DLT1_MASK); ++ /* ths_settle */ ++ reg_write_field(cfg, UNICAM_DLT, 6, UNICAM_DLT2_MASK); ++ /* trx_enable */ ++ reg_write_field(cfg, UNICAM_DLT, 0, UNICAM_DLT3_MASK); ++ ++ reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_SOE); ++ ++ /* Packet compare setup - required to avoid missing frame ends */ ++ val = 0; ++ set_field(&val, 1, UNICAM_PCE); ++ set_field(&val, 1, UNICAM_GI); ++ set_field(&val, 1, UNICAM_CPH); ++ set_field(&val, 0, UNICAM_PCVC_MASK); ++ set_field(&val, 1, UNICAM_PCDT_MASK); ++ reg_write(cfg, UNICAM_CMP0, val); ++ ++ /* Enable clock lane and set up terminations */ ++ val = 0; ++ if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { ++ /* CSI2 */ ++ set_field(&val, 1, UNICAM_CLE); ++ set_field(&val, 1, UNICAM_CLLPE); ++ if (dev->bus_flags & V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) { ++ set_field(&val, 1, UNICAM_CLTRE); ++ set_field(&val, 1, UNICAM_CLHSE); ++ } ++ } else { ++ /* CCP2 */ ++ set_field(&val, 1, UNICAM_CLE); ++ set_field(&val, 1, UNICAM_CLHSE); ++ set_field(&val, 1, UNICAM_CLTRE); ++ } ++ reg_write(cfg, UNICAM_CLK, val); ++ ++ /* ++ * Enable required data lanes with appropriate terminations. ++ * The same value needs to be written to UNICAM_DATn registers for ++ * the active lanes, and 0 for inactive ones. ++ */ ++ val = 0; ++ if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { ++ /* CSI2 */ ++ set_field(&val, 1, UNICAM_DLE); ++ set_field(&val, 1, UNICAM_DLLPE); ++ if (dev->bus_flags & V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) { ++ set_field(&val, 1, UNICAM_DLTRE); ++ set_field(&val, 1, UNICAM_DLHSE); ++ } ++ } else { ++ /* CCP2 */ ++ set_field(&val, 1, UNICAM_DLE); ++ set_field(&val, 1, UNICAM_DLHSE); ++ set_field(&val, 1, UNICAM_DLTRE); ++ } ++ reg_write(cfg, UNICAM_DAT0, val); ++ ++ if (dev->active_data_lanes == 1) ++ val = 0; ++ reg_write(cfg, UNICAM_DAT1, val); ++ ++ if (dev->max_data_lanes > 2) { ++ /* ++ * Registers UNICAM_DAT2 and UNICAM_DAT3 only valid if the ++ * instance supports more than 2 data lanes. ++ */ ++ if (dev->active_data_lanes == 2) ++ val = 0; ++ reg_write(cfg, UNICAM_DAT2, val); ++ ++ if (dev->active_data_lanes == 3) ++ val = 0; ++ reg_write(cfg, UNICAM_DAT3, val); ++ } ++ ++ reg_write(&dev->cfg, UNICAM_IBLS, dev->v_fmt.fmt.pix.bytesperline); ++ unicam_wr_dma_addr(dev, addr); ++ unicam_set_packing_config(dev); ++ unicam_cfg_image_id(dev); ++ ++ /* Disabled embedded data */ ++ val = 0; ++ set_field(&val, 0, UNICAM_EDL_MASK); ++ reg_write(cfg, UNICAM_DCS, val); ++ ++ val = reg_read(cfg, UNICAM_MISC); ++ set_field(&val, 1, UNICAM_FL0); ++ set_field(&val, 1, UNICAM_FL1); ++ reg_write(cfg, UNICAM_MISC, val); ++ ++ /* Enable peripheral */ ++ reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_CPE); ++ ++ /* Load image pointers */ ++ reg_write_field(cfg, UNICAM_ICTL, 1, UNICAM_LIP_MASK); ++ ++ /* ++ * Enable trigger only for the first frame to ++ * sync correctly to the FS from the source. ++ */ ++ reg_write_field(cfg, UNICAM_ICTL, 1, UNICAM_TFC); ++} ++ ++static void unicam_disable(struct unicam_device *dev) ++{ ++ struct unicam_cfg *cfg = &dev->cfg; ++ ++ /* Analogue lane control disable */ ++ reg_write_field(cfg, UNICAM_ANA, 1, UNICAM_DDL); ++ ++ /* Stop the output engine */ ++ reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_SOE); ++ ++ /* Disable the data lanes. */ ++ reg_write(cfg, UNICAM_DAT0, 0); ++ reg_write(cfg, UNICAM_DAT1, 0); ++ ++ if (dev->max_data_lanes > 2) { ++ reg_write(cfg, UNICAM_DAT2, 0); ++ reg_write(cfg, UNICAM_DAT3, 0); ++ } ++ ++ /* Peripheral reset */ ++ reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_CPR); ++ usleep_range(50, 100); ++ reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPR); ++ ++ /* Disable peripheral */ ++ reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPE); ++ ++ /* Disable all lane clocks */ ++ clk_write(cfg, 0); ++} ++ ++static int unicam_start_streaming(struct vb2_queue *vq, unsigned int count) ++{ ++ struct unicam_device *dev = vb2_get_drv_priv(vq); ++ struct unicam_dmaqueue *dma_q = &dev->dma_queue; ++ struct unicam_buffer *buf, *tmp; ++ unsigned long addr = 0; ++ unsigned long flags; ++ int ret; ++ ++ spin_lock_irqsave(&dev->dma_queue_lock, flags); ++ buf = list_entry(dma_q->active.next, struct unicam_buffer, list); ++ dev->cur_frm = buf; ++ dev->next_frm = buf; ++ list_del(&buf->list); ++ spin_unlock_irqrestore(&dev->dma_queue_lock, flags); ++ ++ addr = vb2_dma_contig_plane_dma_addr(&dev->cur_frm->vb.vb2_buf, 0); ++ dev->sequence = 0; ++ ++ ret = unicam_runtime_get(dev); ++ if (ret < 0) { ++ unicam_dbg(3, dev, "unicam_runtime_get failed\n"); ++ goto err_release_buffers; ++ } ++ ++ dev->active_data_lanes = dev->max_data_lanes; ++ if (dev->bus_type == V4L2_MBUS_CSI2_DPHY && ++ v4l2_subdev_has_op(dev->sensor, video, g_mbus_config)) { ++ struct v4l2_mbus_config mbus_config; ++ ++ ret = v4l2_subdev_call(dev->sensor, video, g_mbus_config, ++ &mbus_config); ++ if (ret < 0) { ++ unicam_dbg(3, dev, "g_mbus_config failed\n"); ++ goto err_pm_put; ++ } ++ ++ dev->active_data_lanes = ++ (mbus_config.flags & V4L2_MBUS_CSI2_LANE_MASK) >> ++ __ffs(V4L2_MBUS_CSI2_LANE_MASK); ++ if (!dev->active_data_lanes) ++ dev->active_data_lanes = dev->max_data_lanes; ++ } ++ if (dev->active_data_lanes > dev->max_data_lanes) { ++ unicam_err(dev, "Device has requested %u data lanes, which is >%u configured in DT\n", ++ dev->active_data_lanes, dev->max_data_lanes); ++ ret = -EINVAL; ++ goto err_pm_put; ++ } ++ ++ unicam_dbg(1, dev, "Running with %u data lanes\n", ++ dev->active_data_lanes); ++ ++ ret = clk_set_rate(dev->clock, 100 * 1000 * 1000); ++ if (ret) { ++ unicam_err(dev, "failed to set up clock\n"); ++ goto err_pm_put; ++ } ++ ++ ret = clk_prepare_enable(dev->clock); ++ if (ret) { ++ unicam_err(dev, "Failed to enable CSI clock: %d\n", ret); ++ goto err_pm_put; ++ } ++ dev->streaming = 1; ++ ++ unicam_start_rx(dev, addr); ++ ++ ret = v4l2_subdev_call(dev->sensor, video, s_stream, 1); ++ if (ret < 0) { ++ unicam_err(dev, "stream on failed in subdev\n"); ++ goto err_disable_unicam; ++ } ++ ++ return 0; ++ ++err_disable_unicam: ++ unicam_disable(dev); ++ clk_disable_unprepare(dev->clock); ++err_pm_put: ++ unicam_runtime_put(dev); ++err_release_buffers: ++ list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { ++ list_del(&buf->list); ++ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); ++ } ++ if (dev->cur_frm != dev->next_frm) ++ vb2_buffer_done(&dev->next_frm->vb.vb2_buf, ++ VB2_BUF_STATE_QUEUED); ++ vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); ++ dev->next_frm = NULL; ++ dev->cur_frm = NULL; ++ ++ return ret; ++} ++ ++static void unicam_stop_streaming(struct vb2_queue *vq) ++{ ++ struct unicam_device *dev = vb2_get_drv_priv(vq); ++ struct unicam_dmaqueue *dma_q = &dev->dma_queue; ++ struct unicam_buffer *buf, *tmp; ++ unsigned long flags; ++ ++ if (v4l2_subdev_call(dev->sensor, video, s_stream, 0) < 0) ++ unicam_err(dev, "stream off failed in subdev\n"); ++ ++ unicam_disable(dev); ++ ++ /* Release all active buffers */ ++ spin_lock_irqsave(&dev->dma_queue_lock, flags); ++ list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { ++ list_del(&buf->list); ++ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); ++ } ++ ++ if (dev->cur_frm == dev->next_frm) { ++ vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); ++ } else { ++ vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); ++ vb2_buffer_done(&dev->next_frm->vb.vb2_buf, ++ VB2_BUF_STATE_ERROR); ++ } ++ dev->cur_frm = NULL; ++ dev->next_frm = NULL; ++ spin_unlock_irqrestore(&dev->dma_queue_lock, flags); ++ ++ clk_disable_unprepare(dev->clock); ++ unicam_runtime_put(dev); ++} ++ ++static int unicam_enum_input(struct file *file, void *priv, ++ struct v4l2_input *inp) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ ++ if (inp->index != 0) ++ return -EINVAL; ++ ++ inp->type = V4L2_INPUT_TYPE_CAMERA; ++ if (v4l2_subdev_has_op(dev->sensor, video, s_dv_timings)) { ++ inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; ++ inp->std = 0; ++ } else if (v4l2_subdev_has_op(dev->sensor, video, s_std)) { ++ inp->capabilities = V4L2_IN_CAP_STD; ++ if (v4l2_subdev_call(dev->sensor, video, g_tvnorms, &inp->std) ++ < 0) ++ inp->std = V4L2_STD_ALL; ++ } else { ++ inp->capabilities = 0; ++ inp->std = 0; ++ } ++ sprintf(inp->name, "Camera 0"); ++ return 0; ++} ++ ++static int unicam_g_input(struct file *file, void *priv, unsigned int *i) ++{ ++ *i = 0; ++ ++ return 0; ++} ++ ++static int unicam_s_input(struct file *file, void *priv, unsigned int i) ++{ ++ /* ++ * FIXME: Ideally we would like to be able to query the source ++ * subdevice for information over the input connectors it supports, ++ * and map that through in to a call to video_ops->s_routing. ++ * There is no infrastructure support for defining that within ++ * devicetree at present. Until that is implemented we can't ++ * map a user physical connector number to s_routing input number. ++ */ ++ if (i > 0) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int unicam_querystd(struct file *file, void *priv, ++ v4l2_std_id *std) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ ++ return v4l2_subdev_call(dev->sensor, video, querystd, std); ++} ++ ++static int unicam_g_std(struct file *file, void *priv, v4l2_std_id *std) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ ++ return v4l2_subdev_call(dev->sensor, video, g_std, std); ++} ++ ++static int unicam_s_std(struct file *file, void *priv, v4l2_std_id std) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ int ret; ++ v4l2_std_id current_std; ++ ++ ret = v4l2_subdev_call(dev->sensor, video, g_std, ¤t_std); ++ if (ret) ++ return ret; ++ ++ if (std == current_std) ++ return 0; ++ ++ if (vb2_is_busy(&dev->buffer_queue)) ++ return -EBUSY; ++ ++ ret = v4l2_subdev_call(dev->sensor, video, s_std, std); ++ ++ /* Force recomputation of bytesperline */ ++ dev->v_fmt.fmt.pix.bytesperline = 0; ++ ++ unicam_reset_format(dev); ++ ++ return ret; ++} ++ ++static int unicam_s_edid(struct file *file, void *priv, struct v4l2_edid *edid) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ ++ return v4l2_subdev_call(dev->sensor, pad, set_edid, edid); ++} ++ ++static int unicam_g_edid(struct file *file, void *priv, struct v4l2_edid *edid) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ ++ return v4l2_subdev_call(dev->sensor, pad, get_edid, edid); ++} ++ ++static int unicam_enum_framesizes(struct file *file, void *priv, ++ struct v4l2_frmsizeenum *fsize) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ const struct unicam_fmt *fmt; ++ struct v4l2_subdev_frame_size_enum fse; ++ int ret; ++ ++ /* check for valid format */ ++ fmt = find_format_by_pix(dev, fsize->pixel_format); ++ if (!fmt) { ++ unicam_dbg(3, dev, "Invalid pixel code: %x\n", ++ fsize->pixel_format); ++ return -EINVAL; ++ } ++ ++ fse.index = fsize->index; ++ fse.pad = 0; ++ fse.code = fmt->code; ++ ++ ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_size, NULL, &fse); ++ if (ret) ++ return ret; ++ ++ unicam_dbg(1, dev, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", ++ __func__, fse.index, fse.code, fse.min_width, fse.max_width, ++ fse.min_height, fse.max_height); ++ ++ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; ++ fsize->discrete.width = fse.max_width; ++ fsize->discrete.height = fse.max_height; ++ ++ return 0; ++} ++ ++static int unicam_enum_frameintervals(struct file *file, void *priv, ++ struct v4l2_frmivalenum *fival) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ const struct unicam_fmt *fmt; ++ struct v4l2_subdev_frame_interval_enum fie = { ++ .index = fival->index, ++ .width = fival->width, ++ .height = fival->height, ++ .which = V4L2_SUBDEV_FORMAT_ACTIVE, ++ }; ++ int ret; ++ ++ fmt = find_format_by_pix(dev, fival->pixel_format); ++ if (!fmt) ++ return -EINVAL; ++ ++ fie.code = fmt->code; ++ ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_interval, ++ NULL, &fie); ++ if (ret) ++ return ret; ++ ++ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; ++ fival->discrete = fie.interval; ++ ++ return 0; ++} ++ ++static int unicam_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ ++ return v4l2_g_parm_cap(video_devdata(file), dev->sensor, a); ++} ++ ++static int unicam_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ ++ return v4l2_s_parm_cap(video_devdata(file), dev->sensor, a); ++} ++ ++static int unicam_g_dv_timings(struct file *file, void *priv, ++ struct v4l2_dv_timings *timings) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ ++ return v4l2_subdev_call(dev->sensor, video, g_dv_timings, timings); ++} ++ ++static int unicam_s_dv_timings(struct file *file, void *priv, ++ struct v4l2_dv_timings *timings) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ struct v4l2_dv_timings current_timings; ++ int ret; ++ ++ ret = v4l2_subdev_call(dev->sensor, video, g_dv_timings, ++ ¤t_timings); ++ ++ if (v4l2_match_dv_timings(timings, ¤t_timings, 0, false)) ++ return 0; ++ ++ if (vb2_is_busy(&dev->buffer_queue)) ++ return -EBUSY; ++ ++ ret = v4l2_subdev_call(dev->sensor, video, s_dv_timings, timings); ++ ++ /* Force recomputation of bytesperline */ ++ dev->v_fmt.fmt.pix.bytesperline = 0; ++ ++ unicam_reset_format(dev); ++ ++ return ret; ++} ++ ++static int unicam_query_dv_timings(struct file *file, void *priv, ++ struct v4l2_dv_timings *timings) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ ++ return v4l2_subdev_call(dev->sensor, video, query_dv_timings, timings); ++} ++ ++static int unicam_enum_dv_timings(struct file *file, void *priv, ++ struct v4l2_enum_dv_timings *timings) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ ++ return v4l2_subdev_call(dev->sensor, pad, enum_dv_timings, timings); ++} ++ ++static int unicam_dv_timings_cap(struct file *file, void *priv, ++ struct v4l2_dv_timings_cap *cap) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ ++ return v4l2_subdev_call(dev->sensor, pad, dv_timings_cap, cap); ++} ++ ++static int unicam_subscribe_event(struct v4l2_fh *fh, ++ const struct v4l2_event_subscription *sub) ++{ ++ switch (sub->type) { ++ case V4L2_EVENT_SOURCE_CHANGE: ++ return v4l2_event_subscribe(fh, sub, 4, NULL); ++ } ++ ++ return v4l2_ctrl_subscribe_event(fh, sub); ++} ++ ++static int unicam_log_status(struct file *file, void *fh) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ struct unicam_cfg *cfg = &dev->cfg; ++ u32 reg; ++ ++ /* status for sub devices */ ++ v4l2_device_call_all(&dev->v4l2_dev, 0, core, log_status); ++ ++ unicam_info(dev, "-----Receiver status-----\n"); ++ unicam_info(dev, "V4L2 width/height: %ux%u\n", ++ dev->v_fmt.fmt.pix.width, dev->v_fmt.fmt.pix.height); ++ unicam_info(dev, "Mediabus format: %08x\n", dev->fmt->code); ++ unicam_info(dev, "V4L2 format: %08x\n", ++ dev->v_fmt.fmt.pix.pixelformat); ++ reg = reg_read(&dev->cfg, UNICAM_IPIPE); ++ unicam_info(dev, "Unpacking/packing: %u / %u\n", ++ get_field(reg, UNICAM_PUM_MASK), ++ get_field(reg, UNICAM_PPM_MASK)); ++ unicam_info(dev, "----Live data----\n"); ++ unicam_info(dev, "Programmed stride: %4u\n", ++ reg_read(cfg, UNICAM_IBLS)); ++ unicam_info(dev, "Detected resolution: %ux%u\n", ++ reg_read(cfg, UNICAM_IHSTA), ++ reg_read(cfg, UNICAM_IVSTA)); ++ unicam_info(dev, "Write pointer: %08x\n", ++ reg_read(cfg, UNICAM_IBWP)); ++ ++ return 0; ++} ++ ++static void unicam_notify(struct v4l2_subdev *sd, ++ unsigned int notification, void *arg) ++{ ++ struct unicam_device *dev = ++ container_of(sd->v4l2_dev, struct unicam_device, v4l2_dev); ++ ++ switch (notification) { ++ case V4L2_DEVICE_NOTIFY_EVENT: ++ v4l2_event_queue(&dev->video_dev, arg); ++ break; ++ default: ++ break; ++ } ++} ++ ++static const struct vb2_ops unicam_video_qops = { ++ .wait_prepare = vb2_ops_wait_prepare, ++ .wait_finish = vb2_ops_wait_finish, ++ .queue_setup = unicam_queue_setup, ++ .buf_prepare = unicam_buffer_prepare, ++ .buf_queue = unicam_buffer_queue, ++ .start_streaming = unicam_start_streaming, ++ .stop_streaming = unicam_stop_streaming, ++}; ++ ++/* ++ * unicam_open : This function is based on the v4l2_fh_open helper function. ++ * It has been augmented to handle sensor subdevice power management, ++ */ ++static int unicam_open(struct file *file) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ int ret; ++ ++ mutex_lock(&dev->lock); ++ ++ ret = v4l2_fh_open(file); ++ if (ret) { ++ unicam_err(dev, "v4l2_fh_open failed\n"); ++ goto unlock; ++ } ++ ++ if (!v4l2_fh_is_singular_file(file)) ++ goto unlock; ++ ++ ret = v4l2_subdev_call(dev->sensor, core, s_power, 1); ++ if (ret < 0 && ret != -ENOIOCTLCMD) { ++ v4l2_fh_release(file); ++ goto unlock; ++ } ++ ++ ret = 0; ++ ++unlock: ++ mutex_unlock(&dev->lock); ++ return ret; ++} ++ ++static int unicam_release(struct file *file) ++{ ++ struct unicam_device *dev = video_drvdata(file); ++ struct v4l2_subdev *sd = dev->sensor; ++ bool fh_singular; ++ int ret; ++ ++ mutex_lock(&dev->lock); ++ ++ fh_singular = v4l2_fh_is_singular_file(file); ++ ++ ret = _vb2_fop_release(file, NULL); ++ ++ if (fh_singular) ++ v4l2_subdev_call(sd, core, s_power, 0); ++ ++ mutex_unlock(&dev->lock); ++ ++ return ret; ++} ++ ++/* unicam capture driver file operations */ ++static const struct v4l2_file_operations unicam_fops = { ++ .owner = THIS_MODULE, ++ .open = unicam_open, ++ .release = unicam_release, ++ .read = vb2_fop_read, ++ .poll = vb2_fop_poll, ++ .unlocked_ioctl = video_ioctl2, ++ .mmap = vb2_fop_mmap, ++}; ++ ++/* unicam capture ioctl operations */ ++static const struct v4l2_ioctl_ops unicam_ioctl_ops = { ++ .vidioc_querycap = unicam_querycap, ++ .vidioc_enum_fmt_vid_cap = unicam_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = unicam_g_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = unicam_s_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = unicam_try_fmt_vid_cap, ++ ++ .vidioc_enum_input = unicam_enum_input, ++ .vidioc_g_input = unicam_g_input, ++ .vidioc_s_input = unicam_s_input, ++ ++ .vidioc_querystd = unicam_querystd, ++ .vidioc_s_std = unicam_s_std, ++ .vidioc_g_std = unicam_g_std, ++ ++ .vidioc_g_edid = unicam_g_edid, ++ .vidioc_s_edid = unicam_s_edid, ++ ++ .vidioc_enum_framesizes = unicam_enum_framesizes, ++ .vidioc_enum_frameintervals = unicam_enum_frameintervals, ++ ++ .vidioc_g_parm = unicam_g_parm, ++ .vidioc_s_parm = unicam_s_parm, ++ ++ .vidioc_s_dv_timings = unicam_s_dv_timings, ++ .vidioc_g_dv_timings = unicam_g_dv_timings, ++ .vidioc_query_dv_timings = unicam_query_dv_timings, ++ .vidioc_enum_dv_timings = unicam_enum_dv_timings, ++ .vidioc_dv_timings_cap = unicam_dv_timings_cap, ++ ++ .vidioc_reqbufs = vb2_ioctl_reqbufs, ++ .vidioc_create_bufs = vb2_ioctl_create_bufs, ++ .vidioc_prepare_buf = vb2_ioctl_prepare_buf, ++ .vidioc_querybuf = vb2_ioctl_querybuf, ++ .vidioc_qbuf = vb2_ioctl_qbuf, ++ .vidioc_dqbuf = vb2_ioctl_dqbuf, ++ .vidioc_expbuf = vb2_ioctl_expbuf, ++ .vidioc_streamon = vb2_ioctl_streamon, ++ .vidioc_streamoff = vb2_ioctl_streamoff, ++ ++ .vidioc_log_status = unicam_log_status, ++ .vidioc_subscribe_event = unicam_subscribe_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++}; ++ ++static int ++unicam_async_bound(struct v4l2_async_notifier *notifier, ++ struct v4l2_subdev *subdev, ++ struct v4l2_async_subdev *asd) ++{ ++ struct unicam_device *unicam = container_of(notifier->v4l2_dev, ++ struct unicam_device, v4l2_dev); ++ ++ if (unicam->sensor) { ++ unicam_info(unicam, "Rejecting subdev %s (Already set!!)", ++ subdev->name); ++ return 0; ++ } ++ ++ unicam->sensor = subdev; ++ unicam_dbg(1, unicam, "Using sensor %s for capture\n", subdev->name); ++ ++ return 0; ++} ++ ++static int unicam_probe_complete(struct unicam_device *unicam) ++{ ++ struct video_device *vdev; ++ struct vb2_queue *q; ++ struct v4l2_mbus_framefmt mbus_fmt = {0}; ++ const struct unicam_fmt *fmt; ++ int ret; ++ ++ v4l2_set_subdev_hostdata(unicam->sensor, unicam); ++ ++ unicam->v4l2_dev.notify = unicam_notify; ++ ++ unicam->sensor_config = v4l2_subdev_alloc_pad_config(unicam->sensor); ++ if (!unicam->sensor_config) ++ return -ENOMEM; ++ ++ ret = __subdev_get_format(unicam, &mbus_fmt); ++ if (ret) { ++ unicam_err(unicam, "Failed to get_format - ret %d\n", ret); ++ return ret; ++ } ++ ++ fmt = find_format_by_code(mbus_fmt.code); ++ if (!fmt) { ++ /* Find the first format that the sensor and unicam both ++ * support ++ */ ++ fmt = get_first_supported_format(unicam); ++ ++ if (!fmt) ++ /* No compatible formats */ ++ return -EINVAL; ++ ++ mbus_fmt.code = fmt->code; ++ ret = __subdev_set_format(unicam, &mbus_fmt); ++ if (ret) ++ return -EINVAL; ++ } ++ if (mbus_fmt.field != V4L2_FIELD_NONE) { ++ /* Interlaced not supported - disable it now. */ ++ mbus_fmt.field = V4L2_FIELD_NONE; ++ ret = __subdev_set_format(unicam, &mbus_fmt); ++ if (ret) ++ return -EINVAL; ++ } ++ ++ unicam->fmt = fmt; ++ if (fmt->fourcc) ++ unicam->v_fmt.fmt.pix.pixelformat = fmt->fourcc; ++ else ++ unicam->v_fmt.fmt.pix.pixelformat = fmt->repacked_fourcc; ++ ++ /* Read current subdev format */ ++ unicam_reset_format(unicam); ++ ++ if (v4l2_subdev_has_op(unicam->sensor, video, s_std)) { ++ v4l2_std_id tvnorms; ++ ++ if (WARN_ON(!v4l2_subdev_has_op(unicam->sensor, video, ++ g_tvnorms))) ++ /* ++ * Subdevice should not advertise s_std but not ++ * g_tvnorms ++ */ ++ return -EINVAL; ++ ++ ret = v4l2_subdev_call(unicam->sensor, video, ++ g_tvnorms, &tvnorms); ++ if (WARN_ON(ret)) ++ return -EINVAL; ++ unicam->video_dev.tvnorms |= tvnorms; ++ } ++ ++ spin_lock_init(&unicam->dma_queue_lock); ++ mutex_init(&unicam->lock); ++ ++ /* Add controls from the subdevice */ ++ ret = v4l2_ctrl_add_handler(&unicam->ctrl_handler, ++ unicam->sensor->ctrl_handler, NULL, true); ++ if (ret < 0) ++ return ret; ++ ++ q = &unicam->buffer_queue; ++ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; ++ q->drv_priv = unicam; ++ q->ops = &unicam_video_qops; ++ q->mem_ops = &vb2_dma_contig_memops; ++ q->buf_struct_size = sizeof(struct unicam_buffer); ++ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ++ q->lock = &unicam->lock; ++ q->min_buffers_needed = 2; ++ q->dev = &unicam->pdev->dev; ++ ++ ret = vb2_queue_init(q); ++ if (ret) { ++ unicam_err(unicam, "vb2_queue_init() failed\n"); ++ return ret; ++ } ++ ++ INIT_LIST_HEAD(&unicam->dma_queue.active); ++ ++ vdev = &unicam->video_dev; ++ strlcpy(vdev->name, UNICAM_MODULE_NAME, sizeof(vdev->name)); ++ vdev->release = video_device_release_empty; ++ vdev->fops = &unicam_fops; ++ vdev->ioctl_ops = &unicam_ioctl_ops; ++ vdev->v4l2_dev = &unicam->v4l2_dev; ++ vdev->vfl_dir = VFL_DIR_RX; ++ vdev->queue = q; ++ vdev->lock = &unicam->lock; ++ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | ++ V4L2_CAP_READWRITE; ++ ++ /* If the source has no controls then remove our ctrl handler. */ ++ if (list_empty(&unicam->ctrl_handler.ctrls)) ++ unicam->v4l2_dev.ctrl_handler = NULL; ++ ++ video_set_drvdata(vdev, unicam); ++ vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; ++ ++ if (!v4l2_subdev_has_op(unicam->sensor, video, s_std)) { ++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_STD); ++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_STD); ++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_ENUMSTD); ++ } ++ if (!v4l2_subdev_has_op(unicam->sensor, video, querystd)) ++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_QUERYSTD); ++ if (!v4l2_subdev_has_op(unicam->sensor, video, s_dv_timings)) { ++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_EDID); ++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_EDID); ++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_DV_TIMINGS_CAP); ++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_DV_TIMINGS); ++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_DV_TIMINGS); ++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_ENUM_DV_TIMINGS); ++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_QUERY_DV_TIMINGS); ++ } ++ if (!v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_interval)) ++ v4l2_disable_ioctl(&unicam->video_dev, ++ VIDIOC_ENUM_FRAMEINTERVALS); ++ if (!v4l2_subdev_has_op(unicam->sensor, video, g_frame_interval)) ++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_PARM); ++ if (!v4l2_subdev_has_op(unicam->sensor, video, s_frame_interval)) ++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_PARM); ++ ++ if (!v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_size)) ++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_ENUM_FRAMESIZES); ++ ++ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); ++ if (ret) { ++ unicam_err(unicam, "Unable to register video device.\n"); ++ return ret; ++ } ++ ++ ret = v4l2_device_register_ro_subdev_nodes(&unicam->v4l2_dev); ++ if (ret) { ++ unicam_err(unicam, ++ "Unable to register subdev nodes.\n"); ++ video_unregister_device(&unicam->video_dev); ++ return ret; ++ } ++ ++ ret = media_create_pad_link(&unicam->sensor->entity, 0, ++ &unicam->video_dev.entity, 0, ++ MEDIA_LNK_FL_ENABLED | ++ MEDIA_LNK_FL_IMMUTABLE); ++ if (ret) { ++ unicam_err(unicam, "Unable to create pad links.\n"); ++ video_unregister_device(&unicam->video_dev); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int unicam_async_complete(struct v4l2_async_notifier *notifier) ++{ ++ struct unicam_device *unicam = container_of(notifier->v4l2_dev, ++ struct unicam_device, v4l2_dev); ++ ++ return unicam_probe_complete(unicam); ++} ++ ++static const struct v4l2_async_notifier_operations unicam_async_ops = { ++ .bound = unicam_async_bound, ++ .complete = unicam_async_complete, ++}; ++ ++static int of_unicam_connect_subdevs(struct unicam_device *dev) ++{ ++ struct platform_device *pdev = dev->pdev; ++ struct device_node *parent, *ep_node = NULL, *remote_ep = NULL, ++ *sensor_node = NULL; ++ struct v4l2_fwnode_endpoint *ep; ++ struct v4l2_async_subdev *asd; ++ unsigned int peripheral_data_lanes; ++ int ret = -EINVAL; ++ unsigned int lane; ++ ++ parent = pdev->dev.of_node; ++ ++ asd = &dev->asd; ++ ep = &dev->endpoint; ++ ++ ep_node = of_graph_get_next_endpoint(parent, NULL); ++ if (!ep_node) { ++ unicam_dbg(3, dev, "can't get next endpoint\n"); ++ goto cleanup_exit; ++ } ++ ++ unicam_dbg(3, dev, "ep_node is %s\n", ep_node->name); ++ ++ v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), ep); ++ ++ for (lane = 0; lane < ep->bus.mipi_csi2.num_data_lanes; lane++) { ++ if (ep->bus.mipi_csi2.data_lanes[lane] != lane + 1) { ++ unicam_err(dev, "Local endpoint - data lane reordering not supported\n"); ++ goto cleanup_exit; ++ } ++ } ++ ++ peripheral_data_lanes = ep->bus.mipi_csi2.num_data_lanes; ++ ++ sensor_node = of_graph_get_remote_port_parent(ep_node); ++ if (!sensor_node) { ++ unicam_dbg(3, dev, "can't get remote parent\n"); ++ goto cleanup_exit; ++ } ++ unicam_dbg(3, dev, "sensor_node is %s\n", sensor_node->name); ++ asd->match_type = V4L2_ASYNC_MATCH_FWNODE; ++ asd->match.fwnode = of_fwnode_handle(sensor_node); ++ ++ remote_ep = of_graph_get_remote_endpoint(ep_node); ++ if (!remote_ep) { ++ unicam_dbg(3, dev, "can't get remote-endpoint\n"); ++ goto cleanup_exit; ++ } ++ unicam_dbg(3, dev, "remote_ep is %s\n", remote_ep->name); ++ v4l2_fwnode_endpoint_parse(of_fwnode_handle(remote_ep), ep); ++ unicam_dbg(3, dev, "parsed remote_ep to endpoint. nr_of_link_frequencies %u, bus_type %u\n", ++ ep->nr_of_link_frequencies, ep->bus_type); ++ ++ switch (ep->bus_type) { ++ case V4L2_MBUS_CSI2_DPHY: ++ if (ep->bus.mipi_csi2.num_data_lanes > ++ peripheral_data_lanes) { ++ unicam_err(dev, "Subdevice %s wants too many data lanes (%u > %u)\n", ++ sensor_node->name, ++ ep->bus.mipi_csi2.num_data_lanes, ++ peripheral_data_lanes); ++ goto cleanup_exit; ++ } ++ for (lane = 0; ++ lane < ep->bus.mipi_csi2.num_data_lanes; ++ lane++) { ++ if (ep->bus.mipi_csi2.data_lanes[lane] != lane + 1) { ++ unicam_err(dev, "Subdevice %s - incompatible data lane config\n", ++ sensor_node->name); ++ goto cleanup_exit; ++ } ++ } ++ dev->max_data_lanes = ep->bus.mipi_csi2.num_data_lanes; ++ dev->bus_flags = ep->bus.mipi_csi2.flags; ++ break; ++ case V4L2_MBUS_CCP2: ++ if (ep->bus.mipi_csi1.clock_lane != 0 || ++ ep->bus.mipi_csi1.data_lane != 1) { ++ unicam_err(dev, "Subdevice %s incompatible lane config\n", ++ sensor_node->name); ++ goto cleanup_exit; ++ } ++ dev->max_data_lanes = 1; ++ dev->bus_flags = ep->bus.mipi_csi1.strobe; ++ break; ++ default: ++ /* Unsupported bus type */ ++ unicam_err(dev, "sub-device %s is not a CSI2 or CCP2 device %d\n", ++ sensor_node->name, ep->bus_type); ++ goto cleanup_exit; ++ } ++ ++ /* Store bus type - CSI2 or CCP2 */ ++ dev->bus_type = ep->bus_type; ++ unicam_dbg(3, dev, "bus_type is %d\n", dev->bus_type); ++ ++ /* Store Virtual Channel number */ ++ dev->virtual_channel = ep->base.id; ++ ++ unicam_dbg(3, dev, "v4l2-endpoint: %s\n", ++ dev->bus_type == V4L2_MBUS_CSI2_DPHY ? "CSI2" : "CCP2"); ++ unicam_dbg(3, dev, "Virtual Channel=%d\n", dev->virtual_channel); ++ if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) ++ unicam_dbg(3, dev, "flags=0x%08x\n", ep->bus.mipi_csi2.flags); ++ unicam_dbg(3, dev, "num_data_lanes=%d\n", dev->max_data_lanes); ++ ++ unicam_dbg(1, dev, "found sub-device %s\n", sensor_node->name); ++ ++ v4l2_async_notifier_init(&dev->notifier); ++ ++ ret = v4l2_async_notifier_add_subdev(&dev->notifier, asd); ++ if (ret) { ++ unicam_err(dev, "Error adding subdevice - ret %d\n", ret); ++ goto cleanup_exit; ++ } ++ ++ dev->notifier.ops = &unicam_async_ops; ++ ret = v4l2_async_notifier_register(&dev->v4l2_dev, ++ &dev->notifier); ++ if (ret) { ++ unicam_err(dev, "Error registering async notifier - ret %d\n", ++ ret); ++ ret = -EINVAL; ++ } ++ ++cleanup_exit: ++ if (remote_ep) ++ of_node_put(remote_ep); ++ if (sensor_node) ++ of_node_put(sensor_node); ++ if (ep_node) ++ of_node_put(ep_node); ++ ++ return ret; ++} ++ ++static int unicam_probe(struct platform_device *pdev) ++{ ++ struct unicam_cfg *unicam_cfg; ++ struct unicam_device *unicam; ++ struct v4l2_ctrl_handler *hdl; ++ struct resource *res; ++ int ret; ++ ++ unicam = devm_kzalloc(&pdev->dev, sizeof(*unicam), GFP_KERNEL); ++ if (!unicam) ++ return -ENOMEM; ++ ++ unicam->pdev = pdev; ++ unicam_cfg = &unicam->cfg; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ unicam_cfg->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(unicam_cfg->base)) { ++ unicam_err(unicam, "Failed to get main io block\n"); ++ return PTR_ERR(unicam_cfg->base); ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ unicam_cfg->clk_gate_base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(unicam_cfg->clk_gate_base)) { ++ unicam_err(unicam, "Failed to get 2nd io block\n"); ++ return PTR_ERR(unicam_cfg->clk_gate_base); ++ } ++ ++ unicam->clock = devm_clk_get(&pdev->dev, "lp"); ++ if (IS_ERR(unicam->clock)) { ++ unicam_err(unicam, "Failed to get clock\n"); ++ return PTR_ERR(unicam->clock); ++ } ++ ++ ret = platform_get_irq(pdev, 0); ++ if (ret <= 0) { ++ dev_err(&pdev->dev, "No IRQ resource\n"); ++ return -ENODEV; ++ } ++ ++ ret = devm_request_irq(&pdev->dev, ret, unicam_isr, 0, ++ "unicam_capture0", unicam); ++ if (ret) { ++ dev_err(&pdev->dev, "Unable to request interrupt\n"); ++ return -EINVAL; ++ } ++ ++ unicam->mdev.dev = &pdev->dev; ++ strscpy(unicam->mdev.model, UNICAM_MODULE_NAME, ++ sizeof(unicam->mdev.model)); ++ strscpy(unicam->mdev.serial, "", sizeof(unicam->mdev.serial)); ++ snprintf(unicam->mdev.bus_info, sizeof(unicam->mdev.bus_info), ++ "platform:%s %s", ++ pdev->dev.driver->name, dev_name(&pdev->dev)); ++ unicam->mdev.hw_revision = 1; ++ ++ media_entity_pads_init(&unicam->video_dev.entity, 1, &unicam->pad); ++ media_device_init(&unicam->mdev); ++ ++ unicam->v4l2_dev.mdev = &unicam->mdev; ++ ++ ret = v4l2_device_register(&pdev->dev, &unicam->v4l2_dev); ++ if (ret) { ++ unicam_err(unicam, ++ "Unable to register v4l2 device.\n"); ++ goto media_cleanup; ++ } ++ ++ ret = media_device_register(&unicam->mdev); ++ if (ret < 0) { ++ unicam_err(unicam, ++ "Unable to register media-controller device.\n"); ++ goto probe_out_v4l2_unregister; ++ } ++ ++ /* Reserve space for the controls */ ++ hdl = &unicam->ctrl_handler; ++ ret = v4l2_ctrl_handler_init(hdl, 16); ++ if (ret < 0) ++ goto media_unregister; ++ unicam->v4l2_dev.ctrl_handler = hdl; ++ ++ /* set the driver data in platform device */ ++ platform_set_drvdata(pdev, unicam); ++ ++ ret = of_unicam_connect_subdevs(unicam); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to connect subdevs\n"); ++ goto free_hdl; ++ } ++ ++ /* Enable the block power domain */ ++ pm_runtime_enable(&pdev->dev); ++ ++ return 0; ++ ++free_hdl: ++ v4l2_ctrl_handler_free(hdl); ++media_unregister: ++ media_device_unregister(&unicam->mdev); ++probe_out_v4l2_unregister: ++ v4l2_device_unregister(&unicam->v4l2_dev); ++media_cleanup: ++ media_device_cleanup(&unicam->mdev); ++ ++ return ret; ++} ++ ++static int unicam_remove(struct platform_device *pdev) ++{ ++ struct unicam_device *unicam = platform_get_drvdata(pdev); ++ ++ unicam_dbg(2, unicam, "%s\n", __func__); ++ ++ pm_runtime_disable(&pdev->dev); ++ ++ v4l2_async_notifier_unregister(&unicam->notifier); ++ v4l2_ctrl_handler_free(&unicam->ctrl_handler); ++ v4l2_device_unregister(&unicam->v4l2_dev); ++ video_unregister_device(&unicam->video_dev); ++ if (unicam->sensor_config) ++ v4l2_subdev_free_pad_config(unicam->sensor_config); ++ media_device_unregister(&unicam->mdev); ++ media_device_cleanup(&unicam->mdev); ++ ++ return 0; ++} ++ ++static const struct of_device_id unicam_of_match[] = { ++ { .compatible = "brcm,bcm2835-unicam", }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, unicam_of_match); ++ ++static struct platform_driver unicam_driver = { ++ .probe = unicam_probe, ++ .remove = unicam_remove, ++ .driver = { ++ .name = UNICAM_MODULE_NAME, ++ .of_match_table = of_match_ptr(unicam_of_match), ++ }, ++}; ++ ++module_platform_driver(unicam_driver); ++ ++MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com>"); ++MODULE_DESCRIPTION("BCM2835 Unicam driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(UNICAM_VERSION); +--- /dev/null ++++ b/drivers/media/platform/bcm2835/vc4-regs-unicam.h +@@ -0,0 +1,253 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++ ++/* ++ * Copyright (C) 2017-2020 Raspberry Pi Trading. ++ * Dave Stevenson <dave.stevenson@raspberrypi.com> ++ */ ++ ++#ifndef VC4_REGS_UNICAM_H ++#define VC4_REGS_UNICAM_H ++ ++/* ++ * The following values are taken from files found within the code drop ++ * made by Broadcom for the BCM21553 Graphics Driver, predominantly in ++ * brcm_usrlib/dag/vmcsx/vcinclude/hardware_vc4.h. ++ * They have been modified to be only the register offset. ++ */ ++#define UNICAM_CTRL 0x000 ++#define UNICAM_STA 0x004 ++#define UNICAM_ANA 0x008 ++#define UNICAM_PRI 0x00c ++#define UNICAM_CLK 0x010 ++#define UNICAM_CLT 0x014 ++#define UNICAM_DAT0 0x018 ++#define UNICAM_DAT1 0x01c ++#define UNICAM_DAT2 0x020 ++#define UNICAM_DAT3 0x024 ++#define UNICAM_DLT 0x028 ++#define UNICAM_CMP0 0x02c ++#define UNICAM_CMP1 0x030 ++#define UNICAM_CAP0 0x034 ++#define UNICAM_CAP1 0x038 ++#define UNICAM_ICTL 0x100 ++#define UNICAM_ISTA 0x104 ++#define UNICAM_IDI0 0x108 ++#define UNICAM_IPIPE 0x10c ++#define UNICAM_IBSA0 0x110 ++#define UNICAM_IBEA0 0x114 ++#define UNICAM_IBLS 0x118 ++#define UNICAM_IBWP 0x11c ++#define UNICAM_IHWIN 0x120 ++#define UNICAM_IHSTA 0x124 ++#define UNICAM_IVWIN 0x128 ++#define UNICAM_IVSTA 0x12c ++#define UNICAM_ICC 0x130 ++#define UNICAM_ICS 0x134 ++#define UNICAM_IDC 0x138 ++#define UNICAM_IDPO 0x13c ++#define UNICAM_IDCA 0x140 ++#define UNICAM_IDCD 0x144 ++#define UNICAM_IDS 0x148 ++#define UNICAM_DCS 0x200 ++#define UNICAM_DBSA0 0x204 ++#define UNICAM_DBEA0 0x208 ++#define UNICAM_DBWP 0x20c ++#define UNICAM_DBCTL 0x300 ++#define UNICAM_IBSA1 0x304 ++#define UNICAM_IBEA1 0x308 ++#define UNICAM_IDI1 0x30c ++#define UNICAM_DBSA1 0x310 ++#define UNICAM_DBEA1 0x314 ++#define UNICAM_MISC 0x400 ++ ++/* ++ * The following bitmasks are from the kernel released by Broadcom ++ * for Android - https://android.googlesource.com/kernel/bcm/ ++ * The Rhea, Hawaii, and Java chips all contain the same VideoCore4 ++ * Unicam block as BCM2835, as defined in eg ++ * arch/arm/mach-rhea/include/mach/rdb_A0/brcm_rdb_cam.h and similar. ++ * Values reworked to use the kernel BIT and GENMASK macros. ++ * ++ * Some of the bit mnenomics have been amended to match the datasheet. ++ */ ++/* UNICAM_CTRL Register */ ++#define UNICAM_CPE BIT(0) ++#define UNICAM_MEM BIT(1) ++#define UNICAM_CPR BIT(2) ++#define UNICAM_CPM_MASK GENMASK(3, 3) ++#define UNICAM_CPM_CSI2 0 ++#define UNICAM_CPM_CCP2 1 ++#define UNICAM_SOE BIT(4) ++#define UNICAM_DCM_MASK GENMASK(5, 5) ++#define UNICAM_DCM_STROBE 0 ++#define UNICAM_DCM_DATA 1 ++#define UNICAM_SLS BIT(6) ++#define UNICAM_PFT_MASK GENMASK(11, 8) ++#define UNICAM_OET_MASK GENMASK(20, 12) ++ ++/* UNICAM_STA Register */ ++#define UNICAM_SYN BIT(0) ++#define UNICAM_CS BIT(1) ++#define UNICAM_SBE BIT(2) ++#define UNICAM_PBE BIT(3) ++#define UNICAM_HOE BIT(4) ++#define UNICAM_PLE BIT(5) ++#define UNICAM_SSC BIT(6) ++#define UNICAM_CRCE BIT(7) ++#define UNICAM_OES BIT(8) ++#define UNICAM_IFO BIT(9) ++#define UNICAM_OFO BIT(10) ++#define UNICAM_BFO BIT(11) ++#define UNICAM_DL BIT(12) ++#define UNICAM_PS BIT(13) ++#define UNICAM_IS BIT(14) ++#define UNICAM_PI0 BIT(15) ++#define UNICAM_PI1 BIT(16) ++#define UNICAM_FSI_S BIT(17) ++#define UNICAM_FEI_S BIT(18) ++#define UNICAM_LCI_S BIT(19) ++#define UNICAM_BUF0_RDY BIT(20) ++#define UNICAM_BUF0_NO BIT(21) ++#define UNICAM_BUF1_RDY BIT(22) ++#define UNICAM_BUF1_NO BIT(23) ++#define UNICAM_DI BIT(24) ++ ++#define UNICAM_STA_MASK_ALL \ ++ (UNICAM_DL + \ ++ UNICAM_SBE + \ ++ UNICAM_PBE + \ ++ UNICAM_HOE + \ ++ UNICAM_PLE + \ ++ UNICAM_SSC + \ ++ UNICAM_CRCE + \ ++ UNICAM_IFO + \ ++ UNICAM_OFO + \ ++ UNICAM_PS + \ ++ UNICAM_PI0 + \ ++ UNICAM_PI1) ++ ++/* UNICAM_ANA Register */ ++#define UNICAM_APD BIT(0) ++#define UNICAM_BPD BIT(1) ++#define UNICAM_AR BIT(2) ++#define UNICAM_DDL BIT(3) ++#define UNICAM_CTATADJ_MASK GENMASK(7, 4) ++#define UNICAM_PTATADJ_MASK GENMASK(11, 8) ++ ++/* UNICAM_PRI Register */ ++#define UNICAM_PE BIT(0) ++#define UNICAM_PT_MASK GENMASK(2, 1) ++#define UNICAM_NP_MASK GENMASK(7, 4) ++#define UNICAM_PP_MASK GENMASK(11, 8) ++#define UNICAM_BS_MASK GENMASK(15, 12) ++#define UNICAM_BL_MASK GENMASK(17, 16) ++ ++/* UNICAM_CLK Register */ ++#define UNICAM_CLE BIT(0) ++#define UNICAM_CLPD BIT(1) ++#define UNICAM_CLLPE BIT(2) ++#define UNICAM_CLHSE BIT(3) ++#define UNICAM_CLTRE BIT(4) ++#define UNICAM_CLAC_MASK GENMASK(8, 5) ++#define UNICAM_CLSTE BIT(29) ++ ++/* UNICAM_CLT Register */ ++#define UNICAM_CLT1_MASK GENMASK(7, 0) ++#define UNICAM_CLT2_MASK GENMASK(15, 8) ++ ++/* UNICAM_DATn Registers */ ++#define UNICAM_DLE BIT(0) ++#define UNICAM_DLPD BIT(1) ++#define UNICAM_DLLPE BIT(2) ++#define UNICAM_DLHSE BIT(3) ++#define UNICAM_DLTRE BIT(4) ++#define UNICAM_DLSM BIT(5) ++#define UNICAM_DLFO BIT(28) ++#define UNICAM_DLSTE BIT(29) ++ ++#define UNICAM_DAT_MASK_ALL (UNICAM_DLSTE + UNICAM_DLFO) ++ ++/* UNICAM_DLT Register */ ++#define UNICAM_DLT1_MASK GENMASK(7, 0) ++#define UNICAM_DLT2_MASK GENMASK(15, 8) ++#define UNICAM_DLT3_MASK GENMASK(23, 16) ++ ++/* UNICAM_ICTL Register */ ++#define UNICAM_FSIE BIT(0) ++#define UNICAM_FEIE BIT(1) ++#define UNICAM_IBOB BIT(2) ++#define UNICAM_FCM BIT(3) ++#define UNICAM_TFC BIT(4) ++#define UNICAM_LIP_MASK GENMASK(6, 5) ++#define UNICAM_LCIE_MASK GENMASK(28, 16) ++ ++/* UNICAM_IDI0/1 Register */ ++#define UNICAM_ID0_MASK GENMASK(7, 0) ++#define UNICAM_ID1_MASK GENMASK(15, 8) ++#define UNICAM_ID2_MASK GENMASK(23, 16) ++#define UNICAM_ID3_MASK GENMASK(31, 24) ++ ++/* UNICAM_ISTA Register */ ++#define UNICAM_FSI BIT(0) ++#define UNICAM_FEI BIT(1) ++#define UNICAM_LCI BIT(2) ++ ++#define UNICAM_ISTA_MASK_ALL (UNICAM_FSI + UNICAM_FEI + UNICAM_LCI) ++ ++/* UNICAM_IPIPE Register */ ++#define UNICAM_PUM_MASK GENMASK(2, 0) ++ /* Unpacking modes */ ++ #define UNICAM_PUM_NONE 0 ++ #define UNICAM_PUM_UNPACK6 1 ++ #define UNICAM_PUM_UNPACK7 2 ++ #define UNICAM_PUM_UNPACK8 3 ++ #define UNICAM_PUM_UNPACK10 4 ++ #define UNICAM_PUM_UNPACK12 5 ++ #define UNICAM_PUM_UNPACK14 6 ++ #define UNICAM_PUM_UNPACK16 7 ++#define UNICAM_DDM_MASK GENMASK(6, 3) ++#define UNICAM_PPM_MASK GENMASK(9, 7) ++ /* Packing modes */ ++ #define UNICAM_PPM_NONE 0 ++ #define UNICAM_PPM_PACK8 1 ++ #define UNICAM_PPM_PACK10 2 ++ #define UNICAM_PPM_PACK12 3 ++ #define UNICAM_PPM_PACK14 4 ++ #define UNICAM_PPM_PACK16 5 ++#define UNICAM_DEM_MASK GENMASK(11, 10) ++#define UNICAM_DEBL_MASK GENMASK(14, 12) ++#define UNICAM_ICM_MASK GENMASK(16, 15) ++#define UNICAM_IDM_MASK GENMASK(17, 17) ++ ++/* UNICAM_ICC Register */ ++#define UNICAM_ICFL_MASK GENMASK(4, 0) ++#define UNICAM_ICFH_MASK GENMASK(9, 5) ++#define UNICAM_ICST_MASK GENMASK(12, 10) ++#define UNICAM_ICLT_MASK GENMASK(15, 13) ++#define UNICAM_ICLL_MASK GENMASK(31, 16) ++ ++/* UNICAM_DCS Register */ ++#define UNICAM_DIE BIT(0) ++#define UNICAM_DIM BIT(1) ++#define UNICAM_DBOB BIT(3) ++#define UNICAM_FDE BIT(4) ++#define UNICAM_LDP BIT(5) ++#define UNICAM_EDL_MASK GENMASK(15, 8) ++ ++/* UNICAM_DBCTL Register */ ++#define UNICAM_DBEN BIT(0) ++#define UNICAM_BUF0_IE BIT(1) ++#define UNICAM_BUF1_IE BIT(2) ++ ++/* UNICAM_CMP[0,1] register */ ++#define UNICAM_PCE BIT(31) ++#define UNICAM_GI BIT(9) ++#define UNICAM_CPH BIT(8) ++#define UNICAM_PCVC_MASK GENMASK(7, 6) ++#define UNICAM_PCDT_MASK GENMASK(5, 0) ++ ++/* UNICAM_MISC register */ ++#define UNICAM_FL0 BIT(6) ++#define UNICAM_FL1 BIT(9) ++ ++#endif diff --git a/target/linux/bcm27xx/patches-5.4/950-0650-media-uapi-v4l2-core-Add-sensor-ancillary-data-V4L2-.patch b/target/linux/bcm27xx/patches-5.4/950-0650-media-uapi-v4l2-core-Add-sensor-ancillary-data-V4L2-.patch new file mode 100644 index 0000000000..de8f1d209a --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0650-media-uapi-v4l2-core-Add-sensor-ancillary-data-V4L2-.patch @@ -0,0 +1,85 @@ +From 09f5e82f292a900d17a5205e54a35e24296bd9f7 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Wed, 1 Apr 2020 08:46:29 +0100 +Subject: [PATCH] media: uapi: v4l2-core: Add sensor ancillary data + V4L2 foucc type. + +Add V4L2_META_FMT_SENSOR_DATA format 4CC. + +This new format will be used by the BCM2835 Unicam device to return +out camera sensor embedded data. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + Documentation/media/uapi/v4l/meta-formats.rst | 1 + + .../uapi/v4l/pixfmt-meta-sensor-data.rst | 32 +++++++++++++++++++ + drivers/media/v4l2-core/v4l2-ioctl.c | 1 + + include/uapi/linux/videodev2.h | 1 + + 4 files changed, 35 insertions(+) + create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-sensor-data.rst + +--- a/Documentation/media/uapi/v4l/meta-formats.rst ++++ b/Documentation/media/uapi/v4l/meta-formats.rst +@@ -21,6 +21,7 @@ These formats are used for the :ref:`met + + pixfmt-meta-d4xx + pixfmt-meta-intel-ipu3 ++ pixfmt-meta-sensor-data + pixfmt-meta-uvc + pixfmt-meta-vsp1-hgo + pixfmt-meta-vsp1-hgt +--- /dev/null ++++ b/Documentation/media/uapi/v4l/pixfmt-meta-sensor-data.rst +@@ -0,0 +1,32 @@ ++.. 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-meta-fmt-sensor-data: ++ ++*********************************** ++V4L2_META_FMT_SENSOR_DATA ('SENS') ++*********************************** ++ ++Sensor Ancillary Metadata ++ ++Description ++=========== ++ ++This format describes ancillary data generated by a camera sensor and ++transmitted over a stream on the camera bus. Sensor vendors generally have their ++own custom format for this ancillary data. Some vendors follow a generic ++CSI-2/SMIA embedded data format as described in the `CSI-2 specification. ++<https://mipi.org/specifications/csi-2>`_ ++ ++The size of the embedded buffer is defined as a single line with a pixel width ++width specified in bytes. This is obtained by a call to the ++:c:type:`VIDIOC_SUBDEV_G_FMT` ioctl on the sensor subdevice where the ``pad`` ++field in :c:type:`v4l2_subdev_format` is set to 1. Note that this size is fixed ++and cannot be modified with a call to :c:type:`VIDIOC_SUBDEV_S_FMT`. ++ +--- a/drivers/media/v4l2-core/v4l2-ioctl.c ++++ b/drivers/media/v4l2-core/v4l2-ioctl.c +@@ -1332,6 +1332,7 @@ static void v4l_fill_fmtdesc(struct v4l2 + case V4L2_META_FMT_VSP1_HGT: descr = "R-Car VSP1 2-D Histogram"; break; + case V4L2_META_FMT_UVC: descr = "UVC Payload Header Metadata"; break; + case V4L2_META_FMT_D4XX: descr = "Intel D4xx UVC Metadata"; break; ++ case V4L2_META_FMT_SENSOR_DATA: descr = "Sensor Ancillary Metadata"; break; + + default: + /* Compressed formats */ +--- a/include/uapi/linux/videodev2.h ++++ b/include/uapi/linux/videodev2.h +@@ -769,6 +769,7 @@ struct v4l2_pix_format { + #define V4L2_META_FMT_VSP1_HGT v4l2_fourcc('V', 'S', 'P', 'T') /* R-Car VSP1 2-D Histogram */ + #define V4L2_META_FMT_UVC v4l2_fourcc('U', 'V', 'C', 'H') /* UVC Payload Header metadata */ + #define V4L2_META_FMT_D4XX v4l2_fourcc('D', '4', 'X', 'X') /* D4XX Payload Header metadata */ ++#define V4L2_META_FMT_SENSOR_DATA v4l2_fourcc('S', 'E', 'N', 'S') /* Sensor Ancillary metadata */ + + /* priv field value to indicates that subsequent fields are valid. */ + #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe diff --git a/target/linux/bcm27xx/patches-5.4/950-0651-media-uapi-Add-MEDIA_BUS_FMT_SENSOR_DATA-media-bus-f.patch b/target/linux/bcm27xx/patches-5.4/950-0651-media-uapi-Add-MEDIA_BUS_FMT_SENSOR_DATA-media-bus-f.patch new file mode 100644 index 0000000000..65162cc5d5 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0651-media-uapi-Add-MEDIA_BUS_FMT_SENSOR_DATA-media-bus-f.patch @@ -0,0 +1,64 @@ +From 65573c84d5a9115444cc5e365c94cb3ae0fb7e10 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Tue, 21 Jan 2020 14:06:47 +0000 +Subject: [PATCH] media: uapi: Add MEDIA_BUS_FMT_SENSOR_DATA media bus + format + +This patch adds MEDIA_BUS_FMT_SENSOR_DATA used by the bcm2835-unicam +driver to support CSI-2 embedded data streams from camera sensors. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + .../media/uapi/v4l/subdev-formats.rst | 33 +++++++++++++++++++ + include/uapi/linux/media-bus-format.h | 3 ++ + 2 files changed, 36 insertions(+) + +--- a/Documentation/media/uapi/v4l/subdev-formats.rst ++++ b/Documentation/media/uapi/v4l/subdev-formats.rst +@@ -7794,3 +7794,36 @@ formats. + - 0x5001 + - Interleaved raw UYVY and JPEG image format with embedded meta-data + used by Samsung S3C73MX camera sensors. ++ ++ ++ ++.. _v4l2-mbus-sensor-data: ++ ++Sensor Ancillary Metadata Formats ++^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ++This section lists ancillary data generated by a camera sensor and ++transmitted over a stream on the camera bus. ++ ++The following table lists the existing sensor ancillary metadata formats: ++ ++ ++.. _v4l2-mbus-pixelcode-sensor-metadata: ++ ++.. tabularcolumns:: |p{8.0cm}|p{1.4cm}|p{7.7cm}| ++ ++.. flat-table:: Sensor ancillary metadata formats ++ :header-rows: 1 ++ :stub-columns: 0 ++ ++ * - Identifier ++ - Code ++ - Comments ++ * .. _MEDIA_BUS_FMT_SENSOR_DATA: ++ ++ - MEDIA_BUS_FMT_SENSOR_DATA ++ - 0x7001 ++ - Sensor vendor specific ancillary metadata. Some vendors follow a generic ++ CSI-2/SMIA embedded data format as described in the `CSI-2 specification. ++ <https://mipi.org/specifications/csi-2>`_ ++ +--- a/include/uapi/linux/media-bus-format.h ++++ b/include/uapi/linux/media-bus-format.h +@@ -155,4 +155,7 @@ + /* HSV - next is 0x6002 */ + #define MEDIA_BUS_FMT_AHSV8888_1X32 0x6001 + ++/* Sensor ancillary metadata formats - next is 0x7002 */ ++#define MEDIA_BUS_FMT_SENSOR_DATA 0x7001 ++ + #endif /* __LINUX_MEDIA_BUS_FORMAT_H */ diff --git a/target/linux/bcm27xx/patches-5.4/950-0652-media-bcm2835-unicam-Add-support-for-mulitple-device.patch b/target/linux/bcm27xx/patches-5.4/950-0652-media-bcm2835-unicam-Add-support-for-mulitple-device.patch new file mode 100644 index 0000000000..315feff5d3 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0652-media-bcm2835-unicam-Add-support-for-mulitple-device.patch @@ -0,0 +1,1084 @@ +From b466d74b45466b417e364c85c7fce71e9fc3fc7c Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Tue, 7 Apr 2020 10:42:14 +0100 +Subject: [PATCH] media: bcm2835-unicam: Add support for mulitple + device nodes. + +Move device node specific state out of the device state structure and +into a new node structure. This separation will be needed for future +changes where we will add an embedded data node to the driver to work +alongside the existing image data node. + +Currently only use a single image node, so this commit does not add +any functional changes. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + .../media/platform/bcm2835/bcm2835-unicam.c | 484 ++++++++++-------- + 1 file changed, 283 insertions(+), 201 deletions(-) + +--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c ++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c +@@ -109,7 +109,8 @@ MODULE_PARM_DESC(debug, "Debug level 0-3 + /* Define a nominal minimum image size */ + #define MIN_WIDTH 16 + #define MIN_HEIGHT 16 +- ++/* Maximum number of simulataneous streams Uncaim can handle. */ ++#define MAX_NODES 2 + /* + * struct unicam_fmt - Unicam media bus format information + * @pixelformat: V4L2 pixel format FCC identifier. 0 if n/a. +@@ -346,11 +347,37 @@ struct unicam_cfg { + + #define MAX_POSSIBLE_PIX_FMTS (ARRAY_SIZE(formats)) + +-struct unicam_device { +- /* V4l2 specific parameters */ ++struct unicam_node { ++ bool registered; ++ unsigned int pad_id; ++ /* Pointer pointing to current v4l2_buffer */ ++ struct unicam_buffer *cur_frm; ++ /* Pointer pointing to next v4l2_buffer */ ++ struct unicam_buffer *next_frm; ++ /* video capture */ ++ const struct unicam_fmt *fmt; ++ /* Used to store current pixel format */ ++ struct v4l2_format v_fmt; ++ /* Used to store current mbus frame format */ ++ struct v4l2_mbus_framefmt m_fmt; ++ /* Buffer queue used in video-buf */ ++ struct vb2_queue buffer_queue; ++ /* Queue of filled frames */ ++ struct unicam_dmaqueue dma_queue; ++ /* IRQ lock for DMA queue */ ++ spinlock_t dma_queue_lock; ++ /* lock used to access this structure */ ++ struct mutex lock; + /* Identifies video device for this channel */ + struct video_device video_dev; ++ /* Pointer to the parent handle */ ++ struct unicam_device *dev; ++ struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; ++}; ++ ++struct unicam_device { ++ /* V4l2 specific parameters */ + + struct v4l2_fwnode_endpoint endpoint; + +@@ -363,7 +390,6 @@ struct unicam_device { + /* V4l2 device */ + struct v4l2_device v4l2_dev; + struct media_device mdev; +- struct media_pad pad; + + /* parent device */ + struct platform_device *pdev; +@@ -378,18 +404,6 @@ struct unicam_device { + /* current input at the sub device */ + int current_input; + +- /* Pointer pointing to current v4l2_buffer */ +- struct unicam_buffer *cur_frm; +- /* Pointer pointing to next v4l2_buffer */ +- struct unicam_buffer *next_frm; +- +- /* video capture */ +- const struct unicam_fmt *fmt; +- /* Used to store current pixel format */ +- struct v4l2_format v_fmt; +- /* Used to store current mbus frame format */ +- struct v4l2_mbus_framefmt m_fmt; +- + unsigned int virtual_channel; + enum v4l2_mbus_type bus_type; + /* +@@ -401,20 +415,10 @@ struct unicam_device { + unsigned int active_data_lanes; + + struct v4l2_rect crop; +- +- /* Currently selected input on subdev */ +- int input; +- +- /* Buffer queue used in video-buf */ +- struct vb2_queue buffer_queue; +- /* Queue of filled frames */ +- struct unicam_dmaqueue dma_queue; +- /* IRQ lock for DMA queue */ +- spinlock_t dma_queue_lock; +- /* lock used to access this structure */ +- struct mutex lock; + /* Flag to denote that we are processing buffers */ + int streaming; ++ ++ struct unicam_node node[MAX_NODES]; + }; + + /* Hardware access */ +@@ -526,10 +530,11 @@ static inline unsigned int bytes_per_lin + } + + static int __subdev_get_format(struct unicam_device *dev, +- struct v4l2_mbus_framefmt *fmt) ++ struct v4l2_mbus_framefmt *fmt, int pad_id) + { + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, ++ .pad = pad_id + }; + int ret; + +@@ -598,29 +603,30 @@ static int unicam_calc_format_size_bpl(s + return 0; + } + +-static int unicam_reset_format(struct unicam_device *dev) ++static int unicam_reset_format(struct unicam_node *node) + { ++ struct unicam_device *dev = node->dev; + struct v4l2_mbus_framefmt mbus_fmt; + int ret; + +- ret = __subdev_get_format(dev, &mbus_fmt); ++ ret = __subdev_get_format(dev, &mbus_fmt, node->pad_id); + if (ret) { + unicam_err(dev, "Failed to get_format - ret %d\n", ret); + return ret; + } + +- if (mbus_fmt.code != dev->fmt->code) { ++ if (mbus_fmt.code != dev->node[0].fmt->code) { + unicam_err(dev, "code mismatch - fmt->code %08x, mbus_fmt.code %08x\n", +- dev->fmt->code, mbus_fmt.code); ++ dev->node[0].fmt->code, mbus_fmt.code); + return ret; + } + +- v4l2_fill_pix_format(&dev->v_fmt.fmt.pix, &mbus_fmt); +- dev->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ v4l2_fill_pix_format(&dev->node[0].v_fmt.fmt.pix, &mbus_fmt); ++ dev->node[0].v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + +- unicam_calc_format_size_bpl(dev, dev->fmt, &dev->v_fmt); ++ unicam_calc_format_size_bpl(dev, dev->node[0].fmt, &dev->node[0].v_fmt); + +- dev->m_fmt = mbus_fmt; ++ dev->node[0].m_fmt = mbus_fmt; + + return 0; + } +@@ -635,14 +641,14 @@ static void unicam_wr_dma_addr(struct un + + reg_write(&dev->cfg, UNICAM_IBSA0, dmaaddr); + reg_write(&dev->cfg, UNICAM_IBEA0, +- dmaaddr + dev->v_fmt.fmt.pix.sizeimage); ++ dmaaddr + dev->node[0].v_fmt.fmt.pix.sizeimage); + } + + static inline unsigned int unicam_get_lines_done(struct unicam_device *dev) + { + dma_addr_t start_addr, cur_addr; +- unsigned int stride = dev->v_fmt.fmt.pix.bytesperline; +- struct unicam_buffer *frm = dev->cur_frm; ++ unsigned int stride = dev->node[0].v_fmt.fmt.pix.bytesperline; ++ struct unicam_buffer *frm = dev->node[0].cur_frm; + + if (!frm) + return 0; +@@ -654,12 +660,12 @@ static inline unsigned int unicam_get_li + + static inline void unicam_schedule_next_buffer(struct unicam_device *dev) + { +- struct unicam_dmaqueue *dma_q = &dev->dma_queue; ++ struct unicam_dmaqueue *dma_q = &dev->node[0].dma_queue; + struct unicam_buffer *buf; + dma_addr_t addr; + + buf = list_entry(dma_q->active.next, struct unicam_buffer, list); +- dev->next_frm = buf; ++ dev->node[0].next_frm = buf; + list_del(&buf->list); + + addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); +@@ -668,11 +674,11 @@ static inline void unicam_schedule_next_ + + static inline void unicam_process_buffer_complete(struct unicam_device *dev) + { +- dev->cur_frm->vb.field = dev->m_fmt.field; +- dev->cur_frm->vb.sequence = dev->sequence++; ++ dev->node[0].cur_frm->vb.field = dev->node[0].m_fmt.field; ++ dev->node[0].cur_frm->vb.sequence = dev->sequence++; + +- vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); +- dev->cur_frm = dev->next_frm; ++ vb2_buffer_done(&dev->node[0].cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); ++ dev->node[0].cur_frm = dev->node[0].next_frm; + } + + /* +@@ -687,7 +693,7 @@ static irqreturn_t unicam_isr(int irq, v + { + struct unicam_device *unicam = (struct unicam_device *)dev; + struct unicam_cfg *cfg = &unicam->cfg; +- struct unicam_dmaqueue *dma_q = &unicam->dma_queue; ++ struct unicam_dmaqueue *dma_q = &unicam->node[0].dma_queue; + unsigned int lines_done = unicam_get_lines_done(dev); + unsigned int sequence = unicam->sequence; + int ista, sta; +@@ -720,8 +726,9 @@ static irqreturn_t unicam_isr(int irq, v + * Timestamp is to be when the first data byte was captured, + * aka frame start. + */ +- if (unicam->cur_frm) +- unicam->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns(); ++ if (unicam->node[0].cur_frm) ++ unicam->node[0].cur_frm->vb.vb2_buf.timestamp = ++ ktime_get_ns(); + } + if (ista & UNICAM_FEI || sta & UNICAM_PI0) { + /* +@@ -729,7 +736,8 @@ static irqreturn_t unicam_isr(int irq, v + * stop the peripheral. Overwrite the frame we've just + * captured instead. + */ +- if (unicam->cur_frm && unicam->cur_frm != unicam->next_frm) ++ if (unicam->node[0].cur_frm && ++ unicam->node[0].cur_frm != unicam->node[0].next_frm) + unicam_process_buffer_complete(unicam); + } + +@@ -738,11 +746,11 @@ static irqreturn_t unicam_isr(int irq, v + * already started. + */ + if (ista & (UNICAM_FSI | UNICAM_LCI) && !(ista & UNICAM_FEI)) { +- spin_lock(&unicam->dma_queue_lock); ++ spin_lock(&unicam->node[0].dma_queue_lock); + if (!list_empty(&dma_q->active) && +- unicam->cur_frm == unicam->next_frm) ++ unicam->node[0].cur_frm == unicam->node[0].next_frm) + unicam_schedule_next_buffer(unicam); +- spin_unlock(&unicam->dma_queue_lock); ++ spin_unlock(&unicam->node[0].dma_queue_lock); + } + + if (reg_read(&unicam->cfg, UNICAM_ICTL) & UNICAM_FCM) { +@@ -756,7 +764,8 @@ static irqreturn_t unicam_isr(int irq, v + static int unicam_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + + strlcpy(cap->driver, UNICAM_MODULE_NAME, sizeof(cap->driver)); + strlcpy(cap->card, UNICAM_MODULE_NAME, sizeof(cap->card)); +@@ -770,7 +779,8 @@ static int unicam_querycap(struct file * + static int unicam_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + struct v4l2_subdev_mbus_code_enum mbus_code; + const struct unicam_fmt *fmt = NULL; + int index = 0; +@@ -815,9 +825,9 @@ static int unicam_enum_fmt_vid_cap(struc + static int unicam_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); + +- *f = dev->v_fmt; ++ *f = node->v_fmt; + + return 0; + } +@@ -859,9 +869,11 @@ const struct unicam_fmt *get_first_suppo + static int unicam_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, ++ .pad = 0 + }; + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + const struct unicam_fmt *fmt; +@@ -939,8 +951,9 @@ static int unicam_try_fmt_vid_cap(struct + static int unicam_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) + { +- struct unicam_device *dev = video_drvdata(file); +- struct vb2_queue *q = &dev->buffer_queue; ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; ++ struct vb2_queue *q = &node->buffer_queue; + struct v4l2_mbus_framefmt mbus_fmt = {0}; + const struct unicam_fmt *fmt; + int ret; +@@ -985,17 +998,18 @@ static int unicam_s_fmt_vid_cap(struct f + return -EINVAL; + } + +- dev->fmt = fmt; +- dev->v_fmt.fmt.pix.pixelformat = f->fmt.pix.pixelformat; +- dev->v_fmt.fmt.pix.bytesperline = f->fmt.pix.bytesperline; +- unicam_reset_format(dev); +- +- unicam_dbg(3, dev, "%s %dx%d, mbus_fmt 0x%08X, V4L2 pix 0x%08X.\n", +- __func__, dev->v_fmt.fmt.pix.width, +- dev->v_fmt.fmt.pix.height, mbus_fmt.code, +- dev->v_fmt.fmt.pix.pixelformat); ++ node->fmt = fmt; ++ node->v_fmt.fmt.pix.pixelformat = f->fmt.pix.pixelformat; ++ node->v_fmt.fmt.pix.bytesperline = f->fmt.pix.bytesperline; ++ unicam_reset_format(node); ++ ++ unicam_dbg(3, dev, ++ "%s %dx%d, mbus_fmt 0x%08X, V4L2 pix 0x%08X.\n", ++ __func__, node->v_fmt.fmt.pix.width, ++ node->v_fmt.fmt.pix.height, mbus_fmt.code, ++ node->v_fmt.fmt.pix.pixelformat); + +- *f = dev->v_fmt; ++ *f = node->v_fmt; + + return 0; + } +@@ -1006,8 +1020,9 @@ static int unicam_queue_setup(struct vb2 + unsigned int sizes[], + struct device *alloc_devs[]) + { +- struct unicam_device *dev = vb2_get_drv_priv(vq); +- unsigned int size = dev->v_fmt.fmt.pix.sizeimage; ++ struct unicam_node *node = vb2_get_drv_priv(vq); ++ struct unicam_device *dev = node->dev; ++ unsigned int size = node->v_fmt.fmt.pix.sizeimage; + + if (vq->num_buffers + *nbuffers < 3) + *nbuffers = 3 - vq->num_buffers; +@@ -1029,15 +1044,16 @@ static int unicam_queue_setup(struct vb2 + + static int unicam_buffer_prepare(struct vb2_buffer *vb) + { +- struct unicam_device *dev = vb2_get_drv_priv(vb->vb2_queue); ++ struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue); ++ struct unicam_device *dev = node->dev; + struct unicam_buffer *buf = container_of(vb, struct unicam_buffer, + vb.vb2_buf); + unsigned long size; + +- if (WARN_ON(!dev->fmt)) ++ if (WARN_ON(!node->fmt)) + return -EINVAL; + +- size = dev->v_fmt.fmt.pix.sizeimage; ++ size = node->v_fmt.fmt.pix.sizeimage; + if (vb2_plane_size(vb, 0) < size) { + unicam_err(dev, "data will not fit into plane (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); +@@ -1050,15 +1066,15 @@ static int unicam_buffer_prepare(struct + + static void unicam_buffer_queue(struct vb2_buffer *vb) + { +- struct unicam_device *dev = vb2_get_drv_priv(vb->vb2_queue); ++ struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue); + struct unicam_buffer *buf = container_of(vb, struct unicam_buffer, + vb.vb2_buf); +- struct unicam_dmaqueue *dma_queue = &dev->dma_queue; ++ struct unicam_dmaqueue *dma_queue = &node->dma_queue; + unsigned long flags = 0; + +- spin_lock_irqsave(&dev->dma_queue_lock, flags); ++ spin_lock_irqsave(&node->dma_queue_lock, flags); + list_add_tail(&buf->list, &dma_queue->active); +- spin_unlock_irqrestore(&dev->dma_queue_lock, flags); ++ spin_unlock_irqrestore(&node->dma_queue_lock, flags); + } + + static void unicam_set_packing_config(struct unicam_device *dev) +@@ -1066,11 +1082,12 @@ static void unicam_set_packing_config(st + int pack, unpack; + u32 val; + +- if (dev->v_fmt.fmt.pix.pixelformat == dev->fmt->fourcc) { ++ if (dev->node[0].v_fmt.fmt.pix.pixelformat == ++ dev->node[0].fmt->fourcc) { + unpack = UNICAM_PUM_NONE; + pack = UNICAM_PPM_NONE; + } else { +- switch (dev->fmt->depth) { ++ switch (dev->node[0].fmt->depth) { + case 8: + unpack = UNICAM_PUM_UNPACK8; + break; +@@ -1108,17 +1125,17 @@ static void unicam_cfg_image_id(struct u + if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { + /* CSI2 mode */ + reg_write(cfg, UNICAM_IDI0, +- (dev->virtual_channel << 6) | dev->fmt->csi_dt); ++ (dev->virtual_channel << 6) | dev->node[0].fmt->csi_dt); + } else { + /* CCP2 mode */ +- reg_write(cfg, UNICAM_IDI0, (0x80 | dev->fmt->csi_dt)); ++ reg_write(cfg, UNICAM_IDI0, (0x80 | dev->node[0].fmt->csi_dt)); + } + } + + static void unicam_start_rx(struct unicam_device *dev, unsigned long addr) + { + struct unicam_cfg *cfg = &dev->cfg; +- int line_int_freq = dev->v_fmt.fmt.pix.height >> 2; ++ int line_int_freq = dev->node[0].v_fmt.fmt.pix.height >> 2; + unsigned int i; + u32 val; + +@@ -1266,7 +1283,8 @@ static void unicam_start_rx(struct unica + reg_write(cfg, UNICAM_DAT3, val); + } + +- reg_write(&dev->cfg, UNICAM_IBLS, dev->v_fmt.fmt.pix.bytesperline); ++ reg_write(&dev->cfg, UNICAM_IBLS, ++ dev->node[0].v_fmt.fmt.pix.bytesperline); + unicam_wr_dma_addr(dev, addr); + unicam_set_packing_config(dev); + unicam_cfg_image_id(dev); +@@ -1327,21 +1345,22 @@ static void unicam_disable(struct unicam + + static int unicam_start_streaming(struct vb2_queue *vq, unsigned int count) + { +- struct unicam_device *dev = vb2_get_drv_priv(vq); +- struct unicam_dmaqueue *dma_q = &dev->dma_queue; ++ struct unicam_node *node = vb2_get_drv_priv(vq); ++ struct unicam_device *dev = node->dev; ++ struct unicam_dmaqueue *dma_q = &node->dma_queue; + struct unicam_buffer *buf, *tmp; + unsigned long addr = 0; + unsigned long flags; + int ret; + +- spin_lock_irqsave(&dev->dma_queue_lock, flags); ++ spin_lock_irqsave(&node->dma_queue_lock, flags); + buf = list_entry(dma_q->active.next, struct unicam_buffer, list); +- dev->cur_frm = buf; +- dev->next_frm = buf; ++ node->cur_frm = buf; ++ node->next_frm = buf; + list_del(&buf->list); +- spin_unlock_irqrestore(&dev->dma_queue_lock, flags); ++ spin_unlock_irqrestore(&node->dma_queue_lock, flags); + +- addr = vb2_dma_contig_plane_dma_addr(&dev->cur_frm->vb.vb2_buf, 0); ++ addr = vb2_dma_contig_plane_dma_addr(&node->cur_frm->vb.vb2_buf, 0); + dev->sequence = 0; + + ret = unicam_runtime_get(dev); +@@ -1411,20 +1430,21 @@ err_release_buffers: + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + } +- if (dev->cur_frm != dev->next_frm) +- vb2_buffer_done(&dev->next_frm->vb.vb2_buf, ++ if (node->cur_frm != node->next_frm) ++ vb2_buffer_done(&node->next_frm->vb.vb2_buf, + VB2_BUF_STATE_QUEUED); +- vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); +- dev->next_frm = NULL; +- dev->cur_frm = NULL; ++ vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); ++ node->next_frm = NULL; ++ node->cur_frm = NULL; + + return ret; + } + + static void unicam_stop_streaming(struct vb2_queue *vq) + { +- struct unicam_device *dev = vb2_get_drv_priv(vq); +- struct unicam_dmaqueue *dma_q = &dev->dma_queue; ++ struct unicam_node *node = vb2_get_drv_priv(vq); ++ struct unicam_device *dev = node->dev; ++ struct unicam_dmaqueue *dma_q = &node->dma_queue; + struct unicam_buffer *buf, *tmp; + unsigned long flags; + +@@ -1434,22 +1454,24 @@ static void unicam_stop_streaming(struct + unicam_disable(dev); + + /* Release all active buffers */ +- spin_lock_irqsave(&dev->dma_queue_lock, flags); ++ spin_lock_irqsave(&node->dma_queue_lock, flags); + list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + +- if (dev->cur_frm == dev->next_frm) { +- vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); ++ if (node->cur_frm == node->next_frm) { ++ vb2_buffer_done(&node->cur_frm->vb.vb2_buf, ++ VB2_BUF_STATE_ERROR); + } else { +- vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); +- vb2_buffer_done(&dev->next_frm->vb.vb2_buf, ++ vb2_buffer_done(&node->cur_frm->vb.vb2_buf, ++ VB2_BUF_STATE_ERROR); ++ vb2_buffer_done(&node->next_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); + } +- dev->cur_frm = NULL; +- dev->next_frm = NULL; +- spin_unlock_irqrestore(&dev->dma_queue_lock, flags); ++ node->cur_frm = NULL; ++ node->next_frm = NULL; ++ spin_unlock_irqrestore(&node->dma_queue_lock, flags); + + clk_disable_unprepare(dev->clock); + unicam_runtime_put(dev); +@@ -1458,7 +1480,8 @@ static void unicam_stop_streaming(struct + static int unicam_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + + if (inp->index != 0) + return -EINVAL; +@@ -1506,21 +1529,24 @@ static int unicam_s_input(struct file *f + static int unicam_querystd(struct file *file, void *priv, + v4l2_std_id *std) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + + return v4l2_subdev_call(dev->sensor, video, querystd, std); + } + + static int unicam_g_std(struct file *file, void *priv, v4l2_std_id *std) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + + return v4l2_subdev_call(dev->sensor, video, g_std, std); + } + + static int unicam_s_std(struct file *file, void *priv, v4l2_std_id std) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + int ret; + v4l2_std_id current_std; + +@@ -1531,29 +1557,31 @@ static int unicam_s_std(struct file *fil + if (std == current_std) + return 0; + +- if (vb2_is_busy(&dev->buffer_queue)) ++ if (vb2_is_busy(&node->buffer_queue)) + return -EBUSY; + + ret = v4l2_subdev_call(dev->sensor, video, s_std, std); + + /* Force recomputation of bytesperline */ +- dev->v_fmt.fmt.pix.bytesperline = 0; ++ node->v_fmt.fmt.pix.bytesperline = 0; + +- unicam_reset_format(dev); ++ unicam_reset_format(node); + + return ret; + } + + static int unicam_s_edid(struct file *file, void *priv, struct v4l2_edid *edid) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + + return v4l2_subdev_call(dev->sensor, pad, set_edid, edid); + } + + static int unicam_g_edid(struct file *file, void *priv, struct v4l2_edid *edid) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + + return v4l2_subdev_call(dev->sensor, pad, get_edid, edid); + } +@@ -1561,7 +1589,8 @@ static int unicam_g_edid(struct file *fi + static int unicam_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + const struct unicam_fmt *fmt; + struct v4l2_subdev_frame_size_enum fse; + int ret; +@@ -1596,7 +1625,8 @@ static int unicam_enum_framesizes(struct + static int unicam_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + const struct unicam_fmt *fmt; + struct v4l2_subdev_frame_interval_enum fie = { + .index = fival->index, +@@ -1624,14 +1654,16 @@ static int unicam_enum_frameintervals(st + + static int unicam_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + + return v4l2_g_parm_cap(video_devdata(file), dev->sensor, a); + } + + static int unicam_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + + return v4l2_s_parm_cap(video_devdata(file), dev->sensor, a); + } +@@ -1639,7 +1671,8 @@ static int unicam_s_parm(struct file *fi + static int unicam_g_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + + return v4l2_subdev_call(dev->sensor, video, g_dv_timings, timings); + } +@@ -1647,7 +1680,8 @@ static int unicam_g_dv_timings(struct fi + static int unicam_s_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + struct v4l2_dv_timings current_timings; + int ret; + +@@ -1657,15 +1691,15 @@ static int unicam_s_dv_timings(struct fi + if (v4l2_match_dv_timings(timings, ¤t_timings, 0, false)) + return 0; + +- if (vb2_is_busy(&dev->buffer_queue)) ++ if (vb2_is_busy(&node->buffer_queue)) + return -EBUSY; + + ret = v4l2_subdev_call(dev->sensor, video, s_dv_timings, timings); + + /* Force recomputation of bytesperline */ +- dev->v_fmt.fmt.pix.bytesperline = 0; ++ node->v_fmt.fmt.pix.bytesperline = 0; + +- unicam_reset_format(dev); ++ unicam_reset_format(node); + + return ret; + } +@@ -1673,7 +1707,8 @@ static int unicam_s_dv_timings(struct fi + static int unicam_query_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + + return v4l2_subdev_call(dev->sensor, video, query_dv_timings, timings); + } +@@ -1681,7 +1716,8 @@ static int unicam_query_dv_timings(struc + static int unicam_enum_dv_timings(struct file *file, void *priv, + struct v4l2_enum_dv_timings *timings) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + + return v4l2_subdev_call(dev->sensor, pad, enum_dv_timings, timings); + } +@@ -1689,7 +1725,8 @@ static int unicam_enum_dv_timings(struct + static int unicam_dv_timings_cap(struct file *file, void *priv, + struct v4l2_dv_timings_cap *cap) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + + return v4l2_subdev_call(dev->sensor, pad, dv_timings_cap, cap); + } +@@ -1707,7 +1744,8 @@ static int unicam_subscribe_event(struct + + static int unicam_log_status(struct file *file, void *fh) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + struct unicam_cfg *cfg = &dev->cfg; + u32 reg; + +@@ -1716,10 +1754,10 @@ static int unicam_log_status(struct file + + unicam_info(dev, "-----Receiver status-----\n"); + unicam_info(dev, "V4L2 width/height: %ux%u\n", +- dev->v_fmt.fmt.pix.width, dev->v_fmt.fmt.pix.height); +- unicam_info(dev, "Mediabus format: %08x\n", dev->fmt->code); ++ node->v_fmt.fmt.pix.width, node->v_fmt.fmt.pix.height); ++ unicam_info(dev, "Mediabus format: %08x\n", node->fmt->code); + unicam_info(dev, "V4L2 format: %08x\n", +- dev->v_fmt.fmt.pix.pixelformat); ++ node->v_fmt.fmt.pix.pixelformat); + reg = reg_read(&dev->cfg, UNICAM_IPIPE); + unicam_info(dev, "Unpacking/packing: %u / %u\n", + get_field(reg, UNICAM_PUM_MASK), +@@ -1744,7 +1782,7 @@ static void unicam_notify(struct v4l2_su + + switch (notification) { + case V4L2_DEVICE_NOTIFY_EVENT: +- v4l2_event_queue(&dev->video_dev, arg); ++ v4l2_event_queue(&dev->node[0].video_dev, arg); + break; + default: + break; +@@ -1767,10 +1805,11 @@ static const struct vb2_ops unicam_video + */ + static int unicam_open(struct file *file) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + int ret; + +- mutex_lock(&dev->lock); ++ mutex_lock(&node->lock); + + ret = v4l2_fh_open(file); + if (ret) { +@@ -1790,18 +1829,19 @@ static int unicam_open(struct file *file + ret = 0; + + unlock: +- mutex_unlock(&dev->lock); ++ mutex_unlock(&node->lock); + return ret; + } + + static int unicam_release(struct file *file) + { +- struct unicam_device *dev = video_drvdata(file); ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; + struct v4l2_subdev *sd = dev->sensor; + bool fh_singular; + int ret; + +- mutex_lock(&dev->lock); ++ mutex_lock(&node->lock); + + fh_singular = v4l2_fh_is_singular_file(file); + +@@ -1810,7 +1850,7 @@ static int unicam_release(struct file *f + if (fh_singular) + v4l2_subdev_call(sd, core, s_power, 0); + +- mutex_unlock(&dev->lock); ++ mutex_unlock(&node->lock); + + return ret; + } +@@ -1892,7 +1932,8 @@ unicam_async_bound(struct v4l2_async_not + return 0; + } + +-static int unicam_probe_complete(struct unicam_device *unicam) ++static int register_node(struct unicam_device *unicam, struct unicam_node *node, ++ enum v4l2_buf_type type, int pad_id) + { + struct video_device *vdev; + struct vb2_queue *q; +@@ -1900,15 +1941,7 @@ static int unicam_probe_complete(struct + const struct unicam_fmt *fmt; + int ret; + +- v4l2_set_subdev_hostdata(unicam->sensor, unicam); +- +- unicam->v4l2_dev.notify = unicam_notify; +- +- unicam->sensor_config = v4l2_subdev_alloc_pad_config(unicam->sensor); +- if (!unicam->sensor_config) +- return -ENOMEM; +- +- ret = __subdev_get_format(unicam, &mbus_fmt); ++ ret = __subdev_get_format(unicam, &mbus_fmt, pad_id); + if (ret) { + unicam_err(unicam, "Failed to get_format - ret %d\n", ret); + return ret; +@@ -1938,14 +1971,15 @@ static int unicam_probe_complete(struct + return -EINVAL; + } + +- unicam->fmt = fmt; ++ node->pad_id = pad_id; ++ node->fmt = fmt; + if (fmt->fourcc) +- unicam->v_fmt.fmt.pix.pixelformat = fmt->fourcc; ++ node->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + else +- unicam->v_fmt.fmt.pix.pixelformat = fmt->repacked_fourcc; ++ node->v_fmt.fmt.pix.pixelformat = fmt->repacked_fourcc; + + /* Read current subdev format */ +- unicam_reset_format(unicam); ++ unicam_reset_format(node); + + if (v4l2_subdev_has_op(unicam->sensor, video, s_std)) { + v4l2_std_id tvnorms; +@@ -1962,27 +1996,30 @@ static int unicam_probe_complete(struct + g_tvnorms, &tvnorms); + if (WARN_ON(ret)) + return -EINVAL; +- unicam->video_dev.tvnorms |= tvnorms; ++ node->video_dev.tvnorms |= tvnorms; + } + +- spin_lock_init(&unicam->dma_queue_lock); +- mutex_init(&unicam->lock); ++ spin_lock_init(&node->dma_queue_lock); ++ mutex_init(&node->lock); + +- /* Add controls from the subdevice */ +- ret = v4l2_ctrl_add_handler(&unicam->ctrl_handler, +- unicam->sensor->ctrl_handler, NULL, true); +- if (ret < 0) +- return ret; ++ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ /* Add controls from the subdevice */ ++ ret = v4l2_ctrl_add_handler(&node->ctrl_handler, ++ unicam->sensor->ctrl_handler, NULL, ++ true); ++ if (ret < 0) ++ return ret; ++ } + +- q = &unicam->buffer_queue; +- q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ q = &node->buffer_queue; ++ q->type = type; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; +- q->drv_priv = unicam; ++ q->drv_priv = node; + q->ops = &unicam_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct unicam_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; +- q->lock = &unicam->lock; ++ q->lock = &node->lock; + q->min_buffers_needed = 2; + q->dev = &unicam->pdev->dev; + +@@ -1992,9 +2029,9 @@ static int unicam_probe_complete(struct + return ret; + } + +- INIT_LIST_HEAD(&unicam->dma_queue.active); ++ INIT_LIST_HEAD(&node->dma_queue.active); + +- vdev = &unicam->video_dev; ++ vdev = &node->video_dev; + strlcpy(vdev->name, UNICAM_MODULE_NAME, sizeof(vdev->name)); + vdev->release = video_device_release_empty; + vdev->fops = &unicam_fops; +@@ -2002,69 +2039,113 @@ static int unicam_probe_complete(struct + vdev->v4l2_dev = &unicam->v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + vdev->queue = q; +- vdev->lock = &unicam->lock; ++ vdev->lock = &node->lock; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; +- + /* If the source has no controls then remove our ctrl handler. */ +- if (list_empty(&unicam->ctrl_handler.ctrls)) ++ if (list_empty(&node->ctrl_handler.ctrls)) + unicam->v4l2_dev.ctrl_handler = NULL; + +- video_set_drvdata(vdev, unicam); ++ node->dev = unicam; ++ video_set_drvdata(vdev, node); + vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; + + if (!v4l2_subdev_has_op(unicam->sensor, video, s_std)) { +- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_STD); +- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_STD); +- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_ENUMSTD); ++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_STD); ++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_STD); ++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUMSTD); + } + if (!v4l2_subdev_has_op(unicam->sensor, video, querystd)) +- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_QUERYSTD); ++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_QUERYSTD); + if (!v4l2_subdev_has_op(unicam->sensor, video, s_dv_timings)) { +- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_EDID); +- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_EDID); +- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_DV_TIMINGS_CAP); +- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_DV_TIMINGS); +- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_DV_TIMINGS); +- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_ENUM_DV_TIMINGS); +- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_QUERY_DV_TIMINGS); ++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_EDID); ++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_EDID); ++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_DV_TIMINGS_CAP); ++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_DV_TIMINGS); ++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_DV_TIMINGS); ++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_DV_TIMINGS); ++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_QUERY_DV_TIMINGS); + } + if (!v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_interval)) +- v4l2_disable_ioctl(&unicam->video_dev, ++ v4l2_disable_ioctl(&node->video_dev, + VIDIOC_ENUM_FRAMEINTERVALS); + if (!v4l2_subdev_has_op(unicam->sensor, video, g_frame_interval)) +- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_PARM); ++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_PARM); + if (!v4l2_subdev_has_op(unicam->sensor, video, s_frame_interval)) +- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_PARM); ++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_PARM); + + if (!v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_size)) +- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_ENUM_FRAMESIZES); ++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_FRAMESIZES); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret) { + unicam_err(unicam, "Unable to register video device.\n"); + return ret; + } ++ node->registered = true; + +- ret = v4l2_device_register_ro_subdev_nodes(&unicam->v4l2_dev); ++ ret = media_create_pad_link(&unicam->sensor->entity, ++ 0, &node->video_dev.entity, 0, ++ MEDIA_LNK_FL_ENABLED | ++ MEDIA_LNK_FL_IMMUTABLE); ++ if (ret) ++ unicam_err(unicam, "Unable to create pad links.\n"); ++ ++ return ret; ++} ++ ++static void unregister_nodes(struct unicam_device *unicam) ++{ ++ if (unicam->node[0].registered) { ++ video_unregister_device(&unicam->node[0].video_dev); ++ unicam->node[0].registered = false; ++ } ++ if (unicam->node[1].registered) { ++ video_unregister_device(&unicam->node[1].video_dev); ++ unicam->node[1].registered = false; ++ } ++} ++ ++static int unicam_probe_complete(struct unicam_device *unicam) ++{ ++ int ret; ++ ++ v4l2_set_subdev_hostdata(unicam->sensor, unicam); ++ ++ unicam->v4l2_dev.notify = unicam_notify; ++ ++ unicam->sensor_config = v4l2_subdev_alloc_pad_config(unicam->sensor); ++ if (!unicam->sensor_config) ++ return -ENOMEM; ++ ++ ret = register_node(unicam, &unicam->node[0], ++ V4L2_BUF_TYPE_VIDEO_CAPTURE, 0); + if (ret) { +- unicam_err(unicam, +- "Unable to register subdev nodes.\n"); +- video_unregister_device(&unicam->video_dev); +- return ret; ++ unicam_err(unicam, "Unable to register subdev node 0.\n"); ++ goto unregister; ++ } ++ if (unicam->sensor->entity.num_pads >= 2) { ++ ret = register_node(unicam, &unicam->node[1], ++ V4L2_BUF_TYPE_META_CAPTURE, 1); ++ if (ret) { ++ unicam_err(unicam, ++ "Unable to register subdev node 1.\n"); ++ goto unregister; ++ } + } + +- ret = media_create_pad_link(&unicam->sensor->entity, 0, +- &unicam->video_dev.entity, 0, +- MEDIA_LNK_FL_ENABLED | +- MEDIA_LNK_FL_IMMUTABLE); ++ ret = v4l2_device_register_ro_subdev_nodes(&unicam->v4l2_dev); + if (ret) { +- unicam_err(unicam, "Unable to create pad links.\n"); +- video_unregister_device(&unicam->video_dev); +- return ret; ++ unicam_err(unicam, "Unable to register subdev nodes.\n"); ++ goto unregister; + } + + return 0; ++ ++unregister: ++ unregister_nodes(unicam); ++ ++ return ret; + } + + static int unicam_async_complete(struct v4l2_async_notifier *notifier) +@@ -2274,7 +2355,8 @@ static int unicam_probe(struct platform_ + pdev->dev.driver->name, dev_name(&pdev->dev)); + unicam->mdev.hw_revision = 1; + +- media_entity_pads_init(&unicam->video_dev.entity, 1, &unicam->pad); ++ media_entity_pads_init(&unicam->node[0].video_dev.entity, 1, ++ &unicam->node[0].pad); + media_device_init(&unicam->mdev); + + unicam->v4l2_dev.mdev = &unicam->mdev; +@@ -2294,7 +2376,7 @@ static int unicam_probe(struct platform_ + } + + /* Reserve space for the controls */ +- hdl = &unicam->ctrl_handler; ++ hdl = &unicam->node[0].ctrl_handler; + ret = v4l2_ctrl_handler_init(hdl, 16); + if (ret < 0) + goto media_unregister; +@@ -2335,9 +2417,9 @@ static int unicam_remove(struct platform + pm_runtime_disable(&pdev->dev); + + v4l2_async_notifier_unregister(&unicam->notifier); +- v4l2_ctrl_handler_free(&unicam->ctrl_handler); ++ v4l2_ctrl_handler_free(&unicam->node[0].ctrl_handler); + v4l2_device_unregister(&unicam->v4l2_dev); +- video_unregister_device(&unicam->video_dev); ++ unregister_nodes(unicam); + if (unicam->sensor_config) + v4l2_subdev_free_pad_config(unicam->sensor_config); + media_device_unregister(&unicam->mdev); diff --git a/target/linux/bcm27xx/patches-5.4/950-0653-media-bcm2835-unicam-Add-embedded-data-node.patch b/target/linux/bcm27xx/patches-5.4/950-0653-media-bcm2835-unicam-Add-embedded-data-node.patch new file mode 100644 index 0000000000..a163a6f1a5 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0653-media-bcm2835-unicam-Add-embedded-data-node.patch @@ -0,0 +1,1170 @@ +From 272ee62d6410319ab4d73997de32776cc3e274cb Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Thu, 16 Apr 2020 11:35:41 +0100 +Subject: [PATCH] media: bcm2835-unicam: Add embedded data node. + +This patch adds a new node in the bcm2835-unicam driver to support +CSI-2 embedded data streams. The subdevice is queried to see if +embedded data is available from the sensor. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + .../media/platform/bcm2835/bcm2835-unicam.c | 667 +++++++++++++----- + 1 file changed, 474 insertions(+), 193 deletions(-) + +--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c ++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c +@@ -109,8 +109,15 @@ MODULE_PARM_DESC(debug, "Debug level 0-3 + /* Define a nominal minimum image size */ + #define MIN_WIDTH 16 + #define MIN_HEIGHT 16 +-/* Maximum number of simulataneous streams Uncaim can handle. */ +-#define MAX_NODES 2 ++/* Default size of the embedded buffer */ ++#define UNICAM_EMBEDDED_SIZE 8192 ++ ++enum pad_types { ++ IMAGE_PAD, ++ METADATA_PAD, ++ MAX_NODES ++}; ++ + /* + * struct unicam_fmt - Unicam media bus format information + * @pixelformat: V4L2 pixel format FCC identifier. 0 if n/a. +@@ -327,6 +334,12 @@ static const struct unicam_fmt formats[] + .depth = 12, + .csi_dt = 0x2c, + }, ++ /* Embedded data format */ ++ { ++ .fourcc = V4L2_META_FMT_SENSOR_DATA, ++ .code = MEDIA_BUS_FMT_SENSOR_DATA, ++ .depth = 8, ++ } + }; + + struct unicam_dmaqueue { +@@ -348,7 +361,9 @@ struct unicam_cfg { + #define MAX_POSSIBLE_PIX_FMTS (ARRAY_SIZE(formats)) + + struct unicam_node { +- bool registered; ++ int registered; ++ int open; ++ int streaming; + unsigned int pad_id; + /* Pointer pointing to current v4l2_buffer */ + struct unicam_buffer *cur_frm; +@@ -374,6 +389,7 @@ struct unicam_node { + struct unicam_device *dev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; ++ unsigned int embedded_lines; + }; + + struct unicam_device { +@@ -401,8 +417,6 @@ struct unicam_device { + struct v4l2_subdev *sensor; + /* Pad config for the sensor */ + struct v4l2_subdev_pad_config *sensor_config; +- /* current input at the sub device */ +- int current_input; + + unsigned int virtual_channel; + enum v4l2_mbus_type bus_type; +@@ -413,10 +427,7 @@ struct unicam_device { + unsigned int bus_flags; + unsigned int max_data_lanes; + unsigned int active_data_lanes; +- +- struct v4l2_rect crop; +- /* Flag to denote that we are processing buffers */ +- int streaming; ++ bool sensor_embedded_data; + + struct unicam_node node[MAX_NODES]; + }; +@@ -488,6 +499,7 @@ static int check_mbus_format(struct unic + for (i = 0; !ret && i < MAX_ENUM_MBUS_CODE; i++) { + memset(&mbus_code, 0, sizeof(mbus_code)); + mbus_code.index = i; ++ mbus_code.pad = IMAGE_PAD; + mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, +@@ -552,10 +564,11 @@ static int __subdev_get_format(struct un + } + + static int __subdev_set_format(struct unicam_device *dev, +- struct v4l2_mbus_framefmt *fmt) ++ struct v4l2_mbus_framefmt *fmt, int pad_id) + { + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, ++ .pad = pad_id + }; + int ret; + +@@ -566,8 +579,12 @@ static int __subdev_set_format(struct un + if (ret < 0) + return ret; + +- unicam_dbg(1, dev, "%s %dx%d code:%04x\n", __func__, +- fmt->width, fmt->height, fmt->code); ++ if (pad_id == IMAGE_PAD) ++ unicam_dbg(1, dev, "%s %dx%d code:%04x\n", __func__, fmt->width, ++ fmt->height, fmt->code); ++ else ++ unicam_dbg(1, dev, "%s Embedded data code:%04x\n", __func__, ++ sd_fmt.format.code); + + return 0; + } +@@ -609,46 +626,70 @@ static int unicam_reset_format(struct un + struct v4l2_mbus_framefmt mbus_fmt; + int ret; + +- ret = __subdev_get_format(dev, &mbus_fmt, node->pad_id); +- if (ret) { +- unicam_err(dev, "Failed to get_format - ret %d\n", ret); +- return ret; +- } ++ if (dev->sensor_embedded_data || node->pad_id != METADATA_PAD) { ++ ret = __subdev_get_format(dev, &mbus_fmt, node->pad_id); ++ if (ret) { ++ unicam_err(dev, "Failed to get_format - ret %d\n", ret); ++ return ret; ++ } + +- if (mbus_fmt.code != dev->node[0].fmt->code) { +- unicam_err(dev, "code mismatch - fmt->code %08x, mbus_fmt.code %08x\n", +- dev->node[0].fmt->code, mbus_fmt.code); +- return ret; ++ if (mbus_fmt.code != node->fmt->code) { ++ unicam_err(dev, "code mismatch - fmt->code %08x, mbus_fmt.code %08x\n", ++ node->fmt->code, mbus_fmt.code); ++ return ret; ++ } + } + +- v4l2_fill_pix_format(&dev->node[0].v_fmt.fmt.pix, &mbus_fmt); +- dev->node[0].v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; +- +- unicam_calc_format_size_bpl(dev, dev->node[0].fmt, &dev->node[0].v_fmt); +- +- dev->node[0].m_fmt = mbus_fmt; ++ if (node->pad_id == IMAGE_PAD) { ++ v4l2_fill_pix_format(&node->v_fmt.fmt.pix, &mbus_fmt); ++ node->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ unicam_calc_format_size_bpl(dev, node->fmt, &node->v_fmt); ++ } else { ++ node->v_fmt.type = V4L2_BUF_TYPE_META_CAPTURE; ++ node->v_fmt.fmt.meta.dataformat = V4L2_META_FMT_SENSOR_DATA; ++ if (dev->sensor_embedded_data) { ++ node->v_fmt.fmt.meta.buffersize = ++ mbus_fmt.width * mbus_fmt.height; ++ node->embedded_lines = mbus_fmt.height; ++ } else { ++ node->v_fmt.fmt.meta.buffersize = UNICAM_EMBEDDED_SIZE; ++ node->embedded_lines = 1; ++ } ++ } + ++ node->m_fmt = mbus_fmt; + return 0; + } + +-static void unicam_wr_dma_addr(struct unicam_device *dev, dma_addr_t dmaaddr) ++static void unicam_wr_dma_addr(struct unicam_device *dev, dma_addr_t dmaaddr, ++ int pad_id) + { ++ dma_addr_t endaddr; ++ + /* + * dmaaddr should be a 32-bit address with the top two bits set to 0x3 + * to signify uncached access through the Videocore memory controller. + */ + BUG_ON((dmaaddr >> 30) != 0x3); + +- reg_write(&dev->cfg, UNICAM_IBSA0, dmaaddr); +- reg_write(&dev->cfg, UNICAM_IBEA0, +- dmaaddr + dev->node[0].v_fmt.fmt.pix.sizeimage); ++ if (pad_id == IMAGE_PAD) { ++ endaddr = dmaaddr + ++ dev->node[IMAGE_PAD].v_fmt.fmt.pix.sizeimage; ++ reg_write(&dev->cfg, UNICAM_IBSA0, dmaaddr); ++ reg_write(&dev->cfg, UNICAM_IBEA0, endaddr); ++ } else { ++ endaddr = dmaaddr + ++ dev->node[METADATA_PAD].v_fmt.fmt.meta.buffersize; ++ reg_write(&dev->cfg, UNICAM_DBSA0, dmaaddr); ++ reg_write(&dev->cfg, UNICAM_DBEA0, endaddr); ++ } + } + + static inline unsigned int unicam_get_lines_done(struct unicam_device *dev) + { + dma_addr_t start_addr, cur_addr; +- unsigned int stride = dev->node[0].v_fmt.fmt.pix.bytesperline; +- struct unicam_buffer *frm = dev->node[0].cur_frm; ++ unsigned int stride = dev->node[IMAGE_PAD].v_fmt.fmt.pix.bytesperline; ++ struct unicam_buffer *frm = dev->node[IMAGE_PAD].cur_frm; + + if (!frm) + return 0; +@@ -658,27 +699,51 @@ static inline unsigned int unicam_get_li + return (unsigned int)(cur_addr - start_addr) / stride; + } + +-static inline void unicam_schedule_next_buffer(struct unicam_device *dev) ++static inline void unicam_schedule_next_buffer(struct unicam_node *node) + { +- struct unicam_dmaqueue *dma_q = &dev->node[0].dma_queue; ++ struct unicam_device *dev = node->dev; ++ struct unicam_dmaqueue *dma_q = &node->dma_queue; + struct unicam_buffer *buf; + dma_addr_t addr; + + buf = list_entry(dma_q->active.next, struct unicam_buffer, list); +- dev->node[0].next_frm = buf; ++ node->next_frm = buf; + list_del(&buf->list); + + addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); +- unicam_wr_dma_addr(dev, addr); ++ unicam_wr_dma_addr(dev, addr, node->pad_id); + } + +-static inline void unicam_process_buffer_complete(struct unicam_device *dev) ++static inline void unicam_process_buffer_complete(struct unicam_node *node, ++ unsigned int sequence) + { +- dev->node[0].cur_frm->vb.field = dev->node[0].m_fmt.field; +- dev->node[0].cur_frm->vb.sequence = dev->sequence++; ++ node->cur_frm->vb.field = node->m_fmt.field; ++ node->cur_frm->vb.sequence = sequence; ++ ++ vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); ++ node->cur_frm = node->next_frm; ++} + +- vb2_buffer_done(&dev->node[0].cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); +- dev->node[0].cur_frm = dev->node[0].next_frm; ++static int unicam_num_nodes_streaming(struct unicam_device *dev) ++{ ++ return dev->node[IMAGE_PAD].streaming + ++ dev->node[METADATA_PAD].streaming; ++} ++ ++static int unicam_all_nodes_streaming(struct unicam_device *dev) ++{ ++ int ret; ++ ++ ret = dev->node[IMAGE_PAD].open && dev->node[IMAGE_PAD].streaming; ++ ret &= !dev->node[METADATA_PAD].open || ++ dev->node[METADATA_PAD].streaming; ++ return ret; ++} ++ ++static int unicam_all_nodes_disabled(struct unicam_device *dev) ++{ ++ return !dev->node[IMAGE_PAD].streaming && ++ !dev->node[METADATA_PAD].streaming; + } + + /* +@@ -693,10 +758,12 @@ static irqreturn_t unicam_isr(int irq, v + { + struct unicam_device *unicam = (struct unicam_device *)dev; + struct unicam_cfg *cfg = &unicam->cfg; +- struct unicam_dmaqueue *dma_q = &unicam->node[0].dma_queue; + unsigned int lines_done = unicam_get_lines_done(dev); + unsigned int sequence = unicam->sequence; ++ int num_nodes_streaming = unicam_num_nodes_streaming(dev); + int ista, sta; ++ u64 ts; ++ int i; + + /* + * Don't service interrupts if not streaming. +@@ -704,7 +771,7 @@ static irqreturn_t unicam_isr(int irq, v + * peripheral without the kernel knowing (that + * shouldn't happen, but causes issues if it does). + */ +- if (!unicam->streaming) ++ if (unicam_all_nodes_disabled(unicam)) + return IRQ_HANDLED; + + sta = reg_read(cfg, UNICAM_STA); +@@ -726,9 +793,12 @@ static irqreturn_t unicam_isr(int irq, v + * Timestamp is to be when the first data byte was captured, + * aka frame start. + */ +- if (unicam->node[0].cur_frm) +- unicam->node[0].cur_frm->vb.vb2_buf.timestamp = +- ktime_get_ns(); ++ ts = ktime_get_ns(); ++ for (i = 0; i < num_nodes_streaming; i++) { ++ if (unicam->node[i].cur_frm) ++ unicam->node[i].cur_frm->vb.vb2_buf.timestamp = ++ ts; ++ } + } + if (ista & UNICAM_FEI || sta & UNICAM_PI0) { + /* +@@ -736,9 +806,13 @@ static irqreturn_t unicam_isr(int irq, v + * stop the peripheral. Overwrite the frame we've just + * captured instead. + */ +- if (unicam->node[0].cur_frm && +- unicam->node[0].cur_frm != unicam->node[0].next_frm) +- unicam_process_buffer_complete(unicam); ++ for (i = 0; i < num_nodes_streaming; i++) { ++ if (unicam->node[i].cur_frm && ++ unicam->node[i].cur_frm != unicam->node[i].next_frm) ++ unicam_process_buffer_complete(&unicam->node[i], ++ sequence); ++ } ++ unicam->sequence++; + } + + /* Cannot swap buffer at frame end, there may be a race condition +@@ -746,11 +820,13 @@ static irqreturn_t unicam_isr(int irq, v + * already started. + */ + if (ista & (UNICAM_FSI | UNICAM_LCI) && !(ista & UNICAM_FEI)) { +- spin_lock(&unicam->node[0].dma_queue_lock); +- if (!list_empty(&dma_q->active) && +- unicam->node[0].cur_frm == unicam->node[0].next_frm) +- unicam_schedule_next_buffer(unicam); +- spin_unlock(&unicam->node[0].dma_queue_lock); ++ for (i = 0; i < num_nodes_streaming; i++) { ++ spin_lock(&unicam->node[i].dma_queue_lock); ++ if (!list_empty(&unicam->node[i].dma_queue.active) && ++ unicam->node[i].cur_frm == unicam->node[i].next_frm) ++ unicam_schedule_next_buffer(&unicam->node[i]); ++ spin_unlock(&unicam->node[i].dma_queue_lock); ++ } + } + + if (reg_read(&unicam->cfg, UNICAM_ICTL) & UNICAM_FCM) { +@@ -773,6 +849,15 @@ static int unicam_querycap(struct file * + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", dev->v4l2_dev.name); + ++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | ++ V4L2_CAP_READWRITE | V4L2_CAP_DEVICE_CAPS | ++ V4L2_CAP_META_CAPTURE; ++ ++ if (node->pad_id == IMAGE_PAD) ++ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; ++ else ++ cap->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING; ++ + return 0; + } + +@@ -787,9 +872,14 @@ static int unicam_enum_fmt_vid_cap(struc + int ret = 0; + int i; + ++ if (node->pad_id == METADATA_PAD) ++ return -EINVAL; ++ + for (i = 0; !ret && i < MAX_ENUM_MBUS_CODE; i++) { + memset(&mbus_code, 0, sizeof(mbus_code)); + mbus_code.index = i; ++ mbus_code.pad = IMAGE_PAD; ++ mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, + NULL, &mbus_code); +@@ -827,6 +917,9 @@ static int unicam_g_fmt_vid_cap(struct f + { + struct unicam_node *node = video_drvdata(file); + ++ if (node->pad_id == METADATA_PAD) ++ return -EINVAL; ++ + *f = node->v_fmt; + + return 0; +@@ -843,6 +936,9 @@ const struct unicam_fmt *get_first_suppo + for (j = 0; ret != -EINVAL && ret != -ENOIOCTLCMD; ++j) { + memset(&mbus_code, 0, sizeof(mbus_code)); + mbus_code.index = j; ++ mbus_code.pad = IMAGE_PAD; ++ mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; ++ + ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, NULL, + &mbus_code); + if (ret < 0) { +@@ -873,12 +969,15 @@ static int unicam_try_fmt_vid_cap(struct + struct unicam_device *dev = node->dev; + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, +- .pad = 0 ++ .pad = IMAGE_PAD + }; + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + const struct unicam_fmt *fmt; + int ret; + ++ if (node->pad_id == METADATA_PAD) ++ return -EINVAL; ++ + fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat); + if (!fmt) { + /* Pixel format not supported by unicam. Choose the first +@@ -983,7 +1082,7 @@ static int unicam_s_fmt_vid_cap(struct f + + v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); + +- ret = __subdev_set_format(dev, &mbus_fmt); ++ ret = __subdev_set_format(dev, &mbus_fmt, node->pad_id); + if (ret) { + unicam_dbg(3, dev, "%s __subdev_set_format failed %d\n", + __func__, ret); +@@ -1014,6 +1113,106 @@ static int unicam_s_fmt_vid_cap(struct f + return 0; + } + ++static int unicam_enum_fmt_meta_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; ++ struct v4l2_subdev_mbus_code_enum mbus_code; ++ const struct unicam_fmt *fmt = NULL; ++ int ret = 0; ++ ++ if (node->pad_id != METADATA_PAD || f->index != 0) ++ return -EINVAL; ++ ++ if (dev->sensor_embedded_data) { ++ memset(&mbus_code, 0, sizeof(mbus_code)); ++ mbus_code.index = f->index; ++ mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; ++ mbus_code.pad = METADATA_PAD; ++ ++ ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, NULL, ++ &mbus_code); ++ if (ret < 0) { ++ unicam_dbg(2, dev, ++ "subdev->enum_mbus_code idx 0 returned %d - index invalid\n", ++ ret); ++ return -EINVAL; ++ } ++ } else { ++ mbus_code.code = MEDIA_BUS_FMT_SENSOR_DATA; ++ } ++ ++ fmt = find_format_by_code(mbus_code.code); ++ if (fmt) ++ f->pixelformat = fmt->fourcc; ++ ++ return 0; ++} ++ ++static int unicam_g_fmt_meta_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct unicam_node *node = video_drvdata(file); ++ ++ if (node->pad_id != METADATA_PAD) ++ return -EINVAL; ++ ++ *f = node->v_fmt; ++ ++ return 0; ++} ++ ++static int unicam_try_fmt_meta_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct unicam_node *node = video_drvdata(file); ++ ++ if (node->pad_id != METADATA_PAD) ++ return -EINVAL; ++ ++ *f = node->v_fmt; ++ ++ return 0; ++} ++ ++static int unicam_s_fmt_meta_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; ++ struct v4l2_mbus_framefmt mbus_fmt = { 0 }; ++ const struct unicam_fmt *fmt; ++ int ret; ++ ++ if (node->pad_id == IMAGE_PAD) ++ return -EINVAL; ++ ++ if (dev->sensor_embedded_data) { ++ fmt = find_format_by_pix(dev, f->fmt.meta.dataformat); ++ if (!fmt) { ++ unicam_err(dev, "unknown format: V4L2 pix 0x%08x\n", ++ f->fmt.meta.dataformat); ++ return -EINVAL; ++ } ++ mbus_fmt.code = fmt->code; ++ ret = __subdev_set_format(dev, &mbus_fmt, node->pad_id); ++ if (ret) { ++ unicam_dbg(3, dev, "%s __subdev_set_format failed %d\n", ++ __func__, ret); ++ return ret; ++ } ++ } ++ ++ *f = node->v_fmt; ++ ++ unicam_dbg(3, dev, "%s size %d, V4L2 pix 0x%08x\n", ++ __func__, node->v_fmt.fmt.meta.buffersize, ++ node->v_fmt.fmt.meta.dataformat); ++ ++ return 0; ++} ++ + static int unicam_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, +@@ -1022,7 +1221,9 @@ static int unicam_queue_setup(struct vb2 + { + struct unicam_node *node = vb2_get_drv_priv(vq); + struct unicam_device *dev = node->dev; +- unsigned int size = node->v_fmt.fmt.pix.sizeimage; ++ unsigned int size = node->pad_id == IMAGE_PAD ? ++ node->v_fmt.fmt.pix.sizeimage : ++ node->v_fmt.fmt.meta.buffersize; + + if (vq->num_buffers + *nbuffers < 3) + *nbuffers = 3 - vq->num_buffers; +@@ -1053,7 +1254,8 @@ static int unicam_buffer_prepare(struct + if (WARN_ON(!node->fmt)) + return -EINVAL; + +- size = node->v_fmt.fmt.pix.sizeimage; ++ size = node->pad_id == IMAGE_PAD ? node->v_fmt.fmt.pix.sizeimage : ++ node->v_fmt.fmt.meta.buffersize; + if (vb2_plane_size(vb, 0) < size) { + unicam_err(dev, "data will not fit into plane (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); +@@ -1082,12 +1284,12 @@ static void unicam_set_packing_config(st + int pack, unpack; + u32 val; + +- if (dev->node[0].v_fmt.fmt.pix.pixelformat == +- dev->node[0].fmt->fourcc) { ++ if (dev->node[IMAGE_PAD].v_fmt.fmt.pix.pixelformat == ++ dev->node[IMAGE_PAD].fmt->fourcc) { + unpack = UNICAM_PUM_NONE; + pack = UNICAM_PPM_NONE; + } else { +- switch (dev->node[0].fmt->depth) { ++ switch (dev->node[IMAGE_PAD].fmt->depth) { + case 8: + unpack = UNICAM_PUM_UNPACK8; + break; +@@ -1125,17 +1327,31 @@ static void unicam_cfg_image_id(struct u + if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { + /* CSI2 mode */ + reg_write(cfg, UNICAM_IDI0, +- (dev->virtual_channel << 6) | dev->node[0].fmt->csi_dt); ++ (dev->virtual_channel << 6) | ++ dev->node[IMAGE_PAD].fmt->csi_dt); + } else { + /* CCP2 mode */ +- reg_write(cfg, UNICAM_IDI0, (0x80 | dev->node[0].fmt->csi_dt)); ++ reg_write(cfg, UNICAM_IDI0, ++ 0x80 | dev->node[IMAGE_PAD].fmt->csi_dt); + } + } + +-static void unicam_start_rx(struct unicam_device *dev, unsigned long addr) ++static void unicam_enable_ed(struct unicam_device *dev) ++{ ++ struct unicam_cfg *cfg = &dev->cfg; ++ u32 val = reg_read(cfg, UNICAM_DCS); ++ ++ set_field(&val, 2, UNICAM_EDL_MASK); ++ /* Do not wrap at the end of the embedded data buffer */ ++ set_field(&val, 0, UNICAM_DBOB); ++ ++ reg_write(cfg, UNICAM_DCS, val); ++} ++ ++static void unicam_start_rx(struct unicam_device *dev, dma_addr_t *addr) + { + struct unicam_cfg *cfg = &dev->cfg; +- int line_int_freq = dev->node[0].v_fmt.fmt.pix.height >> 2; ++ int line_int_freq = dev->node[IMAGE_PAD].v_fmt.fmt.pix.height >> 2; + unsigned int i; + u32 val; + +@@ -1284,27 +1500,31 @@ static void unicam_start_rx(struct unica + } + + reg_write(&dev->cfg, UNICAM_IBLS, +- dev->node[0].v_fmt.fmt.pix.bytesperline); +- unicam_wr_dma_addr(dev, addr); ++ dev->node[IMAGE_PAD].v_fmt.fmt.pix.bytesperline); ++ unicam_wr_dma_addr(dev, addr[IMAGE_PAD], IMAGE_PAD); + unicam_set_packing_config(dev); + unicam_cfg_image_id(dev); + +- /* Disabled embedded data */ +- val = 0; +- set_field(&val, 0, UNICAM_EDL_MASK); +- reg_write(cfg, UNICAM_DCS, val); +- + val = reg_read(cfg, UNICAM_MISC); + set_field(&val, 1, UNICAM_FL0); + set_field(&val, 1, UNICAM_FL1); + reg_write(cfg, UNICAM_MISC, val); + ++ if (dev->node[METADATA_PAD].streaming && dev->sensor_embedded_data) { ++ unicam_enable_ed(dev); ++ unicam_wr_dma_addr(dev, addr[METADATA_PAD], METADATA_PAD); ++ } ++ + /* Enable peripheral */ + reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_CPE); + + /* Load image pointers */ + reg_write_field(cfg, UNICAM_ICTL, 1, UNICAM_LIP_MASK); + ++ /* Load embedded data buffer pointers if needed */ ++ if (dev->node[METADATA_PAD].streaming && dev->sensor_embedded_data) ++ reg_write_field(cfg, UNICAM_DCS, 1, UNICAM_LDP); ++ + /* + * Enable trigger only for the first frame to + * sync correctly to the FS from the source. +@@ -1339,6 +1559,9 @@ static void unicam_disable(struct unicam + /* Disable peripheral */ + reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPE); + ++ /* Clear ED setup */ ++ reg_write(cfg, UNICAM_DCS, 0); ++ + /* Disable all lane clocks */ + clk_write(cfg, 0); + } +@@ -1347,26 +1570,23 @@ static int unicam_start_streaming(struct + { + struct unicam_node *node = vb2_get_drv_priv(vq); + struct unicam_device *dev = node->dev; +- struct unicam_dmaqueue *dma_q = &node->dma_queue; +- struct unicam_buffer *buf, *tmp; +- unsigned long addr = 0; ++ struct unicam_buffer *buf; ++ dma_addr_t buffer_addr[MAX_NODES] = { 0 }; ++ int num_nodes_streaming; + unsigned long flags; +- int ret; ++ int ret, i; + +- spin_lock_irqsave(&node->dma_queue_lock, flags); +- buf = list_entry(dma_q->active.next, struct unicam_buffer, list); +- node->cur_frm = buf; +- node->next_frm = buf; +- list_del(&buf->list); +- spin_unlock_irqrestore(&node->dma_queue_lock, flags); ++ node->streaming = 1; ++ if (!unicam_all_nodes_streaming(dev)) { ++ unicam_dbg(3, dev, "Not all nodes are streaming yet."); ++ return 0; ++ } + +- addr = vb2_dma_contig_plane_dma_addr(&node->cur_frm->vb.vb2_buf, 0); + dev->sequence = 0; +- + ret = unicam_runtime_get(dev); + if (ret < 0) { + unicam_dbg(3, dev, "unicam_runtime_get failed\n"); +- goto err_release_buffers; ++ return ret; + } + + dev->active_data_lanes = dev->max_data_lanes; +@@ -1388,7 +1608,7 @@ static int unicam_start_streaming(struct + dev->active_data_lanes = dev->max_data_lanes; + } + if (dev->active_data_lanes > dev->max_data_lanes) { +- unicam_err(dev, "Device has requested %u data lanes, which is >%u configured in DT\n", ++ unicam_err(dev, "Device has requested %u data lanes, which is >%u configured in DT\n", + dev->active_data_lanes, dev->max_data_lanes); + ret = -EINVAL; + goto err_pm_put; +@@ -1408,9 +1628,22 @@ static int unicam_start_streaming(struct + unicam_err(dev, "Failed to enable CSI clock: %d\n", ret); + goto err_pm_put; + } +- dev->streaming = 1; + +- unicam_start_rx(dev, addr); ++ num_nodes_streaming = unicam_num_nodes_streaming(dev); ++ for (i = 0; i < num_nodes_streaming; i++) { ++ spin_lock_irqsave(&dev->node[i].dma_queue_lock, flags); ++ buf = list_entry(dev->node[i].dma_queue.active.next, ++ struct unicam_buffer, list); ++ dev->node[i].cur_frm = buf; ++ dev->node[i].next_frm = buf; ++ list_del(&buf->list); ++ spin_unlock_irqrestore(&dev->node[i].dma_queue_lock, flags); ++ buffer_addr[i] = ++ vb2_dma_contig_plane_dma_addr(&dev->node[i].cur_frm->vb.vb2_buf, ++ 0); ++ } ++ ++ unicam_start_rx(dev, buffer_addr); + + ret = v4l2_subdev_call(dev->sensor, video, s_stream, 1); + if (ret < 0) { +@@ -1421,21 +1654,11 @@ static int unicam_start_streaming(struct + return 0; + + err_disable_unicam: ++ node->streaming = 0; + unicam_disable(dev); + clk_disable_unprepare(dev->clock); + err_pm_put: + unicam_runtime_put(dev); +-err_release_buffers: +- list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { +- list_del(&buf->list); +- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); +- } +- if (node->cur_frm != node->next_frm) +- vb2_buffer_done(&node->next_frm->vb.vb2_buf, +- VB2_BUF_STATE_QUEUED); +- vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); +- node->next_frm = NULL; +- node->cur_frm = NULL; + + return ret; + } +@@ -1448,33 +1671,47 @@ static void unicam_stop_streaming(struct + struct unicam_buffer *buf, *tmp; + unsigned long flags; + +- if (v4l2_subdev_call(dev->sensor, video, s_stream, 0) < 0) +- unicam_err(dev, "stream off failed in subdev\n"); ++ node->streaming = 0; + +- unicam_disable(dev); ++ if (node->pad_id == IMAGE_PAD) { ++ /* Stop streaming the sensor and disable the peripheral. ++ * We cannot continue streaming embedded data with the ++ * image pad disabled. ++ */ ++ if (v4l2_subdev_call(dev->sensor, video, s_stream, 0) < 0) ++ unicam_err(dev, "stream off failed in subdev\n"); + +- /* Release all active buffers */ ++ unicam_disable(dev); ++ clk_disable_unprepare(dev->clock); ++ unicam_runtime_put(dev); ++ ++ } else if (node->pad_id == METADATA_PAD) { ++ /* Null out the embedded data buffer address so the HW does ++ * not use it. This is only really needed if the embedded data ++ * pad is disabled before the image pad. The 0x3 in the top two ++ * bits signifies uncached accesses through the Videocore ++ * memory controller. ++ */ ++ unicam_wr_dma_addr(dev, 0xc0000000, METADATA_PAD); ++ } ++ ++ /* Clear all queued buffers for the node */ + spin_lock_irqsave(&node->dma_queue_lock, flags); + list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + +- if (node->cur_frm == node->next_frm) { +- vb2_buffer_done(&node->cur_frm->vb.vb2_buf, +- VB2_BUF_STATE_ERROR); +- } else { ++ if (node->cur_frm) + vb2_buffer_done(&node->cur_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); ++ if (node->next_frm && node->cur_frm != node->next_frm) + vb2_buffer_done(&node->next_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); +- } ++ + node->cur_frm = NULL; + node->next_frm = NULL; + spin_unlock_irqrestore(&node->dma_queue_lock, flags); +- +- clk_disable_unprepare(dev->clock); +- unicam_runtime_put(dev); + } + + static int unicam_enum_input(struct file *file, void *priv, +@@ -1595,17 +1832,23 @@ static int unicam_enum_framesizes(struct + struct v4l2_subdev_frame_size_enum fse; + int ret; + +- /* check for valid format */ +- fmt = find_format_by_pix(dev, fsize->pixel_format); +- if (!fmt) { +- unicam_dbg(3, dev, "Invalid pixel code: %x\n", +- fsize->pixel_format); +- return -EINVAL; ++ if (node->pad_id == IMAGE_PAD) { ++ /* check for valid format */ ++ fmt = find_format_by_pix(dev, fsize->pixel_format); ++ if (!fmt) { ++ unicam_dbg(3, dev, "Invalid pixel code: %x\n", ++ fsize->pixel_format); ++ return -EINVAL; ++ } ++ fse.code = fmt->code; ++ } else { ++ /* This pad is for embedded data, so just set the format */ ++ fse.code = MEDIA_BUS_FMT_SENSOR_DATA; + } + ++ fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fse.index = fsize->index; +- fse.pad = 0; +- fse.code = fmt->code; ++ fse.pad = node->pad_id; + + ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_size, NULL, &fse); + if (ret) +@@ -1782,7 +2025,7 @@ static void unicam_notify(struct v4l2_su + + switch (notification) { + case V4L2_DEVICE_NOTIFY_EVENT: +- v4l2_event_queue(&dev->node[0].video_dev, arg); ++ v4l2_event_queue(&dev->node[IMAGE_PAD].video_dev, arg); + break; + default: + break; +@@ -1826,6 +2069,7 @@ static int unicam_open(struct file *file + goto unlock; + } + ++ node->open++; + ret = 0; + + unlock: +@@ -1850,6 +2094,10 @@ static int unicam_release(struct file *f + if (fh_singular) + v4l2_subdev_call(sd, core, s_power, 0); + ++ if (node->streaming) ++ unicam_stop_streaming(&node->buffer_queue); ++ ++ node->open--; + mutex_unlock(&node->lock); + + return ret; +@@ -1874,6 +2122,11 @@ static const struct v4l2_ioctl_ops unica + .vidioc_s_fmt_vid_cap = unicam_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = unicam_try_fmt_vid_cap, + ++ .vidioc_enum_fmt_meta_cap = unicam_enum_fmt_meta_cap, ++ .vidioc_g_fmt_meta_cap = unicam_g_fmt_meta_cap, ++ .vidioc_s_fmt_meta_cap = unicam_s_fmt_meta_cap, ++ .vidioc_try_fmt_meta_cap = unicam_try_fmt_meta_cap, ++ + .vidioc_enum_input = unicam_enum_input, + .vidioc_g_input = unicam_g_input, + .vidioc_s_input = unicam_s_input, +@@ -1941,42 +2194,53 @@ static int register_node(struct unicam_d + const struct unicam_fmt *fmt; + int ret; + +- ret = __subdev_get_format(unicam, &mbus_fmt, pad_id); +- if (ret) { +- unicam_err(unicam, "Failed to get_format - ret %d\n", ret); +- return ret; +- } +- +- fmt = find_format_by_code(mbus_fmt.code); +- if (!fmt) { +- /* Find the first format that the sensor and unicam both +- * support +- */ +- fmt = get_first_supported_format(unicam); ++ if (unicam->sensor_embedded_data || pad_id != METADATA_PAD) { ++ ret = __subdev_get_format(unicam, &mbus_fmt, pad_id); ++ if (ret) { ++ unicam_err(unicam, "Failed to get_format - ret %d\n", ++ ret); ++ return ret; ++ } + +- if (!fmt) +- /* No compatible formats */ +- return -EINVAL; ++ fmt = find_format_by_code(mbus_fmt.code); ++ if (!fmt) { ++ /* Find the first format that the sensor and unicam both ++ * support ++ */ ++ fmt = get_first_supported_format(unicam); + +- mbus_fmt.code = fmt->code; +- ret = __subdev_set_format(unicam, &mbus_fmt); +- if (ret) +- return -EINVAL; +- } +- if (mbus_fmt.field != V4L2_FIELD_NONE) { +- /* Interlaced not supported - disable it now. */ +- mbus_fmt.field = V4L2_FIELD_NONE; +- ret = __subdev_set_format(unicam, &mbus_fmt); +- if (ret) +- return -EINVAL; ++ if (!fmt) ++ /* No compatible formats */ ++ return -EINVAL; ++ ++ mbus_fmt.code = fmt->code; ++ ret = __subdev_set_format(unicam, &mbus_fmt, pad_id); ++ if (ret) ++ return -EINVAL; ++ } ++ if (mbus_fmt.field != V4L2_FIELD_NONE) { ++ /* Interlaced not supported - disable it now. */ ++ mbus_fmt.field = V4L2_FIELD_NONE; ++ ret = __subdev_set_format(unicam, &mbus_fmt, pad_id); ++ if (ret) ++ return -EINVAL; ++ } ++ } else { ++ /* Fix this node format as embedded data. */ ++ fmt = find_format_by_code(MEDIA_BUS_FMT_SENSOR_DATA); + } + ++ node->dev = unicam; + node->pad_id = pad_id; + node->fmt = fmt; +- if (fmt->fourcc) +- node->v_fmt.fmt.pix.pixelformat = fmt->fourcc; +- else ++ if (fmt->fourcc) { ++ if (fmt->fourcc != V4L2_META_FMT_SENSOR_DATA) ++ node->v_fmt.fmt.pix.pixelformat = fmt->fourcc; ++ else ++ node->v_fmt.fmt.meta.dataformat = fmt->fourcc; ++ } else { + node->v_fmt.fmt.pix.pixelformat = fmt->repacked_fourcc; ++ } + + /* Read current subdev format */ + unicam_reset_format(node); +@@ -2002,13 +2266,21 @@ static int register_node(struct unicam_d + spin_lock_init(&node->dma_queue_lock); + mutex_init(&node->lock); + +- if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ vdev = &node->video_dev; ++ if (pad_id == IMAGE_PAD) { + /* Add controls from the subdevice */ + ret = v4l2_ctrl_add_handler(&node->ctrl_handler, + unicam->sensor->ctrl_handler, NULL, + true); + if (ret < 0) + return ret; ++ ++ /* ++ * If the sensor subdevice has any controls, associate the node ++ * with the ctrl handler to allow access from userland. ++ */ ++ if (!list_empty(&node->ctrl_handler.ctrls)) ++ vdev->ctrl_handler = &node->ctrl_handler; + } + + q = &node->buffer_queue; +@@ -2031,8 +2303,6 @@ static int register_node(struct unicam_d + + INIT_LIST_HEAD(&node->dma_queue.active); + +- vdev = &node->video_dev; +- strlcpy(vdev->name, UNICAM_MODULE_NAME, sizeof(vdev->name)); + vdev->release = video_device_release_empty; + vdev->fops = &unicam_fops; + vdev->ioctl_ops = &unicam_ioctl_ops; +@@ -2040,24 +2310,28 @@ static int register_node(struct unicam_d + vdev->vfl_dir = VFL_DIR_RX; + vdev->queue = q; + vdev->lock = &node->lock; +- vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | +- V4L2_CAP_READWRITE; +- /* If the source has no controls then remove our ctrl handler. */ +- if (list_empty(&node->ctrl_handler.ctrls)) +- unicam->v4l2_dev.ctrl_handler = NULL; ++ vdev->device_caps = (pad_id == IMAGE_PAD) ? ++ (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING) : ++ (V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING); ++ ++ /* Define the device names */ ++ snprintf(vdev->name, sizeof(vdev->name), "%s-%s", UNICAM_MODULE_NAME, ++ node->pad_id == IMAGE_PAD ? "image" : "embedded"); + +- node->dev = unicam; + video_set_drvdata(vdev, node); + vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; + +- if (!v4l2_subdev_has_op(unicam->sensor, video, s_std)) { ++ if (node->pad_id == METADATA_PAD || ++ !v4l2_subdev_has_op(unicam->sensor, video, s_std)) { + v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_STD); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_STD); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUMSTD); + } +- if (!v4l2_subdev_has_op(unicam->sensor, video, querystd)) ++ if (node->pad_id == METADATA_PAD || ++ !v4l2_subdev_has_op(unicam->sensor, video, querystd)) + v4l2_disable_ioctl(&node->video_dev, VIDIOC_QUERYSTD); +- if (!v4l2_subdev_has_op(unicam->sensor, video, s_dv_timings)) { ++ if (node->pad_id == METADATA_PAD || ++ !v4l2_subdev_has_op(unicam->sensor, video, s_dv_timings)) { + v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_EDID); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_EDID); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_DV_TIMINGS_CAP); +@@ -2066,15 +2340,19 @@ static int register_node(struct unicam_d + v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_DV_TIMINGS); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_QUERY_DV_TIMINGS); + } +- if (!v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_interval)) ++ if (node->pad_id == METADATA_PAD || ++ !v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_interval)) + v4l2_disable_ioctl(&node->video_dev, + VIDIOC_ENUM_FRAMEINTERVALS); +- if (!v4l2_subdev_has_op(unicam->sensor, video, g_frame_interval)) ++ if (node->pad_id == METADATA_PAD || ++ !v4l2_subdev_has_op(unicam->sensor, video, g_frame_interval)) + v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_PARM); +- if (!v4l2_subdev_has_op(unicam->sensor, video, s_frame_interval)) ++ if (node->pad_id == METADATA_PAD || ++ !v4l2_subdev_has_op(unicam->sensor, video, s_frame_interval)) + v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_PARM); + +- if (!v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_size)) ++ if (node->pad_id == METADATA_PAD || ++ !v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_size)) + v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_FRAMESIZES); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); +@@ -2082,27 +2360,29 @@ static int register_node(struct unicam_d + unicam_err(unicam, "Unable to register video device.\n"); + return ret; + } +- node->registered = true; ++ node->registered = 1; + +- ret = media_create_pad_link(&unicam->sensor->entity, +- 0, &node->video_dev.entity, 0, +- MEDIA_LNK_FL_ENABLED | +- MEDIA_LNK_FL_IMMUTABLE); +- if (ret) +- unicam_err(unicam, "Unable to create pad links.\n"); ++ if (unicam->sensor_embedded_data) { ++ ret = media_create_pad_link(&unicam->sensor->entity, pad_id, ++ &node->video_dev.entity, 0, ++ MEDIA_LNK_FL_ENABLED | ++ MEDIA_LNK_FL_IMMUTABLE); ++ if (ret) ++ unicam_err(unicam, "Unable to create pad links.\n"); ++ } + + return ret; + } + + static void unregister_nodes(struct unicam_device *unicam) + { +- if (unicam->node[0].registered) { +- video_unregister_device(&unicam->node[0].video_dev); +- unicam->node[0].registered = false; +- } +- if (unicam->node[1].registered) { +- video_unregister_device(&unicam->node[1].video_dev); +- unicam->node[1].registered = false; ++ if (unicam->node[IMAGE_PAD].registered) { ++ video_unregister_device(&unicam->node[IMAGE_PAD].video_dev); ++ unicam->node[IMAGE_PAD].registered = 0; ++ } ++ if (unicam->node[METADATA_PAD].registered) { ++ video_unregister_device(&unicam->node[METADATA_PAD].video_dev); ++ unicam->node[METADATA_PAD].registered = 0; + } + } + +@@ -2118,20 +2398,20 @@ static int unicam_probe_complete(struct + if (!unicam->sensor_config) + return -ENOMEM; + +- ret = register_node(unicam, &unicam->node[0], +- V4L2_BUF_TYPE_VIDEO_CAPTURE, 0); ++ unicam->sensor_embedded_data = (unicam->sensor->entity.num_pads >= 2); ++ ++ ret = register_node(unicam, &unicam->node[IMAGE_PAD], ++ V4L2_BUF_TYPE_VIDEO_CAPTURE, IMAGE_PAD); + if (ret) { + unicam_err(unicam, "Unable to register subdev node 0.\n"); + goto unregister; + } +- if (unicam->sensor->entity.num_pads >= 2) { +- ret = register_node(unicam, &unicam->node[1], +- V4L2_BUF_TYPE_META_CAPTURE, 1); +- if (ret) { +- unicam_err(unicam, +- "Unable to register subdev node 1.\n"); +- goto unregister; +- } ++ ++ ret = register_node(unicam, &unicam->node[METADATA_PAD], ++ V4L2_BUF_TYPE_META_CAPTURE, METADATA_PAD); ++ if (ret) { ++ unicam_err(unicam, "Unable to register subdev node 1.\n"); ++ goto unregister; + } + + ret = v4l2_device_register_ro_subdev_nodes(&unicam->v4l2_dev); +@@ -2355,8 +2635,10 @@ static int unicam_probe(struct platform_ + pdev->dev.driver->name, dev_name(&pdev->dev)); + unicam->mdev.hw_revision = 1; + +- media_entity_pads_init(&unicam->node[0].video_dev.entity, 1, +- &unicam->node[0].pad); ++ media_entity_pads_init(&unicam->node[IMAGE_PAD].video_dev.entity, 1, ++ &unicam->node[IMAGE_PAD].pad); ++ media_entity_pads_init(&unicam->node[METADATA_PAD].video_dev.entity, 1, ++ &unicam->node[METADATA_PAD].pad); + media_device_init(&unicam->mdev); + + unicam->v4l2_dev.mdev = &unicam->mdev; +@@ -2376,11 +2658,10 @@ static int unicam_probe(struct platform_ + } + + /* Reserve space for the controls */ +- hdl = &unicam->node[0].ctrl_handler; ++ hdl = &unicam->node[IMAGE_PAD].ctrl_handler; + ret = v4l2_ctrl_handler_init(hdl, 16); + if (ret < 0) + goto media_unregister; +- unicam->v4l2_dev.ctrl_handler = hdl; + + /* set the driver data in platform device */ + platform_set_drvdata(pdev, unicam); +@@ -2417,7 +2698,7 @@ static int unicam_remove(struct platform + pm_runtime_disable(&pdev->dev); + + v4l2_async_notifier_unregister(&unicam->notifier); +- v4l2_ctrl_handler_free(&unicam->node[0].ctrl_handler); ++ v4l2_ctrl_handler_free(&unicam->node[IMAGE_PAD].ctrl_handler); + v4l2_device_unregister(&unicam->v4l2_dev); + unregister_nodes(unicam); + if (unicam->sensor_config) diff --git a/target/linux/bcm27xx/patches-5.4/950-0654-media-bcm2835-unicam-Use-dummy-buffer-if-none-have-b.patch b/target/linux/bcm27xx/patches-5.4/950-0654-media-bcm2835-unicam-Use-dummy-buffer-if-none-have-b.patch new file mode 100644 index 0000000000..836ced8a04 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0654-media-bcm2835-unicam-Use-dummy-buffer-if-none-have-b.patch @@ -0,0 +1,308 @@ +From 0eb6753788616ffed17a0484a14fd7d3df2a2a05 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Thu, 2 Apr 2020 16:08:51 +0100 +Subject: [PATCH] media: bcm2835-unicam: Use dummy buffer if none have + been queued + +If no buffer has been queued by a userland application, we use an +internal dummy buffer for the hardware to spin in. This will allow +the driver to release the existing userland buffer back to the +application for processing. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + .../media/platform/bcm2835/bcm2835-unicam.c | 160 ++++++++++++------ + 1 file changed, 110 insertions(+), 50 deletions(-) + +--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c ++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c +@@ -47,6 +47,7 @@ + #include <linux/clk.h> + #include <linux/delay.h> + #include <linux/device.h> ++#include <linux/dma-mapping.h> + #include <linux/err.h> + #include <linux/init.h> + #include <linux/interrupt.h> +@@ -112,6 +113,12 @@ MODULE_PARM_DESC(debug, "Debug level 0-3 + /* Default size of the embedded buffer */ + #define UNICAM_EMBEDDED_SIZE 8192 + ++/* ++ * Size of the dummy buffer. Can be any size really, but the DMA ++ * allocation works in units of page sizes. ++ */ ++#define DUMMY_BUF_SIZE (PAGE_SIZE) ++ + enum pad_types { + IMAGE_PAD, + METADATA_PAD, +@@ -390,6 +397,12 @@ struct unicam_node { + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + unsigned int embedded_lines; ++ /* ++ * Dummy buffer intended to be used by unicam ++ * if we have no other queued buffers to swap to. ++ */ ++ void *dummy_buf_cpu_addr; ++ dma_addr_t dummy_buf_dma_addr; + }; + + struct unicam_device { +@@ -661,27 +674,24 @@ static int unicam_reset_format(struct un + return 0; + } + +-static void unicam_wr_dma_addr(struct unicam_device *dev, dma_addr_t dmaaddr, +- int pad_id) ++static void unicam_wr_dma_addr(struct unicam_cfg *cfg, dma_addr_t dmaaddr, ++ unsigned int buffer_size, int pad_id) + { +- dma_addr_t endaddr; ++ dma_addr_t endaddr = dmaaddr + buffer_size; + + /* +- * dmaaddr should be a 32-bit address with the top two bits set to 0x3 +- * to signify uncached access through the Videocore memory controller. ++ * dmaaddr and endaddr should be a 32-bit address with the top two bits ++ * set to 0x3 to signify uncached access through the Videocore memory ++ * controller. + */ +- BUG_ON((dmaaddr >> 30) != 0x3); ++ BUG_ON((dmaaddr >> 30) != 0x3 && (endaddr >> 30) != 0x3); + + if (pad_id == IMAGE_PAD) { +- endaddr = dmaaddr + +- dev->node[IMAGE_PAD].v_fmt.fmt.pix.sizeimage; +- reg_write(&dev->cfg, UNICAM_IBSA0, dmaaddr); +- reg_write(&dev->cfg, UNICAM_IBEA0, endaddr); ++ reg_write(cfg, UNICAM_IBSA0, dmaaddr); ++ reg_write(cfg, UNICAM_IBEA0, endaddr); + } else { +- endaddr = dmaaddr + +- dev->node[METADATA_PAD].v_fmt.fmt.meta.buffersize; +- reg_write(&dev->cfg, UNICAM_DBSA0, dmaaddr); +- reg_write(&dev->cfg, UNICAM_DBEA0, endaddr); ++ reg_write(cfg, UNICAM_DBSA0, dmaaddr); ++ reg_write(cfg, UNICAM_DBEA0, endaddr); + } + } + +@@ -704,6 +714,7 @@ static inline void unicam_schedule_next_ + struct unicam_device *dev = node->dev; + struct unicam_dmaqueue *dma_q = &node->dma_queue; + struct unicam_buffer *buf; ++ unsigned int size; + dma_addr_t addr; + + buf = list_entry(dma_q->active.next, struct unicam_buffer, list); +@@ -711,7 +722,23 @@ static inline void unicam_schedule_next_ + list_del(&buf->list); + + addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); +- unicam_wr_dma_addr(dev, addr, node->pad_id); ++ size = (node->pad_id == IMAGE_PAD) ? ++ dev->node[IMAGE_PAD].v_fmt.fmt.pix.sizeimage : ++ dev->node[METADATA_PAD].v_fmt.fmt.meta.buffersize; ++ ++ unicam_wr_dma_addr(&dev->cfg, addr, size, node->pad_id); ++} ++ ++static inline void unicam_schedule_dummy_buffer(struct unicam_node *node) ++{ ++ struct unicam_device *dev = node->dev; ++ dma_addr_t addr = node->dummy_buf_dma_addr; ++ ++ unicam_dbg(3, dev, "Scheduling dummy buffer for node %d\n", ++ node->pad_id); ++ ++ unicam_wr_dma_addr(&dev->cfg, addr, DUMMY_BUF_SIZE, node->pad_id); ++ node->next_frm = NULL; + } + + static inline void unicam_process_buffer_complete(struct unicam_node *node, +@@ -721,7 +748,6 @@ static inline void unicam_process_buffer + node->cur_frm->vb.sequence = sequence; + + vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); +- node->cur_frm = node->next_frm; + } + + static int unicam_num_nodes_streaming(struct unicam_device *dev) +@@ -788,6 +814,28 @@ static irqreturn_t unicam_isr(int irq, v + if (!(sta && (UNICAM_IS | UNICAM_PI0))) + return IRQ_HANDLED; + ++ /* ++ * We must run the frame end handler first. If we have a valid next_frm ++ * and we get a simultaneout FE + FS interrupt, running the FS handler ++ * first would null out the next_frm ptr and we would have lost the ++ * buffer forever. ++ */ ++ if (ista & UNICAM_FEI || sta & UNICAM_PI0) { ++ /* ++ * Ensure we have swapped buffers already as we can't ++ * stop the peripheral. If no buffer is available, use a ++ * dummy buffer to dump out frames until we get a new buffer ++ * to use. ++ */ ++ for (i = 0; i < num_nodes_streaming; i++) { ++ if (unicam->node[i].cur_frm) ++ unicam_process_buffer_complete(&unicam->node[i], ++ sequence); ++ unicam->node[i].cur_frm = unicam->node[i].next_frm; ++ } ++ unicam->sequence++; ++ } ++ + if (ista & UNICAM_FSI) { + /* + * Timestamp is to be when the first data byte was captured, +@@ -798,24 +846,16 @@ static irqreturn_t unicam_isr(int irq, v + if (unicam->node[i].cur_frm) + unicam->node[i].cur_frm->vb.vb2_buf.timestamp = + ts; ++ /* ++ * Set the next frame output to go to a dummy frame ++ * if we have not managed to obtain another frame ++ * from the queue. ++ */ ++ unicam_schedule_dummy_buffer(&unicam->node[i]); + } + } +- if (ista & UNICAM_FEI || sta & UNICAM_PI0) { +- /* +- * Ensure we have swapped buffers already as we can't +- * stop the peripheral. Overwrite the frame we've just +- * captured instead. +- */ +- for (i = 0; i < num_nodes_streaming; i++) { +- if (unicam->node[i].cur_frm && +- unicam->node[i].cur_frm != unicam->node[i].next_frm) +- unicam_process_buffer_complete(&unicam->node[i], +- sequence); +- } +- unicam->sequence++; +- } +- +- /* Cannot swap buffer at frame end, there may be a race condition ++ /* ++ * Cannot swap buffer at frame end, there may be a race condition + * where the HW does not actually swap it if the new frame has + * already started. + */ +@@ -823,7 +863,7 @@ static irqreturn_t unicam_isr(int irq, v + for (i = 0; i < num_nodes_streaming; i++) { + spin_lock(&unicam->node[i].dma_queue_lock); + if (!list_empty(&unicam->node[i].dma_queue.active) && +- unicam->node[i].cur_frm == unicam->node[i].next_frm) ++ !unicam->node[i].next_frm) + unicam_schedule_next_buffer(&unicam->node[i]); + spin_unlock(&unicam->node[i].dma_queue_lock); + } +@@ -1352,7 +1392,7 @@ static void unicam_start_rx(struct unica + { + struct unicam_cfg *cfg = &dev->cfg; + int line_int_freq = dev->node[IMAGE_PAD].v_fmt.fmt.pix.height >> 2; +- unsigned int i; ++ unsigned int size, i; + u32 val; + + if (line_int_freq < 128) +@@ -1413,7 +1453,7 @@ static void unicam_start_rx(struct unica + reg_write_field(cfg, UNICAM_ANA, 0, UNICAM_DDL); + + /* Always start in trigger frame capture mode (UNICAM_FCM set) */ +- val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_FCM; ++ val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_FCM | UNICAM_IBOB; + set_field(&val, line_int_freq, UNICAM_LCIE_MASK); + reg_write(cfg, UNICAM_ICTL, val); + reg_write(cfg, UNICAM_STA, UNICAM_STA_MASK_ALL); +@@ -1501,7 +1541,8 @@ static void unicam_start_rx(struct unica + + reg_write(&dev->cfg, UNICAM_IBLS, + dev->node[IMAGE_PAD].v_fmt.fmt.pix.bytesperline); +- unicam_wr_dma_addr(dev, addr[IMAGE_PAD], IMAGE_PAD); ++ size = dev->node[IMAGE_PAD].v_fmt.fmt.pix.sizeimage; ++ unicam_wr_dma_addr(&dev->cfg, addr[IMAGE_PAD], size, IMAGE_PAD); + unicam_set_packing_config(dev); + unicam_cfg_image_id(dev); + +@@ -1511,8 +1552,10 @@ static void unicam_start_rx(struct unica + reg_write(cfg, UNICAM_MISC, val); + + if (dev->node[METADATA_PAD].streaming && dev->sensor_embedded_data) { ++ size = dev->node[METADATA_PAD].v_fmt.fmt.meta.buffersize; + unicam_enable_ed(dev); +- unicam_wr_dma_addr(dev, addr[METADATA_PAD], METADATA_PAD); ++ unicam_wr_dma_addr(&dev->cfg, addr[METADATA_PAD], size, ++ METADATA_PAD); + } + + /* Enable peripheral */ +@@ -1686,13 +1729,14 @@ static void unicam_stop_streaming(struct + unicam_runtime_put(dev); + + } else if (node->pad_id == METADATA_PAD) { +- /* Null out the embedded data buffer address so the HW does +- * not use it. This is only really needed if the embedded data +- * pad is disabled before the image pad. The 0x3 in the top two +- * bits signifies uncached accesses through the Videocore +- * memory controller. ++ /* Allow the hardware to spin in the dummy buffer. ++ * This is only really needed if the embedded data pad is ++ * disabled before the image pad. The 0x3 in the top two bits ++ * signifies uncached accesses through the Videocore memory ++ * controller. + */ +- unicam_wr_dma_addr(dev, 0xc0000000, METADATA_PAD); ++ unicam_wr_dma_addr(&dev->cfg, node->dummy_buf_dma_addr, ++ DUMMY_BUF_SIZE, METADATA_PAD); + } + + /* Clear all queued buffers for the node */ +@@ -2321,6 +2365,15 @@ static int register_node(struct unicam_d + video_set_drvdata(vdev, node); + vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; + ++ node->dummy_buf_cpu_addr = dma_alloc_coherent(&unicam->pdev->dev, ++ DUMMY_BUF_SIZE, ++ &node->dummy_buf_dma_addr, ++ GFP_ATOMIC); ++ if (!node->dummy_buf_cpu_addr) { ++ unicam_err(unicam, "Unable to allocate dummy buffer.\n"); ++ return -ENOMEM; ++ } ++ + if (node->pad_id == METADATA_PAD || + !v4l2_subdev_has_op(unicam->sensor, video, s_std)) { + v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_STD); +@@ -2376,13 +2429,20 @@ static int register_node(struct unicam_d + + static void unregister_nodes(struct unicam_device *unicam) + { +- if (unicam->node[IMAGE_PAD].registered) { +- video_unregister_device(&unicam->node[IMAGE_PAD].video_dev); +- unicam->node[IMAGE_PAD].registered = 0; +- } +- if (unicam->node[METADATA_PAD].registered) { +- video_unregister_device(&unicam->node[METADATA_PAD].video_dev); +- unicam->node[METADATA_PAD].registered = 0; ++ struct unicam_node *node; ++ int i; ++ ++ for (i = 0; i < MAX_NODES; i++) { ++ node = &unicam->node[i]; ++ if (node->dummy_buf_cpu_addr) { ++ dma_free_coherent(&unicam->pdev->dev, DUMMY_BUF_SIZE, ++ node->dummy_buf_cpu_addr, ++ node->dummy_buf_dma_addr); ++ } ++ if (node->registered) { ++ video_unregister_device(&node->video_dev); ++ node->registered = 0; ++ } + } + } + diff --git a/target/linux/bcm27xx/patches-5.4/950-0655-spi-Force-CS_HIGH-if-GPIO-descriptors-are-used.patch b/target/linux/bcm27xx/patches-5.4/950-0655-spi-Force-CS_HIGH-if-GPIO-descriptors-are-used.patch new file mode 100644 index 0000000000..43d10486de --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0655-spi-Force-CS_HIGH-if-GPIO-descriptors-are-used.patch @@ -0,0 +1,48 @@ +From 5f2eface651ba5da9caaa84ccca14b9202ba6202 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Fri, 17 Apr 2020 10:46:19 +0100 +Subject: [PATCH] spi: Force CS_HIGH if GPIO descriptors are used + +Commit f3186dd87669 ("spi: Optionally use GPIO descriptors for CS GPIOs") +amended of_spi_parse_dt() to always set SPI_CS_HIGH for SPI slaves whose +Chip Select is defined by a "cs-gpios" devicetree property. + +This change breaks drivers whose probe functions set the mode field of +the spi_device because in doing so they clear the SPI_CS_HIGH flag. + +Fix by setting SPI_CS_HIGH in spi_setup (under the same conditions as +in of_spi_parse_dt()). + +See also: 83b2a8fe43bd ("spi: spidev: Fix CS polarity if GPIO descriptors are used") + +Fixes: f3186dd87669 ("spi: Optionally use GPIO descriptors for CS GPIOs") +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + drivers/spi/spi.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -3032,6 +3032,7 @@ static int __spi_validate_bits_per_word( + */ + int spi_setup(struct spi_device *spi) + { ++ struct spi_controller *ctlr = spi->controller; + unsigned bad_bits, ugly_bits; + int status; + +@@ -3049,6 +3050,14 @@ int spi_setup(struct spi_device *spi) + (SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL | + SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL))) + return -EINVAL; ++ ++ if (ctlr->use_gpio_descriptors && ctlr->cs_gpiods && ++ ctlr->cs_gpiods[spi->chip_select] && !(spi->mode & SPI_CS_HIGH)) { ++ dev_warn(&spi->dev, ++ "setup: forcing CS_HIGH (use_gpio_descriptors)\n"); ++ spi->mode |= SPI_CS_HIGH; ++ } ++ + /* help drivers fail *cleanly* when they need options + * that aren't supported with their current controller + * SPI_CS_WORD has a fallback software implementation, diff --git a/target/linux/bcm27xx/patches-5.4/950-0656-media-i2c-imx219-Fix-power-sequence.patch b/target/linux/bcm27xx/patches-5.4/950-0656-media-i2c-imx219-Fix-power-sequence.patch new file mode 100644 index 0000000000..b85d917878 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0656-media-i2c-imx219-Fix-power-sequence.patch @@ -0,0 +1,55 @@ +From 0fec1c81707f5335d0b04b5e97d2ddd0b902377b Mon Sep 17 00:00:00 2001 +From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> +Date: Tue, 10 Mar 2020 14:17:07 +0100 +Subject: [PATCH] media: i2c: imx219: Fix power sequence + +Commit ca45448a56659c6df6e0436188e97f6cc65dea8a upstream. + +When supporting Rpi Camera v2 Module on the RZ/G2E, found the driver had +some issues with rcar mipi-csi driver. The sensor never entered into LP-11 +state. + +The powerup sequence in the datasheet[1] shows the sensor entering into +LP-11 in streaming mode, so to fix this issue transitions are performed +from "streaming -> standby" in the probe() after power up. + +With this commit the sensor is able to enter LP-11 mode during power up, +as expected by some CSI-2 controllers. + +[1] https://publiclab.org/system/images/photos/000/023/294/original/ +RASPBERRY_PI_CAMERA_V2_DATASHEET_IMX219PQH5_7.0.0_Datasheet_XXX.PDF + +Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> +Acked-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> +Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> +--- + drivers/media/i2c/imx219.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +--- a/drivers/media/i2c/imx219.c ++++ b/drivers/media/i2c/imx219.c +@@ -1224,6 +1224,23 @@ static int imx219_probe(struct i2c_clien + /* Set default mode to max resolution */ + imx219->mode = &supported_modes[0]; + ++ /* sensor doesn't enter LP-11 state upon power up until and unless ++ * streaming is started, so upon power up switch the modes to: ++ * streaming -> standby ++ */ ++ ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT, ++ IMX219_REG_VALUE_08BIT, IMX219_MODE_STREAMING); ++ if (ret < 0) ++ goto error_power_off; ++ usleep_range(100, 110); ++ ++ /* put sensor back to standby mode */ ++ ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT, ++ IMX219_REG_VALUE_08BIT, IMX219_MODE_STANDBY); ++ if (ret < 0) ++ goto error_power_off; ++ usleep_range(100, 110); ++ + ret = imx219_init_controls(imx219); + if (ret) + goto error_power_off; diff --git a/target/linux/bcm27xx/patches-5.4/950-0657-media-i2c-imx219-Add-support-for-RAW8-bit-bayer-form.patch b/target/linux/bcm27xx/patches-5.4/950-0657-media-i2c-imx219-Add-support-for-RAW8-bit-bayer-form.patch new file mode 100644 index 0000000000..552516bd67 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0657-media-i2c-imx219-Add-support-for-RAW8-bit-bayer-form.patch @@ -0,0 +1,319 @@ +From 1f00b84d993e1f8de17ef936e00f4264266cb5d1 Mon Sep 17 00:00:00 2001 +From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> +Date: Tue, 10 Mar 2020 14:17:08 +0100 +Subject: [PATCH] media: i2c: imx219: Add support for RAW8 bit bayer + format + +Commit 22da1d56e982151e0bdfafe9de6fe94098a51356 upstream. + +IMX219 sensor is capable for RAW8/RAW10 modes. This commit adds support +for RAW8 bayer format. + +Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> +Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> +--- + drivers/media/i2c/imx219.c | 148 +++++++++++++++++++++++++++++-------- + 1 file changed, 116 insertions(+), 32 deletions(-) + +--- a/drivers/media/i2c/imx219.c ++++ b/drivers/media/i2c/imx219.c +@@ -168,15 +168,12 @@ static const struct imx219_reg mode_3280 + {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}, +@@ -230,15 +227,12 @@ static const struct imx219_reg mode_1920 + {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}, +@@ -290,15 +284,12 @@ static const struct imx219_reg mode_1640 + {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}, +@@ -322,6 +313,18 @@ static const struct imx219_reg mode_1640 + {0x0163, 0x78}, + }; + ++static const struct imx219_reg raw8_framefmt_regs[] = { ++ {0x018c, 0x08}, ++ {0x018d, 0x08}, ++ {0x0309, 0x08}, ++}; ++ ++static const struct imx219_reg raw10_framefmt_regs[] = { ++ {0x018c, 0x0a}, ++ {0x018d, 0x0a}, ++ {0x0309, 0x0a}, ++}; ++ + static const char * const imx219_test_pattern_menu[] = { + "Disabled", + "Color Bars", +@@ -349,6 +352,27 @@ static const char * const imx219_supply_ + #define IMX219_NUM_SUPPLIES ARRAY_SIZE(imx219_supply_name) + + /* ++ * The supported formats. ++ * This table MUST contain 4 entries per format, to cover the various flip ++ * combinations in the order ++ * - no flip ++ * - h flip ++ * - v flip ++ * - h&v flips ++ */ ++static const u32 codes[] = { ++ MEDIA_BUS_FMT_SRGGB10_1X10, ++ MEDIA_BUS_FMT_SGRBG10_1X10, ++ MEDIA_BUS_FMT_SGBRG10_1X10, ++ MEDIA_BUS_FMT_SBGGR10_1X10, ++ ++ MEDIA_BUS_FMT_SRGGB8_1X8, ++ MEDIA_BUS_FMT_SGRBG8_1X8, ++ MEDIA_BUS_FMT_SGBRG8_1X8, ++ MEDIA_BUS_FMT_SBGGR8_1X8, ++}; ++ ++/* + * 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>) +@@ -413,6 +437,8 @@ struct imx219 { + struct v4l2_subdev sd; + struct media_pad pad; + ++ struct v4l2_mbus_framefmt fmt; ++ + struct clk *xclk; /* system clock to IMX219 */ + u32 xclk_freq; + +@@ -519,19 +545,40 @@ static int imx219_write_regs(struct imx2 + } + + /* Get bayer order based on flip setting. */ +-static u32 imx219_get_format_code(struct imx219 *imx219) ++static u32 imx219_get_format_code(struct imx219 *imx219, u32 code) + { +- /* +- * 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, }, +- }; ++ unsigned int i; + + lockdep_assert_held(&imx219->mutex); +- return codes[imx219->vflip->val][imx219->hflip->val]; ++ ++ for (i = 0; i < ARRAY_SIZE(codes); i++) ++ if (codes[i] == code) ++ break; ++ ++ if (i >= ARRAY_SIZE(codes)) ++ i = 0; ++ ++ i = (i & ~3) | (imx219->vflip->val ? 2 : 0) | ++ (imx219->hflip->val ? 1 : 0); ++ ++ return codes[i]; ++} ++ ++static void imx219_set_default_format(struct imx219 *imx219) ++{ ++ struct v4l2_mbus_framefmt *fmt; ++ ++ fmt = &imx219->fmt; ++ fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; ++ 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); ++ fmt->width = supported_modes[0].width; ++ fmt->height = supported_modes[0].height; ++ fmt->field = V4L2_FIELD_NONE; + } + + static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +@@ -545,7 +592,8 @@ static int imx219_open(struct v4l2_subde + /* 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->code = imx219_get_format_code(imx219, ++ MEDIA_BUS_FMT_SRGGB10_1X10); + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&imx219->mutex); +@@ -648,14 +696,10 @@ static int imx219_enum_mbus_code(struct + { + struct imx219 *imx219 = to_imx219(sd); + +- /* +- * Only one bayer order is supported (though it depends on the flip +- * settings) +- */ +- if (code->index > 0) ++ if (code->index >= (ARRAY_SIZE(codes) / 4)) + return -EINVAL; + +- code->code = imx219_get_format_code(imx219); ++ code->code = imx219_get_format_code(imx219, codes[code->index * 4]); + + return 0; + } +@@ -669,7 +713,7 @@ static int imx219_enum_frame_size(struct + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + +- if (fse->code != imx219_get_format_code(imx219)) ++ if (fse->code != imx219_get_format_code(imx219, imx219->fmt.code)) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; +@@ -696,9 +740,7 @@ static void imx219_update_pad_format(str + { + 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); + } + +@@ -710,10 +752,12 @@ static int __imx219_get_pad_format(struc + 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); ++ try_fmt->code = imx219_get_format_code(imx219, try_fmt->code); + fmt->format = *try_fmt; + } else { + imx219_update_pad_format(imx219, imx219->mode, fmt); ++ fmt->format.code = imx219_get_format_code(imx219, ++ imx219->fmt.code); + } + + return 0; +@@ -741,11 +785,18 @@ static int imx219_set_pad_format(struct + const struct imx219_mode *mode; + struct v4l2_mbus_framefmt *framefmt; + int exposure_max, exposure_def, hblank; ++ unsigned int i; + + mutex_lock(&imx219->mutex); + ++ for (i = 0; i < ARRAY_SIZE(codes); i++) ++ if (codes[i] == fmt->format.code) ++ break; ++ if (i >= ARRAY_SIZE(codes)) ++ i = 0; ++ + /* Bayer order varies with flips */ +- fmt->format.code = imx219_get_format_code(imx219); ++ fmt->format.code = imx219_get_format_code(imx219, codes[i]); + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), +@@ -755,7 +806,9 @@ static int imx219_set_pad_format(struct + 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) { ++ } else if (imx219->mode != mode || ++ imx219->fmt.code != fmt->format.code) { ++ imx219->fmt = fmt->format; + imx219->mode = mode; + /* Update limits and set FPS to default */ + __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, +@@ -786,6 +839,27 @@ static int imx219_set_pad_format(struct + return 0; + } + ++static int imx219_set_framefmt(struct imx219 *imx219) ++{ ++ switch (imx219->fmt.code) { ++ case MEDIA_BUS_FMT_SRGGB8_1X8: ++ case MEDIA_BUS_FMT_SGRBG8_1X8: ++ case MEDIA_BUS_FMT_SGBRG8_1X8: ++ case MEDIA_BUS_FMT_SBGGR8_1X8: ++ return imx219_write_regs(imx219, raw8_framefmt_regs, ++ ARRAY_SIZE(raw8_framefmt_regs)); ++ ++ case MEDIA_BUS_FMT_SRGGB10_1X10: ++ case MEDIA_BUS_FMT_SGRBG10_1X10: ++ case MEDIA_BUS_FMT_SGBRG10_1X10: ++ case MEDIA_BUS_FMT_SBGGR10_1X10: ++ return imx219_write_regs(imx219, raw10_framefmt_regs, ++ ARRAY_SIZE(raw10_framefmt_regs)); ++ } ++ ++ return -EINVAL; ++} ++ + static int imx219_start_streaming(struct imx219 *imx219) + { + struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); +@@ -800,6 +874,13 @@ static int imx219_start_streaming(struct + return ret; + } + ++ ret = imx219_set_framefmt(imx219); ++ if (ret) { ++ dev_err(&client->dev, "%s failed to set frame format: %d\n", ++ __func__, ret); ++ return ret; ++ } ++ + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler); + if (ret) +@@ -1253,6 +1334,9 @@ static int imx219_probe(struct i2c_clien + /* Initialize source pad */ + imx219->pad.flags = MEDIA_PAD_FL_SOURCE; + ++ /* Initialize default format */ ++ imx219_set_default_format(imx219); ++ + ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad); + if (ret) { + dev_err(dev, "failed to init entity pads: %d\n", ret); diff --git a/target/linux/bcm27xx/patches-5.4/950-0658-media-i2c-imx219-Add-support-for-cropped-640x480-res.patch b/target/linux/bcm27xx/patches-5.4/950-0658-media-i2c-imx219-Add-support-for-cropped-640x480-res.patch new file mode 100644 index 0000000000..3ad377dfbe --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0658-media-i2c-imx219-Add-support-for-cropped-640x480-res.patch @@ -0,0 +1,118 @@ +From d8b1f6de10d7bd64d73b1c3099ad5e69e6fd7d9b Mon Sep 17 00:00:00 2001 +From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> +Date: Tue, 10 Mar 2020 14:17:09 +0100 +Subject: [PATCH] media: i2c: imx219: Add support for cropped 640x480 + resolution + +Commit 25130b8ad409d5532f3763bcf891af74f550a70d upstream. + +This patch adds mode table entry for capturing cropped 640x480 resolution + +Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> +Acked-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> +Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> +--- + drivers/media/i2c/imx219.c | 70 +++++++++++++++++++++++++++++++++++++- + 1 file changed, 69 insertions(+), 1 deletion(-) + +--- a/drivers/media/i2c/imx219.c ++++ b/drivers/media/i2c/imx219.c +@@ -54,6 +54,7 @@ + #define IMX219_VTS_15FPS 0x0dc6 + #define IMX219_VTS_30FPS_1080P 0x06e3 + #define IMX219_VTS_30FPS_BINNED 0x06e3 ++#define IMX219_VTS_30FPS_640x480 0x06e3 + #define IMX219_VTS_MAX 0xffff + + #define IMX219_VBLANK_MIN 4 +@@ -138,7 +139,7 @@ struct imx219_mode { + /* + * Register sets lifted off the i2C interface from the Raspberry Pi firmware + * driver. +- * 3280x2464 = mode 2, 1920x1080 = mode 1, and 1640x1232 = mode 4. ++ * 3280x2464 = mode 2, 1920x1080 = mode 1, 1640x1232 = mode 4, 640x480 = mode 7. + */ + static const struct imx219_reg mode_3280x2464_regs[] = { + {0x0100, 0x00}, +@@ -313,6 +314,63 @@ static const struct imx219_reg mode_1640 + {0x0163, 0x78}, + }; + ++static const struct imx219_reg mode_640_480_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, 0x03}, ++ {0x0165, 0xe8}, ++ {0x0166, 0x08}, ++ {0x0167, 0xe7}, ++ {0x0168, 0x02}, ++ {0x0169, 0xf0}, ++ {0x016a, 0x06}, ++ {0x016b, 0xaf}, ++ {0x016c, 0x02}, ++ {0x016d, 0x80}, ++ {0x016e, 0x01}, ++ {0x016f, 0xe0}, ++ {0x0170, 0x01}, ++ {0x0171, 0x01}, ++ {0x0174, 0x03}, ++ {0x0175, 0x03}, ++ {0x0301, 0x05}, ++ {0x0303, 0x01}, ++ {0x0304, 0x03}, ++ {0x0305, 0x03}, ++ {0x0306, 0x00}, ++ {0x0307, 0x39}, ++ {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}, ++}; ++ + static const struct imx219_reg raw8_framefmt_regs[] = { + {0x018c, 0x08}, + {0x018d, 0x08}, +@@ -431,6 +489,16 @@ static const struct imx219_mode supporte + .regs = mode_1640_1232_regs, + }, + }, ++ { ++ /* 640x480 30fps mode */ ++ .width = 640, ++ .height = 480, ++ .vts_def = IMX219_VTS_30FPS_640x480, ++ .reg_list = { ++ .num_of_regs = ARRAY_SIZE(mode_640_480_regs), ++ .regs = mode_640_480_regs, ++ }, ++ }, + }; + + struct imx219 { diff --git a/target/linux/bcm27xx/patches-5.4/950-0659-media-i2c-imx219-Fix-a-bug-in-imx219_enum_frame_size.patch b/target/linux/bcm27xx/patches-5.4/950-0659-media-i2c-imx219-Fix-a-bug-in-imx219_enum_frame_size.patch new file mode 100644 index 0000000000..ef8eff6c00 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0659-media-i2c-imx219-Fix-a-bug-in-imx219_enum_frame_size.patch @@ -0,0 +1,34 @@ +From 942a240a1999aaf1b8d18a88c63f418991bf7d22 Mon Sep 17 00:00:00 2001 +From: Dafna Hirschfeld <dafna.hirschfeld@collabora.com> +Date: Tue, 31 Mar 2020 20:06:30 +0200 +Subject: [PATCH] media: i2c: imx219: Fix a bug in + imx219_enum_frame_size + +https://patchwork.linuxtv.org/patch/62740/ + +When enumerating the frame sizes, the value sent to +imx219_get_format_code should be fse->code +(the code from the ioctl) and not imx219->fmt.code +which is the code set currently in the driver. + +Fixes: 22da1d56e ("media: i2c: imx219: Add support for RAW8 bit bayer format") + +Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com> +Reviewed-by: Helen Koike <helen.koike@collabora.com> +Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +Reviewed-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> +--- + drivers/media/i2c/imx219.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/media/i2c/imx219.c ++++ b/drivers/media/i2c/imx219.c +@@ -781,7 +781,7 @@ static int imx219_enum_frame_size(struct + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + +- if (fse->code != imx219_get_format_code(imx219, imx219->fmt.code)) ++ if (fse->code != imx219_get_format_code(imx219, fse->code)) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; diff --git a/target/linux/bcm27xx/patches-5.4/950-0660-media-bcm2835-unicam-Disable-event-related-ioctls-on.patch b/target/linux/bcm27xx/patches-5.4/950-0660-media-bcm2835-unicam-Disable-event-related-ioctls-on.patch new file mode 100644 index 0000000000..cb36ce2d96 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0660-media-bcm2835-unicam-Disable-event-related-ioctls-on.patch @@ -0,0 +1,31 @@ +From e8f2c38720e391ce44154f17f3602484c1827a59 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart <laurent.pinchart@ideasonboard.com> +Date: Tue, 24 Mar 2020 23:13:02 +0200 +Subject: [PATCH] media: bcm2835-unicam: Disable event-related ioctls + on metadata node + +The unicam driver supports both the SOURCE_CHANGE and CTRL events. Both +events are only generated on the image video node, so the event-related +ioctls are useless on the medatada node. Disable them. + +Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> +Reviewed-by: Jacopo Mondi <jacopo@jmondi.org> +Reviewed-by: Naushir Patuck <naush@raspberrypi.com> +--- + drivers/media/platform/bcm2835/bcm2835-unicam.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c ++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c +@@ -2374,6 +2374,11 @@ static int register_node(struct unicam_d + return -ENOMEM; + } + ++ if (node->pad_id == METADATA_PAD) { ++ v4l2_disable_ioctl(vdev, VIDIOC_DQEVENT); ++ v4l2_disable_ioctl(vdev, VIDIOC_SUBSCRIBE_EVENT); ++ v4l2_disable_ioctl(vdev, VIDIOC_UNSUBSCRIBE_EVENT); ++ } + if (node->pad_id == METADATA_PAD || + !v4l2_subdev_has_op(unicam->sensor, video, s_std)) { + v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_STD); diff --git a/target/linux/bcm27xx/patches-5.4/950-0661-media-bcm2835-unicam-Add-support-for-the-FRAME_SYNC-.patch b/target/linux/bcm27xx/patches-5.4/950-0661-media-bcm2835-unicam-Add-support-for-the-FRAME_SYNC-.patch new file mode 100644 index 0000000000..544d597a35 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0661-media-bcm2835-unicam-Add-support-for-the-FRAME_SYNC-.patch @@ -0,0 +1,55 @@ +From 20caa3607c972f5c28b3e9aceb0b8a1d2094c0a7 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart <laurent.pinchart@ideasonboard.com> +Date: Tue, 24 Mar 2020 23:13:02 +0200 +Subject: [PATCH] media: bcm2835-unicam: Add support for the FRAME_SYNC + event + +The FRAME_SYNC event is useful for userspace image processing algorithms +to program the camera sensor as early as possible after frame start. +Support it. + +Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> +Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> +Reviewed-by: Jacopo Mondi <jacopo@jmondi.org> +Reviewed-by: Naushir Patuck <naush@raspberrypi.com> +--- + drivers/media/platform/bcm2835/bcm2835-unicam.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c ++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c +@@ -772,6 +772,16 @@ static int unicam_all_nodes_disabled(str + !dev->node[METADATA_PAD].streaming; + } + ++static void unicam_queue_event_sof(struct unicam_device *unicam) ++{ ++ struct v4l2_event event = { ++ .type = V4L2_EVENT_FRAME_SYNC, ++ .u.frame_sync.frame_sequence = unicam->sequence, ++ }; ++ ++ v4l2_event_queue(&unicam->node[IMAGE_PAD].video_dev, &event); ++} ++ + /* + * unicam_isr : ISR handler for unicam capture + * @irq: irq number +@@ -853,6 +863,8 @@ static irqreturn_t unicam_isr(int irq, v + */ + unicam_schedule_dummy_buffer(&unicam->node[i]); + } ++ ++ unicam_queue_event_sof(unicam); + } + /* + * Cannot swap buffer at frame end, there may be a race condition +@@ -2022,6 +2034,8 @@ static int unicam_subscribe_event(struct + const struct v4l2_event_subscription *sub) + { + switch (sub->type) { ++ case V4L2_EVENT_FRAME_SYNC: ++ return v4l2_event_subscribe(fh, sub, 2, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_event_subscribe(fh, sub, 4, NULL); + } diff --git a/target/linux/bcm27xx/patches-5.4/950-0662-Revert-firmware-raspberrypi-register-clk-device.patch b/target/linux/bcm27xx/patches-5.4/950-0662-Revert-firmware-raspberrypi-register-clk-device.patch new file mode 100644 index 0000000000..01533d1f3f --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0662-Revert-firmware-raspberrypi-register-clk-device.patch @@ -0,0 +1,58 @@ +From b2691aee386b18425a7b96cca05e3bcceb8678ae Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Fri, 17 Apr 2020 16:20:55 +0100 +Subject: [PATCH] Revert "firmware: raspberrypi: register clk device" + +This reverts commit 91f2cf4a6b2131016b1ae9c9500245f0572112c7. + +Revert this because the clock driver won't probe without a DT node, and +creating it as a platform device won't give it one. Doing so avoids the +following error message: + + raspberrypi-clk raspberrypi-clk: Missing firmware node + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + drivers/firmware/raspberrypi.c | 10 ---------- + 1 file changed, 10 deletions(-) + +--- a/drivers/firmware/raspberrypi.c ++++ b/drivers/firmware/raspberrypi.c +@@ -21,7 +21,6 @@ + #define MBOX_CHAN_PROPERTY 8 + + static struct platform_device *rpi_hwmon; +-static struct platform_device *rpi_clk; + + struct rpi_firmware { + struct mbox_client cl; +@@ -299,12 +298,6 @@ rpi_register_hwmon_driver(struct device + } + } + +-static void rpi_register_clk_driver(struct device *dev) +-{ +- rpi_clk = platform_device_register_data(dev, "raspberrypi-clk", +- -1, NULL, 0); +-} +- + static int rpi_firmware_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -334,7 +327,6 @@ static int rpi_firmware_probe(struct pla + rpi_firmware_print_firmware_revision(fw); + rpi_firmware_print_firmware_hash(fw); + rpi_register_hwmon_driver(dev, fw); +- rpi_register_clk_driver(dev); + + return 0; + } +@@ -355,8 +347,6 @@ static int rpi_firmware_remove(struct pl + + platform_device_unregister(rpi_hwmon); + rpi_hwmon = NULL; +- platform_device_unregister(rpi_clk); +- rpi_clk = NULL; + mbox_free_channel(fw->chan); + g_pdev = NULL; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0663-media-imx219-Advertise-embedded-data-node-on-media-p.patch b/target/linux/bcm27xx/patches-5.4/950-0663-media-imx219-Advertise-embedded-data-node-on-media-p.patch new file mode 100644 index 0000000000..c4c25d1512 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0663-media-imx219-Advertise-embedded-data-node-on-media-p.patch @@ -0,0 +1,335 @@ +From 372b138a6bc43b0fdff4e46ae4c799fd1b1f2785 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Thu, 12 Mar 2020 14:09:38 +0000 +Subject: [PATCH] media: imx219: Advertise embedded data node on media + pad 1 + +This commit updates the imx219 driver to adverise support for embedded +data streams. This can then be used by the bcm2835-unicam driver, which +has recently been updated to expose the embedded data stream to +userland. + +The imx219 sensor subdevice overloads the media pad to differentiate +between image stream (pad 0) and embedded data stream (pad 1) when +performing the v4l2_subdev_pad_ops functions. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + drivers/media/i2c/imx219.c | 226 +++++++++++++++++++++++++------------ + 1 file changed, 155 insertions(+), 71 deletions(-) + +--- a/drivers/media/i2c/imx219.c ++++ b/drivers/media/i2c/imx219.c +@@ -112,6 +112,16 @@ + #define IMX219_TESTP_BLUE_DEFAULT 0 + #define IMX219_TESTP_GREENB_DEFAULT 0 + ++/* Embedded metadata stream structure */ ++#define IMX219_EMBEDDED_LINE_WIDTH 16384 ++#define IMX219_NUM_EMBEDDED_LINES 1 ++ ++enum pad_types { ++ IMAGE_PAD, ++ METADATA_PAD, ++ NUM_PADS ++}; ++ + struct imx219_reg { + u16 address; + u8 val; +@@ -503,7 +513,7 @@ static const struct imx219_mode supporte + + struct imx219 { + struct v4l2_subdev sd; +- struct media_pad pad; ++ struct media_pad pad[NUM_PADS]; + + struct v4l2_mbus_framefmt fmt; + +@@ -652,17 +662,25 @@ static void imx219_set_default_format(st + 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); ++ struct v4l2_mbus_framefmt *try_fmt_img = ++ v4l2_subdev_get_try_format(sd, fh->pad, IMAGE_PAD); ++ struct v4l2_mbus_framefmt *try_fmt_meta = ++ v4l2_subdev_get_try_format(sd, fh->pad, METADATA_PAD); + + 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, +- MEDIA_BUS_FMT_SRGGB10_1X10); +- try_fmt->field = V4L2_FIELD_NONE; ++ /* Initialize try_fmt for the image pad */ ++ try_fmt_img->width = supported_modes[0].width; ++ try_fmt_img->height = supported_modes[0].height; ++ try_fmt_img->code = imx219_get_format_code(imx219, ++ MEDIA_BUS_FMT_SRGGB10_1X10); ++ try_fmt_img->field = V4L2_FIELD_NONE; ++ ++ /* Initialize try_fmt for the embedded metadata pad */ ++ try_fmt_meta->width = IMX219_EMBEDDED_LINE_WIDTH; ++ try_fmt_meta->height = IMX219_NUM_EMBEDDED_LINES; ++ try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA; ++ try_fmt_meta->field = V4L2_FIELD_NONE; + + mutex_unlock(&imx219->mutex); + +@@ -764,10 +782,21 @@ static int imx219_enum_mbus_code(struct + { + struct imx219 *imx219 = to_imx219(sd); + +- if (code->index >= (ARRAY_SIZE(codes) / 4)) ++ if (code->pad >= NUM_PADS) + return -EINVAL; + +- code->code = imx219_get_format_code(imx219, codes[code->index * 4]); ++ if (code->pad == IMAGE_PAD) { ++ if (code->index >= (ARRAY_SIZE(codes) / 4)) ++ return -EINVAL; ++ ++ code->code = imx219_get_format_code(imx219, ++ codes[code->index * 4]); ++ } else { ++ if (code->index > 0) ++ return -EINVAL; ++ ++ code->code = MEDIA_BUS_FMT_SENSOR_DATA; ++ } + + return 0; + } +@@ -778,16 +807,29 @@ static int imx219_enum_frame_size(struct + { + struct imx219 *imx219 = to_imx219(sd); + +- if (fse->index >= ARRAY_SIZE(supported_modes)) ++ if (fse->pad >= NUM_PADS) + return -EINVAL; + +- if (fse->code != imx219_get_format_code(imx219, fse->code)) +- return -EINVAL; ++ if (fse->pad == IMAGE_PAD) { ++ if (fse->index >= ARRAY_SIZE(supported_modes)) ++ return -EINVAL; ++ ++ if (fse->code != imx219_get_format_code(imx219, fse->code)) ++ 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; ++ } else { ++ if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) ++ 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; ++ fse->min_width = IMX219_EMBEDDED_LINE_WIDTH; ++ fse->max_width = fse->min_width; ++ fse->min_height = IMX219_NUM_EMBEDDED_LINES; ++ fse->max_height = fse->min_height; ++ } + + return 0; + } +@@ -802,9 +844,9 @@ static void imx219_reset_colorspace(stru + 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) ++static void imx219_update_image_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; +@@ -812,20 +854,38 @@ static void imx219_update_pad_format(str + imx219_reset_colorspace(&fmt->format); + } + ++static void imx219_update_metadata_pad_format(struct v4l2_subdev_format *fmt) ++{ ++ fmt->format.width = IMX219_EMBEDDED_LINE_WIDTH; ++ fmt->format.height = IMX219_NUM_EMBEDDED_LINES; ++ fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA; ++ fmt->format.field = V4L2_FIELD_NONE; ++} ++ + static int __imx219_get_pad_format(struct imx219 *imx219, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) + { ++ if (fmt->pad >= NUM_PADS) ++ return -EINVAL; ++ + 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, try_fmt->code); ++ try_fmt->code = fmt->pad == IMAGE_PAD ? ++ imx219_get_format_code(imx219, try_fmt->code) : ++ MEDIA_BUS_FMT_SENSOR_DATA; + fmt->format = *try_fmt; + } else { +- imx219_update_pad_format(imx219, imx219->mode, fmt); +- fmt->format.code = imx219_get_format_code(imx219, +- imx219->fmt.code); ++ if (fmt->pad == IMAGE_PAD) { ++ imx219_update_image_pad_format(imx219, imx219->mode, ++ fmt); ++ fmt->format.code = imx219_get_format_code(imx219, ++ imx219->fmt.code); ++ } else { ++ imx219_update_metadata_pad_format(fmt); ++ } + } + + return 0; +@@ -855,51 +915,74 @@ static int imx219_set_pad_format(struct + int exposure_max, exposure_def, hblank; + unsigned int i; + +- mutex_lock(&imx219->mutex); +- +- for (i = 0; i < ARRAY_SIZE(codes); i++) +- if (codes[i] == fmt->format.code) +- break; +- if (i >= ARRAY_SIZE(codes)) +- i = 0; ++ if (fmt->pad >= NUM_PADS) ++ return -EINVAL; + +- /* Bayer order varies with flips */ +- fmt->format.code = imx219_get_format_code(imx219, codes[i]); ++ mutex_lock(&imx219->mutex); + +- 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->fmt.code != fmt->format.code) { +- imx219->fmt = fmt->format; +- 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); ++ if (fmt->pad == IMAGE_PAD) { ++ for (i = 0; i < ARRAY_SIZE(codes); i++) ++ if (codes[i] == fmt->format.code) ++ break; ++ if (i >= ARRAY_SIZE(codes)) ++ i = 0; ++ ++ /* Bayer order varies with flips */ ++ fmt->format.code = imx219_get_format_code(imx219, codes[i]); ++ ++ mode = v4l2_find_nearest_size(supported_modes, ++ ARRAY_SIZE(supported_modes), ++ width, height, ++ fmt->format.width, ++ fmt->format.height); ++ imx219_update_image_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->fmt.code != fmt->format.code) { ++ imx219->fmt = fmt->format; ++ 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); ++ } ++ } else { ++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { ++ framefmt = v4l2_subdev_get_try_format(sd, cfg, ++ fmt->pad); ++ *framefmt = fmt->format; ++ } else { ++ /* Only one embedded data mode is supported */ ++ imx219_update_metadata_pad_format(fmt); ++ } + } + + mutex_unlock(&imx219->mutex); +@@ -1399,13 +1482,14 @@ static int imx219_probe(struct i2c_clien + 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; ++ /* Initialize source pads */ ++ imx219->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE; ++ imx219->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE; + + /* Initialize default format */ + imx219_set_default_format(imx219); + +- ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad); ++ ret = media_entity_pads_init(&imx219->sd.entity, NUM_PADS, imx219->pad); + if (ret) { + dev_err(dev, "failed to init entity pads: %d\n", ret); + goto error_handler_free; diff --git a/target/linux/bcm27xx/patches-5.4/950-0664-dts-bcm2711-EMMC2-can-address-the-whole-first-GB.patch b/target/linux/bcm27xx/patches-5.4/950-0664-dts-bcm2711-EMMC2-can-address-the-whole-first-GB.patch new file mode 100644 index 0000000000..4c5e50636c --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0664-dts-bcm2711-EMMC2-can-address-the-whole-first-GB.patch @@ -0,0 +1,31 @@ +From 4f2da50bb75ec7b74a23e119062d945626398e30 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Mon, 20 Apr 2020 11:25:18 +0100 +Subject: [PATCH] dts: bcm2711: EMMC2 can address the whole first GB + +Although 0xfc000000 looks like an inaccessible RAM address (due to the +peripheral mappings), with RAM mapped at 0xc0000000 (as it is on the +30/32-bit VPU bus) this is actually 0x3c000000 in the ARM memory space, +which is fine. + +This difference is potentially the cause of some warnings seen in +sdhci_send_command. + +Fixes: "dts: bcm2711: Move emmc2 to its own 'bus'" + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts ++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts +@@ -223,7 +223,7 @@ + #size-cells = <1>; + + ranges = <0x0 0x7e000000 0x0 0xfe000000 0x01800000>; +- dma-ranges = <0x0 0xc0000000 0x0 0x00000000 0x3c000000>; ++ dma-ranges = <0x0 0xc0000000 0x0 0x00000000 0x40000000>; + + emmc2: emmc2@7e340000 { + compatible = "brcm,bcm2711-emmc2"; diff --git a/target/linux/bcm27xx/patches-5.4/950-0665-driver-char-rpivid-Remove-legacy-name-support.patch b/target/linux/bcm27xx/patches-5.4/950-0665-driver-char-rpivid-Remove-legacy-name-support.patch new file mode 100644 index 0000000000..a3896be439 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0665-driver-char-rpivid-Remove-legacy-name-support.patch @@ -0,0 +1,53 @@ +From 77beb3055b14910fa3ef9af606476520e956bf93 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Mon, 20 Apr 2020 22:18:52 +0100 +Subject: [PATCH] driver: char: rpivid: Remove legacy name support + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + drivers/char/broadcom/rpivid-mem.c | 22 ---------------------- + 1 file changed, 22 deletions(-) + +--- a/drivers/char/broadcom/rpivid-mem.c ++++ b/drivers/char/broadcom/rpivid-mem.c +@@ -193,32 +193,11 @@ static int rpivid_mem_probe(struct platf + goto failed_device_create; + } + +- /* Legacy alias */ +- { +- char *oldname = kstrdup(priv->name, GFP_KERNEL); +- +- oldname[1] = 'a'; +- oldname[2] = 'r'; +- oldname[3] = 'g'; +- oldname[4] = 'o'; +- oldname[5] = 'n'; +- dev = device_create(priv->class, NULL, priv->devid + 1, NULL, +- oldname + 1); +- kfree(oldname); +- +- if (IS_ERR(dev)) { +- err = PTR_ERR(dev); +- goto failed_legacy_device_create; +- } +- } +- + dev_info(priv->dev, "%s initialised: Registers at 0x%08lx length 0x%08lx", + priv->name, priv->regs_phys, priv->mem_window_len); + + return 0; + +-failed_legacy_device_create: +- device_destroy(priv->class, priv->devid); + failed_device_create: + class_destroy(priv->class); + failed_class_create: +@@ -238,7 +217,6 @@ static int rpivid_mem_remove(struct plat + struct device *dev = &pdev->dev; + struct rpivid_mem_priv *priv = platform_get_drvdata(pdev); + +- device_destroy(priv->class, priv->devid + 1); + device_destroy(priv->class, priv->devid); + class_destroy(priv->class); + cdev_del(&priv->rpivid_mem_cdev); diff --git a/target/linux/bcm27xx/patches-5.4/950-0666-driver-char-rpivid-Don-t-map-more-than-wanted.patch b/target/linux/bcm27xx/patches-5.4/950-0666-driver-char-rpivid-Don-t-map-more-than-wanted.patch new file mode 100644 index 0000000000..afc1a5a6a8 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0666-driver-char-rpivid-Don-t-map-more-than-wanted.patch @@ -0,0 +1,51 @@ +From 8c2369b39b1dafe7a26907173bb47d37ec53bfa2 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Tue, 21 Apr 2020 11:30:23 +0100 +Subject: [PATCH] driver: char: rpivid: Don't map more than wanted + +Limit mappings to the permitted range, but don't map more than asked +for otherwise we walk off the end of the allocated VMA. + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + drivers/char/broadcom/rpivid-mem.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +--- a/drivers/char/broadcom/rpivid-mem.c ++++ b/drivers/char/broadcom/rpivid-mem.c +@@ -100,6 +100,7 @@ static int rpivid_mem_mmap(struct file * + { + struct rpivid_mem_priv *priv; + unsigned long pages; ++ unsigned long len; + + priv = file->private_data; + pages = priv->regs_phys >> PAGE_SHIFT; +@@ -107,14 +108,13 @@ static int rpivid_mem_mmap(struct file * + * The address decode is far larger than the actual number of registers. + * Just map the whole lot in. + */ +- vma->vm_page_prot = phys_mem_access_prot(file, pages, +- priv->mem_window_len, ++ len = min(vma->vm_end - vma->vm_start, priv->mem_window_len); ++ vma->vm_page_prot = phys_mem_access_prot(file, pages, len, + vma->vm_page_prot); + vma->vm_ops = &rpivid_mem_vm_ops; + if (remap_pfn_range(vma, vma->vm_start, +- pages, +- priv->mem_window_len, +- vma->vm_page_prot)) { ++ pages, len, ++ vma->vm_page_prot)) { + return -EAGAIN; + } + return 0; +@@ -156,7 +156,7 @@ static int rpivid_mem_probe(struct platf + ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (ioresource) { + priv->regs_phys = ioresource->start; +- priv->mem_window_len = ioresource->end - ioresource->start; ++ priv->mem_window_len = (ioresource->end + 1) - ioresource->start; + } else { + dev_err(priv->dev, "failed to get IO resource"); + err = -ENOENT; diff --git a/target/linux/bcm27xx/patches-5.4/950-0667-dt-Implement-an-I2C-pinctrl-mux-for-BSC0.patch b/target/linux/bcm27xx/patches-5.4/950-0667-dt-Implement-an-I2C-pinctrl-mux-for-BSC0.patch new file mode 100644 index 0000000000..002ad8686e --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0667-dt-Implement-an-I2C-pinctrl-mux-for-BSC0.patch @@ -0,0 +1,434 @@ +From 77d7427bed21c92d1c10e0cc9beabb5ce9bb6c0b Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Thu, 9 Apr 2020 12:46:13 +0100 +Subject: [PATCH] dt: Implement an I2C pinctrl mux for BSC0. + +BSC0 serves either the HAT EEPROM pins on the 40pin connector, +or the display and camera on a board specific pairing of either +GPIO 28&29, or 44&45. + +Use I2C_MUX_PINCTRL to allow exposing both pairs of pins as I2C +busses. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 9 ++++--- + arch/arm/boot/dts/bcm2708-rpi-b.dts | 9 ++++--- + arch/arm/boot/dts/bcm2708-rpi-cm.dts | 9 ++++--- + arch/arm/boot/dts/bcm2708-rpi-zero-w.dts | 9 ++++--- + arch/arm/boot/dts/bcm2708-rpi-zero.dts | 9 ++++--- + arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 9 ++++--- + arch/arm/boot/dts/bcm270x-rpi.dtsi | 7 ++--- + arch/arm/boot/dts/bcm2710-rpi-2-b.dts | 9 ++++--- + arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts | 9 ++++--- + arch/arm/boot/dts/bcm2710-rpi-3-b.dts | 9 ++++--- + arch/arm/boot/dts/bcm2710-rpi-cm3.dts | 10 ++++--- + arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 5 ++-- + arch/arm/boot/dts/bcm2711.dtsi | 2 +- + .../boot/dts/bcm283x-rpi-i2c0mux_0_28.dtsi | 4 +++ + .../boot/dts/bcm283x-rpi-i2c0mux_0_44.dtsi | 4 +++ + arch/arm/boot/dts/bcm283x.dtsi | 26 ++++++++++++++++++- + 16 files changed, 100 insertions(+), 39 deletions(-) + create mode 100644 arch/arm/boot/dts/bcm283x-rpi-i2c0mux_0_28.dtsi + create mode 100644 arch/arm/boot/dts/bcm283x-rpi-i2c0mux_0_44.dtsi + +--- a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts +@@ -4,6 +4,7 @@ + #include "bcm2708-rpi.dtsi" + #include "bcm283x-rpi-smsc9514.dtsi" + #include "bcm283x-rpi-csi1-2lane.dtsi" ++#include "bcm283x-rpi-i2c0mux_0_28.dtsi" + + / { + compatible = "raspberrypi,model-b-plus", "brcm,bcm2835"; +@@ -68,12 +69,14 @@ + }; + }; + +-&i2c0 { +- pinctrl-names = "default"; +- pinctrl-0 = <&i2c0_pins>; ++&i2c0if { + clock-frequency = <100000>; + }; + ++&i2c0mux { ++ pinctrl-0 = <&i2c0_pins>; ++}; ++ + &i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; +--- a/arch/arm/boot/dts/bcm2708-rpi-b.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts +@@ -4,6 +4,7 @@ + #include "bcm2708-rpi.dtsi" + #include "bcm283x-rpi-smsc9512.dtsi" + #include "bcm283x-rpi-csi1-2lane.dtsi" ++#include "bcm283x-rpi-i2c0mux_0_28.dtsi" + + / { + compatible = "raspberrypi,model-b", "brcm,bcm2835"; +@@ -68,12 +69,14 @@ + }; + }; + +-&i2c0 { +- pinctrl-names = "default"; +- pinctrl-0 = <&i2c0_pins>; ++&i2c0if { + clock-frequency = <100000>; + }; + ++&i2c0mux { ++ pinctrl-0 = <&i2c0_pins>; ++}; ++ + &i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; +--- a/arch/arm/boot/dts/bcm2708-rpi-cm.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-cm.dts +@@ -3,6 +3,7 @@ + #include "bcm2708-rpi-cm.dtsi" + #include "bcm283x-rpi-csi0-2lane.dtsi" + #include "bcm283x-rpi-csi1-4lane.dtsi" ++#include "bcm283x-rpi-i2c0mux_0_28.dtsi" + + / { + compatible = "raspberrypi,compute-module", "brcm,bcm2835"; +@@ -67,12 +68,14 @@ + }; + }; + +-&i2c0 { +- pinctrl-names = "default"; +- pinctrl-0 = <&i2c0_pins>; ++&i2c0if { + clock-frequency = <100000>; + }; + ++&i2c0mux { ++ pinctrl-0 = <&i2c0_pins>; ++}; ++ + &i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; +--- a/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts +@@ -3,6 +3,7 @@ + #include "bcm2708.dtsi" + #include "bcm2708-rpi.dtsi" + #include "bcm283x-rpi-csi1-2lane.dtsi" ++#include "bcm283x-rpi-i2c0mux_0_28.dtsi" + + / { + compatible = "raspberrypi,model-zero-w", "brcm,bcm2835"; +@@ -116,12 +117,14 @@ + }; + }; + +-&i2c0 { +- pinctrl-names = "default"; +- pinctrl-0 = <&i2c0_pins>; ++&i2c0if { + clock-frequency = <100000>; + }; + ++&i2c0mux { ++ pinctrl-0 = <&i2c0_pins>; ++}; ++ + &i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; +--- a/arch/arm/boot/dts/bcm2708-rpi-zero.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-zero.dts +@@ -3,6 +3,7 @@ + #include "bcm2708.dtsi" + #include "bcm2708-rpi.dtsi" + #include "bcm283x-rpi-csi1-2lane.dtsi" ++#include "bcm283x-rpi-i2c0mux_0_28.dtsi" + + / { + compatible = "raspberrypi,model-zero", "brcm,bcm2835"; +@@ -71,12 +72,14 @@ + }; + }; + +-&i2c0 { +- pinctrl-names = "default"; +- pinctrl-0 = <&i2c0_pins>; ++&i2c0if { + clock-frequency = <100000>; + }; + ++&i2c0mux { ++ pinctrl-0 = <&i2c0_pins>; ++}; ++ + &i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; +--- a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts ++++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts +@@ -4,6 +4,7 @@ + #include "bcm2709-rpi.dtsi" + #include "bcm283x-rpi-smsc9514.dtsi" + #include "bcm283x-rpi-csi1-2lane.dtsi" ++#include "bcm283x-rpi-i2c0mux_0_28.dtsi" + + / { + compatible = "raspberrypi,2-model-b", "brcm,bcm2836"; +@@ -68,12 +69,14 @@ + }; + }; + +-&i2c0 { +- pinctrl-names = "default"; +- pinctrl-0 = <&i2c0_pins>; ++&i2c0if { + clock-frequency = <100000>; + }; + ++&i2c0mux { ++ pinctrl-0 = <&i2c0_pins>; ++}; ++ + &i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; +--- a/arch/arm/boot/dts/bcm270x-rpi.dtsi ++++ b/arch/arm/boot/dts/bcm270x-rpi.dtsi +@@ -21,6 +21,7 @@ + i2s = &i2s; + i2c0 = &i2c0; + i2c1 = &i2c1; ++ i2c10 = &i2c_csi_dsi; + spi0 = &spi0; + spi1 = &spi1; + spi2 = &spi2; +@@ -83,9 +84,9 @@ + uart1 = <&uart1>,"status"; + i2s = <&i2s>,"status"; + spi = <&spi0>,"status"; +- i2c0 = <&i2c0>,"status"; ++ i2c0 = <&i2c0if>,"status",<&i2c0mux>,"status"; + i2c1 = <&i2c1>,"status"; +- i2c0_baudrate = <&i2c0>,"clock-frequency:0"; ++ i2c0_baudrate = <&i2c0if>,"clock-frequency:0"; + i2c1_baudrate = <&i2c1>,"clock-frequency:0"; + + audio = <&audio>,"status"; +@@ -105,7 +106,7 @@ + status = "disabled"; + }; + +-&i2c0 { ++&i2c0if { + status = "disabled"; + }; + +--- a/arch/arm/boot/dts/bcm2710-rpi-2-b.dts ++++ b/arch/arm/boot/dts/bcm2710-rpi-2-b.dts +@@ -4,6 +4,7 @@ + #include "bcm2709-rpi.dtsi" + #include "bcm283x-rpi-smsc9514.dtsi" + #include "bcm283x-rpi-csi1-2lane.dtsi" ++#include "bcm283x-rpi-i2c0mux_0_28.dtsi" + + / { + compatible = "raspberrypi,2-model-b-rev2", "brcm,bcm2837"; +@@ -68,12 +69,14 @@ + }; + }; + +-&i2c0 { +- pinctrl-names = "default"; +- pinctrl-0 = <&i2c0_pins>; ++&i2c0if { + clock-frequency = <100000>; + }; + ++&i2c0mux { ++ pinctrl-0 = <&i2c0_pins>; ++}; ++ + &i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; +--- a/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts ++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts +@@ -4,6 +4,7 @@ + #include "bcm2709-rpi.dtsi" + #include "bcm283x-rpi-lan7515.dtsi" + #include "bcm283x-rpi-csi1-2lane.dtsi" ++#include "bcm283x-rpi-i2c0mux_0_44.dtsi" + + / { + compatible = "raspberrypi,3-model-b-plus", "brcm,bcm2837"; +@@ -126,12 +127,14 @@ + }; + }; + +-&i2c0 { +- pinctrl-names = "default"; +- pinctrl-0 = <&i2c0_pins>; ++&i2c0if { + clock-frequency = <100000>; + }; + ++&i2c0mux { ++ pinctrl-0 = <&i2c0_pins>; ++}; ++ + &i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; +--- a/arch/arm/boot/dts/bcm2710-rpi-3-b.dts ++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts +@@ -4,6 +4,7 @@ + #include "bcm2709-rpi.dtsi" + #include "bcm283x-rpi-smsc9514.dtsi" + #include "bcm283x-rpi-csi1-2lane.dtsi" ++#include "bcm283x-rpi-i2c0mux_0_44.dtsi" + + / { + compatible = "raspberrypi,3-model-b", "brcm,bcm2837"; +@@ -137,12 +138,14 @@ + }; + }; + +-&i2c0 { +- pinctrl-names = "default"; +- pinctrl-0 = <&i2c0_pins>; ++&i2c0if { + clock-frequency = <100000>; + }; + ++&i2c0mux { ++ pinctrl-0 = <&i2c0_pins>; ++}; ++ + &i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; +--- a/arch/arm/boot/dts/bcm2710-rpi-cm3.dts ++++ b/arch/arm/boot/dts/bcm2710-rpi-cm3.dts +@@ -4,7 +4,7 @@ + #include "bcm2709-rpi.dtsi" + #include "bcm283x-rpi-csi0-2lane.dtsi" + #include "bcm283x-rpi-csi1-4lane.dtsi" +- ++#include "bcm283x-rpi-i2c0mux_0_28.dtsi" + / { + compatible = "raspberrypi,3-compute-module", "brcm,bcm2837"; + model = "Raspberry Pi Compute Module 3"; +@@ -88,12 +88,14 @@ + }; + }; + +-&i2c0 { +- pinctrl-names = "default"; +- pinctrl-0 = <&i2c0_pins>; ++&i2c0if { + clock-frequency = <100000>; + }; + ++&i2c0mux { ++ pinctrl-0 = <&i2c0_pins>; ++}; ++ + &i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; +--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts ++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts +@@ -192,6 +192,7 @@ + + #include "bcm2711-rpi.dtsi" + #include "bcm283x-rpi-csi1-2lane.dtsi" ++#include "bcm283x-rpi-i2c0mux_0_44.dtsi" + + /delete-node/ &emmc2; + +@@ -421,9 +422,7 @@ + }; + }; + +-&i2c0 { +- pinctrl-names = "default"; +- pinctrl-0 = <&i2c0_pins>; ++&i2c0if { + clock-frequency = <100000>; + }; + +--- a/arch/arm/boot/dts/bcm2711.dtsi ++++ b/arch/arm/boot/dts/bcm2711.dtsi +@@ -986,7 +986,7 @@ + alloc-ranges = <0x0 0x00000000 0x40000000>; + }; + +-&i2c0 { ++&i2c0if { + compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c"; + interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>; + }; +--- /dev/null ++++ b/arch/arm/boot/dts/bcm283x-rpi-i2c0mux_0_28.dtsi +@@ -0,0 +1,4 @@ ++&i2c0mux { ++ pinctrl-0 = <&i2c0_gpio0>; ++ pinctrl-1 = <&i2c0_gpio28>; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/bcm283x-rpi-i2c0mux_0_44.dtsi +@@ -0,0 +1,4 @@ ++&i2c0mux { ++ pinctrl-0 = <&i2c0_gpio0>; ++ pinctrl-1 = <&i2c0_gpio44>; ++}; +--- a/arch/arm/boot/dts/bcm283x.dtsi ++++ b/arch/arm/boot/dts/bcm283x.dtsi +@@ -340,7 +340,7 @@ + status = "disabled"; + }; + +- i2c0: i2c@7e205000 { ++ i2c0if: i2c@7e205000 { + compatible = "brcm,bcm2835-i2c"; + reg = <0x7e205000 0x200>; + interrupts = <2 21>; +@@ -350,6 +350,30 @@ + status = "disabled"; + }; + ++ i2c0mux: i2c0mux { ++ compatible = "i2c-mux-pinctrl"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ i2c-parent = <&i2c0if>; ++ ++ pinctrl-names = "i2c0", "i2c_csi_dsi"; ++ ++ status = "disabled"; ++ ++ i2c0: i2c@0 { ++ reg = <0>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ i2c_csi_dsi: i2c@1 { ++ reg = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ }; ++ + dpi: dpi@7e208000 { + compatible = "brcm,bcm2835-dpi"; + reg = <0x7e208000 0x8c>; diff --git a/target/linux/bcm27xx/patches-5.4/950-0668-dtoverlays-Update-CSI-overlays-to-use-i2c_csi_dsi.patch b/target/linux/bcm27xx/patches-5.4/950-0668-dtoverlays-Update-CSI-overlays-to-use-i2c_csi_dsi.patch new file mode 100644 index 0000000000..f6b6340c7f --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0668-dtoverlays-Update-CSI-overlays-to-use-i2c_csi_dsi.patch @@ -0,0 +1,425 @@ +From bd24924fea541c114c7761f4698f3fe29d7257e1 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Thu, 9 Apr 2020 15:04:14 +0100 +Subject: [PATCH] dtoverlays: Update CSI overlays to use i2c_csi_dsi + +Update all overlays that were using i2c_vc for talking to CSI +source devices to use the new i2c_csi_dsi node via i2c_mux_pinctrl. +Remove the pins overrides as well. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + arch/arm/boot/dts/overlays/README | 42 ++++--------------- + .../boot/dts/overlays/adv7282m-overlay.dts | 29 +++---------- + arch/arm/boot/dts/overlays/imx219-overlay.dts | 41 +++++------------- + .../arm/boot/dts/overlays/irs1125-overlay.dts | 33 ++++----------- + arch/arm/boot/dts/overlays/ov5647-overlay.dts | 33 ++++----------- + .../boot/dts/overlays/tc358743-overlay.dts | 32 ++++---------- + 6 files changed, 47 insertions(+), 163 deletions(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -331,22 +331,14 @@ Info: Analog Devices ADV7282M analogue + Uses Unicam1, which is the standard camera connector on most Pi + variants. + Load: dtoverlay=adv7282m,<param>=<val> +-Params: i2c_pins_0_1 Use pins 0&1 for the I2C instead of 44&45. +- Useful on Compute Modules. +- i2c_pins_28_29 Use pins 28&29 for the I2C instead of 44&45. +- This is required for Pi B+, 2, 0, and 0W. +- addr Overrides the I2C address (default 0x21) ++Params: addr Overrides the I2C address (default 0x21) + + + Name: adv728x-m + Info: Analog Devices ADV728[0|1|2]-M analogue video to CSI2 bridges. + This is a wrapper for adv7282m, and defaults to ADV7282M. + Load: dtoverlay=adv728x-m,<param>=<val> +-Params: i2c_pins_0_1 Use pins 0&1 for the I2C instead of 44&45. +- Useful on Compute Modules. +- i2c_pins_28_29 Use pins 28&29 for the I2C instead of 44&45. +- This is required for Pi B+, 2, 0, and 0W. +- addr Overrides the I2C address (default 0x21) ++Params: addr Overrides the I2C address (default 0x21) + adv7280m Select ADV7280-M. + adv7281m Select ADV7281-M. + adv7281ma Select ADV7281-MA. +@@ -1384,12 +1376,8 @@ Name: imx219 + Info: Sony IMX219 camera module. + Uses Unicam 1, which is the standard camera connector on most Pi + variants. +-Load: dtoverlay=imx219,<param>=<val> +-Params: i2c_pins_0_1 Use pins 0&1 for the I2C instead of 44&45. +- Useful on Compute Modules. +- +- i2c_pins_28_29 Use pins 28&29 for the I2C instead of 44&45. +- This is required for Pi B+, 2, 0, and 0W. ++Load: dtoverlay=imx219 ++Params: <None> + + + Name: iqaudio-codec +@@ -1453,12 +1441,8 @@ Name: irs1125 + Info: Infineon irs1125 TOF camera module. + Uses Unicam 1, which is the standard camera connector on most Pi + variants. +-Load: dtoverlay=irs1125,<param>=<val> +-Params: i2c_pins_0_1 Use pins 0&1 for the I2C instead of 44&45. +- Useful on Compute Modules. +- +- i2c_pins_28_29 Use pins 28&29 for the I2C instead of 44&45. +- This is required for Pi B+, 2, 0, and 0W. ++Load: dtoverlay=irs1125 ++Params: <None> + + + Name: jedec-spi-nor +@@ -1743,12 +1727,8 @@ Name: ov5647 + Info: Omnivision OV5647 camera module. + Uses Unicam 1, which is the standard camera connector on most Pi + variants. +-Load: dtoverlay=ov5647,<param>=<val> +-Params: i2c_pins_0_1 Use pins 0&1 for the I2C instead of 44&45. +- Useful on Compute Modules. +- +- i2c_pins_28_29 Use pins 28&29 for the I2C instead of 44&45. +- This is required for Pi B+, 2, 0, and 0W. ++Load: dtoverlay=ov5647 ++Params: <None> + + + Name: papirus +@@ -2555,12 +2535,6 @@ Params: 4lane Use 4 la + (574Mbit/s) and 486000000 (972Mbit/s - default) + are supported by the driver. + +- i2c_pins_0_1 Use pins 0&1 for the I2C instead of 44&45. +- Useful on Compute Modules. +- +- i2c_pins_28_29 Use pins 28&29 for the I2C instead of 44&45. +- This is required for Pi B+, 2, 0, and 0W. +- + + Name: tc358743-audio + Info: Used in combination with the tc358743-fast overlay to route the audio +--- a/arch/arm/boot/dts/overlays/adv7282m-overlay.dts ++++ b/arch/arm/boot/dts/overlays/adv7282m-overlay.dts +@@ -7,7 +7,7 @@ + compatible = "brcm,bcm2835"; + + fragment@0 { +- target = <&i2c_vc>; ++ target = <&i2c_csi_dsi>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; +@@ -45,37 +45,20 @@ + }; + }; + fragment@2 { +- target = <&i2c0_pins>; +- __dormant__ { +- brcm,pins = <28 29>; +- brcm,function = <4>; /* alt0 */ +- }; +- +- }; +- fragment@3 { +- target = <&i2c0_pins>; ++ target = <&i2c0if>; + __overlay__ { +- brcm,pins = <44 45>; +- brcm,function = <5>; /* alt1 */ +- }; +- }; +- fragment@4 { +- target = <&i2c0_pins>; +- __dormant__ { +- brcm,pins = <0 1>; +- brcm,function = <4>; /* alt0 */ ++ status = "okay"; + }; + }; +- fragment@5 { +- target = <&i2c_vc>; ++ ++ fragment@3 { ++ target = <&i2c0mux>; + __overlay__ { + status = "okay"; + }; + }; + + __overrides__ { +- i2c_pins_0_1 = <0>,"-2-3+4"; +- i2c_pins_28_29 = <0>,"+2-3-4"; + addr = <&adv728x>,"reg:0"; + }; + }; +--- a/arch/arm/boot/dts/overlays/imx219-overlay.dts ++++ b/arch/arm/boot/dts/overlays/imx219-overlay.dts +@@ -9,7 +9,7 @@ + compatible = "brcm,bcm2835"; + + fragment@0 { +- target = <&i2c_vc>; ++ target = <&i2c_csi_dsi>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; +@@ -61,34 +61,13 @@ + }; + + fragment@2 { +- target = <&i2c0_pins>; +- __dormant__ { +- brcm,pins = <28 29>; +- brcm,function = <4>; /* alt0 */ +- }; +- }; +- fragment@3 { +- target = <&i2c0_pins>; +- __overlay__ { +- brcm,pins = <44 45>; +- brcm,function = <5>; /* alt1 */ +- }; +- }; +- fragment@4 { +- target = <&i2c0_pins>; +- __dormant__ { +- brcm,pins = <0 1>; +- brcm,function = <4>; /* alt0 */ +- }; +- }; +- fragment@5 { +- target = <&i2c_vc>; ++ target = <&i2c0if>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@6 { ++ fragment@3 { + target-path="/"; + __overlay__ { + imx219_vana: fixedregulator@0 { +@@ -114,16 +93,18 @@ + }; + }; + +- fragment@7 { ++ fragment@4 { ++ target = <&i2c0mux>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@5 { + target-path="/__overrides__"; + __overlay__ { + cam0-pwdn-ctrl = <&imx219_vana>,"gpio:0"; + cam0-pwdn = <&imx219_vana>,"gpio:4"; + }; + }; +- +- __overrides__ { +- i2c_pins_0_1 = <0>,"-2-3+4"; +- i2c_pins_28_29 = <0>,"+2-3-4"; +- }; + }; +--- a/arch/arm/boot/dts/overlays/irs1125-overlay.dts ++++ b/arch/arm/boot/dts/overlays/irs1125-overlay.dts +@@ -7,7 +7,7 @@ + compatible = "brcm,bcm2835"; + + fragment@0 { +- target = <&i2c_vc>; ++ target = <&i2c_csi_dsi>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; +@@ -55,43 +55,24 @@ + }; + + fragment@2 { +- target = <&i2c0_pins>; +- __dormant__ { +- brcm,pins = <28 29>; +- brcm,function = <4>; /* alt0 */ +- }; +- }; +- fragment@3 { +- target = <&i2c0_pins>; ++ target = <&i2c0if>; + __overlay__ { +- brcm,pins = <44 45>; +- brcm,function = <5>; /* alt1 */ +- }; +- }; +- fragment@4 { +- target = <&i2c0_pins>; +- __dormant__ { +- brcm,pins = <0 1>; +- brcm,function = <4>; /* alt0 */ ++ status = "okay"; + }; + }; +- fragment@5 { +- target = <&i2c_vc>; ++ ++ fragment@3 { ++ target = <&i2c0mux>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@6 { ++ fragment@4 { + target-path="/__overrides__"; + __overlay__ { + cam0-pwdn-ctrl = <&irs1125>,"pwdn-gpios:0"; + cam0-pwdn = <&irs1125>,"pwdn-gpios:4"; + }; + }; +- +- __overrides__ { +- i2c_pins_0_1 = <0>,"-2-3+4"; +- i2c_pins_28_29 = <0>,"+2-3-4"; +- }; + }; +--- a/arch/arm/boot/dts/overlays/ov5647-overlay.dts ++++ b/arch/arm/boot/dts/overlays/ov5647-overlay.dts +@@ -7,7 +7,7 @@ + compatible = "brcm,bcm2835"; + + fragment@0 { +- target = <&i2c_vc>; ++ target = <&i2c_csi_dsi>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; +@@ -55,34 +55,20 @@ + }; + + fragment@2 { +- target = <&i2c0_pins>; +- __dormant__ { +- brcm,pins = <28 29>; +- brcm,function = <4>; /* alt0 */ +- }; +- }; +- fragment@3 { +- target = <&i2c0_pins>; ++ target = <&i2c0if>; + __overlay__ { +- brcm,pins = <44 45>; +- brcm,function = <5>; /* alt1 */ +- }; +- }; +- fragment@4 { +- target = <&i2c0_pins>; +- __dormant__ { +- brcm,pins = <0 1>; +- brcm,function = <4>; /* alt0 */ ++ status = "okay"; + }; + }; +- fragment@5 { +- target = <&i2c_vc>; ++ ++ fragment@3 { ++ target = <&i2c0mux>; + __overlay__ { + status = "okay"; + }; + }; + +- fragment@6 { ++ fragment@4 { + target-path="/__overrides__"; + __overlay__ { + cam0-pwdn-ctrl = <&ov5647>,"pwdn-gpios:0"; +@@ -91,9 +77,4 @@ + cam0-led = <&ov5647>,"pwdn-gpios:16"; + }; + }; +- +- __overrides__ { +- i2c_pins_0_1 = <0>,"-2-3+4"; +- i2c_pins_28_29 = <0>,"+2-3-4"; +- }; + }; +--- a/arch/arm/boot/dts/overlays/tc358743-overlay.dts ++++ b/arch/arm/boot/dts/overlays/tc358743-overlay.dts +@@ -7,7 +7,7 @@ + compatible = "brcm,bcm2835"; + + fragment@0 { +- target = <&i2c_vc>; ++ target = <&i2c_csi_dsi>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; +@@ -54,7 +54,7 @@ + }; + + fragment@2 { +- target = <&i2c_vc>; ++ target = <&i2c_csi_dsi>; + __overlay__ { + tc358743@0f { + port { +@@ -67,7 +67,7 @@ + }; + + fragment@3 { +- target = <&i2c_vc>; ++ target = <&i2c_csi_dsi>; + __dormant__ { + tc358743@0f { + port { +@@ -80,36 +80,20 @@ + }; + + fragment@4 { +- target = <&i2c0_pins>; +- __dormant__ { +- brcm,pins = <28 29>; +- brcm,function = <4>; /* alt0 */ +- }; +- }; +- fragment@5 { +- target = <&i2c0_pins>; ++ target = <&i2c0if>; + __overlay__ { +- brcm,pins = <44 45>; +- brcm,function = <5>; /* alt1 */ +- }; +- }; +- fragment@6 { +- target = <&i2c0_pins>; +- __dormant__ { +- brcm,pins = <0 1>; +- brcm,function = <4>; /* alt0 */ ++ status = "okay"; + }; + }; +- fragment@7 { +- target = <&i2c_vc>; ++ ++ fragment@5 { ++ target = <&i2c0mux>; + __overlay__ { + status = "okay"; + }; + }; + + __overrides__ { +- i2c_pins_0_1 = <0>,"-4-5+6"; +- i2c_pins_28_29 = <0>,"+4-5-6"; + 4lane = <0>, "-2+3"; + link-frequency = <&tc358743>,"link-frequencies#0"; + }; diff --git a/target/linux/bcm27xx/patches-5.4/950-0669-dt-Update-all-mainline-bcm283x-dt-files-for-i2c0-pin.patch b/target/linux/bcm27xx/patches-5.4/950-0669-dt-Update-all-mainline-bcm283x-dt-files-for-i2c0-pin.patch new file mode 100644 index 0000000000..f5e4e4b008 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0669-dt-Update-all-mainline-bcm283x-dt-files-for-i2c0-pin.patch @@ -0,0 +1,201 @@ +From 393b01ee7330723b5f27b86d1b03bed88f8a8ffa Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Thu, 9 Apr 2020 17:26:13 +0100 +Subject: [PATCH] dt: Update all mainline bcm283x dt files for i2c0 + pinctrl mux + +BSC0 (aka i2c0) can me muxed via pinctrl to GPIOs 0&1, 28&29, or +44&45. These have different uses based on the platform (40pin header, +and CSI/DSI connectors), so add a pinctrl I2C mux between the +different options. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + arch/arm/boot/dts/bcm2835-rpi-a-plus.dts | 5 +++++ + arch/arm/boot/dts/bcm2835-rpi-a.dts | 7 +++++++ + arch/arm/boot/dts/bcm2835-rpi-b-plus.dts | 5 +++++ + arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts | 7 +++++++ + arch/arm/boot/dts/bcm2835-rpi-b.dts | 7 +++++++ + arch/arm/boot/dts/bcm2835-rpi-cm1-io1.dts | 5 +++++ + arch/arm/boot/dts/bcm2835-rpi-zero-w.dts | 5 +++++ + arch/arm/boot/dts/bcm2835-rpi-zero.dts | 5 +++++ + arch/arm/boot/dts/bcm2835-rpi.dtsi | 10 +++++++--- + arch/arm/boot/dts/bcm2836-rpi-2-b.dts | 5 +++++ + arch/arm/boot/dts/bcm2837-rpi-3-a-plus.dts | 5 +++++ + arch/arm/boot/dts/bcm2837-rpi-3-b-plus.dts | 5 +++++ + arch/arm/boot/dts/bcm2837-rpi-3-b.dts | 5 +++++ + arch/arm/boot/dts/bcm2837-rpi-cm3-io3.dts | 5 +++++ + 14 files changed, 78 insertions(+), 3 deletions(-) + +--- a/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts +@@ -126,3 +126,8 @@ + pinctrl-0 = <&uart0_gpio14>; + status = "okay"; + }; ++ ++/* i2c on camera/display connector is gpio 28&29 */ ++&i2c0mux { ++ pinctrl-1 = <&i2c0_gpio28>; ++}; +--- a/arch/arm/boot/dts/bcm2835-rpi-a.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-a.dts +@@ -121,3 +121,10 @@ + pinctrl-0 = <&uart0_gpio14>; + status = "okay"; + }; ++ ++/* i2c0 on camera/display connector is gpio 0&1. Not exposed on header. ++ * To avoid having to remap everything, map both ports to gpios 0&1 ++ */ ++&i2c0mux { ++ pinctrl-1 = <&i2c0_gpio0>; ++}; +--- a/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts +@@ -128,3 +128,8 @@ + pinctrl-0 = <&uart0_gpio14>; + status = "okay"; + }; ++ ++/* i2c on camera/display connector is gpio 28&29 */ ++&i2c0mux { ++ pinctrl-1 = <&i2c0_gpio28>; ++}; +--- a/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts +@@ -121,3 +121,10 @@ + pinctrl-0 = <&uart0_gpio14>; + status = "okay"; + }; ++ ++/* i2c0 on camera/display connector is gpio 0&1. Not exposed on header. ++ * To avoid having to remap everything, map both ports to gpios 0&1 ++ */ ++&i2c0mux { ++ pinctrl-1 = <&i2c0_gpio0>; ++}; +--- a/arch/arm/boot/dts/bcm2835-rpi-b.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-b.dts +@@ -116,3 +116,10 @@ + pinctrl-0 = <&uart0_gpio14>; + status = "okay"; + }; ++ ++/* camera/display connector use BSC1 on GPIOS 2&3. ++ * To avoid having to remap everything, map both ports to gpios 0&1 ++ */ ++&i2c0mux { ++ pinctrl-1 = <&i2c0_gpio0>; ++}; +--- a/arch/arm/boot/dts/bcm2835-rpi-cm1-io1.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-cm1-io1.dts +@@ -95,3 +95,8 @@ + pinctrl-0 = <&uart0_gpio14>; + status = "okay"; + }; ++ ++/* WHAT TO DO HERE? */ ++&i2c0mux { ++ pinctrl-1 = <&i2c0_gpio28>; ++}; +--- a/arch/arm/boot/dts/bcm2835-rpi-zero-w.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-zero-w.dts +@@ -149,3 +149,8 @@ + pinctrl-0 = <&uart1_gpio14>; + status = "okay"; + }; ++ ++/* i2c on camera/display connector is gpio 28&29 */ ++&i2c0mux { ++ pinctrl-1 = <&i2c0_gpio28>; ++}; +--- a/arch/arm/boot/dts/bcm2835-rpi-zero.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-zero.dts +@@ -117,3 +117,8 @@ + pinctrl-0 = <&uart0_gpio14>; + status = "okay"; + }; ++ ++/* i2c on camera/display connector is gpio 28&29 */ ++&i2c0mux { ++ pinctrl-1 = <&i2c0_gpio28>; ++}; +--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi ++++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi +@@ -46,13 +46,17 @@ + }; + }; + +-&i2c0 { +- pinctrl-names = "default"; +- pinctrl-0 = <&i2c0_gpio0>; ++&i2c0if { + status = "okay"; + clock-frequency = <100000>; + }; + ++&i2c0mux { ++ pinctrl-0 = <&i2c0_gpio0>; ++ /* pinctrl-1 varies based on platform */ ++ status = "okay"; ++}; ++ + &i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_gpio2>; +--- a/arch/arm/boot/dts/bcm2836-rpi-2-b.dts ++++ b/arch/arm/boot/dts/bcm2836-rpi-2-b.dts +@@ -128,3 +128,8 @@ + pinctrl-0 = <&uart0_gpio14>; + status = "okay"; + }; ++ ++/* i2c on camera/display connector is gpio 28&29 */ ++&i2c0mux { ++ pinctrl-1 = <&i2c0_gpio28>; ++}; +--- a/arch/arm/boot/dts/bcm2837-rpi-3-a-plus.dts ++++ b/arch/arm/boot/dts/bcm2837-rpi-3-a-plus.dts +@@ -176,3 +176,8 @@ + pinctrl-0 = <&uart1_gpio14>; + status = "okay"; + }; ++ ++/* i2c on camera/display connector is gpio 44&45 */ ++&i2c0mux { ++ pinctrl-1 = <&i2c0_gpio44>; ++}; +--- a/arch/arm/boot/dts/bcm2837-rpi-3-b-plus.dts ++++ b/arch/arm/boot/dts/bcm2837-rpi-3-b-plus.dts +@@ -179,3 +179,8 @@ + pinctrl-0 = <&uart1_gpio14>; + status = "okay"; + }; ++ ++/* i2c on camera/display connector is gpio 44&45 */ ++&i2c0mux { ++ pinctrl-1 = <&i2c0_gpio44>; ++}; +--- a/arch/arm/boot/dts/bcm2837-rpi-3-b.dts ++++ b/arch/arm/boot/dts/bcm2837-rpi-3-b.dts +@@ -174,3 +174,8 @@ + status = "okay"; + bus-width = <4>; + }; ++ ++/* i2c on camera/display connector is gpio 44&45 */ ++&i2c0mux { ++ pinctrl-1 = <&i2c0_gpio44>; ++}; +--- a/arch/arm/boot/dts/bcm2837-rpi-cm3-io3.dts ++++ b/arch/arm/boot/dts/bcm2837-rpi-cm3-io3.dts +@@ -94,3 +94,8 @@ + pinctrl-0 = <&uart0_gpio14>; + status = "okay"; + }; ++ ++/* WHAT TO DO HERE? */ ++&i2c0mux { ++ pinctrl-1 = <&i2c0_gpio28>; ++}; diff --git a/target/linux/bcm27xx/patches-5.4/950-0670-ARM-dts-Create-bcm2708-rpi-b-rev1.dts.patch b/target/linux/bcm27xx/patches-5.4/950-0670-ARM-dts-Create-bcm2708-rpi-b-rev1.dts.patch new file mode 100644 index 0000000000..42067a7816 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0670-ARM-dts-Create-bcm2708-rpi-b-rev1.dts.patch @@ -0,0 +1,182 @@ +From bd291f0ff613ad270a7c9352f3f27a09c058553f Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Tue, 21 Apr 2020 17:34:27 +0100 +Subject: [PATCH] ARM: dts: Create bcm2708-rpi-b-rev1.dts + +The first revision of the Pi Model B used I2C0 to address the camera +and I2C0 was available for user applications on the 26-pin header. +The second revision switched the roles, kept I2C0 on the 26-pin header +and added I2C1 on a new 8-way header (P5). + +Up to now, downstream DTS has used a single file for both revisions of +the board, with a small amount of patching from the firmware. With the +introduction of an I2C mux to share I2C0 between the camera/display +connectors and the IDC headers, the difference between the two versions +becomes too great to comfortably manage with tweaking, hence this split. + +Upstream DTS files already have bcm2835-rpi-b.dts and +bcm2835-rpi-b-rev2.dts, but for backwards compatibility the new file is +being added as bcm2708-rpi-b-rev1.dts, rather than renaming the old +shared version to bcm2708-rpi-b-rev2.dts. + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/Makefile | 1 + + arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts | 127 +++++++++++++++++++++++ + arch/arm/boot/dts/bcm270x-rpi.dtsi | 4 + + 3 files changed, 132 insertions(+) + create mode 100644 arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts + +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -2,6 +2,7 @@ + + dtb-$(CONFIG_ARCH_BCM2835) += \ + bcm2708-rpi-b.dtb \ ++ bcm2708-rpi-b-rev1.dtb \ + bcm2708-rpi-b-plus.dtb \ + bcm2708-rpi-cm.dtb \ + bcm2708-rpi-zero.dtb \ +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts +@@ -0,0 +1,127 @@ ++/dts-v1/; ++ ++#include "bcm2708.dtsi" ++#include "bcm2708-rpi.dtsi" ++#include "bcm283x-rpi-smsc9512.dtsi" ++#include "bcm283x-rpi-csi1-2lane.dtsi" ++ ++/ { ++ compatible = "raspberrypi,model-b", "brcm,bcm2835"; ++ model = "Raspberry Pi Model B"; ++}; ++ ++&gpio { ++ spi0_pins: spi0_pins { ++ brcm,pins = <9 10 11>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++ ++ spi0_cs_pins: spi0_cs_pins { ++ brcm,pins = <8 7>; ++ brcm,function = <1>; /* output */ ++ }; ++ ++ i2c0_pins: i2c0 { ++ brcm,pins = <0 1>; ++ brcm,function = <4>; ++ }; ++ ++ i2c1_pins: i2c1 { ++ brcm,pins = <2 3>; ++ brcm,function = <4>; ++ }; ++ ++ i2s_pins: i2s { ++ brcm,pins = <28 29 30 31>; ++ brcm,function = <6>; /* alt2 */ ++ }; ++ ++ audio_pins: audio_pins { ++ brcm,pins = <40 45>; ++ brcm,function = <4>; ++ }; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&spi0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi0_pins &spi0_cs_pins>; ++ cs-gpios = <&gpio 8 1>, <&gpio 7 1>; ++ ++ spidev0: spidev@0{ ++ compatible = "spidev"; ++ reg = <0>; /* CE0 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <125000000>; ++ }; ++ ++ spidev1: spidev@1{ ++ compatible = "spidev"; ++ reg = <1>; /* CE1 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <125000000>; ++ }; ++}; ++ ++/delete-node/ &i2c0mux; ++ ++i2c0: &i2c0if { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c0_pins>; ++ clock-frequency = <100000>; ++}; ++ ++i2c_csi_dsi: &i2c1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c1_pins>; ++ clock-frequency = <100000>; ++}; ++ ++/ { ++ aliases { ++ i2c0 = &i2c0; ++ }; ++ ++ __overrides__ { ++ i2c0 = <&i2c0>, "status"; ++ }; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++}; ++ ++&i2s { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s_pins>; ++}; ++ ++&leds { ++ act_led: act { ++ label = "led0"; ++ linux,default-trigger = "mmc0"; ++ gpios = <&gpio 16 1>; ++ }; ++}; ++ ++&hdmi { ++ hpd-gpios = <&gpio 46 GPIO_ACTIVE_HIGH>; ++}; ++ ++&audio { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&audio_pins>; ++}; ++ ++/ { ++ __overrides__ { ++ act_led_gpio = <&act_led>,"gpios:4"; ++ act_led_activelow = <&act_led>,"gpios:8"; ++ act_led_trigger = <&act_led>,"linux,default-trigger"; ++ }; ++}; +--- a/arch/arm/boot/dts/bcm270x-rpi.dtsi ++++ b/arch/arm/boot/dts/bcm270x-rpi.dtsi +@@ -110,6 +110,10 @@ + status = "disabled"; + }; + ++&i2c0mux { ++ status = "disabled"; ++}; ++ + &i2c1 { + status = "disabled"; + }; diff --git a/target/linux/bcm27xx/patches-5.4/950-0671-dts-bcm2711-set-size-cells-2.patch b/target/linux/bcm27xx/patches-5.4/950-0671-dts-bcm2711-set-size-cells-2.patch new file mode 100644 index 0000000000..fa5a2d4f54 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0671-dts-bcm2711-set-size-cells-2.patch @@ -0,0 +1,117 @@ +From 6fe41cac345c8010943defa4ebc2496dd7ca05a1 Mon Sep 17 00:00:00 2001 +From: Hristo Venev <hristo@venev.name> +Date: Wed, 22 Apr 2020 13:40:47 +0300 +Subject: [PATCH] dts: bcm2711: set #size-cells = <2> + +There already is one 4 GiB range, and one more will appear when high +peripheral mode is enabled. + +Signed-off-by: Hristo Venev <hristo@venev.name> +--- + arch/arm/boot/dts/bcm2711-rpi.dtsi | 27 +++++++++++++-------------- + arch/arm/boot/dts/bcm2711.dtsi | 10 +++++----- + 2 files changed, 18 insertions(+), 19 deletions(-) + +--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi ++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi +@@ -65,17 +65,16 @@ + }; + + &scb { +- ranges = <0x0 0x7c000000 0x0 0xfc000000 0x03800000>, +- <0x0 0x40000000 0x0 0xff800000 0x00800000>, +- <0x6 0x00000000 0x6 0x00000000 0x40000000>, +- <0x0 0x00000000 0x0 0x00000000 0xfc000000>; +- dma-ranges = <0x0 0x00000000 0x0 0x00000000 0xfc000000>, +- <0x1 0x00000000 0x1 0x00000000 0x80000000>, +- <0x1 0x80000000 0x1 0x80000000 0x80000000>; ++ ranges = <0x0 0x7c000000 0x0 0xfc000000 0x0 0x03800000>, ++ <0x0 0x40000000 0x0 0xff800000 0x0 0x00800000>, ++ <0x6 0x00000000 0x6 0x00000000 0x0 0x40000000>, ++ <0x0 0x00000000 0x0 0x00000000 0x0 0xfc000000>; ++ dma-ranges = <0x0 0x00000000 0x0 0x00000000 0x0 0xfc000000>, ++ <0x1 0x00000000 0x1 0x00000000 0x1 0x00000000>; + + dma40: dma@7e007b00 { + compatible = "brcm,bcm2711-dma"; +- reg = <0x0 0x7e007b00 0x400>; ++ reg = <0x0 0x7e007b00 0x0 0x400>; + interrupts = + <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>, /* dma4 11 */ + <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>, /* dma4 12 */ +@@ -91,39 +90,39 @@ + + vchiq: mailbox@7e00b840 { + compatible = "brcm,bcm2711-vchiq"; +- reg = <0 0x7e00b840 0x3c>; ++ reg = <0 0x7e00b840 0x0 0x3c>; + interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>; + }; + + xhci: xhci@7e9c0000 { + compatible = "generic-xhci"; + status = "disabled"; +- reg = <0x0 0x7e9c0000 0x100000>; ++ reg = <0x0 0x7e9c0000 0x0 0x100000>; + interrupts = <GIC_SPI 176 IRQ_TYPE_LEVEL_HIGH>; + }; + + hevc-decoder@7eb00000 { + compatible = "raspberrypi,rpivid-hevc-decoder"; +- reg = <0x0 0x7eb00000 0x10000>; ++ reg = <0x0 0x7eb00000 0x0 0x10000>; + status = "okay"; + }; + + rpivid-local-intc@7eb10000 { + compatible = "raspberrypi,rpivid-local-intc"; +- reg = <0x0 0x7eb10000 0x1000>; ++ reg = <0x0 0x7eb10000 0x0 0x1000>; + status = "okay"; + interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>; + }; + + h264-decoder@7eb20000 { + compatible = "raspberrypi,rpivid-h264-decoder"; +- reg = <0x0 0x7eb20000 0x10000>; ++ reg = <0x0 0x7eb20000 0x0 0x10000>; + status = "okay"; + }; + + vp9-decoder@7eb30000 { + compatible = "raspberrypi,rpivid-vp9-decoder"; +- reg = <0x0 0x7eb30000 0x10000>; ++ reg = <0x0 0x7eb30000 0x0 0x10000>; + status = "okay"; + }; + }; +--- a/arch/arm/boot/dts/bcm2711.dtsi ++++ b/arch/arm/boot/dts/bcm2711.dtsi +@@ -445,14 +445,14 @@ + scb { + compatible = "simple-bus"; + #address-cells = <2>; +- #size-cells = <1>; ++ #size-cells = <2>; + +- ranges = <0x0 0x7c000000 0x0 0xfc000000 0x03800000>, +- <0x6 0x00000000 0x6 0x00000000 0x40000000>; ++ ranges = <0x0 0x7c000000 0x0 0xfc000000 0x0 0x03800000>, ++ <0x6 0x00000000 0x6 0x00000000 0x0 0x40000000>; + + pcie0: pcie@7d500000 { + compatible = "brcm,bcm2711-pcie"; +- reg = <0x0 0x7d500000 0x9310>; ++ reg = <0x0 0x7d500000 0x0 0x9310>; + device_type = "pci"; + #address-cells = <3>; + #interrupt-cells = <1>; +@@ -480,7 +480,7 @@ + + genet: ethernet@7d580000 { + compatible = "brcm,bcm2711-genet-v5"; +- reg = <0x0 0x7d580000 0x10000>; ++ reg = <0x0 0x7d580000 0x0 0x10000>; + #address-cells = <0x1>; + #size-cells = <0x1>; + interrupts = <GIC_SPI 157 IRQ_TYPE_LEVEL_HIGH>, diff --git a/target/linux/bcm27xx/patches-5.4/950-0672-dts-bcm2711-add-High-Peripheral-mode-overlay.patch b/target/linux/bcm27xx/patches-5.4/950-0672-dts-bcm2711-add-High-Peripheral-mode-overlay.patch new file mode 100644 index 0000000000..e24f5de000 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0672-dts-bcm2711-add-High-Peripheral-mode-overlay.patch @@ -0,0 +1,140 @@ +From f6d6731d8e896ab029466547dfa66d91a9a6b73a Mon Sep 17 00:00:00 2001 +From: Hristo Venev <hristo@venev.name> +Date: Wed, 22 Apr 2020 16:34:59 +0300 +Subject: [PATCH] dts: bcm2711: add "High Peripheral" mode overlay + +The following addresses change: + + - 0xfc00_0000 -> 0x4_7c00_0000 + - 0xff80_0000 -> 0x4_c000_0000 + +The range 0xfc00_0000-0xffff_ffff becomes available as system RAM on +devices with >= 4 GiB of RAM. Firmware should initialize the memory node +appropriately. + +Signed-off-by: Hristo Venev <hristo@venev.name> +--- + arch/arm/boot/dts/bcm2711-rpi.dtsi | 2 +- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 6 ++ + .../boot/dts/overlays/highperi-overlay.dts | 64 +++++++++++++++++++ + arch/arm/boot/dts/overlays/overlay_map.dts | 4 ++ + 5 files changed, 76 insertions(+), 1 deletion(-) + create mode 100644 arch/arm/boot/dts/overlays/highperi-overlay.dts + +--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi ++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi +@@ -16,7 +16,7 @@ + compatible = "arm,cortex-a72-pmu", "arm,cortex-a15-pmu"; + }; + +- v3dbus { ++ v3dbus: v3dbus { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <2>; +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -64,6 +64,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + hifiberry-dacplushd.dtbo \ + hifiberry-digi.dtbo \ + hifiberry-digi-pro.dtbo \ ++ highperi.dtbo \ + hy28a.dtbo \ + hy28b.dtbo \ + hy28b-2017.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -1019,6 +1019,12 @@ Load: dtoverlay=hifiberry-digi-pro + Params: <None> + + ++Name: highperi ++Info: Enables "High Peripheral" mode ++Load: dtoverlay=highperi ++Params: <None> ++ ++ + Name: hy28a + Info: HY28A - 2.8" TFT LCD Display Module by HAOYU Electronics + Default values match Texy's display shield +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/highperi-overlay.dts +@@ -0,0 +1,64 @@ ++/* ++ * highperi.dts ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2711"; ++ ++ fragment@0 { ++ target = <&soc>; ++ #address-cells = <2>; ++ #size-cells = <1>; ++ ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0x7c000000 0x4 0x7c000000 0x04000000>, ++ <0x40000000 0x4 0xc0000000 0x00800000>; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&scb>; ++ #address-cells = <2>; ++ #size-cells = <1>; ++ ++ __overlay__ { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges = <0x0 0x7c000000 0x4 0x7c000000 0x0 0x04000000>, ++ <0x0 0x40000000 0x4 0xc0000000 0x0 0x00800000>, ++ <0x6 0x00000000 0x6 0x00000000 0x0 0x40000000>, ++ <0x0 0x00000000 0x0 0x00000000 0x1 0x00000000>; ++ dma-ranges = <0x0 0x00000000 0x0 0x00000000 0x2 0x00000000>; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&v3dbus>; ++ #address-cells = <2>; ++ #size-cells = <1>; ++ ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <2>; ++ ranges = <0x7c500000 0x4 0x7c500000 0x0 0x03300000>, ++ <0x40000000 0x4 0xc0000000 0x0 0x00800000>; ++ }; ++ }; ++ ++ fragment@3 { ++ target = <&emmc2bus>; ++ #address-cells = <2>; ++ #size-cells = <1>; ++ ++ __overlay__ { ++ #address-cells = <2>; ++ #size-cells = <1>; ++ ranges = <0x0 0x7e000000 0x4 0x7e000000 0x01800000>; ++ }; ++ }; ++}; +--- a/arch/arm/boot/dts/overlays/overlay_map.dts ++++ b/arch/arm/boot/dts/overlays/overlay_map.dts +@@ -5,6 +5,10 @@ + deprecated = "use i2c-sensor,bmp085"; + }; + ++ highperi { ++ bcm2711; ++ }; ++ + i2c0-bcm2708 { + deprecated = "use i2c0"; + }; diff --git a/target/linux/bcm27xx/patches-5.4/950-0673-Revert-spi-spidev-Fix-CS-polarity-if-GPIO-descriptor.patch b/target/linux/bcm27xx/patches-5.4/950-0673-Revert-spi-spidev-Fix-CS-polarity-if-GPIO-descriptor.patch new file mode 100644 index 0000000000..fa01cd8532 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0673-Revert-spi-spidev-Fix-CS-polarity-if-GPIO-descriptor.patch @@ -0,0 +1,32 @@ +From 4c7f1a1c3d1bfd35b5a4089766ff0882d7b4ee0d Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Mon, 20 Apr 2020 13:41:10 +0100 +Subject: [PATCH] Revert "spi: spidev: Fix CS polarity if GPIO + descriptors are used" + +This reverts commit 83b2a8fe43bda0c11981ad6afa5dd0104d78be28. +--- + drivers/spi/spidev.c | 5 ----- + 1 file changed, 5 deletions(-) + +--- a/drivers/spi/spidev.c ++++ b/drivers/spi/spidev.c +@@ -394,7 +394,6 @@ spidev_ioctl(struct file *filp, unsigned + else + retval = get_user(tmp, (u32 __user *)arg); + if (retval == 0) { +- struct spi_controller *ctlr = spi->controller; + u32 save = spi->mode; + + if (tmp & ~SPI_MODE_MASK) { +@@ -402,10 +401,6 @@ spidev_ioctl(struct file *filp, unsigned + break; + } + +- if (ctlr->use_gpio_descriptors && ctlr->cs_gpiods && +- ctlr->cs_gpiods[spi->chip_select]) +- tmp |= SPI_CS_HIGH; +- + tmp |= spi->mode & ~SPI_MODE_MASK; + spi->mode = (u16)tmp; + retval = spi_setup(spi); diff --git a/target/linux/bcm27xx/patches-5.4/950-0674-spi-use_gpio_descriptor-fixup-moved-to-spi_setup.patch b/target/linux/bcm27xx/patches-5.4/950-0674-spi-use_gpio_descriptor-fixup-moved-to-spi_setup.patch new file mode 100644 index 0000000000..8068ecc9c4 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0674-spi-use_gpio_descriptor-fixup-moved-to-spi_setup.patch @@ -0,0 +1,55 @@ +From b4659f44df3454c6b37ba206a0347af3b8d6a744 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Mon, 20 Apr 2020 13:30:49 +0100 +Subject: [PATCH] spi: use_gpio_descriptor fixup moved to spi_setup + +Commits [1] and [2] including code that forces SPI_CS_HIGH for SPI +controllers that use GPIO descriptors, the SPI_CS_HIGH flag being +there to avoid a double-negation (since SPI CS is usually active-low). +The motivation for pushing the knowledge of the required polarity into +the GPIO descriptor allows the switch to an output to request the +correct inactive level, avoiding a needless glitch. + +The problem with setting the flag early as [1] does is that it appears +in the mode field that is passed to client drivers during their probing, +when they may want to choose SPI_POL, SPI_PHA and (just possibly) +SPI_CS_HIGH. Since SPI_CS_HIGH is the exception, most drivers won't +set it and the anti-negation negation is lost. [2] acknowledges that +problem and patches things up for the case of users of spidev, but +omits regular kernel-mode drivers. + +Downstream commit [3] moves the forcing of SPI_CS_HIGH to spi_setup, +after the driver probing. Since this code is called before any CS +manipulation it is early enough to be effective, but late enough that +clients have already had their chance to change the mode field. + +This is a partial reversion of [1], and is accompanied by a complete +reversion of [2], neither of which is needed any longer. + +[1] f3186dd87669 ("spi: Optionally use GPIO descriptors for CS GPIOs") +[2] 83b2a8fe43bd ("spi: spidev: Fix CS polarity if GPIO descriptors are used") +[3] <varies> ("spi: Force CS_HIGH if GPIO descriptors are used") + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + drivers/spi/spi.c | 9 --------- + 1 file changed, 9 deletions(-) + +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -1775,15 +1775,6 @@ static int of_spi_parse_dt(struct spi_co + } + spi->chip_select = value; + +- /* +- * For descriptors associated with the device, polarity inversion is +- * handled in the gpiolib, so all gpio chip selects are "active high" +- * in the logical sense, the gpiolib will invert the line if need be. +- */ +- if ((ctlr->use_gpio_descriptors) && ctlr->cs_gpiods && +- ctlr->cs_gpiods[spi->chip_select]) +- spi->mode |= SPI_CS_HIGH; +- + /* Device speed */ + rc = of_property_read_u32(nc, "spi-max-frequency", &value); + if (rc) { diff --git a/target/linux/bcm27xx/patches-5.4/950-0675-overlays-rpivid-v4l2-also-needs-size-cells-2.patch b/target/linux/bcm27xx/patches-5.4/950-0675-overlays-rpivid-v4l2-also-needs-size-cells-2.patch new file mode 100644 index 0000000000..4a1757dd44 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0675-overlays-rpivid-v4l2-also-needs-size-cells-2.patch @@ -0,0 +1,30 @@ +From 096fc044170aa40a99dd66b0a8b072ef76327ddb Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Fri, 24 Apr 2020 15:17:06 +0100 +Subject: [PATCH] overlays: rpivid-v4l2 also needs size-cells = 2 + +Fixes: "dts: bcm2711: set #size-cells = <2>" + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/overlays/rpivid-v4l2-overlay.dts | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +--- a/arch/arm/boot/dts/overlays/rpivid-v4l2-overlay.dts ++++ b/arch/arm/boot/dts/overlays/rpivid-v4l2-overlay.dts +@@ -13,11 +13,12 @@ + __overlay__ { + /* needed to avoid dtc warning */ + #address-cells = <2>; +- #size-cells = <1>; ++ #size-cells = <2>; ++ + codec@7eb10000 { + compatible = "raspberrypi,rpivid-vid-decoder"; +- reg = <0x0 0x7eb10000 0x1000>, /* INTC */ +- <0x0 0x7eb00000 0x10000>; /* HEVC */ ++ reg = <0x0 0x7eb10000 0x0 0x1000>, /* INTC */ ++ <0x0 0x7eb00000 0x0 0x10000>; /* HEVC */ + reg-names = "intc", + "hevc"; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0676-media-bcm2835-unicam-Re-fetch-mbus-code-from-subdev-.patch b/target/linux/bcm27xx/patches-5.4/950-0676-media-bcm2835-unicam-Re-fetch-mbus-code-from-subdev-.patch new file mode 100644 index 0000000000..4d43c97e56 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0676-media-bcm2835-unicam-Re-fetch-mbus-code-from-subdev-.patch @@ -0,0 +1,49 @@ +From 53d7c93a14d1e0804a96e3a21f472ba5ff033b14 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Tue, 21 Apr 2020 16:26:03 +0100 +Subject: [PATCH] media: bcm2835-unicam: Re-fetch mbus code from subdev + on a g_fmt call + +The sensor subdevice may change the Bayer order if a H/V flip is +requested after a s_fmt call. Unicam g_fmt must call the subdev get_fmt +in case this has happened and return out the correct format 4cc. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + .../media/platform/bcm2835/bcm2835-unicam.c | 21 ++++++++++++++++++- + 1 file changed, 20 insertions(+), 1 deletion(-) + +--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c ++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c +@@ -967,11 +967,30 @@ static int unicam_enum_fmt_vid_cap(struc + static int unicam_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) + { ++ struct v4l2_mbus_framefmt mbus_fmt = {0}; + struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; ++ const struct unicam_fmt *fmt = NULL; ++ int ret; + +- if (node->pad_id == METADATA_PAD) ++ if (node->pad_id != IMAGE_PAD) + return -EINVAL; + ++ /* ++ * If a flip has occurred in the sensor, the fmt code might have ++ * changed. So we will need to re-fetch the format from the subdevice. ++ */ ++ ret = __subdev_get_format(dev, &mbus_fmt, node->pad_id); ++ if (ret) ++ return -EINVAL; ++ ++ /* Find the V4L2 format from mbus code. We must match a known format. */ ++ fmt = find_format_by_code(mbus_fmt.code); ++ if (!fmt) ++ return -EINVAL; ++ ++ node->fmt = fmt; ++ node->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + *f = node->v_fmt; + + return 0; diff --git a/target/linux/bcm27xx/patches-5.4/950-0677-uapi-bcm2835-isp-Add-bcm2835-isp-uapi-header-file.patch b/target/linux/bcm27xx/patches-5.4/950-0677-uapi-bcm2835-isp-Add-bcm2835-isp-uapi-header-file.patch new file mode 100644 index 0000000000..9f41783b46 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0677-uapi-bcm2835-isp-Add-bcm2835-isp-uapi-header-file.patch @@ -0,0 +1,337 @@ +From b5d50012157f909eff0e8775c1e040b7dfde0705 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Thu, 23 Apr 2020 10:18:15 +0100 +Subject: [PATCH] uapi: bcm2835-isp: Add bcm2835-isp uapi header file + +This file defines the userland interface to the bcm2835-isp driver +that will follow in a separate commit. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + include/uapi/linux/bcm2835-isp.h | 320 +++++++++++++++++++++++++++++++ + 1 file changed, 320 insertions(+) + create mode 100644 include/uapi/linux/bcm2835-isp.h + +--- /dev/null ++++ b/include/uapi/linux/bcm2835-isp.h +@@ -0,0 +1,320 @@ ++/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) */ ++/* ++ * bcm2835-isp.h ++ * ++ * BCM2835 ISP driver - user space header file. ++ * ++ * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd. ++ * ++ * Author: Naushir Patuck (naush@raspberrypi.com) ++ * ++ */ ++ ++#ifndef __BCM2835_ISP_H_ ++#define __BCM2835_ISP_H_ ++ ++#include <linux/v4l2-controls.h> ++ ++#define V4L2_CID_USER_BCM2835_ISP_CC_MATRIX \ ++ (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0001) ++#define V4L2_CID_USER_BCM2835_ISP_LENS_SHADING \ ++ (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0002) ++#define V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL \ ++ (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0003) ++#define V4L2_CID_USER_BCM2835_ISP_GEQ \ ++ (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0004) ++#define V4L2_CID_USER_BCM2835_ISP_GAMMA \ ++ (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0005) ++#define V4L2_CID_USER_BCM2835_ISP_DENOISE \ ++ (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0006) ++#define V4L2_CID_USER_BCM2835_ISP_SHARPEN \ ++ (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0007) ++#define V4L2_CID_USER_BCM2835_ISP_DPC \ ++ (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0008) ++ ++/* ++ * All structs below are directly mapped onto the equivalent structs in ++ * drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h ++ * for convenience. ++ */ ++ ++/** ++ * struct bcm2835_isp_rational - Rational value type. ++ * ++ * @num: Numerator. ++ * @den: Denominator. ++ */ ++struct bcm2835_isp_rational { ++ __s32 num; ++ __s32 den; ++}; ++ ++/** ++ * struct bcm2835_isp_ccm - Colour correction matrix. ++ * ++ * @ccm: 3x3 correction matrix coefficients. ++ * @offsets: 1x3 correction offsets. ++ */ ++struct bcm2835_isp_ccm { ++ struct bcm2835_isp_rational ccm[3][3]; ++ __s32 offsets[3]; ++}; ++ ++/** ++ * struct bcm2835_isp_custom_ccm - Custom CCM applied with the ++ * V4L2_CID_USER_BCM2835_ISP_CC_MATRIX ctrl. ++ * ++ * @enabled: Enable custom CCM. ++ * @ccm: Custom CCM coefficients and offsets. ++ */ ++struct bcm2835_isp_custom_ccm { ++ __u32 enabled; ++ struct bcm2835_isp_ccm ccm; ++}; ++ ++/** ++ * enum bcm2835_isp_gain_format - format of the gains in the lens shading ++ * tables used with the ++ * V4L2_CID_USER_BCM2835_ISP_LENS_SHADING ctrl. ++ * ++ * @GAIN_FORMAT_U0P8_1: Gains are u0.8 format, starting at 1.0 ++ * @GAIN_FORMAT_U1P7_0: Gains are u1.7 format, starting at 0.0 ++ * @GAIN_FORMAT_U1P7_1: Gains are u1.7 format, starting at 1.0 ++ * @GAIN_FORMAT_U2P6_0: Gains are u2.6 format, starting at 0.0 ++ * @GAIN_FORMAT_U2P6_1: Gains are u2.6 format, starting at 1.0 ++ * @GAIN_FORMAT_U3P5_0: Gains are u3.5 format, starting at 0.0 ++ * @GAIN_FORMAT_U3P5_1: Gains are u3.5 format, starting at 1.0 ++ * @GAIN_FORMAT_U4P10: Gains are u4.10 format, starting at 0.0 ++ */ ++enum bcm2835_isp_gain_format { ++ GAIN_FORMAT_U0P8_1 = 0, ++ GAIN_FORMAT_U1P7_0 = 1, ++ GAIN_FORMAT_U1P7_1 = 2, ++ GAIN_FORMAT_U2P6_0 = 3, ++ GAIN_FORMAT_U2P6_1 = 4, ++ GAIN_FORMAT_U3P5_0 = 5, ++ GAIN_FORMAT_U3P5_1 = 6, ++ GAIN_FORMAT_U4P10 = 7, ++}; ++ ++/** ++ * struct bcm2835_isp_lens_shading - Lens shading tables supplied with the ++ * V4L2_CID_USER_BCM2835_ISP_LENS_SHADING ++ * ctrl. ++ * ++ * @enabled: Enable lens shading. ++ * @grid_cell_size: Size of grid cells in samples (16, 32, 64, 128 or 256). ++ * @grid_width: Width of lens shading tables in grid cells. ++ * @grid_stride: Row to row distance (in grid cells) between grid cells ++ * in the same horizontal location. ++ * @grid_height: Height of lens shading tables in grid cells. ++ * @mem_handle_table: Memory handle to the tables. ++ * @ref_transform: Reference transform - unsupported, please pass zero. ++ * @corner_sampled: Whether the gains are sampled at the corner points ++ * of the grid cells or in the cell centres. ++ * @gain_format: Format of the gains (see enum &bcm2835_isp_gain_format). ++ */ ++struct bcm2835_isp_lens_shading { ++ __u32 enabled; ++ __u32 grid_cell_size; ++ __u32 grid_width; ++ __u32 grid_stride; ++ __u32 grid_height; ++ __u32 mem_handle_table; ++ __u32 ref_transform; ++ __u32 corner_sampled; ++ __u32 gain_format; ++}; ++ ++/** ++ * struct bcm2835_isp_black_level - Sensor black level set with the ++ * V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL ctrl. ++ * ++ * @enabled: Enable black level. ++ * @black_level_r: Black level for red channel. ++ * @black_level_g: Black level for green channels. ++ * @black_level_b: Black level for blue channel. ++ */ ++struct bcm2835_isp_black_level { ++ __u32 enabled; ++ __u16 black_level_r; ++ __u16 black_level_g; ++ __u16 black_level_b; ++ __u8 pad_[2]; /* Unused */ ++}; ++ ++/** ++ * struct bcm2835_isp_geq - Green equalisation parameters set with the ++ * V4L2_CID_USER_BCM2835_ISP_GEQ ctrl. ++ * ++ * @enabled: Enable green equalisation. ++ * @offset: Fixed offset of the green equalisation threshold. ++ * @slope: Slope of the green equalisation threshold. ++ */ ++struct bcm2835_isp_geq { ++ __u32 enabled; ++ __u32 offset; ++ struct bcm2835_isp_rational slope; ++}; ++ ++#define BCM2835_NUM_GAMMA_PTS 33 ++ ++/** ++ * struct bcm2835_isp_gamma - Gamma parameters set with the ++ * V4L2_CID_USER_BCM2835_ISP_GAMMA ctrl. ++ * ++ * @enabled: Enable gamma adjustment. ++ * @X: X values of the points defining the gamma curve. ++ * Values should be scaled to 16 bits. ++ * @Y: Y values of the points defining the gamma curve. ++ * Values should be scaled to 16 bits. ++ */ ++struct bcm2835_isp_gamma { ++ __u32 enabled; ++ __u16 x[BCM2835_NUM_GAMMA_PTS]; ++ __u16 y[BCM2835_NUM_GAMMA_PTS]; ++}; ++ ++/** ++ * struct bcm2835_isp_denoise - Denoise parameters set with the ++ * V4L2_CID_USER_BCM2835_ISP_DENOISE ctrl. ++ * ++ * @enabled: Enable denoise. ++ * @constant: Fixed offset of the noise threshold. ++ * @slope: Slope of the noise threshold. ++ * @strength: Denoise strength between 0.0 (off) and 1.0 (maximum). ++ */ ++struct bcm2835_isp_denoise { ++ __u32 enabled; ++ __u32 constant; ++ struct bcm2835_isp_rational slope; ++ struct bcm2835_isp_rational strength; ++}; ++ ++/** ++ * struct bcm2835_isp_sharpen - Sharpen parameters set with the ++ * V4L2_CID_USER_BCM2835_ISP_SHARPEN ctrl. ++ * ++ * @enabled: Enable sharpening. ++ * @threshold: Threshold at which to start sharpening pixels. ++ * @strength: Strength with which pixel sharpening increases. ++ * @limit: Limit to the amount of sharpening applied. ++ */ ++struct bcm2835_isp_sharpen { ++ __u32 enabled; ++ struct bcm2835_isp_rational threshold; ++ struct bcm2835_isp_rational strength; ++ struct bcm2835_isp_rational limit; ++}; ++ ++/** ++ * enum bcm2835_isp_dpc_mode - defective pixel correction (DPC) strength. ++ * ++ * @DPC_MODE_OFF: No DPC. ++ * @DPC_MODE_NORMAL: Normal DPC. ++ * @DPC_MODE_STRONG: Strong DPC. ++ */ ++enum bcm2835_isp_dpc_mode { ++ DPC_MODE_OFF = 0, ++ DPC_MODE_NORMAL = 1, ++ DPC_MODE_STRONG = 2, ++}; ++ ++/** ++ * struct bcm2835_isp_dpc - Defective pixel correction (DPC) parameters set ++ * with the V4L2_CID_USER_BCM2835_ISP_DPC ctrl. ++ * ++ * @enabled: Enable DPC. ++ * @strength: DPC strength (see enum &bcm2835_isp_dpc_mode). ++ */ ++struct bcm2835_isp_dpc { ++ __u32 enabled; ++ __u32 strength; ++}; ++ ++/* ++ * ISP statistics structures. ++ * ++ * The bcm2835_isp_stats structure is generated at the output of the ++ * statistics node. Note that this does not directly map onto the statistics ++ * output of the ISP HW. Instead, the MMAL firmware code maps the HW statistics ++ * to the bcm2835_isp_stats structure. ++ */ ++#define DEFAULT_AWB_REGIONS_X 16 ++#define DEFAULT_AWB_REGIONS_Y 12 ++ ++#define NUM_HISTOGRAMS 2 ++#define NUM_HISTOGRAM_BINS 128 ++#define AWB_REGIONS (DEFAULT_AWB_REGIONS_X * DEFAULT_AWB_REGIONS_Y) ++#define FLOATING_REGIONS 16 ++#define AGC_REGIONS 16 ++#define FOCUS_REGIONS 12 ++ ++/** ++ * struct bcm2835_isp_stats_hist - Histogram statistics ++ * ++ * @r_hist: Red channel histogram. ++ * @g_hist: Combined green channel histogram. ++ * @b_hist: Blue channel histogram. ++ */ ++struct bcm2835_isp_stats_hist { ++ __u32 r_hist[NUM_HISTOGRAM_BINS]; ++ __u32 g_hist[NUM_HISTOGRAM_BINS]; ++ __u32 b_hist[NUM_HISTOGRAM_BINS]; ++}; ++ ++/** ++ * struct bcm2835_isp_stats_region - Region sums. ++ * ++ * @counted: The number of 2x2 bayer tiles accumulated. ++ * @notcounted: The number of 2x2 bayer tiles not accumulated. ++ * @r_sum: Total sum of counted pixels in the red channel for a region. ++ * @g_sum: Total sum of counted pixels in the green channel for a region. ++ * @b_sum: Total sum of counted pixels in the blue channel for a region. ++ */ ++struct bcm2835_isp_stats_region { ++ __u32 counted; ++ __u32 notcounted; ++ __u64 r_sum; ++ __u64 g_sum; ++ __u64 b_sum; ++}; ++ ++/** ++ * struct bcm2835_isp_stats_focus - Focus statistics. ++ * ++ * @contrast_val: Focus measure - accumulated output of the focus filter. ++ * In the first dimension, index [0] counts pixels below a ++ * preset threshold, and index [1] counts pixels above the ++ * threshold. In the second dimension, index [0] uses the ++ * first predefined filter, and index [1] uses the second ++ * predefined filter. ++ * @contrast_val_num: The number of counted pixels in the above accumulation. ++ */ ++struct bcm2835_isp_stats_focus { ++ __u64 contrast_val[2][2]; ++ __u32 contrast_val_num[2][2]; ++}; ++ ++/** ++ * struct bcm2835_isp_stats - ISP statistics. ++ * ++ * @version: Version of the bcm2835_isp_stats structure. ++ * @size: Size of the bcm2835_isp_stats structure. ++ * @hist: Histogram statistics for the entire image. ++ * @awb_stats: Statistics for the regions defined for AWB calculations. ++ * @floating_stats: Statistics for arbitrarily placed (floating) regions. ++ * @agc_stats: Statistics for the regions defined for AGC calculations. ++ * @focus_stats: Focus filter statistics for the focus regions. ++ */ ++struct bcm2835_isp_stats { ++ __u32 version; ++ __u32 size; ++ struct bcm2835_isp_stats_hist hist[NUM_HISTOGRAMS]; ++ struct bcm2835_isp_stats_region awb_stats[AWB_REGIONS]; ++ struct bcm2835_isp_stats_region floating_stats[FLOATING_REGIONS]; ++ struct bcm2835_isp_stats_region agc_stats[AGC_REGIONS]; ++ struct bcm2835_isp_stats_focus focus_stats[FOCUS_REGIONS]; ++}; ++ ++#endif /* __BCM2835_ISP_H_ */ diff --git a/target/linux/bcm27xx/patches-5.4/950-0678-media-uapi-v4l2-core-Add-ISP-statistics-output-V4L2-.patch b/target/linux/bcm27xx/patches-5.4/950-0678-media-uapi-v4l2-core-Add-ISP-statistics-output-V4L2-.patch new file mode 100644 index 0000000000..1af97e8351 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0678-media-uapi-v4l2-core-Add-ISP-statistics-output-V4L2-.patch @@ -0,0 +1,94 @@ +From 8dbbff7b75eee842c00ebaa53fa0a34b3e9bbcaa Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Thu, 23 Apr 2020 10:20:26 +0100 +Subject: [PATCH] media: uapi: v4l2-core: Add ISP statistics output + V4L2 fourcc type + +Add V4L2_META_FMT_BCM2835_ISP_STATS V4L2 format type. + +This new format will be used by the BCM2835 ISP device to return +out ISP statistics for 3A. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + Documentation/media/uapi/v4l/meta-formats.rst | 1 + + .../v4l/pixfmt-meta-bcm2835-isp-stats.rst | 41 +++++++++++++++++++ + drivers/media/v4l2-core/v4l2-ioctl.c | 1 + + include/uapi/linux/videodev2.h | 1 + + 4 files changed, 44 insertions(+) + create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-bcm2835-isp-stats.rst + +--- a/Documentation/media/uapi/v4l/meta-formats.rst ++++ b/Documentation/media/uapi/v4l/meta-formats.rst +@@ -19,6 +19,7 @@ These formats are used for the :ref:`met + .. toctree:: + :maxdepth: 1 + ++ pixfmt-meta-bcm2835-isp-stats + pixfmt-meta-d4xx + pixfmt-meta-intel-ipu3 + pixfmt-meta-sensor-data +--- /dev/null ++++ b/Documentation/media/uapi/v4l/pixfmt-meta-bcm2835-isp-stats.rst +@@ -0,0 +1,41 @@ ++.. 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-meta-fmt-bcm2835-isp-stats: ++ ++***************************************** ++V4L2_META_FMT_BCM2835_ISP_STATS ('BSTA') ++***************************************** ++ ++BCM2835 ISP Statistics ++ ++Description ++=========== ++ ++The BCM2835 ISP hardware calculate image statistics for an input Bayer frame. ++These statistics are obtained from the "bcm2835-isp0-capture3" device node ++using the :c:type:`v4l2_meta_format` interface. They are formatted as described ++by the :c:type:`bcm2835_isp_stats` structure below. ++ ++.. code-block:: c ++ ++ #define DEFAULT_AWB_REGIONS_X 16 ++ #define DEFAULT_AWB_REGIONS_Y 12 ++ ++ #define NUM_HISTOGRAMS 2 ++ #define NUM_HISTOGRAM_BINS 128 ++ #define AWB_REGIONS (DEFAULT_AWB_REGIONS_X * DEFAULT_AWB_REGIONS_Y) ++ #define FLOATING_REGIONS 16 ++ #define AGC_REGIONS 16 ++ #define FOCUS_REGIONS 12 ++ ++.. kernel-doc:: include/uapi/linux/bcm2835-isp.h ++ :functions: bcm2835_isp_stats_hist bcm2835_isp_stats_region ++ bcm2835_isp_stats_focus bcm2835_isp_stats ++ +--- a/drivers/media/v4l2-core/v4l2-ioctl.c ++++ b/drivers/media/v4l2-core/v4l2-ioctl.c +@@ -1333,6 +1333,7 @@ static void v4l_fill_fmtdesc(struct v4l2 + case V4L2_META_FMT_UVC: descr = "UVC Payload Header Metadata"; break; + case V4L2_META_FMT_D4XX: descr = "Intel D4xx UVC Metadata"; break; + case V4L2_META_FMT_SENSOR_DATA: descr = "Sensor Ancillary Metadata"; break; ++ case V4L2_META_FMT_BCM2835_ISP_STATS: descr = "BCM2835 ISP Image Statistics"; break; + + default: + /* Compressed formats */ +--- a/include/uapi/linux/videodev2.h ++++ b/include/uapi/linux/videodev2.h +@@ -770,6 +770,7 @@ struct v4l2_pix_format { + #define V4L2_META_FMT_UVC v4l2_fourcc('U', 'V', 'C', 'H') /* UVC Payload Header metadata */ + #define V4L2_META_FMT_D4XX v4l2_fourcc('D', '4', 'X', 'X') /* D4XX Payload Header metadata */ + #define V4L2_META_FMT_SENSOR_DATA v4l2_fourcc('S', 'E', 'N', 'S') /* Sensor Ancillary metadata */ ++#define V4L2_META_FMT_BCM2835_ISP_STATS v4l2_fourcc('B', 'S', 'T', 'A') /* BCM2835 ISP image statistics output */ + + /* priv field value to indicates that subsequent fields are valid. */ + #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe diff --git a/target/linux/bcm27xx/patches-5.4/950-0679-media-uapi-v4l-ctrls-Add-CID-base-for-the-bcm2835-is.patch b/target/linux/bcm27xx/patches-5.4/950-0679-media-uapi-v4l-ctrls-Add-CID-base-for-the-bcm2835-is.patch new file mode 100644 index 0000000000..96e6e124af --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0679-media-uapi-v4l-ctrls-Add-CID-base-for-the-bcm2835-is.patch @@ -0,0 +1,169 @@ +From 23afbeb993acfd94fa21341f01819ab6505444d2 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Tue, 21 Apr 2020 15:06:19 +0100 +Subject: [PATCH] media: uapi: v4l-ctrls: Add CID base for the + bcm2835-isp driver + +We are reserving controls for the new bcm2835-isp driver. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + .../media/v4l-drivers/bcm2835-isp.rst | 127 ++++++++++++++++++ + Documentation/media/v4l-drivers/index.rst | 1 + + include/uapi/linux/v4l2-controls.h | 4 + + 3 files changed, 132 insertions(+) + create mode 100644 Documentation/media/v4l-drivers/bcm2835-isp.rst + +--- /dev/null ++++ b/Documentation/media/v4l-drivers/bcm2835-isp.rst +@@ -0,0 +1,127 @@ ++.. SPDX-License-Identifier: GPL-2.0 ++ ++BCM2835 ISP Driver ++================== ++ ++Introduction ++------------ ++ ++The BCM2835 Image Sensor Pipeline (ISP) is a fixed function hardware pipeline ++for performing image processing operations. Images are fed to the input ++of the ISP through memory frame buffers. These images may be in various YUV, ++RGB, or Bayer formats. A typical use case would have Bayer images obtained from ++an image sensor by the BCM2835 Unicam peripheral, written to a memory ++frame buffer, and finally fed into the input of the ISP. Two concurrent output ++images may be generated in YUV or RGB format at different resolutions. ++Statistics output is also generated for Bayer input images. ++ ++The bcm2835-isp driver exposes the following media pads as V4L2 device nodes: ++ ++.. tabularcolumns:: |l|l|l|l| ++ ++.. cssclass: longtable ++ ++.. flat-table:: ++ ++ * - *Pad* ++ - *Direction* ++ - *Purpose* ++ - *Formats* ++ ++ * - "bcm2835-isp0-output0" ++ - sink ++ - Accepts Bayer, RGB or YUV format frame buffers as input to the ISP HW ++ pipeline. ++ - :ref:`RAW8 <V4L2-PIX-FMT-SRGGB8>`, ++ :ref:`RAW10P <V4L2-PIX-FMT-SRGGB10P>`, ++ :ref:`RAW12P <V4L2-PIX-FMT-SRGGB12P>`, ++ :ref:`RAW14P <V4L2-PIX-FMT-SRGGB14P>`, ++ :ref:`RAW16 <V4L2-PIX-FMT-SRGGB16>`, ++ :ref:`RGB24/BGR24 <V4L2-PIX-FMT-RGB24>`, ++ :ref:`YUYV <V4L2-PIX-FMT-YUYV>`, ++ :ref:`YVYU <V4L2-PIX-FMT-YVYU>`, ++ :ref:`UYVY <V4L2-PIX-FMT-UYVY>`, ++ :ref:`VYUY <V4L2-PIX-FMT-VYUY>`, ++ :ref:`YUV420/YVU420 <V4L2-PIX-FMT-YUV420>` ++ ++ * - "bcm2835-isp0-capture1" ++ - source ++ - High resolution YUV or RGB processed output from the ISP. ++ - :ref:`RGB565 <V4L2-PIX-FMT-RGB565>`, ++ :ref:`RGB24/BGR24 <V4L2-PIX-FMT-RGB24>`, ++ :ref:`ABGR32 <V4L2-PIX-FMT-ABGR32>`, ++ :ref:`YUYV <V4L2-PIX-FMT-YUYV>`, ++ :ref:`YVYU <V4L2-PIX-FMT-YVYU>`, ++ :ref:`UYVY <V4L2-PIX-FMT-UYVY>`, ++ :ref:`VYUY <V4L2-PIX-FMT-VYUY>`. ++ :ref:`YUV420/YVU420 <V4L2-PIX-FMT-YUV420>`, ++ :ref:`NV12/NV21 <V4L2-PIX-FMT-NV12>`, ++ ++ * - "bcm2835-isp0-capture2" ++ - source ++ - Low resolution YUV processed output from the ISP. The output of ++ this pad cannot have a resolution larger than the "bcm2835-isp0-capture1" pad in any dimension. ++ - :ref:`YUYV <V4L2-PIX-FMT-YUYV>`, ++ :ref:`YVYU <V4L2-PIX-FMT-YVYU>`, ++ :ref:`UYVY <V4L2-PIX-FMT-UYVY>`, ++ :ref:`VYUY <V4L2-PIX-FMT-VYUY>`. ++ :ref:`YUV420/YVU420 <V4L2-PIX-FMT-YUV420>`, ++ :ref:`NV12/NV21 <V4L2-PIX-FMT-NV12>`, ++ ++ * - "bcm2835-isp0-capture1" ++ - source ++ - Image statistics calculated from the input image provided on the ++ "bcm2835-isp0-output0" pad. Statistics are only available for Bayer ++ format input images. ++ - :ref:`v4l2-meta-fmt-bcm2835-isp-stats`. ++ ++Pipeline Configuration ++---------------------- ++ ++The ISP pipeline can be configure through user-space by calling ++:ref:`VIDIOC_S_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` on the “bcm2835-isp0-output0” ++node with the appropriate parameters as shown in the table below. ++ ++.. tabularcolumns:: |p{2cm}|p{5.0cm}| ++ ++.. cssclass: longtable ++ ++.. flat-table:: ++ ++ * - *id* ++ - *Parameter* ++ ++ * - ``V4L2_CID_USER_BCM2835_ISP_CC_MATRIX`` ++ - struct :c:type:`bcm2835_isp_custom_ccm` ++ ++ * - ``V4L2_CID_USER_BCM2835_ISP_LENS_SHADING`` ++ - struct :c:type:`bcm2835_isp_lens_shading` ++ ++ * - ``V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL`` ++ - struct :c:type:`bcm2835_isp_black_level` ++ ++ * - ``V4L2_CID_USER_BCM2835_ISP_GEQ`` ++ - struct :c:type:`bcm2835_isp_geq` ++ ++ * - ``V4L2_CID_USER_BCM2835_ISP_GAMMA`` ++ - struct :c:type:`bcm2835_isp_gamma` ++ ++ * - ``V4L2_CID_USER_BCM2835_ISP_DENOISE`` ++ - struct :c:type:`bcm2835_isp_denoise` ++ ++ * - ``V4L2_CID_USER_BCM2835_ISP_SHARPEN`` ++ - struct :c:type:`bcm2835_isp_sharpen` ++ ++ * - ``V4L2_CID_USER_BCM2835_ISP_DPC`` ++ - struct :c:type:`bcm2835_isp_dpc` ++ ++++++++++++++++++++++++++ ++Configuration Parameters ++++++++++++++++++++++++++ ++ ++.. kernel-doc:: include/uapi/linux/bcm2835-isp.h ++ :functions: bcm2835_isp_rational bcm2835_isp_ccm bcm2835_isp_custom_ccm ++ bcm2835_isp_gain_format bcm2835_isp_lens_shading ++ bcm2835_isp_black_level bcm2835_isp_geq bcm2835_isp_gamma ++ bcm2835_isp_denoise bcm2835_isp_sharpen ++ bcm2835_isp_dpc_mode bcm2835_isp_dpc +--- a/Documentation/media/v4l-drivers/index.rst ++++ b/Documentation/media/v4l-drivers/index.rst +@@ -35,6 +35,7 @@ For more details see the file COPYING in + v4l-with-ir + tuners + cardlist ++ bcm2835-isp + bttv + cafe_ccic + cpia2 +--- a/include/uapi/linux/v4l2-controls.h ++++ b/include/uapi/linux/v4l2-controls.h +@@ -192,6 +192,10 @@ enum v4l2_colorfx { + * We reserve 16 controls for this driver. */ + #define V4L2_CID_USER_IMX_BASE (V4L2_CID_USER_BASE + 0x10b0) + ++/* The base for the bcm2835-isp driver controls. ++ * We reserve 16 controls for this driver. */ ++#define V4L2_CID_USER_BCM2835_ISP_BASE (V4L2_CID_USER_BASE + 0x10c0) ++ + /* MPEG-class control IDs */ + /* The MPEG controls are applicable to all codec controls + * and the 'MPEG' part of the define is historical */ diff --git a/target/linux/bcm27xx/patches-5.4/950-0680-staging-mmal-vchiq-Fix-formatting-errors-in-mmal_par.patch b/target/linux/bcm27xx/patches-5.4/950-0680-staging-mmal-vchiq-Fix-formatting-errors-in-mmal_par.patch new file mode 100644 index 0000000000..f428ce5e9f --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0680-staging-mmal-vchiq-Fix-formatting-errors-in-mmal_par.patch @@ -0,0 +1,116 @@ +From 3319293da05e444e0673c1aba5507e539ccff043 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Thu, 23 Apr 2020 10:12:24 +0100 +Subject: [PATCH] staging: mmal-vchiq: Fix formatting errors in + mmal_parameters.h + +No functional changes in this commit. + +- Remove erroneous whitespace. +- Remove _t postfix label on structs and enums. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + .../bcm2835-camera/bcm2835-camera.c | 2 +- + .../vchiq-mmal/mmal-parameters.h | 46 +++++++++---------- + 2 files changed, 24 insertions(+), 24 deletions(-) + +--- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c ++++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c +@@ -1523,7 +1523,7 @@ static int get_num_cameras(struct vchiq_ + { + int ret; + struct vchiq_mmal_component *cam_info_component; +- struct mmal_parameter_camera_info_t cam_info = {0}; ++ struct mmal_parameter_camera_info cam_info = {0}; + u32 param_size = sizeof(cam_info); + int i; + +--- a/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h ++++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h +@@ -23,21 +23,21 @@ + #define MMAL_PARAMETERS_H + + /** Common parameter ID group, used with many types of component. */ +-#define MMAL_PARAMETER_GROUP_COMMON (0 << 16) ++#define MMAL_PARAMETER_GROUP_COMMON (0 << 16) + /** Camera-specific parameter ID group. */ +-#define MMAL_PARAMETER_GROUP_CAMERA (1 << 16) ++#define MMAL_PARAMETER_GROUP_CAMERA (1 << 16) + /** Video-specific parameter ID group. */ +-#define MMAL_PARAMETER_GROUP_VIDEO (2 << 16) ++#define MMAL_PARAMETER_GROUP_VIDEO (2 << 16) + /** Audio-specific parameter ID group. */ +-#define MMAL_PARAMETER_GROUP_AUDIO (3 << 16) ++#define MMAL_PARAMETER_GROUP_AUDIO (3 << 16) + /** Clock-specific parameter ID group. */ +-#define MMAL_PARAMETER_GROUP_CLOCK (4 << 16) ++#define MMAL_PARAMETER_GROUP_CLOCK (4 << 16) + /** Miracast-specific parameter ID group. */ +-#define MMAL_PARAMETER_GROUP_MIRACAST (5 << 16) ++#define MMAL_PARAMETER_GROUP_MIRACAST (5 << 16) + + /* Common parameters */ + enum mmal_parameter_common_type { +- /**< Never a valid parameter ID */ ++ /**< Never a valid parameter ID */ + MMAL_PARAMETER_UNUSED = MMAL_PARAMETER_GROUP_COMMON, + + /**< MMAL_PARAMETER_ENCODING_T */ +@@ -342,7 +342,7 @@ enum mmal_parameter_imagefx { + MMAL_PARAM_IMAGEFX_CARTOON, + }; + +-enum MMAL_PARAM_FLICKERAVOID_T { ++enum MMAL_PARAM_FLICKERAVOID { + MMAL_PARAM_FLICKERAVOID_OFF, + MMAL_PARAM_FLICKERAVOID_AUTO, + MMAL_PARAM_FLICKERAVOID_50HZ, +@@ -754,15 +754,15 @@ struct mmal_parameter_imagefx_parameters + #define MMAL_PARAMETER_CAMERA_INFO_MAX_FLASHES 2 + #define MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN 16 + +-struct mmal_parameter_camera_info_camera_t { +- u32 port_id; +- u32 max_width; +- u32 max_height; +- u32 lens_present; +- u8 camera_name[MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN]; ++struct mmal_parameter_camera_info_camera { ++ u32 port_id; ++ u32 max_width; ++ u32 max_height; ++ u32 lens_present; ++ u8 camera_name[MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN]; + }; + +-enum mmal_parameter_camera_info_flash_type_t { ++enum mmal_parameter_camera_info_flash_type { + /* Make values explicit to ensure they match values in config ini */ + MMAL_PARAMETER_CAMERA_INFO_FLASH_TYPE_XENON = 0, + MMAL_PARAMETER_CAMERA_INFO_FLASH_TYPE_LED = 1, +@@ -770,16 +770,16 @@ enum mmal_parameter_camera_info_flash_ty + MMAL_PARAMETER_CAMERA_INFO_FLASH_TYPE_MAX = 0x7FFFFFFF + }; + +-struct mmal_parameter_camera_info_flash_t { +- enum mmal_parameter_camera_info_flash_type_t flash_type; ++struct mmal_parameter_camera_info_flash { ++ enum mmal_parameter_camera_info_flash_type flash_type; + }; + +-struct mmal_parameter_camera_info_t { +- u32 num_cameras; +- u32 num_flashes; +- struct mmal_parameter_camera_info_camera_t +- cameras[MMAL_PARAMETER_CAMERA_INFO_MAX_CAMERAS]; +- struct mmal_parameter_camera_info_flash_t ++struct mmal_parameter_camera_info { ++ u32 num_cameras; ++ u32 num_flashes; ++ struct mmal_parameter_camera_info_camera ++ cameras[MMAL_PARAMETER_CAMERA_INFO_MAX_CAMERAS]; ++ struct mmal_parameter_camera_info_flash + flashes[MMAL_PARAMETER_CAMERA_INFO_MAX_FLASHES]; + }; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0681-staging-vc04_services-ISP-Add-a-more-complex-ISP-pro.patch b/target/linux/bcm27xx/patches-5.4/950-0681-staging-vc04_services-ISP-Add-a-more-complex-ISP-pro.patch new file mode 100644 index 0000000000..38015cc98a --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0681-staging-vc04_services-ISP-Add-a-more-complex-ISP-pro.patch @@ -0,0 +1,2255 @@ +From 05a5bc2bfa028885c844ccc2263029b5db9160b4 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Thu, 23 Apr 2020 10:17:37 +0100 +Subject: [PATCH] staging: vc04_services: ISP: Add a more complex ISP + processing component + +Driver for the BCM2835 ISP hardware block. This driver uses the MMAL +component to program the ISP hardware through the VC firmware. + +The ISP component can produce two video stream outputs, and Bayer +image statistics. This can't be encompassed in a simple V4L2 +M2M device, so create a new device that registers 4 video nodes. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + MAINTAINERS | 9 + + drivers/staging/vc04_services/Kconfig | 1 + + drivers/staging/vc04_services/Makefile | 1 + + .../staging/vc04_services/bcm2835-isp/Kconfig | 14 + + .../vc04_services/bcm2835-isp/Makefile | 8 + + .../bcm2835-isp/bcm2835-v4l2-isp.c | 1627 +++++++++++++++++ + .../bcm2835-isp/bcm2835_isp_ctrls.h | 67 + + .../bcm2835-isp/bcm2835_isp_fmts.h | 272 +++ + .../vc04_services/vchiq-mmal/mmal-encodings.h | 4 + + .../vchiq-mmal/mmal-parameters.h | 153 +- + 10 files changed, 2155 insertions(+), 1 deletion(-) + create mode 100644 drivers/staging/vc04_services/bcm2835-isp/Kconfig + create mode 100644 drivers/staging/vc04_services/bcm2835-isp/Makefile + create mode 100644 drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c + create mode 100644 drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_ctrls.h + create mode 100644 drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -3212,6 +3212,15 @@ S: Maintained + F: drivers/media/platform/bcm2835/ + F: Documentation/devicetree/bindings/media/bcm2835-unicam.txt + ++BROADCOM BCM2835 ISP DRIVER ++M: Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com> ++L: linux-media@vger.kernel.org ++S: Maintained ++F: drivers/staging/vc04_services/bcm2835-isp ++F: include/uapi/linux/bcm2835-isp.h ++F: Documentation/media/v4l-drivers/bcm2835-isp.rst ++F: Documentation/media/uapi/v4l/pixfmt-meta-bcm2835-isp-stats.rst ++ + BROADCOM BCM47XX MIPS ARCHITECTURE + M: Hauke Mehrtens <hauke@hauke-m.de> + M: Rafał Miłecki <zajec5@gmail.com> +--- a/drivers/staging/vc04_services/Kconfig ++++ b/drivers/staging/vc04_services/Kconfig +@@ -25,6 +25,7 @@ source "drivers/staging/vc04_services/bc + source "drivers/staging/vc04_services/vchiq-mmal/Kconfig" + source "drivers/staging/vc04_services/vc-sm-cma/Kconfig" + source "drivers/staging/vc04_services/bcm2835-codec/Kconfig" ++source "drivers/staging/vc04_services/bcm2835-isp/Kconfig" + + endif + +--- a/drivers/staging/vc04_services/Makefile ++++ b/drivers/staging/vc04_services/Makefile +@@ -15,6 +15,7 @@ obj-$(CONFIG_VIDEO_BCM2835) += bcm2835- + obj-$(CONFIG_BCM2835_VCHIQ_MMAL) += vchiq-mmal/ + obj-$(CONFIG_BCM_VC_SM_CMA) += vc-sm-cma/ + obj-$(CONFIG_VIDEO_CODEC_BCM2835) += bcm2835-codec/ ++obj-$(CONFIG_VIDEO_ISP_BCM2835) += bcm2835-isp/ + + ccflags-y += -Idrivers/staging/vc04_services -D__VCCOREVER__=0x04000000 + +--- /dev/null ++++ b/drivers/staging/vc04_services/bcm2835-isp/Kconfig +@@ -0,0 +1,14 @@ ++config VIDEO_ISP_BCM2835 ++ tristate "BCM2835 ISP support" ++ depends on MEDIA_SUPPORT ++ depends on VIDEO_V4L2 && (ARCH_BCM2835 || COMPILE_TEST) ++ depends on MEDIA_CONTROLLER ++ select BCM2835_VCHIQ_MMAL ++ select VIDEOBUF2_DMA_CONTIG ++ help ++ This is the V4L2 driver for the Broadcom BCM2835 ISP hardware. ++ This operates over the VCHIQ interface to a service running on ++ VideoCore. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called bcm2835-isp. +--- /dev/null ++++ b/drivers/staging/vc04_services/bcm2835-isp/Makefile +@@ -0,0 +1,8 @@ ++# SPDX-License-Identifier: GPL-2.0 ++bcm2835-isp-objs := bcm2835-v4l2-isp.o ++ ++obj-$(CONFIG_VIDEO_ISP_BCM2835) += bcm2835-isp.o ++ ++ccflags-y += \ ++ -I$(srctree)/drivers/staging/vc04_services \ ++ -D__VCCOREVER__=0x04000000 +--- /dev/null ++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c +@@ -0,0 +1,1627 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Broadcom BCM2835 ISP driver ++ * ++ * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd. ++ * ++ * Author: Naushir Patuck (naush@raspberrypi.com) ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/platform_device.h> ++ ++#include <media/v4l2-ctrls.h> ++#include <media/v4l2-device.h> ++#include <media/v4l2-event.h> ++#include <media/v4l2-ioctl.h> ++#include <media/videobuf2-dma-contig.h> ++ ++#include "vchiq-mmal/mmal-msg.h" ++#include "vchiq-mmal/mmal-parameters.h" ++#include "vchiq-mmal/mmal-vchiq.h" ++ ++#include "bcm2835_isp_ctrls.h" ++#include "bcm2835_isp_fmts.h" ++ ++static unsigned int debug; ++module_param(debug, uint, 0644); ++MODULE_PARM_DESC(debug, "activates debug info"); ++ ++static unsigned int video_nr = 13; ++module_param(video_nr, uint, 0644); ++MODULE_PARM_DESC(video_nr, "base video device number"); ++ ++#define BCM2835_ISP_NAME "bcm2835-isp" ++#define BCM2835_ISP_ENTITY_NAME_LEN 32 ++ ++#define BCM2835_ISP_NUM_OUTPUTS 1 ++#define BCM2835_ISP_NUM_CAPTURES 2 ++#define BCM2835_ISP_NUM_METADATA 1 ++ ++#define BCM2835_ISP_NUM_NODES \ ++ (BCM2835_ISP_NUM_OUTPUTS + BCM2835_ISP_NUM_CAPTURES + \ ++ BCM2835_ISP_NUM_METADATA) ++ ++/* Default frame dimension of 1280 pixels. */ ++#define DEFAULT_DIM 1280U ++/* ++ * Maximum frame dimension of 16384 pixels. Even though the ISP runs in tiles, ++ * have a sensible limit so that we do not create an excessive number of tiles ++ * to process. ++ */ ++#define MAX_DIM 16384U ++/* ++ * Minimum frame dimension of 64 pixels. Anything lower, and the tiling ++ * algorihtm may not be able to cope when applying filter context. ++ */ ++#define MIN_DIM 64U ++ ++/* Per-queue, driver-specific private data */ ++struct bcm2835_isp_q_data { ++ /* ++ * These parameters should be treated as gospel, with everything else ++ * being determined from them. ++ */ ++ unsigned int bytesperline; ++ unsigned int width; ++ unsigned int height; ++ unsigned int sizeimage; ++ struct bcm2835_isp_fmt *fmt; ++}; ++ ++/* ++ * Structure to describe a single node /dev/video<N> which represents a single ++ * input or output queue to the ISP device. ++ */ ++struct bcm2835_isp_node { ++ int vfl_dir; ++ unsigned int id; ++ const char *name; ++ struct video_device vfd; ++ struct media_pad pad; ++ struct media_intf_devnode *intf_devnode; ++ struct media_link *intf_link; ++ struct mutex lock; /* top level device node lock */ ++ struct mutex queue_lock; ++ ++ struct vb2_queue queue; ++ unsigned int sequence; ++ ++ /* The list of formats supported on the node. */ ++ struct bcm2835_isp_fmt_list supported_fmts; ++ ++ struct bcm2835_isp_q_data q_data; ++ ++ /* Parent device structure */ ++ struct bcm2835_isp_dev *dev; ++ ++ bool registered; ++ bool media_node_registered; ++ bool queue_init; ++}; ++ ++/* ++ * Structure representing the entire ISP device, comprising several input and ++ * output nodes /dev/video<N>. ++ */ ++struct bcm2835_isp_dev { ++ struct v4l2_device v4l2_dev; ++ struct device *dev; ++ struct v4l2_ctrl_handler ctrl_handler; ++ struct media_device mdev; ++ struct media_entity entity; ++ bool media_device_registered; ++ bool media_entity_registered; ++ struct vchiq_mmal_instance *mmal_instance; ++ struct vchiq_mmal_component *component; ++ struct completion frame_cmplt; ++ ++ struct bcm2835_isp_node node[BCM2835_ISP_NUM_NODES]; ++ struct media_pad pad[BCM2835_ISP_NUM_NODES]; ++ atomic_t num_streaming; ++ ++ /* Image pipeline controls. */ ++ int r_gain; ++ int b_gain; ++}; ++ ++struct bcm2835_isp_buffer { ++ struct vb2_v4l2_buffer vb; ++ struct mmal_buffer mmal; ++}; ++ ++static ++inline struct bcm2835_isp_dev *node_get_dev(struct bcm2835_isp_node *node) ++{ ++ return node->dev; ++} ++ ++static inline bool node_is_output(struct bcm2835_isp_node *node) ++{ ++ return node->queue.type == V4L2_BUF_TYPE_VIDEO_OUTPUT; ++} ++ ++static inline bool node_is_capture(struct bcm2835_isp_node *node) ++{ ++ return node->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE; ++} ++ ++static inline bool node_is_stats(struct bcm2835_isp_node *node) ++{ ++ return node->queue.type == V4L2_BUF_TYPE_META_CAPTURE; ++} ++ ++static inline enum v4l2_buf_type index_to_queue_type(int index) ++{ ++ if (index < BCM2835_ISP_NUM_OUTPUTS) ++ return V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ else if (index < BCM2835_ISP_NUM_OUTPUTS + BCM2835_ISP_NUM_CAPTURES) ++ return V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ else ++ return V4L2_BUF_TYPE_META_CAPTURE; ++} ++ ++static struct vchiq_mmal_port *get_port_data(struct bcm2835_isp_node *node) ++{ ++ struct bcm2835_isp_dev *dev = node_get_dev(node); ++ ++ if (!dev->component) ++ return NULL; ++ ++ switch (node->queue.type) { ++ case V4L2_BUF_TYPE_VIDEO_OUTPUT: ++ return &dev->component->input[node->id]; ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ case V4L2_BUF_TYPE_META_CAPTURE: ++ return &dev->component->output[node->id]; ++ default: ++ v4l2_err(&dev->v4l2_dev, "%s: Invalid queue type %u\n", ++ __func__, node->queue.type); ++ break; ++ } ++ return NULL; ++} ++ ++static int set_isp_param(struct bcm2835_isp_node *node, u32 parameter, ++ void *value, u32 value_size) ++{ ++ struct vchiq_mmal_port *port = get_port_data(node); ++ struct bcm2835_isp_dev *dev = node_get_dev(node); ++ ++ return vchiq_mmal_port_parameter_set(dev->mmal_instance, port, ++ parameter, value, value_size); ++} ++ ++static int set_wb_gains(struct bcm2835_isp_node *node) ++{ ++ struct bcm2835_isp_dev *dev = node_get_dev(node); ++ struct mmal_parameter_awbgains gains = { ++ .r_gain = { dev->r_gain, 1000 }, ++ .b_gain = { dev->b_gain, 1000 } ++ }; ++ ++ return set_isp_param(node, MMAL_PARAMETER_CUSTOM_AWB_GAINS, ++ &gains, sizeof(gains)); ++} ++ ++static int set_digital_gain(struct bcm2835_isp_node *node, uint32_t gain) ++{ ++ struct mmal_parameter_rational digital_gain = { ++ .num = gain, ++ .den = 1000 ++ }; ++ ++ return set_isp_param(node, MMAL_PARAMETER_DIGITAL_GAIN, ++ &digital_gain, sizeof(digital_gain)); ++} ++ ++static const struct bcm2835_isp_fmt *get_fmt(u32 mmal_fmt) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(supported_formats); i++) { ++ if (supported_formats[i].mmal_fmt == mmal_fmt) ++ return &supported_formats[i]; ++ } ++ return NULL; ++} ++ ++static struct bcm2835_isp_fmt *find_format(struct v4l2_format *f, ++ struct bcm2835_isp_node *node) ++{ ++ struct bcm2835_isp_fmt_list *fmts = &node->supported_fmts; ++ struct bcm2835_isp_fmt *fmt; ++ unsigned int i; ++ ++ for (i = 0; i < fmts->num_entries; i++) { ++ fmt = &fmts->list[i]; ++ if (fmt->fourcc == (node_is_stats(node) ? ++ f->fmt.meta.dataformat : ++ f->fmt.pix.pixelformat)) ++ return fmt; ++ } ++ ++ return NULL; ++} ++ ++/* vb2_to_mmal_buffer() - converts vb2 buffer header to MMAL ++ * ++ * Copies all the required fields from a VB2 buffer to the MMAL buffer header, ++ * ready for sending to the VPU. ++ */ ++static void vb2_to_mmal_buffer(struct mmal_buffer *buf, ++ struct vb2_v4l2_buffer *vb2) ++{ ++ u64 pts; ++ ++ buf->mmal_flags = 0; ++ if (vb2->flags & V4L2_BUF_FLAG_KEYFRAME) ++ buf->mmal_flags |= MMAL_BUFFER_HEADER_FLAG_KEYFRAME; ++ ++ /* Data must be framed correctly as one frame per buffer. */ ++ buf->mmal_flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END; ++ ++ buf->length = vb2->vb2_buf.planes[0].bytesused; ++ /* ++ * Minor ambiguity in the V4L2 spec as to whether passing in a 0 length ++ * buffer, or one with V4L2_BUF_FLAG_LAST set denotes end of stream. ++ * Handle either. ++ */ ++ if (!buf->length || vb2->flags & V4L2_BUF_FLAG_LAST) ++ buf->mmal_flags |= MMAL_BUFFER_HEADER_FLAG_EOS; ++ ++ /* vb2 timestamps in nsecs, mmal in usecs */ ++ pts = vb2->vb2_buf.timestamp; ++ do_div(pts, 1000); ++ buf->pts = pts; ++ buf->dts = MMAL_TIME_UNKNOWN; ++} ++ ++static void mmal_buffer_cb(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, int status, ++ struct mmal_buffer *mmal_buf) ++{ ++ struct bcm2835_isp_buffer *q_buf; ++ struct bcm2835_isp_node *node = port->cb_ctx; ++ struct bcm2835_isp_dev *dev = node_get_dev(node); ++ struct vb2_v4l2_buffer *vb2; ++ ++ q_buf = container_of(mmal_buf, struct bcm2835_isp_buffer, mmal); ++ vb2 = &q_buf->vb; ++ v4l2_dbg(2, debug, &dev->v4l2_dev, ++ "%s: port:%s[%d], status:%d, buf:%p, dmabuf:%p, length:%lu, flags %u, pts %lld\n", ++ __func__, node_is_output(node) ? "input" : "output", node->id, ++ status, mmal_buf, mmal_buf->dma_buf, mmal_buf->length, ++ mmal_buf->mmal_flags, mmal_buf->pts); ++ ++ if (mmal_buf->cmd) ++ v4l2_err(&dev->v4l2_dev, ++ "%s: Unexpected event on output callback - %08x\n", ++ __func__, mmal_buf->cmd); ++ ++ if (status) { ++ /* error in transfer */ ++ if (vb2) { ++ /* there was a buffer with the error so return it */ ++ vb2_buffer_done(&vb2->vb2_buf, VB2_BUF_STATE_ERROR); ++ } ++ return; ++ } ++ ++ /* vb2 timestamps in nsecs, mmal in usecs */ ++ vb2->vb2_buf.timestamp = mmal_buf->pts * 1000; ++ vb2->sequence = node->sequence++; ++ vb2_set_plane_payload(&vb2->vb2_buf, 0, mmal_buf->length); ++ vb2_buffer_done(&vb2->vb2_buf, VB2_BUF_STATE_DONE); ++ ++ if (!port->enabled) ++ complete(&dev->frame_cmplt); ++} ++ ++static void setup_mmal_port_format(struct bcm2835_isp_node *node, ++ struct vchiq_mmal_port *port) ++{ ++ struct bcm2835_isp_q_data *q_data = &node->q_data; ++ ++ port->format.encoding = q_data->fmt->mmal_fmt; ++ /* Raw image format - set width/height */ ++ port->es.video.width = (q_data->bytesperline << 3) / q_data->fmt->depth; ++ port->es.video.height = q_data->height; ++ port->es.video.crop.width = q_data->width; ++ port->es.video.crop.height = q_data->height; ++ port->es.video.crop.x = 0; ++ port->es.video.crop.y = 0; ++}; ++ ++static int setup_mmal_port(struct bcm2835_isp_node *node) ++{ ++ struct vchiq_mmal_port *port = get_port_data(node); ++ struct bcm2835_isp_dev *dev = node_get_dev(node); ++ unsigned int enable = 1; ++ int ret; ++ ++ v4l2_dbg(2, debug, &dev->v4l2_dev, "%s: setup %s[%d]\n", __func__, ++ node->name, node->id); ++ ++ vchiq_mmal_port_parameter_set(dev->mmal_instance, port, ++ MMAL_PARAMETER_ZERO_COPY, &enable, ++ sizeof(enable)); ++ setup_mmal_port_format(node, port); ++ ret = vchiq_mmal_port_set_format(dev->mmal_instance, port); ++ if (ret < 0) { ++ v4l2_dbg(1, debug, &dev->v4l2_dev, ++ "%s: vchiq_mmal_port_set_format failed\n", ++ __func__); ++ return ret; ++ } ++ ++ if (node->q_data.sizeimage < port->minimum_buffer.size) { ++ v4l2_err(&dev->v4l2_dev, ++ "buffer size mismatch sizeimage %u < min size %u\n", ++ node->q_data.sizeimage, port->minimum_buffer.size); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int bcm2835_isp_mmal_buf_cleanup(struct mmal_buffer *mmal_buf) ++{ ++ mmal_vchi_buffer_cleanup(mmal_buf); ++ ++ if (mmal_buf->dma_buf) { ++ dma_buf_put(mmal_buf->dma_buf); ++ mmal_buf->dma_buf = NULL; ++ } ++ ++ return 0; ++} ++ ++static int bcm2835_isp_node_queue_setup(struct vb2_queue *q, ++ unsigned int *nbuffers, ++ unsigned int *nplanes, ++ unsigned int sizes[], ++ struct device *alloc_devs[]) ++{ ++ struct bcm2835_isp_node *node = vb2_get_drv_priv(q); ++ struct vchiq_mmal_port *port; ++ unsigned int size; ++ ++ if (setup_mmal_port(node)) ++ return -EINVAL; ++ ++ size = node->q_data.sizeimage; ++ if (size == 0) { ++ v4l2_info(&node_get_dev(node)->v4l2_dev, ++ "%s: Image size unset in queue_setup for node %s[%d]\n", ++ __func__, node->name, node->id); ++ return -EINVAL; ++ } ++ ++ if (*nplanes) ++ return sizes[0] < size ? -EINVAL : 0; ++ ++ *nplanes = 1; ++ sizes[0] = size; ++ ++ port = get_port_data(node); ++ port->current_buffer.size = size; ++ ++ if (*nbuffers < port->minimum_buffer.num) ++ *nbuffers = port->minimum_buffer.num; ++ ++ port->current_buffer.num = *nbuffers; ++ ++ v4l2_dbg(2, debug, &node_get_dev(node)->v4l2_dev, ++ "%s: Image size %u, nbuffers %u for node %s[%d]\n", ++ __func__, sizes[0], *nbuffers, node->name, node->id); ++ return 0; ++} ++ ++static int bcm2835_isp_buf_init(struct vb2_buffer *vb) ++{ ++ struct bcm2835_isp_node *node = vb2_get_drv_priv(vb->vb2_queue); ++ struct bcm2835_isp_dev *dev = node_get_dev(node); ++ struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb); ++ struct bcm2835_isp_buffer *buf = ++ container_of(vb2, struct bcm2835_isp_buffer, vb); ++ ++ v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: vb %p\n", __func__, vb); ++ ++ buf->mmal.buffer = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); ++ buf->mmal.buffer_size = vb2_plane_size(&buf->vb.vb2_buf, 0); ++ mmal_vchi_buffer_init(dev->mmal_instance, &buf->mmal); ++ return 0; ++} ++ ++static int bcm2835_isp_buf_prepare(struct vb2_buffer *vb) ++{ ++ struct bcm2835_isp_node *node = vb2_get_drv_priv(vb->vb2_queue); ++ struct bcm2835_isp_dev *dev = node_get_dev(node); ++ struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb); ++ struct bcm2835_isp_buffer *buf = ++ container_of(vb2, struct bcm2835_isp_buffer, vb); ++ struct dma_buf *dma_buf; ++ int ret; ++ ++ v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: type: %d ptr %p\n", ++ __func__, vb->vb2_queue->type, vb); ++ ++ if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { ++ if (vb2->field == V4L2_FIELD_ANY) ++ vb2->field = V4L2_FIELD_NONE; ++ if (vb2->field != V4L2_FIELD_NONE) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s field isn't supported\n", __func__); ++ return -EINVAL; ++ } ++ } ++ ++ if (vb2_plane_size(vb, 0) < node->q_data.sizeimage) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s data will not fit into plane (%lu < %lu)\n", ++ __func__, vb2_plane_size(vb, 0), ++ (long)node->q_data.sizeimage); ++ return -EINVAL; ++ } ++ ++ if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) ++ vb2_set_plane_payload(vb, 0, node->q_data.sizeimage); ++ ++ switch (vb->memory) { ++ case VB2_MEMORY_DMABUF: ++ dma_buf = dma_buf_get(vb->planes[0].m.fd); ++ ++ if (dma_buf != buf->mmal.dma_buf) { ++ /* ++ * dmabuf either hasn't already been mapped, or it has ++ * changed. ++ */ ++ if (buf->mmal.dma_buf) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s Buffer changed - why did the core not call cleanup?\n", ++ __func__); ++ bcm2835_isp_mmal_buf_cleanup(&buf->mmal); ++ } ++ ++ buf->mmal.dma_buf = dma_buf; ++ } else { ++ /* ++ * Already have a reference to the buffer, so release it ++ * here. ++ */ ++ dma_buf_put(dma_buf); ++ } ++ ret = 0; ++ break; ++ case VB2_MEMORY_MMAP: ++ /* ++ * We want to do this at init, but vb2_core_expbuf checks that ++ * the index < q->num_buffers, and q->num_buffers only gets ++ * updated once all the buffers are allocated. ++ */ ++ if (!buf->mmal.dma_buf) { ++ ret = vb2_core_expbuf_dmabuf(vb->vb2_queue, ++ vb->vb2_queue->type, ++ vb->index, 0, O_CLOEXEC, ++ &buf->mmal.dma_buf); ++ v4l2_dbg(3, debug, &dev->v4l2_dev, ++ "%s: exporting ptr %p to dmabuf %p\n", ++ __func__, vb, buf->mmal.dma_buf); ++ if (ret) ++ v4l2_err(&dev->v4l2_dev, ++ "%s: Failed to expbuf idx %d, ret %d\n", ++ __func__, vb->index, ret); ++ } else { ++ ret = 0; ++ } ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++static void bcm2835_isp_node_buffer_queue(struct vb2_buffer *buf) ++{ ++ struct bcm2835_isp_node *node = vb2_get_drv_priv(buf->vb2_queue); ++ struct vb2_v4l2_buffer *vbuf = ++ container_of(buf, struct vb2_v4l2_buffer, vb2_buf); ++ struct bcm2835_isp_buffer *buffer = ++ container_of(vbuf, struct bcm2835_isp_buffer, vb); ++ struct bcm2835_isp_dev *dev = node_get_dev(node); ++ ++ v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: node %s[%d], buffer %p\n", ++ __func__, node->name, node->id, buffer); ++ ++ vb2_to_mmal_buffer(&buffer->mmal, &buffer->vb); ++ v4l2_dbg(3, debug, &dev->v4l2_dev, ++ "%s: node %s[%d] - submitting mmal dmabuf %p\n", __func__, ++ node->name, node->id, buffer->mmal.dma_buf); ++ vchiq_mmal_submit_buffer(dev->mmal_instance, get_port_data(node), ++ &buffer->mmal); ++} ++ ++static void bcm2835_isp_buffer_cleanup(struct vb2_buffer *vb) ++{ ++ struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb); ++ struct bcm2835_isp_buffer *buffer = ++ container_of(vb2, struct bcm2835_isp_buffer, vb); ++ ++ bcm2835_isp_mmal_buf_cleanup(&buffer->mmal); ++} ++ ++static int bcm2835_isp_node_start_streaming(struct vb2_queue *q, ++ unsigned int count) ++{ ++ struct bcm2835_isp_node *node = vb2_get_drv_priv(q); ++ struct bcm2835_isp_dev *dev = node_get_dev(node); ++ struct vchiq_mmal_port *port = get_port_data(node); ++ int ret; ++ ++ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: node %s[%d] (count %u)\n", ++ __func__, node->name, node->id, count); ++ ++ ret = vchiq_mmal_component_enable(dev->mmal_instance, dev->component); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, "%s: Failed enabling component, ret %d\n", ++ __func__, ret); ++ return -EIO; ++ } ++ ++ node->sequence = 0; ++ port->cb_ctx = node; ++ ret = vchiq_mmal_port_enable(dev->mmal_instance, port, ++ mmal_buffer_cb); ++ if (!ret) ++ atomic_inc(&dev->num_streaming); ++ else ++ v4l2_err(&dev->v4l2_dev, ++ "%s: Failed enabling port, ret %d\n", __func__, ret); ++ ++ return ret; ++} ++ ++static void bcm2835_isp_node_stop_streaming(struct vb2_queue *q) ++{ ++ struct bcm2835_isp_node *node = vb2_get_drv_priv(q); ++ struct bcm2835_isp_dev *dev = node_get_dev(node); ++ struct vchiq_mmal_port *port = get_port_data(node); ++ unsigned int i; ++ int ret; ++ ++ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: node %s[%d], mmal port %p\n", ++ __func__, node->name, node->id, port); ++ ++ init_completion(&dev->frame_cmplt); ++ ++ /* Disable MMAL port - this will flush buffers back */ ++ ret = vchiq_mmal_port_disable(dev->mmal_instance, port); ++ if (ret) ++ v4l2_err(&dev->v4l2_dev, ++ "%s: Failed disabling %s port, ret %d\n", __func__, ++ node_is_output(node) ? "i/p" : "o/p", ++ ret); ++ ++ while (atomic_read(&port->buffers_with_vpu)) { ++ v4l2_dbg(1, debug, &dev->v4l2_dev, ++ "%s: Waiting for buffers to be returned - %d outstanding\n", ++ __func__, atomic_read(&port->buffers_with_vpu)); ++ ret = wait_for_completion_timeout(&dev->frame_cmplt, HZ); ++ if (ret <= 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: Timeout waiting for buffers to be returned - %d outstanding\n", ++ __func__, ++ atomic_read(&port->buffers_with_vpu)); ++ break; ++ } ++ } ++ ++ /* Release the VCSM handle here to release the associated dmabuf */ ++ for (i = 0; i < q->num_buffers; i++) { ++ struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(q->bufs[i]); ++ struct bcm2835_isp_buffer *buf = ++ container_of(vb2, struct bcm2835_isp_buffer, vb); ++ bcm2835_isp_mmal_buf_cleanup(&buf->mmal); ++ } ++ ++ atomic_dec(&dev->num_streaming); ++ /* If all ports disabled, then disable the component */ ++ if (atomic_read(&dev->num_streaming) == 0) { ++ ret = vchiq_mmal_component_disable(dev->mmal_instance, ++ dev->component); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: Failed disabling component, ret %d\n", ++ __func__, ret); ++ } ++ } ++ ++ /* ++ * Simply wait for any vb2 buffers to finish. We could take steps to ++ * make them complete more quickly if we care, or even return them ++ * ourselves. ++ */ ++ vb2_wait_for_all_buffers(&node->queue); ++} ++ ++static const struct vb2_ops bcm2835_isp_node_queue_ops = { ++ .queue_setup = bcm2835_isp_node_queue_setup, ++ .buf_init = bcm2835_isp_buf_init, ++ .buf_prepare = bcm2835_isp_buf_prepare, ++ .buf_queue = bcm2835_isp_node_buffer_queue, ++ .buf_cleanup = bcm2835_isp_buffer_cleanup, ++ .start_streaming = bcm2835_isp_node_start_streaming, ++ .stop_streaming = bcm2835_isp_node_stop_streaming, ++}; ++ ++static struct bcm2835_isp_fmt *get_default_format(struct bcm2835_isp_node *node) ++{ ++ return &node->supported_fmts.list[0]; ++} ++ ++static inline unsigned int get_bytesperline(int width, ++ struct bcm2835_isp_fmt *fmt) ++{ ++ return ALIGN((width * fmt->depth) >> 3, fmt->bytesperline_align); ++} ++ ++static inline unsigned int get_sizeimage(int bpl, int width, int height, ++ struct bcm2835_isp_fmt *fmt) ++{ ++ return (bpl * height * fmt->size_multiplier_x2) >> 1; ++} ++ ++static int bcm2835_isp_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct bcm2835_isp_dev *dev = ++ container_of(ctrl->handler, struct bcm2835_isp_dev, ctrl_handler); ++ struct bcm2835_isp_node *node = &dev->node[0]; ++ int ret = 0; ++ ++ /* ++ * The ISP firmware driver will ensure these settings are applied on ++ * a frame boundary, so we are safe to write them as they come in. ++ * ++ * Note that the bcm2835_isp_* param structures are identical to the ++ * mmal-parameters.h definitions. This avoids the need for unnecessary ++ * field-by-field copying between structures. ++ */ ++ switch (ctrl->id) { ++ case V4L2_CID_RED_BALANCE: ++ dev->r_gain = ctrl->val; ++ ret = set_wb_gains(node); ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ dev->b_gain = ctrl->val; ++ ret = set_wb_gains(node); ++ break; ++ case V4L2_CID_DIGITAL_GAIN: ++ ret = set_digital_gain(node, ctrl->val); ++ break; ++ case V4L2_CID_USER_BCM2835_ISP_CC_MATRIX: ++ ret = set_isp_param(node, MMAL_PARAMETER_CUSTOM_CCM, ++ ctrl->p_new.p_u8, ++ sizeof(struct bcm2835_isp_custom_ccm)); ++ break; ++ case V4L2_CID_USER_BCM2835_ISP_LENS_SHADING: ++ ret = set_isp_param(node, MMAL_PARAMETER_LENS_SHADING_OVERRIDE, ++ ctrl->p_new.p_u8, ++ sizeof(struct bcm2835_isp_lens_shading)); ++ break; ++ case V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL: ++ ret = set_isp_param(node, MMAL_PARAMETER_BLACK_LEVEL, ++ ctrl->p_new.p_u8, ++ sizeof(struct bcm2835_isp_black_level)); ++ break; ++ case V4L2_CID_USER_BCM2835_ISP_GEQ: ++ ret = set_isp_param(node, MMAL_PARAMETER_GEQ, ++ ctrl->p_new.p_u8, ++ sizeof(struct bcm2835_isp_geq)); ++ break; ++ case V4L2_CID_USER_BCM2835_ISP_GAMMA: ++ ret = set_isp_param(node, MMAL_PARAMETER_GAMMA, ++ ctrl->p_new.p_u8, ++ sizeof(struct bcm2835_isp_gamma)); ++ break; ++ case V4L2_CID_USER_BCM2835_ISP_DENOISE: ++ ret = set_isp_param(node, MMAL_PARAMETER_DENOISE, ++ ctrl->p_new.p_u8, ++ sizeof(struct bcm2835_isp_denoise)); ++ break; ++ case V4L2_CID_USER_BCM2835_ISP_SHARPEN: ++ ret = set_isp_param(node, MMAL_PARAMETER_SHARPEN, ++ ctrl->p_new.p_u8, ++ sizeof(struct bcm2835_isp_sharpen)); ++ break; ++ case V4L2_CID_USER_BCM2835_ISP_DPC: ++ ret = set_isp_param(node, MMAL_PARAMETER_DPC, ++ ctrl->p_new.p_u8, ++ sizeof(struct bcm2835_isp_dpc)); ++ break; ++ default: ++ v4l2_info(&dev->v4l2_dev, "Unrecognised control\n"); ++ ret = -EINVAL; ++ } ++ ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, "%s: Failed setting ctrl \"%s\" (%08x), err %d\n", ++ __func__, ctrl->name, ctrl->id, ret); ++ ret = -EIO; ++ } ++ ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops bcm2835_isp_ctrl_ops = { ++ .s_ctrl = bcm2835_isp_s_ctrl, ++}; ++ ++static const struct v4l2_file_operations bcm2835_isp_fops = { ++ .owner = THIS_MODULE, ++ .open = v4l2_fh_open, ++ .release = vb2_fop_release, ++ .poll = vb2_fop_poll, ++ .unlocked_ioctl = video_ioctl2, ++ .mmap = vb2_fop_mmap ++}; ++ ++static int populate_qdata_fmt(struct v4l2_format *f, ++ struct bcm2835_isp_node *node) ++{ ++ struct bcm2835_isp_dev *dev = node_get_dev(node); ++ struct bcm2835_isp_q_data *q_data = &node->q_data; ++ struct vchiq_mmal_port *port; ++ int ret; ++ ++ if (!node_is_stats(node)) { ++ v4l2_dbg(1, debug, &dev->v4l2_dev, ++ "%s: Setting pix format for type %d, wxh: %ux%u, fmt: %08x, size %u\n", ++ __func__, f->type, f->fmt.pix.width, f->fmt.pix.height, ++ f->fmt.pix.pixelformat, f->fmt.pix.sizeimage); ++ ++ q_data->fmt = find_format(f, node); ++ q_data->width = f->fmt.pix.width; ++ q_data->height = f->fmt.pix.height; ++ q_data->height = f->fmt.pix.height; ++ ++ /* All parameters should have been set correctly by try_fmt */ ++ q_data->bytesperline = f->fmt.pix.bytesperline; ++ q_data->sizeimage = f->fmt.pix.sizeimage; ++ } else { ++ v4l2_dbg(1, debug, &dev->v4l2_dev, ++ "%s: Setting meta format for fmt: %08x, size %u\n", ++ __func__, f->fmt.meta.dataformat, ++ f->fmt.meta.buffersize); ++ ++ q_data->fmt = find_format(f, node); ++ q_data->width = 0; ++ q_data->height = 0; ++ q_data->bytesperline = 0; ++ q_data->sizeimage = f->fmt.meta.buffersize; ++ } ++ ++ v4l2_dbg(1, debug, &dev->v4l2_dev, ++ "%s: Calculated bpl as %u, size %u\n", __func__, ++ q_data->bytesperline, q_data->sizeimage); ++ ++ /* If we have a component then setup the port as well */ ++ port = get_port_data(node); ++ setup_mmal_port_format(node, port); ++ ret = vchiq_mmal_port_set_format(dev->mmal_instance, port); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: Failed vchiq_mmal_port_set_format on port, ret %d\n", ++ __func__, ret); ++ ret = -EINVAL; ++ } ++ ++ if (q_data->sizeimage < port->minimum_buffer.size) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: Current buffer size of %u < min buf size %u - driver mismatch to MMAL\n", ++ __func__, ++ q_data->sizeimage, ++ port->minimum_buffer.size); ++ } ++ ++ v4l2_dbg(1, debug, &dev->v4l2_dev, ++ "%s: Set format for type %d, wxh: %dx%d, fmt: %08x, size %u\n", ++ __func__, f->type, q_data->width, q_data->height, ++ q_data->fmt->fourcc, q_data->sizeimage); ++ ++ return ret; ++} ++ ++static int bcm2835_isp_node_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ strscpy(cap->driver, BCM2835_ISP_NAME, sizeof(cap->driver)); ++ strscpy(cap->card, BCM2835_ISP_NAME, sizeof(cap->card)); ++ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", ++ BCM2835_ISP_NAME); ++ ++ return 0; ++} ++ ++static int bcm2835_isp_node_g_fmt(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bcm2835_isp_node *node = video_drvdata(file); ++ ++ if (f->type != node->queue.type) ++ return -EINVAL; ++ ++ if (node_is_stats(node)) { ++ f->fmt.meta.dataformat = V4L2_META_FMT_BCM2835_ISP_STATS; ++ f->fmt.meta.buffersize = ++ get_port_data(node)->minimum_buffer.size; ++ } else { ++ struct bcm2835_isp_q_data *q_data = &node->q_data; ++ ++ f->fmt.pix.width = q_data->width; ++ f->fmt.pix.height = q_data->height; ++ f->fmt.pix.field = V4L2_FIELD_NONE; ++ f->fmt.pix.pixelformat = q_data->fmt->fourcc; ++ f->fmt.pix.bytesperline = q_data->bytesperline; ++ f->fmt.pix.sizeimage = q_data->sizeimage; ++ f->fmt.pix.colorspace = q_data->fmt->colorspace; ++ } ++ ++ return 0; ++} ++ ++static int bcm2835_isp_node_enum_fmt(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ struct bcm2835_isp_node *node = video_drvdata(file); ++ struct bcm2835_isp_fmt_list *fmts = &node->supported_fmts; ++ ++ if (f->type != node->queue.type) ++ return -EINVAL; ++ ++ if (f->index < fmts->num_entries) { ++ /* Format found */ ++ f->pixelformat = fmts->list[f->index].fourcc; ++ f->flags = fmts->list[f->index].flags; ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static int bcm2835_isp_node_try_fmt(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bcm2835_isp_node *node = video_drvdata(file); ++ struct bcm2835_isp_fmt *fmt; ++ ++ if (f->type != node->queue.type) ++ return -EINVAL; ++ ++ fmt = find_format(f, node); ++ if (!fmt) ++ fmt = get_default_format(node); ++ ++ if (!node_is_stats(node)) { ++ f->fmt.pix.width = max(min(f->fmt.pix.width, MAX_DIM), ++ MIN_DIM); ++ f->fmt.pix.height = max(min(f->fmt.pix.height, MAX_DIM), ++ MIN_DIM); ++ ++ f->fmt.pix.pixelformat = fmt->fourcc; ++ f->fmt.pix.colorspace = fmt->colorspace; ++ f->fmt.pix.bytesperline = get_bytesperline(f->fmt.pix.width, ++ fmt); ++ f->fmt.pix.field = V4L2_FIELD_NONE; ++ f->fmt.pix.sizeimage = ++ get_sizeimage(f->fmt.pix.bytesperline, f->fmt.pix.width, ++ f->fmt.pix.height, fmt); ++ } else { ++ f->fmt.meta.dataformat = fmt->fourcc; ++ f->fmt.meta.buffersize = ++ get_port_data(node)->minimum_buffer.size; ++ } ++ ++ return 0; ++} ++ ++static int bcm2835_isp_node_s_fmt(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bcm2835_isp_node *node = video_drvdata(file); ++ int ret; ++ ++ if (f->type != node->queue.type) ++ return -EINVAL; ++ ++ ret = bcm2835_isp_node_try_fmt(file, priv, f); ++ if (ret) ++ return ret; ++ ++ v4l2_dbg(1, debug, &node_get_dev(node)->v4l2_dev, ++ "%s: Set format for node %s[%d]\n", ++ __func__, node->name, node->id); ++ ++ return populate_qdata_fmt(f, node); ++} ++ ++static int bcm2835_isp_node_s_selection(struct file *file, void *fh, ++ struct v4l2_selection *s) ++{ ++ struct mmal_parameter_crop crop; ++ struct bcm2835_isp_node *node = video_drvdata(file); ++ struct bcm2835_isp_dev *dev = node_get_dev(node); ++ struct vchiq_mmal_port *port = get_port_data(node); ++ ++ /* This return value is required fro V4L2 compliance. */ ++ if (node_is_stats(node)) ++ return -ENOTTY; ++ ++ if (!s->r.width || !s->r.height) ++ return -EINVAL; ++ ++ /* Adjust the crop window if goes outside the frame dimensions. */ ++ s->r.left = min((unsigned int)max(s->r.left, 0), ++ node->q_data.width - MIN_DIM); ++ s->r.top = min((unsigned int)max(s->r.top, 0), ++ node->q_data.height - MIN_DIM); ++ s->r.width = max(min(s->r.width, node->q_data.width - s->r.left), ++ MIN_DIM); ++ s->r.height = max(min(s->r.height, node->q_data.height - s->r.top), ++ MIN_DIM); ++ ++ crop.rect.x = s->r.left; ++ crop.rect.y = s->r.top; ++ crop.rect.width = s->r.width; ++ crop.rect.height = s->r.height; ++ ++ return vchiq_mmal_port_parameter_set(dev->mmal_instance, port, ++ MMAL_PARAMETER_CROP, ++ &crop, sizeof(crop)); ++} ++ ++static int bcm2835_isp_node_g_selection(struct file *file, void *fh, ++ struct v4l2_selection *s) ++{ ++ struct bcm2835_isp_node *node = video_drvdata(file); ++ struct bcm2835_isp_dev *dev = node_get_dev(node); ++ struct vchiq_mmal_port *port = get_port_data(node); ++ struct mmal_parameter_crop crop; ++ u32 crop_size = sizeof(crop); ++ int ret; ++ ++ /* This return value is required for V4L2 compliance. */ ++ if (node_is_stats(node)) ++ return -ENOTTY; ++ ++ /* We can only return out an input crop. */ ++ if (s->target != V4L2_SEL_TGT_CROP) ++ return -EINVAL; ++ ++ ret = vchiq_mmal_port_parameter_get(dev->mmal_instance, port, ++ MMAL_PARAMETER_CROP, ++ &crop, &crop_size); ++ if (!ret) ++ return -EINVAL; ++ ++ s->r.left = crop.rect.x; ++ s->r.top = crop.rect.y; ++ s->r.width = crop.rect.width; ++ s->r.height = crop.rect.height; ++ ++ return 0; ++} ++ ++static int bcm3285_isp_subscribe_event(struct v4l2_fh *fh, ++ const struct v4l2_event_subscription *s) ++{ ++ switch (s->type) { ++ /* Cannot change source parameters dynamically at runtime. */ ++ case V4L2_EVENT_SOURCE_CHANGE: ++ return -EINVAL; ++ case V4L2_EVENT_CTRL: ++ return v4l2_ctrl_subscribe_event(fh, s); ++ default: ++ return v4l2_event_subscribe(fh, s, 4, NULL); ++ } ++} ++ ++static const struct v4l2_ioctl_ops bcm2835_isp_node_ioctl_ops = { ++ .vidioc_querycap = bcm2835_isp_node_querycap, ++ .vidioc_g_fmt_vid_cap = bcm2835_isp_node_g_fmt, ++ .vidioc_g_fmt_vid_out = bcm2835_isp_node_g_fmt, ++ .vidioc_g_fmt_meta_cap = bcm2835_isp_node_g_fmt, ++ .vidioc_s_fmt_vid_cap = bcm2835_isp_node_s_fmt, ++ .vidioc_s_fmt_vid_out = bcm2835_isp_node_s_fmt, ++ .vidioc_s_fmt_meta_cap = bcm2835_isp_node_s_fmt, ++ .vidioc_try_fmt_vid_cap = bcm2835_isp_node_try_fmt, ++ .vidioc_try_fmt_vid_out = bcm2835_isp_node_try_fmt, ++ .vidioc_try_fmt_meta_cap = bcm2835_isp_node_try_fmt, ++ .vidioc_s_selection = bcm2835_isp_node_s_selection, ++ .vidioc_g_selection = bcm2835_isp_node_g_selection, ++ ++ .vidioc_enum_fmt_vid_cap = bcm2835_isp_node_enum_fmt, ++ .vidioc_enum_fmt_vid_out = bcm2835_isp_node_enum_fmt, ++ .vidioc_enum_fmt_meta_cap = bcm2835_isp_node_enum_fmt, ++ ++ .vidioc_reqbufs = vb2_ioctl_reqbufs, ++ .vidioc_querybuf = vb2_ioctl_querybuf, ++ .vidioc_qbuf = vb2_ioctl_qbuf, ++ .vidioc_dqbuf = vb2_ioctl_dqbuf, ++ .vidioc_expbuf = vb2_ioctl_expbuf, ++ .vidioc_create_bufs = vb2_ioctl_create_bufs, ++ .vidioc_prepare_buf = vb2_ioctl_prepare_buf, ++ ++ .vidioc_streamon = vb2_ioctl_streamon, ++ .vidioc_streamoff = vb2_ioctl_streamoff, ++ ++ .vidioc_subscribe_event = bcm3285_isp_subscribe_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++}; ++ ++/* ++ * Size of the array to provide to the VPU when asking for the list of supported ++ * formats. ++ * ++ * The ISP component currently advertises 33 input formats, so add a small ++ * overhead on that. ++ */ ++#define MAX_SUPPORTED_ENCODINGS 40 ++ ++/* Populate node->supported_fmts with the formats supported by those ports. */ ++static int bcm2835_isp_get_supported_fmts(struct bcm2835_isp_node *node) ++{ ++ struct bcm2835_isp_dev *dev = node_get_dev(node); ++ struct bcm2835_isp_fmt *list; ++ unsigned int i, j, num_encodings; ++ u32 fourccs[MAX_SUPPORTED_ENCODINGS]; ++ u32 param_size = sizeof(fourccs); ++ int ret; ++ ++ ret = vchiq_mmal_port_parameter_get(dev->mmal_instance, ++ get_port_data(node), ++ MMAL_PARAMETER_SUPPORTED_ENCODINGS, ++ &fourccs, ¶m_size); ++ ++ if (ret) { ++ if (ret == MMAL_MSG_STATUS_ENOSPC) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: port has more encoding than we provided space for. Some are dropped.\n", ++ __func__); ++ num_encodings = MAX_SUPPORTED_ENCODINGS; ++ } else { ++ v4l2_err(&dev->v4l2_dev, "%s: get_param ret %u.\n", ++ __func__, ret); ++ return -EINVAL; ++ } ++ } else { ++ num_encodings = param_size / sizeof(u32); ++ } ++ ++ /* ++ * Assume at this stage that all encodings will be supported in V4L2. ++ * Any that aren't supported will waste a very small amount of memory. ++ */ ++ list = devm_kzalloc(dev->dev, ++ sizeof(struct bcm2835_isp_fmt) * num_encodings, ++ GFP_KERNEL); ++ if (!list) ++ return -ENOMEM; ++ node->supported_fmts.list = list; ++ ++ for (i = 0, j = 0; i < num_encodings; i++) { ++ const struct bcm2835_isp_fmt *fmt = get_fmt(fourccs[i]); ++ ++ if (fmt) { ++ list[j] = *fmt; ++ j++; ++ } ++ } ++ node->supported_fmts.num_entries = j; ++ ++ param_size = sizeof(fourccs); ++ ret = vchiq_mmal_port_parameter_get(dev->mmal_instance, ++ get_port_data(node), ++ MMAL_PARAMETER_SUPPORTED_ENCODINGS, ++ &fourccs, ¶m_size); ++ ++ if (ret) { ++ if (ret == MMAL_MSG_STATUS_ENOSPC) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: port has more encoding than we provided space for. Some are dropped.\n", ++ __func__); ++ num_encodings = MAX_SUPPORTED_ENCODINGS; ++ } else { ++ return -EINVAL; ++ } ++ } else { ++ num_encodings = param_size / sizeof(u32); ++ } ++ /* Assume at this stage that all encodings will be supported in V4L2. */ ++ list = devm_kzalloc(dev->dev, ++ sizeof(struct bcm2835_isp_fmt) * num_encodings, ++ GFP_KERNEL); ++ if (!list) ++ return -ENOMEM; ++ node->supported_fmts.list = list; ++ ++ for (i = 0, j = 0; i < num_encodings; i++) { ++ const struct bcm2835_isp_fmt *fmt = get_fmt(fourccs[i]); ++ ++ if (fmt) { ++ list[j] = *fmt; ++ j++; ++ } ++ } ++ node->supported_fmts.num_entries = j; ++ return 0; ++} ++ ++/* ++ * Register a device node /dev/video<N> to go along with one of the ISP's input ++ * or output nodes. ++ */ ++static int register_node(struct bcm2835_isp_dev *dev, ++ struct bcm2835_isp_node *node, ++ int index) ++{ ++ struct video_device *vfd; ++ struct vb2_queue *queue; ++ int ret; ++ ++ mutex_init(&node->lock); ++ ++ node->dev = dev; ++ vfd = &node->vfd; ++ queue = &node->queue; ++ queue->type = index_to_queue_type(index); ++ /* ++ * Setup the node type-specific params. ++ * ++ * Only the OUTPUT node can set controls and crop windows. However, ++ * we must allow the s/g_selection ioctl on the stats node as v4l2 ++ * compliance expects it to return a -ENOTTY, and the framework ++ * does not handle it if the ioctl is disabled. ++ */ ++ switch (queue->type) { ++ case V4L2_BUF_TYPE_VIDEO_OUTPUT: ++ vfd->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; ++ node->id = index; ++ node->vfl_dir = VFL_DIR_TX; ++ node->name = "output"; ++ break; ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; ++ /* First Capture node starts at id 0, etc. */ ++ node->id = index - BCM2835_ISP_NUM_OUTPUTS; ++ node->vfl_dir = VFL_DIR_RX; ++ node->name = "capture"; ++ v4l2_disable_ioctl(&node->vfd, VIDIOC_S_CTRL); ++ v4l2_disable_ioctl(&node->vfd, VIDIOC_S_SELECTION); ++ v4l2_disable_ioctl(&node->vfd, VIDIOC_G_SELECTION); ++ break; ++ case V4L2_BUF_TYPE_META_CAPTURE: ++ vfd->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING; ++ node->id = index - BCM2835_ISP_NUM_OUTPUTS; ++ node->vfl_dir = VFL_DIR_RX; ++ node->name = "stats"; ++ v4l2_disable_ioctl(&node->vfd, VIDIOC_S_CTRL); ++ break; ++ } ++ ++ /* We use the selection API instead of the old crop API. */ ++ v4l2_disable_ioctl(vfd, VIDIOC_CROPCAP); ++ v4l2_disable_ioctl(vfd, VIDIOC_G_CROP); ++ v4l2_disable_ioctl(vfd, VIDIOC_S_CROP); ++ ++ ret = bcm2835_isp_get_supported_fmts(node); ++ if (ret) ++ return ret; ++ ++ /* Initialise the the video node. */ ++ vfd->vfl_type = VFL_TYPE_GRABBER; ++ vfd->fops = &bcm2835_isp_fops, ++ vfd->ioctl_ops = &bcm2835_isp_node_ioctl_ops, ++ vfd->minor = -1, ++ vfd->release = video_device_release_empty, ++ vfd->queue = &node->queue; ++ vfd->lock = &node->lock; ++ vfd->v4l2_dev = &dev->v4l2_dev; ++ vfd->vfl_dir = node->vfl_dir; ++ ++ node->q_data.fmt = get_default_format(node); ++ node->q_data.width = DEFAULT_DIM; ++ node->q_data.height = DEFAULT_DIM; ++ node->q_data.bytesperline = ++ get_bytesperline(DEFAULT_DIM, node->q_data.fmt); ++ node->q_data.sizeimage = node_is_stats(node) ? ++ get_port_data(node)->recommended_buffer.size : ++ get_sizeimage(node->q_data.bytesperline, ++ node->q_data.width, ++ node->q_data.height, ++ node->q_data.fmt); ++ ++ queue->io_modes = VB2_MMAP | VB2_DMABUF; ++ queue->drv_priv = node; ++ queue->ops = &bcm2835_isp_node_queue_ops; ++ queue->mem_ops = &vb2_dma_contig_memops; ++ queue->buf_struct_size = sizeof(struct bcm2835_isp_buffer); ++ queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; ++ queue->dev = dev->dev; ++ queue->lock = &node->queue_lock; ++ ++ ret = vb2_queue_init(queue); ++ if (ret < 0) { ++ v4l2_info(&dev->v4l2_dev, "vb2_queue_init failed\n"); ++ return ret; ++ } ++ node->queue_init = true; ++ ++ /* Define the device names */ ++ snprintf(vfd->name, sizeof(node->vfd.name), "%s-%s%d", BCM2835_ISP_NAME, ++ node->name, node->id); ++ ++ ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr + index); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed to register video %s[%d] device node\n", ++ node->name, node->id); ++ return ret; ++ } ++ ++ node->registered = true; ++ video_set_drvdata(vfd, node); ++ ++ /* Set some controls and defaults, but only on the VIDEO_OUTPUT node. */ ++ if (node_is_output(node)) { ++ unsigned int i; ++ ++ /* Use this ctrl template to assign all out ISP custom ctrls. */ ++ struct v4l2_ctrl_config ctrl_template = { ++ .ops = &bcm2835_isp_ctrl_ops, ++ .type = V4L2_CTRL_TYPE_U8, ++ .def = 0, ++ .min = 0x00, ++ .max = 0xff, ++ .step = 1, ++ }; ++ ++ v4l2_ctrl_handler_init(&dev->ctrl_handler, 4); ++ ++ dev->r_gain = 1000; ++ dev->b_gain = 1000; ++ ++ v4l2_ctrl_new_std(&dev->ctrl_handler, &bcm2835_isp_ctrl_ops, ++ V4L2_CID_RED_BALANCE, 1, 0xffff, 1, ++ dev->r_gain); ++ ++ v4l2_ctrl_new_std(&dev->ctrl_handler, &bcm2835_isp_ctrl_ops, ++ V4L2_CID_BLUE_BALANCE, 1, 0xffff, 1, ++ dev->b_gain); ++ ++ v4l2_ctrl_new_std(&dev->ctrl_handler, &bcm2835_isp_ctrl_ops, ++ V4L2_CID_DIGITAL_GAIN, 1, 0xffff, 1, 1000); ++ ++ for (i = 0; i < ARRAY_SIZE(custom_ctrls); i++) { ++ ctrl_template.name = custom_ctrls[i].name; ++ ctrl_template.id = custom_ctrls[i].id; ++ ctrl_template.dims[0] = custom_ctrls[i].size; ++ ctrl_template.flags = custom_ctrls[i].flags; ++ v4l2_ctrl_new_custom(&dev->ctrl_handler, ++ &ctrl_template, NULL); ++ } ++ ++ node->vfd.ctrl_handler = &dev->ctrl_handler; ++ } ++ ++ v4l2_info(&dev->v4l2_dev, ++ "Device node %s[%d] registered as /dev/video%d\n", ++ node->name, node->id, vfd->num); ++ ++ return 0; ++} ++ ++/* Unregister one of the /dev/video<N> nodes associated with the ISP. */ ++static void unregister_node(struct bcm2835_isp_node *node) ++{ ++ struct bcm2835_isp_dev *dev = node_get_dev(node); ++ ++ v4l2_info(&dev->v4l2_dev, ++ "Unregistering node %s[%d] device node /dev/video%d\n", ++ node->name, node->id, node->vfd.num); ++ ++ if (node->queue_init) ++ vb2_queue_release(&node->queue); ++ ++ if (node->registered) { ++ video_unregister_device(&node->vfd); ++ if (node_is_output(node)) ++ v4l2_ctrl_handler_free(&dev->ctrl_handler); ++ } ++ ++ /* ++ * node->supported_fmts.list is free'd automatically ++ * as a managed resource. ++ */ ++ node->supported_fmts.list = NULL; ++ node->supported_fmts.num_entries = 0; ++ node->vfd.ctrl_handler = NULL; ++ node->registered = false; ++ node->queue_init = false; ++} ++ ++static void media_controller_unregister(struct bcm2835_isp_dev *dev) ++{ ++ unsigned int i; ++ ++ v4l2_info(&dev->v4l2_dev, "Unregister from media controller\n"); ++ ++ if (dev->media_device_registered) { ++ media_device_unregister(&dev->mdev); ++ media_device_cleanup(&dev->mdev); ++ dev->media_device_registered = false; ++ } ++ ++ kfree(dev->entity.name); ++ dev->entity.name = NULL; ++ ++ if (dev->media_entity_registered) { ++ media_device_unregister_entity(&dev->entity); ++ dev->media_entity_registered = false; ++ } ++ ++ for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) { ++ struct bcm2835_isp_node *node = &dev->node[i]; ++ ++ if (node->media_node_registered) { ++ media_remove_intf_links(node->intf_link->intf); ++ media_entity_remove_links(&dev->node[i].vfd.entity); ++ media_devnode_remove(node->intf_devnode); ++ media_device_unregister_entity(&node->vfd.entity); ++ kfree(node->vfd.entity.name); ++ } ++ node->media_node_registered = false; ++ } ++ ++ dev->v4l2_dev.mdev = NULL; ++} ++ ++static int media_controller_register_node(struct bcm2835_isp_dev *dev, int num) ++{ ++ struct bcm2835_isp_node *node = &dev->node[num]; ++ struct media_entity *entity = &node->vfd.entity; ++ int output = node_is_output(node); ++ char *name; ++ int ret; ++ ++ v4l2_info(&dev->v4l2_dev, ++ "Register %s node %d with media controller\n", ++ output ? "output" : "capture", num); ++ entity->obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE; ++ entity->function = MEDIA_ENT_F_IO_V4L; ++ entity->info.dev.major = VIDEO_MAJOR; ++ entity->info.dev.minor = node->vfd.minor; ++ name = kmalloc(BCM2835_ISP_ENTITY_NAME_LEN, GFP_KERNEL); ++ if (!name) { ++ ret = -ENOMEM; ++ goto error_no_mem; ++ } ++ snprintf(name, BCM2835_ISP_ENTITY_NAME_LEN, "%s0-%s%d", ++ BCM2835_ISP_NAME, output ? "output" : "capture", num); ++ entity->name = name; ++ node->pad.flags = output ? MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK; ++ ret = media_entity_pads_init(entity, 1, &node->pad); ++ if (ret) ++ goto error_pads_init; ++ ret = media_device_register_entity(&dev->mdev, entity); ++ if (ret) ++ goto error_register_entity; ++ ++ node->intf_devnode = media_devnode_create(&dev->mdev, ++ MEDIA_INTF_T_V4L_VIDEO, 0, ++ VIDEO_MAJOR, node->vfd.minor); ++ if (!node->intf_devnode) { ++ ret = -ENOMEM; ++ goto error_devnode_create; ++ } ++ ++ node->intf_link = media_create_intf_link(entity, ++ &node->intf_devnode->intf, ++ MEDIA_LNK_FL_IMMUTABLE | ++ MEDIA_LNK_FL_ENABLED); ++ if (!node->intf_link) { ++ ret = -ENOMEM; ++ goto error_create_intf_link; ++ } ++ ++ if (output) ++ ret = media_create_pad_link(entity, 0, &dev->entity, num, ++ MEDIA_LNK_FL_IMMUTABLE | ++ MEDIA_LNK_FL_ENABLED); ++ else ++ ret = media_create_pad_link(&dev->entity, num, entity, 0, ++ MEDIA_LNK_FL_IMMUTABLE | ++ MEDIA_LNK_FL_ENABLED); ++ if (ret) ++ goto error_create_pad_link; ++ ++ dev->node[num].media_node_registered = true; ++ return 0; ++ ++error_create_pad_link: ++ media_remove_intf_links(&node->intf_devnode->intf); ++error_create_intf_link: ++ media_devnode_remove(node->intf_devnode); ++error_devnode_create: ++ media_device_unregister_entity(&node->vfd.entity); ++error_register_entity: ++error_pads_init: ++ kfree(entity->name); ++ entity->name = NULL; ++error_no_mem: ++ if (ret) ++ v4l2_info(&dev->v4l2_dev, "Error registering node\n"); ++ ++ return ret; ++} ++ ++static int media_controller_register(struct bcm2835_isp_dev *dev) ++{ ++ char *name; ++ unsigned int i; ++ int ret; ++ ++ v4l2_dbg(2, debug, &dev->v4l2_dev, "Registering with media controller\n"); ++ dev->mdev.dev = dev->dev; ++ strscpy(dev->mdev.model, "bcm2835-isp", ++ sizeof(dev->mdev.model)); ++ strscpy(dev->mdev.bus_info, "platform:bcm2835-isp", ++ sizeof(dev->mdev.bus_info)); ++ media_device_init(&dev->mdev); ++ dev->v4l2_dev.mdev = &dev->mdev; ++ ++ v4l2_dbg(2, debug, &dev->v4l2_dev, "Register entity for nodes\n"); ++ ++ name = kmalloc(BCM2835_ISP_ENTITY_NAME_LEN, GFP_KERNEL); ++ if (!name) { ++ ret = -ENOMEM; ++ goto done; ++ } ++ snprintf(name, BCM2835_ISP_ENTITY_NAME_LEN, "bcm2835_isp0"); ++ dev->entity.name = name; ++ dev->entity.obj_type = MEDIA_ENTITY_TYPE_BASE; ++ dev->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; ++ ++ for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) { ++ dev->pad[i].flags = node_is_output(&dev->node[i]) ? ++ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; ++ } ++ ++ ret = media_entity_pads_init(&dev->entity, BCM2835_ISP_NUM_NODES, ++ dev->pad); ++ if (ret) ++ goto done; ++ ++ ret = media_device_register_entity(&dev->mdev, &dev->entity); ++ if (ret) ++ goto done; ++ ++ dev->media_entity_registered = true; ++ for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) { ++ ret = media_controller_register_node(dev, i); ++ if (ret) ++ goto done; ++ } ++ ++ ret = media_device_register(&dev->mdev); ++ if (!ret) ++ dev->media_device_registered = true; ++done: ++ return ret; ++} ++ ++static int bcm2835_isp_remove(struct platform_device *pdev) ++{ ++ struct bcm2835_isp_dev *dev = platform_get_drvdata(pdev); ++ unsigned int i; ++ ++ media_controller_unregister(dev); ++ ++ for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) ++ unregister_node(&dev->node[i]); ++ ++ v4l2_device_unregister(&dev->v4l2_dev); ++ ++ if (dev->component) ++ vchiq_mmal_component_finalise(dev->mmal_instance, ++ dev->component); ++ ++ vchiq_mmal_finalise(dev->mmal_instance); ++ ++ return 0; ++} ++ ++static int bcm2835_isp_probe(struct platform_device *pdev) ++{ ++ struct bcm2835_isp_dev *dev; ++ unsigned int i; ++ int ret; ++ ++ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ ++ dev->dev = &pdev->dev; ++ ++ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); ++ if (ret) ++ return ret; ++ ++ ret = vchiq_mmal_init(&dev->mmal_instance); ++ if (ret) { ++ v4l2_device_unregister(&dev->v4l2_dev); ++ return ret; ++ } ++ ++ ret = vchiq_mmal_component_init(dev->mmal_instance, "ril.isp", ++ &dev->component); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: failed to create ril.isp component\n", __func__); ++ goto error; ++ } ++ ++ if ((dev->component->inputs != BCM2835_ISP_NUM_OUTPUTS) || ++ (dev->component->outputs != BCM2835_ISP_NUM_CAPTURES + ++ BCM2835_ISP_NUM_METADATA)) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: ril.isp returned %d i/p (%d expected), %d o/p (%d expected) ports\n", ++ __func__, dev->component->inputs, ++ BCM2835_ISP_NUM_OUTPUTS, ++ dev->component->outputs, ++ BCM2835_ISP_NUM_CAPTURES + BCM2835_ISP_NUM_METADATA); ++ goto error; ++ } ++ ++ atomic_set(&dev->num_streaming, 0); ++ ++ for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) { ++ struct bcm2835_isp_node *node = &dev->node[i]; ++ ++ ret = register_node(dev, node, i); ++ if (ret) ++ goto error; ++ } ++ ++ ret = media_controller_register(dev); ++ if (ret) ++ goto error; ++ ++ platform_set_drvdata(pdev, dev); ++ v4l2_info(&dev->v4l2_dev, "Loaded V4L2 %s\n", BCM2835_ISP_NAME); ++ return 0; ++ ++error: ++ bcm2835_isp_remove(pdev); ++ ++ return ret; ++} ++ ++static struct platform_driver bcm2835_isp_pdrv = { ++ .probe = bcm2835_isp_probe, ++ .remove = bcm2835_isp_remove, ++ .driver = { ++ .name = BCM2835_ISP_NAME, ++ }, ++}; ++ ++module_platform_driver(bcm2835_isp_pdrv); ++ ++MODULE_DESCRIPTION("BCM2835 ISP driver"); ++MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("1.0"); ++MODULE_ALIAS("platform:bcm2835-isp"); +--- /dev/null ++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_ctrls.h +@@ -0,0 +1,67 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Broadcom BCM2835 ISP driver ++ * ++ * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd. ++ * ++ * Author: Naushir Patuck (naush@raspberrypi.com) ++ * ++ */ ++ ++#ifndef BCM2835_ISP_CTRLS ++#define BCM2835_ISP_CTRLS ++ ++#include <linux/bcm2835-isp.h> ++ ++struct bcm2835_isp_custom_ctrl { ++ const char *name; ++ u32 id; ++ u32 size; ++ u32 flags; ++}; ++ ++static const struct bcm2835_isp_custom_ctrl custom_ctrls[] = { ++ { ++ .name = "Colour Correction Matrix", ++ .id = V4L2_CID_USER_BCM2835_ISP_CC_MATRIX, ++ .size = sizeof(struct bcm2835_isp_custom_ccm), ++ .flags = 0 ++ }, { ++ .name = "Lens Shading", ++ .id = V4L2_CID_USER_BCM2835_ISP_LENS_SHADING, ++ .size = sizeof(struct bcm2835_isp_lens_shading), ++ .flags = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE ++ }, { ++ .name = "Black Level", ++ .id = V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL, ++ .size = sizeof(struct bcm2835_isp_black_level), ++ .flags = 0 ++ }, { ++ .name = "Green Equalisation", ++ .id = V4L2_CID_USER_BCM2835_ISP_GEQ, ++ .size = sizeof(struct bcm2835_isp_geq), ++ .flags = 0 ++ }, { ++ .name = "Gamma", ++ .id = V4L2_CID_USER_BCM2835_ISP_GAMMA, ++ .size = sizeof(struct bcm2835_isp_gamma), ++ .flags = 0 ++ }, { ++ .name = "Sharpen", ++ .id = V4L2_CID_USER_BCM2835_ISP_SHARPEN, ++ .size = sizeof(struct bcm2835_isp_sharpen), ++ .flags = 0 ++ }, { ++ .name = "Denoise", ++ .id = V4L2_CID_USER_BCM2835_ISP_DENOISE, ++ .size = sizeof(struct bcm2835_isp_denoise), ++ .flags = 0 ++ }, { ++ .name = "Defective Pixel Correction", ++ .id = V4L2_CID_USER_BCM2835_ISP_DPC, ++ .size = sizeof(struct bcm2835_isp_dpc), ++ .flags = 0 ++ } ++}; ++ ++#endif +--- /dev/null ++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h +@@ -0,0 +1,272 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Broadcom BCM2835 ISP driver ++ * ++ * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd. ++ * ++ * Author: Naushir Patuck (naush@raspberrypi.com) ++ * ++ */ ++ ++#ifndef BCM2835_ISP_FMTS ++#define BCM2835_ISP_FMTS ++ ++#include <linux/videodev2.h> ++#include "vchiq-mmal/mmal-encodings.h" ++ ++struct bcm2835_isp_fmt { ++ u32 fourcc; ++ int depth; ++ int bytesperline_align; ++ u32 flags; ++ u32 mmal_fmt; ++ int size_multiplier_x2; ++ enum v4l2_colorspace colorspace; ++}; ++ ++struct bcm2835_isp_fmt_list { ++ struct bcm2835_isp_fmt *list; ++ unsigned int num_entries; ++}; ++ ++static const struct bcm2835_isp_fmt supported_formats[] = { ++ { ++ /* YUV formats */ ++ .fourcc = V4L2_PIX_FMT_YUV420, ++ .depth = 8, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_I420, ++ .size_multiplier_x2 = 3, ++ .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ }, { ++ .fourcc = V4L2_PIX_FMT_YVU420, ++ .depth = 8, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_YV12, ++ .size_multiplier_x2 = 3, ++ .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ }, { ++ .fourcc = V4L2_PIX_FMT_NV12, ++ .depth = 8, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_NV12, ++ .size_multiplier_x2 = 3, ++ .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ }, { ++ .fourcc = V4L2_PIX_FMT_NV21, ++ .depth = 8, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_NV21, ++ .size_multiplier_x2 = 3, ++ .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ }, { ++ .fourcc = V4L2_PIX_FMT_YUYV, ++ .depth = 16, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_YUYV, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ }, { ++ .fourcc = V4L2_PIX_FMT_UYVY, ++ .depth = 16, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_UYVY, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ }, { ++ .fourcc = V4L2_PIX_FMT_YVYU, ++ .depth = 16, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_YVYU, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ }, { ++ .fourcc = V4L2_PIX_FMT_VYUY, ++ .depth = 16, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_VYUY, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ }, { ++ /* RGB formats */ ++ .fourcc = V4L2_PIX_FMT_RGB24, ++ .depth = 24, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_RGB24, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ }, { ++ .fourcc = V4L2_PIX_FMT_RGB565, ++ .depth = 16, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_RGB16, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ }, { ++ .fourcc = V4L2_PIX_FMT_BGR24, ++ .depth = 24, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_BGR24, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ }, { ++ .fourcc = V4L2_PIX_FMT_ABGR32, ++ .depth = 32, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_BGRA, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ }, { ++ /* Bayer formats */ ++ /* 8 bit */ ++ .fourcc = V4L2_PIX_FMT_SRGGB8, ++ .depth = 8, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB8, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_RAW, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SBGGR8, ++ .depth = 8, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR8, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_RAW, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SGRBG8, ++ .depth = 8, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG8, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_RAW, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SGBRG8, ++ .depth = 8, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG8, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_RAW, ++ }, { ++ /* 10 bit */ ++ .fourcc = V4L2_PIX_FMT_SRGGB10P, ++ .depth = 10, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB10P, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_RAW, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SBGGR10P, ++ .depth = 10, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR10P, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_RAW, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SGRBG10P, ++ .depth = 10, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG10P, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_RAW, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SGBRG10P, ++ .depth = 10, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG10P, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_RAW, ++ }, { ++ /* 12 bit */ ++ .fourcc = V4L2_PIX_FMT_SRGGB12P, ++ .depth = 12, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB12P, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_RAW, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SBGGR12P, ++ .depth = 12, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR12P, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_RAW, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SGRBG12P, ++ .depth = 12, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG12P, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_RAW, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SGBRG12P, ++ .depth = 12, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG12P, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_RAW, ++ }, { ++ /* 16 bit */ ++ .fourcc = V4L2_PIX_FMT_SRGGB16, ++ .depth = 16, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB16, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_RAW, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SBGGR16, ++ .depth = 16, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR16, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_RAW, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SGRBG16, ++ .depth = 16, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG16, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_RAW, ++ }, { ++ .fourcc = V4L2_PIX_FMT_SGBRG16, ++ .depth = 16, ++ .bytesperline_align = 32, ++ .flags = 0, ++ .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG16, ++ .size_multiplier_x2 = 2, ++ .colorspace = V4L2_COLORSPACE_RAW, ++ }, { ++ /* ISP statistics format */ ++ .fourcc = V4L2_META_FMT_BCM2835_ISP_STATS, ++ .mmal_fmt = MMAL_ENCODING_BRCM_STATS, ++ /* The rest are not valid fields for stats. */ ++ } ++}; ++ ++#endif +--- a/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h ++++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h +@@ -100,6 +100,10 @@ + */ + #define MMAL_ENCODING_EGL_IMAGE MMAL_FOURCC('E', 'G', 'L', 'I') + ++/** ISP image statistics format ++ */ ++#define MMAL_ENCODING_BRCM_STATS MMAL_FOURCC('S', 'T', 'A', 'T') ++ + /* }@ */ + + /** \name Pre-defined audio encodings */ +--- a/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h ++++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h +@@ -221,6 +221,62 @@ enum mmal_parameter_camera_type { + MMAL_PARAMETER_SHUTTER_SPEED, + /**< Takes a @ref MMAL_PARAMETER_AWB_GAINS_T */ + MMAL_PARAMETER_CUSTOM_AWB_GAINS, ++ /**< Takes a @ref MMAL_PARAMETER_CAMERA_SETTINGS_T */ ++ MMAL_PARAMETER_CAMERA_SETTINGS, ++ /**< Takes a @ref MMAL_PARAMETER_PRIVACY_INDICATOR_T */ ++ MMAL_PARAMETER_PRIVACY_INDICATOR, ++ /**< Takes a @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_DENOISE, ++ /**< Takes a @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_STILLS_DENOISE, ++ /**< Takes a @ref MMAL_PARAMETER_CAMERA_ANNOTATE_T */ ++ MMAL_PARAMETER_ANNOTATE, ++ /**< Takes a @ref MMAL_PARAMETER_STEREOSCOPIC_MODE_T */ ++ MMAL_PARAMETER_STEREOSCOPIC_MODE, ++ /**< Takes a @ref MMAL_PARAMETER_CAMERA_INTERFACE_T */ ++ MMAL_PARAMETER_CAMERA_INTERFACE, ++ /**< Takes a @ref MMAL_PARAMETER_CAMERA_CLOCKING_MODE_T */ ++ MMAL_PARAMETER_CAMERA_CLOCKING_MODE, ++ /**< Takes a @ref MMAL_PARAMETER_CAMERA_RX_CONFIG_T */ ++ MMAL_PARAMETER_CAMERA_RX_CONFIG, ++ /**< Takes a @ref MMAL_PARAMETER_CAMERA_RX_TIMING_T */ ++ MMAL_PARAMETER_CAMERA_RX_TIMING, ++ /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_DPF_CONFIG, ++ ++ /* 0x50 */ ++ /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_JPEG_RESTART_INTERVAL, ++ /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_CAMERA_ISP_BLOCK_OVERRIDE, ++ /**< Takes a @ref MMAL_PARAMETER_LENS_SHADING_T */ ++ MMAL_PARAMETER_LENS_SHADING_OVERRIDE, ++ /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_BLACK_LEVEL, ++ /**< Takes a @ref MMAL_PARAMETER_RESIZE_T */ ++ MMAL_PARAMETER_RESIZE_PARAMS, ++ /**< Takes a @ref MMAL_PARAMETER_CROP_T */ ++ MMAL_PARAMETER_CROP, ++ /**< Takes a @ref MMAL_PARAMETER_INT32_T */ ++ MMAL_PARAMETER_OUTPUT_SHIFT, ++ /**< Takes a @ref MMAL_PARAMETER_INT32_T */ ++ MMAL_PARAMETER_CCM_SHIFT, ++ /**< Takes a @ref MMAL_PARAMETER_CUSTOM_CCM_T */ ++ MMAL_PARAMETER_CUSTOM_CCM, ++ /**< Takes a @ref MMAL_PARAMETER_RATIONAL_T */ ++ MMAL_PARAMETER_ANALOG_GAIN, ++ /**< Takes a @ref MMAL_PARAMETER_RATIONAL_T */ ++ MMAL_PARAMETER_DIGITAL_GAIN, ++ /**< Takes a @ref MMAL_PARAMETER_DENOISE_T */ ++ MMAL_PARAMETER_DENOISE, ++ /**< Takes a @ref MMAL_PARAMETER_SHARPEN_T */ ++ MMAL_PARAMETER_SHARPEN, ++ /**< Takes a @ref MMAL_PARAMETER_GEQ_T */ ++ MMAL_PARAMETER_GEQ, ++ /**< Tales a @ref MMAP_PARAMETER_DPC_T */ ++ MMAL_PARAMETER_DPC, ++ /**< Tales a @ref MMAP_PARAMETER_GAMMA_T */ ++ MMAL_PARAMETER_GAMMA, + }; + + struct mmal_parameter_rational { +@@ -780,7 +836,102 @@ struct mmal_parameter_camera_info { + struct mmal_parameter_camera_info_camera + cameras[MMAL_PARAMETER_CAMERA_INFO_MAX_CAMERAS]; + struct mmal_parameter_camera_info_flash +- flashes[MMAL_PARAMETER_CAMERA_INFO_MAX_FLASHES]; ++ flashes[MMAL_PARAMETER_CAMERA_INFO_MAX_FLASHES]; ++}; ++ ++struct mmal_parameter_ccm { ++ struct mmal_parameter_rational ccm[3][3]; ++ s32 offsets[3]; ++}; ++ ++struct mmal_parameter_custom_ccm { ++ u32 enabled; /**< Enable the custom CCM. */ ++ struct mmal_parameter_ccm ccm; /**< CCM to be used. */ ++}; ++ ++struct mmal_parameter_lens_shading { ++ u32 enabled; ++ u32 grid_cell_size; ++ u32 grid_width; ++ u32 grid_stride; ++ u32 grid_height; ++ u32 mem_handle_table; ++ u32 ref_transform; ++}; ++ ++enum mmal_parameter_ls_gain_format_type { ++ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U0P8_1 = 0, ++ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U1P7_0 = 1, ++ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U1P7_1 = 2, ++ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U2P6_0 = 3, ++ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U2P6_1 = 4, ++ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U3P5_0 = 5, ++ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U3P5_1 = 6, ++ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U4P10 = 7, ++ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_DUMMY = 0x7FFFFFFF ++}; ++ ++struct mmal_parameter_lens_shading_v2 { ++ u32 enabled; ++ u32 grid_cell_size; ++ u32 grid_width; ++ u32 grid_stride; ++ u32 grid_height; ++ u32 mem_handle_table; ++ u32 ref_transform; ++ u32 corner_sampled; ++ enum mmal_parameter_ls_gain_format_type gain_format; ++}; ++ ++struct mmal_parameter_black_level { ++ u32 enabled; ++ u16 black_level_r; ++ u16 black_level_g; ++ u16 black_level_b; ++ u8 pad_[2]; /* Unused */ ++}; ++ ++struct mmal_parameter_geq { ++ u32 enabled; ++ u32 offset; ++ struct mmal_parameter_rational slope; ++}; ++ ++#define MMAL_NUM_GAMMA_PTS 33 ++struct mmal_parameter_gamma { ++ u32 enabled; ++ u16 x[MMAL_NUM_GAMMA_PTS]; ++ u16 y[MMAL_NUM_GAMMA_PTS]; ++}; ++ ++struct mmal_parameter_denoise { ++ u32 enabled; ++ u32 constant; ++ struct mmal_parameter_rational slope; ++ struct mmal_parameter_rational strength; ++}; ++ ++struct mmal_parameter_sharpen { ++ u32 enabled; ++ struct mmal_parameter_rational threshold; ++ struct mmal_parameter_rational strength; ++ struct mmal_parameter_rational limit; ++}; ++ ++enum mmal_dpc_mode { ++ MMAL_DPC_MODE_OFF = 0, ++ MMAL_DPC_MODE_NORMAL = 1, ++ MMAL_DPC_MODE_STRONG = 2, ++ MMAL_DPC_MODE_MAX = 0x7FFFFFFF, ++}; ++ ++struct mmal_parameter_dpc { ++ u32 enabled; ++ u32 strength; ++}; ++ ++struct mmal_parameter_crop { ++ struct vchiq_mmal_rect rect; + }; + + #endif diff --git a/target/linux/bcm27xx/patches-5.4/950-0682-staging-vchiq-Load-bcm2835_isp-driver-from-vchiq.patch b/target/linux/bcm27xx/patches-5.4/950-0682-staging-vchiq-Load-bcm2835_isp-driver-from-vchiq.patch new file mode 100644 index 0000000000..70fe392a3b --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0682-staging-vchiq-Load-bcm2835_isp-driver-from-vchiq.patch @@ -0,0 +1,39 @@ +From 7f2f9b54862f7df5cdef95b85234fad83b6b3480 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Wed, 22 Apr 2020 08:32:32 +0100 +Subject: [PATCH] staging: vchiq: Load bcm2835_isp driver from vchiq + +bcmn2835_isp is a platform driver dependent on vchiq, +therefore add the load/unload functions for it to vchiq. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c ++++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +@@ -142,6 +142,7 @@ static struct platform_device *bcm2835_c + static struct platform_device *bcm2835_audio; + static struct platform_device *bcm2835_codec; + static struct platform_device *vcsm_cma; ++static struct platform_device *bcm2835_isp; + + static struct vchiq_drvdata bcm2835_drvdata = { + .cache_line_size = 32, +@@ -3281,6 +3282,7 @@ static int vchiq_probe(struct platform_d + bcm2835_codec = vchiq_register_child(pdev, "bcm2835-codec"); + bcm2835_camera = vchiq_register_child(pdev, "bcm2835-camera"); + bcm2835_audio = vchiq_register_child(pdev, "bcm2835_audio"); ++ bcm2835_isp = vchiq_register_child(pdev, "bcm2835-isp"); + + return 0; + +@@ -3293,6 +3295,7 @@ failed_platform_init: + + static int vchiq_remove(struct platform_device *pdev) + { ++ platform_device_unregister(bcm2835_isp); + platform_device_unregister(bcm2835_audio); + platform_device_unregister(bcm2835_camera); + platform_device_unregister(bcm2835_codec); diff --git a/target/linux/bcm27xx/patches-5.4/950-0683-vc4_hvs-Mark-core-clock-as-optional.patch b/target/linux/bcm27xx/patches-5.4/950-0683-vc4_hvs-Mark-core-clock-as-optional.patch new file mode 100644 index 0000000000..816ca3c589 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0683-vc4_hvs-Mark-core-clock-as-optional.patch @@ -0,0 +1,23 @@ +From 41b2f1242ff3f90c88de2de93dbec1f5734b45fd Mon Sep 17 00:00:00 2001 +From: popcornmix <popcornmix@gmail.com> +Date: Tue, 28 Apr 2020 17:35:07 +0100 +Subject: [PATCH] vc4_hvs: Mark core clock as optional + +This isn't required on Pi3, so don't treat as an error + +Signed-off-by: popcornmix <popcornmix@gmail.com> +--- + drivers/gpu/drm/vc4/vc4_hvs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/gpu/drm/vc4/vc4_hvs.c ++++ b/drivers/gpu/drm/vc4/vc4_hvs.c +@@ -240,7 +240,7 @@ static int vc4_hvs_bind(struct device *d + hvs->regset.regs = hvs_regs; + hvs->regset.nregs = ARRAY_SIZE(hvs_regs); + +- hvs->core_clk = devm_clk_get(&pdev->dev, NULL); ++ hvs->core_clk = devm_clk_get_optional(&pdev->dev, NULL); + if (IS_ERR(hvs->core_clk)) { + dev_err(&pdev->dev, "Couldn't get core clock\n"); + return PTR_ERR(hvs->regs); diff --git a/target/linux/bcm27xx/patches-5.4/950-0684-vc4_hdmi-BCM2835-requires-a-fixed-hsm-clock-for-CEC-.patch b/target/linux/bcm27xx/patches-5.4/950-0684-vc4_hdmi-BCM2835-requires-a-fixed-hsm-clock-for-CEC-.patch new file mode 100644 index 0000000000..0ea80ae6c8 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0684-vc4_hdmi-BCM2835-requires-a-fixed-hsm-clock-for-CEC-.patch @@ -0,0 +1,93 @@ +From af3f381a59c10f6bd49d86a5ff2325b6ebeb79e9 Mon Sep 17 00:00:00 2001 +From: popcornmix <popcornmix@gmail.com> +Date: Mon, 27 Apr 2020 19:07:50 +0100 +Subject: [PATCH] vc4_hdmi: BCM2835 requires a fixed hsm clock for CEC + to work + +Signed-off-by: popcornmix <popcornmix@gmail.com> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 32 ++++++++++++++++++++++++++------ + drivers/gpu/drm/vc4/vc4_hdmi.h | 3 +++ + 2 files changed, 29 insertions(+), 6 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -580,12 +580,7 @@ static void vc4_hdmi_encoder_enable(stru + return; + } + +- /* +- * The HSM rate needs to be slightly greater than the pixel clock, with +- * a minimum of 108MHz. +- * Use 101% as this is what the firmware uses. +- */ +- hsm_rate = max_t(unsigned long, 108000000, (pixel_rate / 100) * 101); ++ hsm_rate = vc4_hdmi->variant->calc_hsm_clock(vc4_hdmi, pixel_rate); + ret = clk_set_rate(vc4_hdmi->hsm_clock, hsm_rate); + if (ret) { + DRM_ERROR("Failed to set HSM clock rate: %d\n", ret); +@@ -753,6 +748,28 @@ static u32 vc5_hdmi_get_hsm_clock(struct + return 108000000; + } + ++static u32 vc4_hdmi_calc_hsm_clock(struct vc4_hdmi *vc4_hdmi, unsigned long pixel_rate) ++{ ++ /* ++ * This is the rate that is set by the firmware. The number ++ * needs to be a bit higher than the pixel clock rate ++ * (generally 148.5Mhz). ++ */ ++ ++ return 163682864; ++} ++ ++static u32 vc5_hdmi_calc_hsm_clock(struct vc4_hdmi *vc4_hdmi, unsigned long pixel_rate) ++{ ++ /* ++ * The HSM rate needs to be slightly greater than the pixel clock, with ++ * a minimum of 108MHz. ++ * Use 101% as this is what the firmware uses. ++ */ ++ ++ return max_t(unsigned long, 108000000, (pixel_rate / 100) * 101); ++} ++ + static u32 vc4_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask) + { + int i; +@@ -1748,6 +1765,7 @@ static const struct vc4_hdmi_variant bcm + .phy_rng_enable = vc4_hdmi_phy_rng_enable, + .phy_rng_disable = vc4_hdmi_phy_rng_disable, + .get_hsm_clock = vc4_hdmi_get_hsm_clock, ++ .calc_hsm_clock = vc4_hdmi_calc_hsm_clock, + .channel_map = vc4_hdmi_channel_map, + }; + +@@ -1772,6 +1790,7 @@ static const struct vc4_hdmi_variant bcm + .phy_rng_enable = vc5_hdmi_phy_rng_enable, + .phy_rng_disable = vc5_hdmi_phy_rng_disable, + .get_hsm_clock = vc5_hdmi_get_hsm_clock, ++ .calc_hsm_clock = vc5_hdmi_calc_hsm_clock, + .channel_map = vc5_hdmi_channel_map, + }; + +@@ -1796,6 +1815,7 @@ static const struct vc4_hdmi_variant bcm + .phy_rng_enable = vc5_hdmi_phy_rng_enable, + .phy_rng_disable = vc5_hdmi_phy_rng_disable, + .get_hsm_clock = vc5_hdmi_get_hsm_clock, ++ .calc_hsm_clock = vc5_hdmi_calc_hsm_clock, + .channel_map = vc5_hdmi_channel_map, + }; + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -92,6 +92,9 @@ struct vc4_hdmi_variant { + /* Callback to get hsm clock */ + u32 (*get_hsm_clock)(struct vc4_hdmi *vc4_hdmi); + ++ /* Callback to get hsm clock */ ++ u32 (*calc_hsm_clock)(struct vc4_hdmi *vc4_hdmi, unsigned long pixel_rate); ++ + /* Callback to get channel map */ + u32 (*channel_map)(struct vc4_hdmi *vc4_hdmi, u32 channel_mask); + }; diff --git a/target/linux/bcm27xx/patches-5.4/950-0685-media-i2c-imx219-Implement-get_selection.patch b/target/linux/bcm27xx/patches-5.4/950-0685-media-i2c-imx219-Implement-get_selection.patch new file mode 100644 index 0000000000..cb7a0af605 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0685-media-i2c-imx219-Implement-get_selection.patch @@ -0,0 +1,181 @@ +From f479cf37ccda2be7204a964fe2747dfcb4b56bf6 Mon Sep 17 00:00:00 2001 +From: Jacopo Mondi <jacopo@jmondi.org> +Date: Wed, 29 Apr 2020 11:50:38 +0200 +Subject: [PATCH] media: i2c: imx219: Implement get_selection + +Implement the get_selection pad operation for the IMX219 sensor driver. +The supported targets report the sensor's native size, the crop default +rectangle and the crop rectangle. + +Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> +Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> +--- + drivers/media/i2c/imx219.c | 94 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 94 insertions(+) + +--- a/drivers/media/i2c/imx219.c ++++ b/drivers/media/i2c/imx219.c +@@ -122,6 +122,14 @@ enum pad_types { + NUM_PADS + }; + ++/* IMX219 native and active pixel array size. */ ++#define IMX219_NATIVE_WIDTH 3296U ++#define IMX219_NATIVE_HEIGHT 2480U ++#define IMX219_PIXEL_ARRAY_LEFT 8U ++#define IMX219_PIXEL_ARRAY_TOP 8U ++#define IMX219_PIXEL_ARRAY_WIDTH 3280U ++#define IMX219_PIXEL_ARRAY_HEIGHT 2464U ++ + struct imx219_reg { + u16 address; + u8 val; +@@ -139,6 +147,9 @@ struct imx219_mode { + /* Frame height */ + unsigned int height; + ++ /* Analog crop rectangle. */ ++ struct v4l2_rect crop; ++ + /* V-timing */ + unsigned int vts_def; + +@@ -473,6 +484,12 @@ static const struct imx219_mode supporte + /* 8MPix 15fps mode */ + .width = 3280, + .height = 2464, ++ .crop = { ++ .left = 0, ++ .top = 0, ++ .width = 3280, ++ .height = 2464 ++ }, + .vts_def = IMX219_VTS_15FPS, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), +@@ -483,6 +500,12 @@ static const struct imx219_mode supporte + /* 1080P 30fps cropped */ + .width = 1920, + .height = 1080, ++ .crop = { ++ .left = 680, ++ .top = 692, ++ .width = 1920, ++ .height = 1080 ++ }, + .vts_def = IMX219_VTS_30FPS_1080P, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs), +@@ -493,6 +516,12 @@ static const struct imx219_mode supporte + /* 2x2 binned 30fps mode */ + .width = 1640, + .height = 1232, ++ .crop = { ++ .left = 0, ++ .top = 0, ++ .width = 3280, ++ .height = 2464 ++ }, + .vts_def = IMX219_VTS_30FPS_BINNED, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs), +@@ -503,6 +532,12 @@ static const struct imx219_mode supporte + /* 640x480 30fps mode */ + .width = 640, + .height = 480, ++ .crop = { ++ .left = 1000, ++ .top = 752, ++ .width = 1280, ++ .height = 960 ++ }, + .vts_def = IMX219_VTS_30FPS_640x480, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_640_480_regs), +@@ -666,6 +701,7 @@ static int imx219_open(struct v4l2_subde + v4l2_subdev_get_try_format(sd, fh->pad, IMAGE_PAD); + struct v4l2_mbus_framefmt *try_fmt_meta = + v4l2_subdev_get_try_format(sd, fh->pad, METADATA_PAD); ++ struct v4l2_rect *try_crop; + + mutex_lock(&imx219->mutex); + +@@ -682,6 +718,13 @@ static int imx219_open(struct v4l2_subde + try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA; + try_fmt_meta->field = V4L2_FIELD_NONE; + ++ /* Initialize try_crop rectangle. */ ++ try_crop = v4l2_subdev_get_try_crop(sd, fh->pad, 0); ++ try_crop->top = IMX219_PIXEL_ARRAY_TOP; ++ try_crop->left = IMX219_PIXEL_ARRAY_LEFT; ++ try_crop->width = IMX219_PIXEL_ARRAY_WIDTH; ++ try_crop->height = IMX219_PIXEL_ARRAY_HEIGHT; ++ + mutex_unlock(&imx219->mutex); + + return 0; +@@ -1011,6 +1054,56 @@ static int imx219_set_framefmt(struct im + return -EINVAL; + } + ++static const struct v4l2_rect * ++__imx219_get_pad_crop(struct imx219 *imx219, struct v4l2_subdev_pad_config *cfg, ++ unsigned int pad, enum v4l2_subdev_format_whence which) ++{ ++ switch (which) { ++ case V4L2_SUBDEV_FORMAT_TRY: ++ return v4l2_subdev_get_try_crop(&imx219->sd, cfg, pad); ++ case V4L2_SUBDEV_FORMAT_ACTIVE: ++ return &imx219->mode->crop; ++ } ++ ++ return NULL; ++} ++ ++static int imx219_get_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_selection *sel) ++{ ++ switch (sel->target) { ++ case V4L2_SEL_TGT_CROP: { ++ struct imx219 *imx219 = to_imx219(sd); ++ ++ mutex_lock(&imx219->mutex); ++ sel->r = *__imx219_get_pad_crop(imx219, cfg, sel->pad, ++ sel->which); ++ mutex_unlock(&imx219->mutex); ++ ++ return 0; ++ } ++ ++ case V4L2_SEL_TGT_NATIVE_SIZE: ++ sel->r.top = 0; ++ sel->r.left = 0; ++ sel->r.width = IMX219_NATIVE_WIDTH; ++ sel->r.height = IMX219_NATIVE_HEIGHT; ++ ++ return 0; ++ ++ case V4L2_SEL_TGT_CROP_DEFAULT: ++ sel->r.top = IMX219_PIXEL_ARRAY_TOP; ++ sel->r.left = IMX219_PIXEL_ARRAY_LEFT; ++ sel->r.width = IMX219_PIXEL_ARRAY_WIDTH; ++ sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT; ++ ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ + static int imx219_start_streaming(struct imx219 *imx219) + { + struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); +@@ -1235,6 +1328,7 @@ static const struct v4l2_subdev_pad_ops + .enum_mbus_code = imx219_enum_mbus_code, + .get_fmt = imx219_get_pad_format, + .set_fmt = imx219_set_pad_format, ++ .get_selection = imx219_get_selection, + .enum_frame_size = imx219_enum_frame_size, + }; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0686-media-i2c-ov5647-Add-support-for-g_selection-to-refl.patch b/target/linux/bcm27xx/patches-5.4/950-0686-media-i2c-ov5647-Add-support-for-g_selection-to-refl.patch new file mode 100644 index 0000000000..66715a108e --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0686-media-i2c-ov5647-Add-support-for-g_selection-to-refl.patch @@ -0,0 +1,206 @@ +From 940cac315aaeca33483bffcf09a235195e3f5272 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 29 Apr 2020 11:46:07 +0100 +Subject: [PATCH] media: i2c: ov5647: Add support for g_selection to + reflect cropping/binning + +In order to apply lens shading correctly the client needs to know how +each mode crops or scales the image compared to the full sensor array. +Implement this (based on the imx219 equivalent). + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/media/i2c/ov5647.c | 119 ++++++++++++++++++++++++++++++------- + 1 file changed, 96 insertions(+), 23 deletions(-) + +--- a/drivers/media/i2c/ov5647.c ++++ b/drivers/media/i2c/ov5647.c +@@ -70,25 +70,14 @@ + #define VAL_TERM 0xfe + #define REG_DLY 0xffff + +-#define OV5647_ROW_START 0x01 +-#define OV5647_ROW_START_MIN 0 +-#define OV5647_ROW_START_MAX 2004 +-#define OV5647_ROW_START_DEF 54 +- +-#define OV5647_COLUMN_START 0x02 +-#define OV5647_COLUMN_START_MIN 0 +-#define OV5647_COLUMN_START_MAX 2750 +-#define OV5647_COLUMN_START_DEF 16 +- +-#define OV5647_WINDOW_HEIGHT 0x03 +-#define OV5647_WINDOW_HEIGHT_MIN 2 +-#define OV5647_WINDOW_HEIGHT_MAX 2006 +-#define OV5647_WINDOW_HEIGHT_DEF 1944 +- +-#define OV5647_WINDOW_WIDTH 0x04 +-#define OV5647_WINDOW_WIDTH_MIN 2 +-#define OV5647_WINDOW_WIDTH_MAX 2752 +-#define OV5647_WINDOW_WIDTH_DEF 2592 ++/* OV5647 native and active pixel array size */ ++#define OV5647_NATIVE_WIDTH 2624U ++#define OV5647_NATIVE_HEIGHT 1956U ++ ++#define OV5647_PIXEL_ARRAY_LEFT 16U ++#define OV5647_PIXEL_ARRAY_TOP 16U ++#define OV5647_PIXEL_ARRAY_WIDTH 2592U ++#define OV5647_PIXEL_ARRAY_HEIGHT 1944U + + struct regval_list { + u16 addr; +@@ -97,6 +86,9 @@ struct regval_list { + + struct ov5647_mode { + struct v4l2_mbus_framefmt format; ++ /* Analog crop rectangle. */ ++ struct v4l2_rect crop; ++ + struct regval_list *reg_list; + unsigned int num_regs; + }; +@@ -603,6 +595,12 @@ static struct ov5647_mode supported_mode + .width = 640, + .height = 480 + }, ++ .crop = { ++ .left = 0, ++ .top = 0, ++ .width = 1280, ++ .height = 960, ++ }, + ov5647_640x480_8bit, + ARRAY_SIZE(ov5647_640x480_8bit) + }, +@@ -620,6 +618,12 @@ static struct ov5647_mode supported_mode + .width = 2592, + .height = 1944 + }, ++ .crop = { ++ .left = 0, ++ .top = 0, ++ .width = 2592, ++ .height = 1944 ++ }, + ov5647_2592x1944_10bit, + ARRAY_SIZE(ov5647_2592x1944_10bit) + }, +@@ -635,6 +639,12 @@ static struct ov5647_mode supported_mode + .width = 1920, + .height = 1080 + }, ++ .crop = { ++ .left = 348, ++ .top = 434, ++ .width = 1928, ++ .height = 1080, ++ }, + ov5647_1080p30_10bit, + ARRAY_SIZE(ov5647_1080p30_10bit) + }, +@@ -649,6 +659,12 @@ static struct ov5647_mode supported_mode + .width = 1296, + .height = 972 + }, ++ .crop = { ++ .left = 0, ++ .top = 0, ++ .width = 2592, ++ .height = 1944, ++ }, + ov5647_2x2binned_10bit, + ARRAY_SIZE(ov5647_2x2binned_10bit) + }, +@@ -664,6 +680,12 @@ static struct ov5647_mode supported_mode + .width = 640, + .height = 480 + }, ++ .crop = { ++ .left = 16, ++ .top = 0, ++ .width = 2560, ++ .height = 1920, ++ }, + ov5647_640x480_10bit, + ARRAY_SIZE(ov5647_640x480_10bit) + }, +@@ -971,6 +993,56 @@ static const struct v4l2_subdev_core_ops + #endif + }; + ++static const struct v4l2_rect * ++__ov5647_get_pad_crop(struct ov5647 *ov5647, struct v4l2_subdev_pad_config *cfg, ++ unsigned int pad, enum v4l2_subdev_format_whence which) ++{ ++ switch (which) { ++ case V4L2_SUBDEV_FORMAT_TRY: ++ return v4l2_subdev_get_try_crop(&ov5647->sd, cfg, pad); ++ case V4L2_SUBDEV_FORMAT_ACTIVE: ++ return &ov5647->mode->crop; ++ } ++ ++ return NULL; ++} ++ ++static int ov5647_get_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_selection *sel) ++{ ++ switch (sel->target) { ++ case V4L2_SEL_TGT_CROP: { ++ struct ov5647 *state = to_state(sd); ++ ++ mutex_lock(&state->lock); ++ sel->r = *__ov5647_get_pad_crop(state, cfg, sel->pad, ++ sel->which); ++ mutex_unlock(&state->lock); ++ ++ return 0; ++ } ++ ++ case V4L2_SEL_TGT_NATIVE_SIZE: ++ sel->r.top = 0; ++ sel->r.left = 0; ++ sel->r.width = OV5647_NATIVE_WIDTH; ++ sel->r.height = OV5647_NATIVE_HEIGHT; ++ ++ return 0; ++ ++ case V4L2_SEL_TGT_CROP_DEFAULT: ++ sel->r.top = OV5647_PIXEL_ARRAY_TOP; ++ sel->r.left = OV5647_PIXEL_ARRAY_LEFT; ++ sel->r.width = OV5647_PIXEL_ARRAY_WIDTH; ++ sel->r.height = OV5647_PIXEL_ARRAY_HEIGHT; ++ ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ + static int ov5647_s_stream(struct v4l2_subdev *sd, int enable) + { + struct ov5647 *state = to_state(sd); +@@ -1122,6 +1194,7 @@ static const struct v4l2_subdev_pad_ops + .enum_mbus_code = ov5647_enum_mbus_code, + .set_fmt = ov5647_set_fmt, + .get_fmt = ov5647_get_fmt, ++ .get_selection = ov5647_get_selection, + .enum_frame_size = ov5647_enum_frame_size, + }; + +@@ -1170,10 +1243,10 @@ static int ov5647_open(struct v4l2_subde + 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; ++ crop->left = OV5647_PIXEL_ARRAY_LEFT; ++ crop->top = OV5647_PIXEL_ARRAY_TOP; ++ crop->width = OV5647_PIXEL_ARRAY_WIDTH; ++ crop->height = OV5647_PIXEL_ARRAY_HEIGHT; + + /* Set the default format to the same as the sensor. */ + *format = state->mode->format; diff --git a/target/linux/bcm27xx/patches-5.4/950-0687-media-i2c-ov5467-Fixup-error-path-to-release-mutex.patch b/target/linux/bcm27xx/patches-5.4/950-0687-media-i2c-ov5467-Fixup-error-path-to-release-mutex.patch new file mode 100644 index 0000000000..d8a0104739 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0687-media-i2c-ov5467-Fixup-error-path-to-release-mutex.patch @@ -0,0 +1,29 @@ +From 0c447a38b9d561a80d78ffd7f4533fef75cd1393 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 29 Apr 2020 11:50:52 +0100 +Subject: [PATCH] media: i2c: ov5467: Fixup error path to release mutex + +"87f3ab9 media: ov5647: Add basic support for multiple sensor modes." +added a return path ov5647_set_fmt that didn't release the device +mutex that it had claimed. +Release the mutex. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/media/i2c/ov5647.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/media/i2c/ov5647.c ++++ b/drivers/media/i2c/ov5647.c +@@ -1146,8 +1146,10 @@ static int ov5647_set_fmt(struct v4l2_su + else + mode = mode_8bit; + +- if (!mode) ++ if (!mode) { ++ mutex_unlock(&state->lock); + return -EINVAL; ++ } + + *fmt = mode->format; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { diff --git a/target/linux/bcm27xx/patches-5.4/950-0688-media-i2c-ov5647-Support-V4L2_CID_PIXEL_RATE.patch b/target/linux/bcm27xx/patches-5.4/950-0688-media-i2c-ov5647-Support-V4L2_CID_PIXEL_RATE.patch new file mode 100644 index 0000000000..ce16832d35 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0688-media-i2c-ov5647-Support-V4L2_CID_PIXEL_RATE.patch @@ -0,0 +1,131 @@ +From e947a531a5c6a61fc568dc6a502543e1145efc29 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 29 Apr 2020 12:25:13 +0100 +Subject: [PATCH] media: i2c: ov5647: Support V4L2_CID_PIXEL_RATE + +Clients need to know the pixel rate in order to compute exposure +and frame rate values. +Advertise it. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/media/i2c/ov5647.c | 31 +++++++++++++++++++++++++++---- + 1 file changed, 27 insertions(+), 4 deletions(-) + +--- a/drivers/media/i2c/ov5647.c ++++ b/drivers/media/i2c/ov5647.c +@@ -89,6 +89,8 @@ struct ov5647_mode { + /* Analog crop rectangle. */ + struct v4l2_rect crop; + ++ u64 pixel_rate; ++ + struct regval_list *reg_list; + unsigned int num_regs; + }; +@@ -103,6 +105,7 @@ struct ov5647 { + struct gpio_desc *pwdn; + unsigned int flags; + struct v4l2_ctrl_handler ctrls; ++ struct v4l2_ctrl *pixel_rate; + bool write_mode_regs; + }; + +@@ -601,6 +604,7 @@ static struct ov5647_mode supported_mode + .width = 1280, + .height = 960, + }, ++ .pixel_rate = 77291670, + ov5647_640x480_8bit, + ARRAY_SIZE(ov5647_640x480_8bit) + }, +@@ -624,6 +628,7 @@ static struct ov5647_mode supported_mode + .width = 2592, + .height = 1944 + }, ++ .pixel_rate = 87500000, + ov5647_2592x1944_10bit, + ARRAY_SIZE(ov5647_2592x1944_10bit) + }, +@@ -645,6 +650,7 @@ static struct ov5647_mode supported_mode + .width = 1928, + .height = 1080, + }, ++ .pixel_rate = 81666700, + ov5647_1080p30_10bit, + ARRAY_SIZE(ov5647_1080p30_10bit) + }, +@@ -665,6 +671,7 @@ static struct ov5647_mode supported_mode + .width = 2592, + .height = 1944, + }, ++ .pixel_rate = 81666700, + ov5647_2x2binned_10bit, + ARRAY_SIZE(ov5647_2x2binned_10bit) + }, +@@ -686,6 +693,7 @@ static struct ov5647_mode supported_mode + .width = 2560, + .height = 1920, + }, ++ .pixel_rate = 55000000, + ov5647_640x480_10bit, + ARRAY_SIZE(ov5647_640x480_10bit) + }, +@@ -1163,6 +1171,11 @@ static int ov5647_set_fmt(struct v4l2_su + if (state->mode != mode) + state->write_mode_regs = true; + state->mode = mode; ++ ++ __v4l2_ctrl_modify_range(state->pixel_rate, ++ mode->pixel_rate, ++ mode->pixel_rate, 1, ++ mode->pixel_rate); + } + + mutex_unlock(&state->lock); +@@ -1379,6 +1392,9 @@ static int ov5647_s_ctrl(struct v4l2_ctr + case V4L2_CID_EXPOSURE: + ret = ov5647_s_exposure(sd, ctrl->val); + break; ++ case V4L2_CID_PIXEL_RATE: ++ /* Read-only, but we adjust it based on mode. */ ++ break; + default: + dev_info(&client->dev, + "ctrl(id:0x%x,val:0x%x) is not handled\n", +@@ -1436,7 +1452,7 @@ static int ov5647_probe(struct i2c_clien + mutex_init(&sensor->lock); + + /* Initialise controls. */ +- v4l2_ctrl_handler_init(&sensor->ctrls, 3); ++ v4l2_ctrl_handler_init(&sensor->ctrls, 6); + v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_AUTOGAIN, + 0, /* min */ +@@ -1469,6 +1485,16 @@ static int ov5647_probe(struct i2c_clien + 32); /* default, 32 = 2.0x */ + ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; + ++ /* Set the default mode before we init the subdev */ ++ sensor->mode = OV5647_DEFAULT_MODE; ++ ++ /* By default, PIXEL_RATE is read only, but it does change per mode */ ++ sensor->pixel_rate = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, ++ V4L2_CID_PIXEL_RATE, ++ sensor->mode->pixel_rate, ++ sensor->mode->pixel_rate, 1, ++ sensor->mode->pixel_rate); ++ + if (sensor->ctrls.error) { + ret = sensor->ctrls.error; + dev_err(&client->dev, "%s control init failed (%d)\n", +@@ -1477,9 +1503,6 @@ static int ov5647_probe(struct i2c_clien + } + sensor->sd.ctrl_handler = &sensor->ctrls; + +- /* 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; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0689-media-i2c-ov5647-Set-V4L2_SUBDEV_FL_HAS_EVENTS-flag.patch b/target/linux/bcm27xx/patches-5.4/950-0689-media-i2c-ov5647-Set-V4L2_SUBDEV_FL_HAS_EVENTS-flag.patch new file mode 100644 index 0000000000..b7a3f19753 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0689-media-i2c-ov5647-Set-V4L2_SUBDEV_FL_HAS_EVENTS-flag.patch @@ -0,0 +1,143 @@ +From 0e864ac98ffc97d0bb5fc343ca62d860fbe8da09 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 29 Apr 2020 17:25:56 +0100 +Subject: [PATCH] media: i2c: ov5647: Set V4L2_SUBDEV_FL_HAS_EVENTS + flag + +The ov5647 subdev can generate control events, therefore set +the V4L2_SUBDEV_FL_HAS_EVENTS flag. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/media/i2c/ov5647.c | 29 +++++++++++++++++++++++++++-- + 1 file changed, 27 insertions(+), 2 deletions(-) + +--- a/drivers/media/i2c/ov5647.c ++++ b/drivers/media/i2c/ov5647.c +@@ -90,6 +90,8 @@ struct ov5647_mode { + struct v4l2_rect crop; + + u64 pixel_rate; ++ /* HTS as defined in the register set (0x380C/0x380D) */ ++ int hts; + + struct regval_list *reg_list; + unsigned int num_regs; +@@ -106,6 +108,7 @@ struct ov5647 { + unsigned int flags; + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *pixel_rate; ++ struct v4l2_ctrl *hblank; + bool write_mode_regs; + }; + +@@ -605,6 +608,7 @@ static struct ov5647_mode supported_mode + .height = 960, + }, + .pixel_rate = 77291670, ++ .hts = 1896, + ov5647_640x480_8bit, + ARRAY_SIZE(ov5647_640x480_8bit) + }, +@@ -629,6 +633,7 @@ static struct ov5647_mode supported_mode + .height = 1944 + }, + .pixel_rate = 87500000, ++ .hts = 2844, + ov5647_2592x1944_10bit, + ARRAY_SIZE(ov5647_2592x1944_10bit) + }, +@@ -651,6 +656,7 @@ static struct ov5647_mode supported_mode + .height = 1080, + }, + .pixel_rate = 81666700, ++ .hts = 2416, + ov5647_1080p30_10bit, + ARRAY_SIZE(ov5647_1080p30_10bit) + }, +@@ -672,6 +678,7 @@ static struct ov5647_mode supported_mode + .height = 1944, + }, + .pixel_rate = 81666700, ++ .hts = 1896, + ov5647_2x2binned_10bit, + ARRAY_SIZE(ov5647_2x2binned_10bit) + }, +@@ -694,6 +701,7 @@ static struct ov5647_mode supported_mode + .height = 1920, + }, + .pixel_rate = 55000000, ++ .hts = 1852, + ov5647_640x480_10bit, + ARRAY_SIZE(ov5647_640x480_10bit) + }, +@@ -1168,6 +1176,8 @@ static int ov5647_set_fmt(struct v4l2_su + * If we have changed modes, write the I2C register list on + * a stream_on(). + */ ++ int hblank; ++ + if (state->mode != mode) + state->write_mode_regs = true; + state->mode = mode; +@@ -1176,6 +1186,9 @@ static int ov5647_set_fmt(struct v4l2_su + mode->pixel_rate, + mode->pixel_rate, 1, + mode->pixel_rate); ++ hblank = mode->hts - mode->format.width; ++ __v4l2_ctrl_modify_range(state->hblank, hblank, hblank, 1, ++ hblank); + } + + mutex_unlock(&state->lock); +@@ -1395,6 +1408,9 @@ static int ov5647_s_ctrl(struct v4l2_ctr + case V4L2_CID_PIXEL_RATE: + /* Read-only, but we adjust it based on mode. */ + break; ++ case V4L2_CID_HBLANK: ++ /* Read-only, but we adjust it based on mode. */ ++ break; + default: + dev_info(&client->dev, + "ctrl(id:0x%x,val:0x%x) is not handled\n", +@@ -1419,6 +1435,7 @@ static int ov5647_probe(struct i2c_clien + struct device_node *np = client->dev.of_node; + u32 xclk_freq; + struct v4l2_ctrl *ctrl; ++ int hblank; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) +@@ -1452,7 +1469,7 @@ static int ov5647_probe(struct i2c_clien + mutex_init(&sensor->lock); + + /* Initialise controls. */ +- v4l2_ctrl_handler_init(&sensor->ctrls, 6); ++ v4l2_ctrl_handler_init(&sensor->ctrls, 7); + v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_AUTOGAIN, + 0, /* min */ +@@ -1495,6 +1512,13 @@ static int ov5647_probe(struct i2c_clien + sensor->mode->pixel_rate, 1, + sensor->mode->pixel_rate); + ++ /* By default, HBLANK is read only, but it does change per mode */ ++ hblank = sensor->mode->hts - sensor->mode->format.width; ++ sensor->hblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, ++ V4L2_CID_HBLANK, hblank, hblank, 1, ++ hblank); ++ sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ + if (sensor->ctrls.error) { + ret = sensor->ctrls.error; + dev_err(&client->dev, "%s control init failed (%d)\n", +@@ -1509,7 +1533,8 @@ static int ov5647_probe(struct i2c_clien + sd = &sensor->sd; + v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops); + sensor->sd.internal_ops = &ov5647_subdev_internal_ops; +- sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | ++ V4L2_SUBDEV_FL_HAS_EVENTS; + + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; diff --git a/target/linux/bcm27xx/patches-5.4/950-0690-media-i2c-ov5647-Add-support-for-V4L2_CID_VBLANK.patch b/target/linux/bcm27xx/patches-5.4/950-0690-media-i2c-ov5647-Add-support-for-V4L2_CID_VBLANK.patch new file mode 100644 index 0000000000..85d63cf1a3 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0690-media-i2c-ov5647-Add-support-for-V4L2_CID_VBLANK.patch @@ -0,0 +1,205 @@ +From fbb943e35b519549eac8ee17bf20d651388a27dd Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 29 Apr 2020 21:39:58 +0100 +Subject: [PATCH] media: i2c: ov5647: Add support for V4L2_CID_VBLANK + +Adds vblank control to allow for frame rate control. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/media/i2c/ov5647.c | 65 ++++++++++++++++++++++++++++++++------ + 1 file changed, 55 insertions(+), 10 deletions(-) + +--- a/drivers/media/i2c/ov5647.c ++++ b/drivers/media/i2c/ov5647.c +@@ -61,6 +61,8 @@ + #define OV5647_REG_AEC_AGC 0x3503 + #define OV5647_REG_GAIN_HI 0x350A + #define OV5647_REG_GAIN_LO 0x350B ++#define OV5647_REG_VTS_HI 0x380e ++#define OV5647_REG_VTS_LO 0x380f + #define OV5647_REG_FRAME_OFF_NUMBER 0x4202 + #define OV5647_REG_MIPI_CTRL00 0x4800 + #define OV5647_REG_MIPI_CTRL14 0x4814 +@@ -79,6 +81,9 @@ + #define OV5647_PIXEL_ARRAY_WIDTH 2592U + #define OV5647_PIXEL_ARRAY_HEIGHT 1944U + ++#define OV5647_VBLANK_MIN 4 ++#define OV5647_VTS_MAX 32767 ++ + struct regval_list { + u16 addr; + u8 data; +@@ -92,6 +97,8 @@ struct ov5647_mode { + u64 pixel_rate; + /* HTS as defined in the register set (0x380C/0x380D) */ + int hts; ++ /* Default VTS value for this mode */ ++ int vts_def; + + struct regval_list *reg_list; + unsigned int num_regs; +@@ -109,6 +116,7 @@ struct ov5647 { + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *hblank; ++ struct v4l2_ctrl *vblank; + bool write_mode_regs; + }; + +@@ -161,8 +169,6 @@ static struct regval_list ov5647_640x480 + {0x3b07, 0x0c}, + {0x380c, 0x07}, + {0x380d, 0x68}, +- {0x380e, 0x03}, +- {0x380f, 0xd8}, + {0x3814, 0x31}, + {0x3815, 0x31}, + {0x3708, 0x64}, +@@ -251,8 +257,6 @@ static struct regval_list ov5647_2592x19 + {0x3b07, 0x0c}, + {0x380c, 0x0b}, + {0x380d, 0x1c}, +- {0x380e, 0x07}, +- {0x380f, 0xb0}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3708, 0x64}, +@@ -342,8 +346,6 @@ static struct regval_list ov5647_1080p30 + {0x3b07, 0x0c}, + {0x380c, 0x09}, + {0x380d, 0x70}, +- {0x380e, 0x04}, +- {0x380f, 0x50}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3708, 0x64}, +@@ -485,8 +487,6 @@ static struct regval_list ov5647_2x2binn + {0x3503, 0x03}, + {0x3820, 0x41}, + {0x3821, 0x07}, +- {0x380E, 0x05}, +- {0x380F, 0x9B}, + {0x350A, 0x00}, + {0x350B, 0x10}, + {0x3500, 0x00}, +@@ -520,8 +520,6 @@ static struct regval_list ov5647_640x480 + {0x3b07, 0x0c}, + {0x380c, 0x07}, + {0x380d, 0x3c}, +- {0x380e, 0x01}, +- {0x380f, 0xf8}, + {0x3814, 0x35}, + {0x3815, 0x35}, + {0x3708, 0x64}, +@@ -609,6 +607,7 @@ static struct ov5647_mode supported_mode + }, + .pixel_rate = 77291670, + .hts = 1896, ++ .vts_def = 0x3d8, + ov5647_640x480_8bit, + ARRAY_SIZE(ov5647_640x480_8bit) + }, +@@ -634,6 +633,7 @@ static struct ov5647_mode supported_mode + }, + .pixel_rate = 87500000, + .hts = 2844, ++ .vts_def = 0x7b0, + ov5647_2592x1944_10bit, + ARRAY_SIZE(ov5647_2592x1944_10bit) + }, +@@ -657,6 +657,7 @@ static struct ov5647_mode supported_mode + }, + .pixel_rate = 81666700, + .hts = 2416, ++ .vts_def = 0x450, + ov5647_1080p30_10bit, + ARRAY_SIZE(ov5647_1080p30_10bit) + }, +@@ -679,6 +680,7 @@ static struct ov5647_mode supported_mode + }, + .pixel_rate = 81666700, + .hts = 1896, ++ .vts_def = 0x59b, + ov5647_2x2binned_10bit, + ARRAY_SIZE(ov5647_2x2binned_10bit) + }, +@@ -702,6 +704,7 @@ static struct ov5647_mode supported_mode + }, + .pixel_rate = 55000000, + .hts = 1852, ++ .vts_def = 0x1f8, + ov5647_640x480_10bit, + ARRAY_SIZE(ov5647_640x480_10bit) + }, +@@ -710,6 +713,29 @@ static struct ov5647_mode supported_mode + /* Use 2x2 binned 10-bit mode as default. */ + #define OV5647_DEFAULT_MODE (&supported_modes_10bit[2]) + ++static int ov5647_write16(struct v4l2_subdev *sd, u16 reg, u16 val) ++{ ++ int ret; ++ unsigned char data[4] = { reg >> 8, reg & 0xff, val >> 8, val & 0xff}; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ ret = i2c_master_send(client, data, 4); ++ /* ++ * Writing the wrong number of bytes also needs to be flagged as an ++ * error. Success needs to produce a 0 return code. ++ */ ++ if (ret == 4) { ++ ret = 0; ++ } else { ++ dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", ++ __func__, reg); ++ if (ret >= 0) ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ + static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val) + { + int ret; +@@ -1189,6 +1215,14 @@ static int ov5647_set_fmt(struct v4l2_su + hblank = mode->hts - mode->format.width; + __v4l2_ctrl_modify_range(state->hblank, hblank, hblank, 1, + hblank); ++ ++ __v4l2_ctrl_modify_range(state->vblank, ++ OV5647_VBLANK_MIN, ++ OV5647_VTS_MAX - mode->format.height, ++ 1, ++ mode->vts_def - mode->format.height); ++ __v4l2_ctrl_s_ctrl(state->vblank, ++ mode->vts_def - mode->format.height); + } + + mutex_unlock(&state->lock); +@@ -1411,6 +1445,10 @@ static int ov5647_s_ctrl(struct v4l2_ctr + case V4L2_CID_HBLANK: + /* Read-only, but we adjust it based on mode. */ + break; ++ case V4L2_CID_VBLANK: ++ ret = ov5647_write16(sd, OV5647_REG_VTS_HI, ++ state->mode->format.height + ctrl->val); ++ break; + default: + dev_info(&client->dev, + "ctrl(id:0x%x,val:0x%x) is not handled\n", +@@ -1519,6 +1557,13 @@ static int ov5647_probe(struct i2c_clien + hblank); + sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + ++ sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, ++ V4L2_CID_VBLANK, OV5647_VBLANK_MIN, ++ OV5647_VTS_MAX - ++ sensor->mode->format.height, 1, ++ sensor->mode->vts_def - ++ sensor->mode->format.height); ++ + if (sensor->ctrls.error) { + ret = sensor->ctrls.error; + dev_err(&client->dev, "%s control init failed (%d)\n", diff --git a/target/linux/bcm27xx/patches-5.4/950-0691-media-i2c-ov5647-Neither-analogue-gain-nor-exposure-.patch b/target/linux/bcm27xx/patches-5.4/950-0691-media-i2c-ov5647-Neither-analogue-gain-nor-exposure-.patch new file mode 100644 index 0000000000..039809e94e --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0691-media-i2c-ov5647-Neither-analogue-gain-nor-exposure-.patch @@ -0,0 +1,58 @@ +From ea0b801a818e837e657c53687f03d62805e2e586 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 29 Apr 2020 21:47:25 +0100 +Subject: [PATCH] media: i2c: ov5647: Neither analogue gain nor + exposure need EXECUTE_ON_WRITE + +The controls for analogue gain and exposure were defined with +V4L2_CTRL_FLAG_EXECUTE_ON_WRITE. This is not required as we only need +to send changes to the sensor. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/media/i2c/ov5647.c | 27 ++++++++++++--------------- + 1 file changed, 12 insertions(+), 15 deletions(-) + +--- a/drivers/media/i2c/ov5647.c ++++ b/drivers/media/i2c/ov5647.c +@@ -1472,7 +1472,6 @@ 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; + int hblank; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); +@@ -1525,20 +1524,18 @@ static int ov5647_probe(struct i2c_clien + V4L2_EXPOSURE_MANUAL, /* max */ + 0, /* skip_mask */ + V4L2_EXPOSURE_MANUAL); /* 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; ++ 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 */ ++ 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 */ + + /* Set the default mode before we init the subdev */ + sensor->mode = OV5647_DEFAULT_MODE; diff --git a/target/linux/bcm27xx/patches-5.4/950-0692-media-i2c-ov5647-Use-member-names-in-mode-tables.patch b/target/linux/bcm27xx/patches-5.4/950-0692-media-i2c-ov5647-Use-member-names-in-mode-tables.patch new file mode 100644 index 0000000000..d8ef700a59 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0692-media-i2c-ov5647-Use-member-names-in-mode-tables.patch @@ -0,0 +1,111 @@ +From 95b6a6fea10497ca8f583768522d80317f8d700e Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 29 Apr 2020 22:11:01 +0100 +Subject: [PATCH] media: i2c: ov5647: Use member names in mode tables + +To make adding new members to the mode structures easier, use +the member names in the initialisers. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/media/i2c/ov5647.c | 30 +++++++++++++++--------------- + 1 file changed, 15 insertions(+), 15 deletions(-) + +--- a/drivers/media/i2c/ov5647.c ++++ b/drivers/media/i2c/ov5647.c +@@ -592,7 +592,7 @@ static struct ov5647_mode supported_mode + * Uncentred crop (top left quarter) from 2x2 binned 1296x972 image. + */ + { +- { ++ .format = { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, +@@ -608,8 +608,8 @@ static struct ov5647_mode supported_mode + .pixel_rate = 77291670, + .hts = 1896, + .vts_def = 0x3d8, +- ov5647_640x480_8bit, +- ARRAY_SIZE(ov5647_640x480_8bit) ++ .reg_list = ov5647_640x480_8bit, ++ .num_regs = ARRAY_SIZE(ov5647_640x480_8bit) + }, + }; + +@@ -618,7 +618,7 @@ static struct ov5647_mode supported_mode + * MODE 0: 2592x1944 full resolution full FOV 10-bit mode. + */ + { +- { ++ .format = { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, +@@ -634,15 +634,15 @@ static struct ov5647_mode supported_mode + .pixel_rate = 87500000, + .hts = 2844, + .vts_def = 0x7b0, +- ov5647_2592x1944_10bit, +- ARRAY_SIZE(ov5647_2592x1944_10bit) ++ .reg_list = ov5647_2592x1944_10bit, ++ .num_regs = ARRAY_SIZE(ov5647_2592x1944_10bit) + }, + /* + * MODE 1: 1080p30 10-bit mode. + * Full resolution centre-cropped down to 1080p. + */ + { +- { ++ .format = { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, +@@ -658,14 +658,14 @@ static struct ov5647_mode supported_mode + .pixel_rate = 81666700, + .hts = 2416, + .vts_def = 0x450, +- ov5647_1080p30_10bit, +- ARRAY_SIZE(ov5647_1080p30_10bit) ++ .reg_list = ov5647_1080p30_10bit, ++ .num_regs = ARRAY_SIZE(ov5647_1080p30_10bit) + }, + /* + * MODE 2: 2x2 binned full FOV 10-bit mode. + */ + { +- { ++ .format = { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, +@@ -681,15 +681,15 @@ static struct ov5647_mode supported_mode + .pixel_rate = 81666700, + .hts = 1896, + .vts_def = 0x59b, +- ov5647_2x2binned_10bit, +- ARRAY_SIZE(ov5647_2x2binned_10bit) ++ .reg_list = ov5647_2x2binned_10bit, ++ .num_regs = ARRAY_SIZE(ov5647_2x2binned_10bit) + }, + /* + * MODE 3: 10-bit VGA full FOV mode 60fps. + * 2x2 binned and subsampled down to VGA. + */ + { +- { ++ .format = { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, +@@ -705,8 +705,8 @@ static struct ov5647_mode supported_mode + .pixel_rate = 55000000, + .hts = 1852, + .vts_def = 0x1f8, +- ov5647_640x480_10bit, +- ARRAY_SIZE(ov5647_640x480_10bit) ++ .reg_list = ov5647_640x480_10bit, ++ .num_regs = ARRAY_SIZE(ov5647_640x480_10bit) + }, + }; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0693-media-i2c-ov5647-Advertise-the-correct-exposure-rang.patch b/target/linux/bcm27xx/patches-5.4/950-0693-media-i2c-ov5647-Advertise-the-correct-exposure-rang.patch new file mode 100644 index 0000000000..9b874485a8 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0693-media-i2c-ov5647-Advertise-the-correct-exposure-rang.patch @@ -0,0 +1,119 @@ +From c92b64465d1f6dbfaf189dbf68128ec52fcb0521 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Thu, 30 Apr 2020 11:03:00 +0100 +Subject: [PATCH] media: i2c: ov5647: Advertise the correct exposure + range + +Exposure is clipped by the VTS of the mode, so needs to be updated as +and when this is changed. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/media/i2c/ov5647.c | 47 +++++++++++++++++++++++++++++++------- + 1 file changed, 39 insertions(+), 8 deletions(-) + +--- a/drivers/media/i2c/ov5647.c ++++ b/drivers/media/i2c/ov5647.c +@@ -84,6 +84,11 @@ + #define OV5647_VBLANK_MIN 4 + #define OV5647_VTS_MAX 32767 + ++#define OV5647_EXPOSURE_MIN 4 ++#define OV5647_EXPOSURE_STEP 1 ++#define OV5647_EXPOSURE_DEFAULT 1000 ++#define OV5647_EXPOSURE_MAX 65535 ++ + struct regval_list { + u16 addr; + u8 data; +@@ -117,6 +122,7 @@ struct ov5647 { + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; ++ struct v4l2_ctrl *exposure; + bool write_mode_regs; + }; + +@@ -1202,7 +1208,7 @@ static int ov5647_set_fmt(struct v4l2_su + * If we have changed modes, write the I2C register list on + * a stream_on(). + */ +- int hblank; ++ int exposure_max, exposure_def, hblank; + + if (state->mode != mode) + state->write_mode_regs = true; +@@ -1223,6 +1229,15 @@ static int ov5647_set_fmt(struct v4l2_su + mode->vts_def - mode->format.height); + __v4l2_ctrl_s_ctrl(state->vblank, + mode->vts_def - mode->format.height); ++ ++ exposure_max = mode->vts_def - 4; ++ exposure_def = (exposure_max < OV5647_EXPOSURE_DEFAULT) ? ++ exposure_max : OV5647_EXPOSURE_DEFAULT; ++ __v4l2_ctrl_modify_range(state->exposure, ++ state->exposure->minimum, ++ exposure_max, ++ state->exposure->step, ++ exposure_def); + } + + mutex_unlock(&state->lock); +@@ -1415,6 +1430,19 @@ static int ov5647_s_ctrl(struct v4l2_ctr + + /* v4l2_ctrl_lock() locks our own mutex */ + ++ if (ctrl->id == V4L2_CID_VBLANK) { ++ int exposure_max, exposure_def; ++ ++ /* Update max exposure while meeting expected vblanking */ ++ exposure_max = state->mode->format.height + ctrl->val - 4; ++ exposure_def = (exposure_max < OV5647_EXPOSURE_DEFAULT) ? ++ exposure_max : OV5647_EXPOSURE_DEFAULT; ++ __v4l2_ctrl_modify_range(state->exposure, ++ state->exposure->minimum, ++ exposure_max, state->exposure->step, ++ exposure_def); ++ } ++ + /* + * If the device is not powered up by the host driver do + * not apply any controls to H/W at this time. Instead +@@ -1472,7 +1500,7 @@ static int ov5647_probe(struct i2c_clien + struct v4l2_subdev *sd; + struct device_node *np = client->dev.of_node; + u32 xclk_freq; +- int hblank; ++ int hblank, exposure_max, exposure_def; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) +@@ -1525,12 +1553,6 @@ static int ov5647_probe(struct i2c_clien + 0, /* skip_mask */ + V4L2_EXPOSURE_MANUAL); /* default */ + 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 */ +- v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + 16, /* min, 16 = 1.0x */ + 1023, /* max (10 bits) */ +@@ -1540,6 +1562,15 @@ static int ov5647_probe(struct i2c_clien + /* Set the default mode before we init the subdev */ + sensor->mode = OV5647_DEFAULT_MODE; + ++ exposure_max = sensor->mode->vts_def - 4; ++ exposure_def = (exposure_max < OV5647_EXPOSURE_DEFAULT) ? ++ exposure_max : OV5647_EXPOSURE_DEFAULT; ++ sensor->exposure = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, ++ V4L2_CID_EXPOSURE, ++ OV5647_EXPOSURE_MIN, exposure_max, ++ OV5647_EXPOSURE_STEP, ++ exposure_def); ++ + /* By default, PIXEL_RATE is read only, but it does change per mode */ + sensor->pixel_rate = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_PIXEL_RATE, diff --git a/target/linux/bcm27xx/patches-5.4/950-0694-media-i2c-imx219-Declare-that-the-driver-can-create-.patch b/target/linux/bcm27xx/patches-5.4/950-0694-media-i2c-imx219-Declare-that-the-driver-can-create-.patch new file mode 100644 index 0000000000..fa700a81a3 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0694-media-i2c-imx219-Declare-that-the-driver-can-create-.patch @@ -0,0 +1,27 @@ +From 58694d6b23e4138640042fd779df95c425be825b Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Mon, 20 Apr 2020 11:01:21 +0100 +Subject: [PATCH] media: i2c: imx219: Declare that the driver can + create events + +The flag V4L2_SUBDEV_FL_HAS_EVENTS is required if the subdev can +generate events. It can create events from the ctrl handler, therefore +this is required. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/media/i2c/imx219.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/media/i2c/imx219.c ++++ b/drivers/media/i2c/imx219.c +@@ -1573,7 +1573,8 @@ static int imx219_probe(struct i2c_clien + + /* Initialize subdev */ + imx219->sd.internal_ops = &imx219_internal_ops; +- imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | ++ V4L2_SUBDEV_FL_HAS_EVENTS; + imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pads */ diff --git a/target/linux/bcm27xx/patches-5.4/950-0695-media-bcm2835-unicam-Add-support-for-VIDIOC_-S-G-_SE.patch b/target/linux/bcm27xx/patches-5.4/950-0695-media-bcm2835-unicam-Add-support-for-VIDIOC_-S-G-_SE.patch new file mode 100644 index 0000000000..b30d106f6e --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0695-media-bcm2835-unicam-Add-support-for-VIDIOC_-S-G-_SE.patch @@ -0,0 +1,82 @@ +From 40aaca6ed160e67e518c512908cf49efb4cbed8b Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 29 Apr 2020 16:45:02 +0100 +Subject: [PATCH] media: bcm2835-unicam: Add support for + VIDIOC_[S|G]_SELECTION + +Sensors are now reflecting cropping and scaling parameters through +the selection API, therefore Unicam needs to forward the requests +through to the subdev. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + .../media/platform/bcm2835/bcm2835-unicam.c | 44 +++++++++++++++++++ + 1 file changed, 44 insertions(+) + +--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c ++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c +@@ -1898,6 +1898,39 @@ static int unicam_g_edid(struct file *fi + return v4l2_subdev_call(dev->sensor, pad, get_edid, edid); + } + ++static int unicam_s_selection(struct file *file, void *priv, ++ struct v4l2_selection *sel) ++{ ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; ++ struct v4l2_subdev_selection sdsel = { ++ .which = V4L2_SUBDEV_FORMAT_ACTIVE, ++ .target = sel->target, ++ .flags = sel->flags, ++ .r = sel->r, ++ }; ++ ++ return v4l2_subdev_call(dev->sensor, pad, set_selection, NULL, &sdsel); ++} ++ ++static int unicam_g_selection(struct file *file, void *priv, ++ struct v4l2_selection *sel) ++{ ++ struct unicam_node *node = video_drvdata(file); ++ struct unicam_device *dev = node->dev; ++ struct v4l2_subdev_selection sdsel = { ++ .which = V4L2_SUBDEV_FORMAT_ACTIVE, ++ .target = sel->target, ++ }; ++ int ret; ++ ++ ret = v4l2_subdev_call(dev->sensor, pad, get_selection, NULL, &sdsel); ++ if (!ret) ++ sel->r = sdsel.r; ++ ++ return ret; ++} ++ + static int unicam_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) + { +@@ -2218,6 +2251,9 @@ static const struct v4l2_ioctl_ops unica + .vidioc_enum_framesizes = unicam_enum_framesizes, + .vidioc_enum_frameintervals = unicam_enum_frameintervals, + ++ .vidioc_g_selection = unicam_g_selection, ++ .vidioc_s_selection = unicam_s_selection, ++ + .vidioc_g_parm = unicam_g_parm, + .vidioc_s_parm = unicam_s_parm, + +@@ -2446,6 +2482,14 @@ static int register_node(struct unicam_d + !v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_size)) + v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_FRAMESIZES); + ++ if (node->pad_id == METADATA_PAD || ++ !v4l2_subdev_has_op(unicam->sensor, pad, set_selection)) ++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_SELECTION); ++ ++ if (node->pad_id == METADATA_PAD || ++ !v4l2_subdev_has_op(unicam->sensor, pad, get_selection)) ++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_SELECTION); ++ + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret) { + unicam_err(unicam, "Unable to register video device.\n"); diff --git a/target/linux/bcm27xx/patches-5.4/950-0696-media-bcm2835-unicam-Do-not-stop-streaming-in-unicam.patch b/target/linux/bcm27xx/patches-5.4/950-0696-media-bcm2835-unicam-Do-not-stop-streaming-in-unicam.patch new file mode 100644 index 0000000000..74abba4ac9 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0696-media-bcm2835-unicam-Do-not-stop-streaming-in-unicam.patch @@ -0,0 +1,28 @@ +From 37e9677654a48cfa748c1d22fbf782920ab2cb23 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 29 Apr 2020 22:05:09 +0100 +Subject: [PATCH] media: bcm2835-unicam: Do not stop streaming in + unicam_release + +unicam_release calls _vb2_fop_release, which will call stop_streaming +if that particular node was streaming. Calling it unconditionally (as +the code was) means that if a second handle was opened eg to alter +a setting, on closing that connection it also stopped Unicam. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/media/platform/bcm2835/bcm2835-unicam.c | 3 --- + 1 file changed, 3 deletions(-) + +--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c ++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c +@@ -2204,9 +2204,6 @@ static int unicam_release(struct file *f + if (fh_singular) + v4l2_subdev_call(sd, core, s_power, 0); + +- if (node->streaming) +- unicam_stop_streaming(&node->buffer_queue); +- + node->open--; + mutex_unlock(&node->lock); + diff --git a/target/linux/bcm27xx/patches-5.4/950-0697-media-bcm2835-unicam-Fix-reference-counting-in-unica.patch b/target/linux/bcm27xx/patches-5.4/950-0697-media-bcm2835-unicam-Fix-reference-counting-in-unica.patch new file mode 100644 index 0000000000..ba979632ae --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0697-media-bcm2835-unicam-Fix-reference-counting-in-unica.patch @@ -0,0 +1,38 @@ +From 3681c556d79797f132e18973611a14681ed47f76 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Thu, 30 Apr 2020 09:52:50 +0100 +Subject: [PATCH] media: bcm2835-unicam: Fix reference counting in + unicam_open + +The reference counting of node->open was only incremented after +a check that the node was v4l2_fh_is_singular_file, which resulted +in the counting going wrong and s_power not being called at an +appropriate time. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/media/platform/bcm2835/bcm2835-unicam.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c ++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c +@@ -2170,16 +2170,18 @@ static int unicam_open(struct file *file + goto unlock; + } + ++ node->open++; ++ + if (!v4l2_fh_is_singular_file(file)) + goto unlock; + + ret = v4l2_subdev_call(dev->sensor, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) { + v4l2_fh_release(file); ++ node->open--; + goto unlock; + } + +- node->open++; + ret = 0; + + unlock: diff --git a/target/linux/bcm27xx/patches-5.4/950-0698-staging-vc04_services-ISP-Add-enum_framesizes-ioctl.patch b/target/linux/bcm27xx/patches-5.4/950-0698-staging-vc04_services-ISP-Add-enum_framesizes-ioctl.patch new file mode 100644 index 0000000000..1673bde514 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0698-staging-vc04_services-ISP-Add-enum_framesizes-ioctl.patch @@ -0,0 +1,333 @@ +From 01a1601512893ce9a3353e1686a95a9861f3d685 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Fri, 1 May 2020 14:15:24 +0100 +Subject: [PATCH] staging: vc04_services: ISP: Add enum_framesizes + ioctl + +This is used to enumerate available frame sizes on all nodes +apart from statistics output. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + .../bcm2835-isp/bcm2835-v4l2-isp.c | 48 +++++++++++++++++-- + .../bcm2835-isp/bcm2835_isp_fmts.h | 29 +++++++++++ + 2 files changed, 72 insertions(+), 5 deletions(-) + +--- a/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c ++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c +@@ -227,8 +227,9 @@ static const struct bcm2835_isp_fmt *get + return NULL; + } + +-static struct bcm2835_isp_fmt *find_format(struct v4l2_format *f, +- struct bcm2835_isp_node *node) ++static const ++struct bcm2835_isp_fmt *find_format_by_fourcc(unsigned int fourcc, ++ struct bcm2835_isp_node *node) + { + struct bcm2835_isp_fmt_list *fmts = &node->supported_fmts; + struct bcm2835_isp_fmt *fmt; +@@ -236,15 +237,22 @@ static struct bcm2835_isp_fmt *find_form + + for (i = 0; i < fmts->num_entries; i++) { + fmt = &fmts->list[i]; +- if (fmt->fourcc == (node_is_stats(node) ? +- f->fmt.meta.dataformat : +- f->fmt.pix.pixelformat)) ++ if (fmt->fourcc == fourcc) + return fmt; + } + + return NULL; + } + ++static struct bcm2835_isp_fmt *find_format(struct v4l2_format *f, ++ struct bcm2835_isp_node *node) ++{ ++ return find_format_by_fourcc(node_is_stats(node) ? ++ f->fmt.meta.dataformat : ++ f->fmt.pix.pixelformat, ++ node); ++} ++ + /* vb2_to_mmal_buffer() - converts vb2 buffer header to MMAL + * + * Copies all the required fields from a VB2 buffer to the MMAL buffer header, +@@ -892,6 +900,35 @@ static int bcm2835_isp_node_enum_fmt(str + return -EINVAL; + } + ++static int bcm2835_isp_enum_framesizes(struct file *file, void *priv, ++ struct v4l2_frmsizeenum *fsize) ++{ ++ struct bcm2835_isp_node *node = video_drvdata(file); ++ struct bcm2835_isp_dev *dev = node_get_dev(node); ++ struct bcm2835_isp_fmt *fmt; ++ ++ if (node_is_stats(node) || fsize->index) ++ return -EINVAL; ++ ++ fmt = find_format_by_fourcc(fsize->pixel_format, node); ++ if (!fmt) { ++ v4l2_err(&dev->v4l2_dev, "Invalid pixel code: %x\n", ++ fsize->pixel_format); ++ return -EINVAL; ++ } ++ ++ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; ++ fsize->stepwise.min_width = MIN_DIM; ++ fsize->stepwise.max_width = MAX_DIM; ++ fsize->stepwise.step_width = fmt->step_size; ++ ++ fsize->stepwise.min_height = MIN_DIM; ++ fsize->stepwise.max_height = MAX_DIM; ++ fsize->stepwise.step_height = fmt->step_size; ++ ++ return 0; ++} ++ + static int bcm2835_isp_node_try_fmt(struct file *file, void *priv, + struct v4l2_format *f) + { +@@ -1046,6 +1083,7 @@ static const struct v4l2_ioctl_ops bcm28 + .vidioc_enum_fmt_vid_cap = bcm2835_isp_node_enum_fmt, + .vidioc_enum_fmt_vid_out = bcm2835_isp_node_enum_fmt, + .vidioc_enum_fmt_meta_cap = bcm2835_isp_node_enum_fmt, ++ .vidioc_enum_framesizes = bcm2835_isp_enum_framesizes, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, +--- a/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h ++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h +@@ -22,6 +22,7 @@ struct bcm2835_isp_fmt { + u32 mmal_fmt; + int size_multiplier_x2; + enum v4l2_colorspace colorspace; ++ unsigned int step_size; + }; + + struct bcm2835_isp_fmt_list { +@@ -39,6 +40,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_I420, + .size_multiplier_x2 = 3, + .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_YVU420, + .depth = 8, +@@ -47,6 +49,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_YV12, + .size_multiplier_x2 = 3, + .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_NV12, + .depth = 8, +@@ -55,6 +58,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_NV12, + .size_multiplier_x2 = 3, + .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_NV21, + .depth = 8, +@@ -63,6 +67,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_NV21, + .size_multiplier_x2 = 3, + .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, +@@ -71,6 +76,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_YUYV, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, +@@ -79,6 +85,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_UYVY, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_YVYU, + .depth = 16, +@@ -87,6 +94,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_YVYU, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .depth = 16, +@@ -95,6 +103,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_VYUY, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ .step_size = 2, + }, { + /* RGB formats */ + .fourcc = V4L2_PIX_FMT_RGB24, +@@ -104,6 +113,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_RGB24, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_SRGB, ++ .step_size = 1, + }, { + .fourcc = V4L2_PIX_FMT_RGB565, + .depth = 16, +@@ -112,6 +122,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_RGB16, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_SRGB, ++ .step_size = 1, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, + .depth = 24, +@@ -120,6 +131,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_BGR24, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_SRGB, ++ .step_size = 1, + }, { + .fourcc = V4L2_PIX_FMT_ABGR32, + .depth = 32, +@@ -128,6 +140,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_BGRA, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_SRGB, ++ .step_size = 1, + }, { + /* Bayer formats */ + /* 8 bit */ +@@ -138,6 +151,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB8, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .depth = 8, +@@ -146,6 +160,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR8, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .depth = 8, +@@ -154,6 +169,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG8, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .depth = 8, +@@ -162,6 +178,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG8, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, ++ .step_size = 2, + }, { + /* 10 bit */ + .fourcc = V4L2_PIX_FMT_SRGGB10P, +@@ -171,6 +188,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB10P, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10P, + .depth = 10, +@@ -179,6 +197,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR10P, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10P, + .depth = 10, +@@ -187,6 +206,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG10P, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10P, + .depth = 10, +@@ -195,6 +215,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG10P, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, ++ .step_size = 2, + }, { + /* 12 bit */ + .fourcc = V4L2_PIX_FMT_SRGGB12P, +@@ -204,6 +225,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB12P, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR12P, + .depth = 12, +@@ -212,6 +234,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR12P, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG12P, + .depth = 12, +@@ -220,6 +243,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG12P, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG12P, + .depth = 12, +@@ -228,6 +252,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG12P, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, ++ .step_size = 2, + }, { + /* 16 bit */ + .fourcc = V4L2_PIX_FMT_SRGGB16, +@@ -237,6 +262,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB16, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR16, + .depth = 16, +@@ -245,6 +271,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR16, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG16, + .depth = 16, +@@ -253,6 +280,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG16, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, ++ .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG16, + .depth = 16, +@@ -261,6 +289,7 @@ static const struct bcm2835_isp_fmt supp + .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG16, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, ++ .step_size = 2, + }, { + /* ISP statistics format */ + .fourcc = V4L2_META_FMT_BCM2835_ISP_STATS, diff --git a/target/linux/bcm27xx/patches-5.4/950-0699-SQUASH-spi-Demote-SPI_CS_HIGH-warning-to-KERN_DEBUG.patch b/target/linux/bcm27xx/patches-5.4/950-0699-SQUASH-spi-Demote-SPI_CS_HIGH-warning-to-KERN_DEBUG.patch new file mode 100644 index 0000000000..2be5524a07 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0699-SQUASH-spi-Demote-SPI_CS_HIGH-warning-to-KERN_DEBUG.patch @@ -0,0 +1,28 @@ +From 4172a6bd7e4afada99911947a335d47e94802be5 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Fri, 1 May 2020 14:58:23 +0100 +Subject: [PATCH] SQUASH: spi: Demote SPI_CS_HIGH warning to KERN_DEBUG + +This warning is unavoidable from a client's perspective and +doesn't indicate anything wrong (just surprising). + +SQUASH with "spi: use_gpio_descriptor fixup moved to spi_setup" + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + drivers/spi/spi.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -3044,8 +3044,8 @@ int spi_setup(struct spi_device *spi) + + if (ctlr->use_gpio_descriptors && ctlr->cs_gpiods && + ctlr->cs_gpiods[spi->chip_select] && !(spi->mode & SPI_CS_HIGH)) { +- dev_warn(&spi->dev, +- "setup: forcing CS_HIGH (use_gpio_descriptors)\n"); ++ dev_dbg(&spi->dev, ++ "setup: forcing CS_HIGH (use_gpio_descriptors)\n"); + spi->mode |= SPI_CS_HIGH; + } + diff --git a/target/linux/bcm27xx/patches-5.4/950-0700-bcm2835-dma-Add-proper-40-bit-DMA-support.patch b/target/linux/bcm27xx/patches-5.4/950-0700-bcm2835-dma-Add-proper-40-bit-DMA-support.patch new file mode 100644 index 0000000000..3fb47589ee --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0700-bcm2835-dma-Add-proper-40-bit-DMA-support.patch @@ -0,0 +1,801 @@ +From a2f673d1aa39752608e5f4838ed9656b38cbc4b9 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.org> +Date: Thu, 4 Apr 2019 13:33:47 +0100 +Subject: [PATCH] bcm2835-dma: Add proper 40-bit DMA support + +BCM2711 has 4 DMA channels with a 40-bit address range, allowing them +to access the full 4GB of memory on a Pi 4. + +Signed-off-by: Phil Elwell <phil@raspberrypi.org> +--- + drivers/dma/bcm2835-dma.c | 485 ++++++++++++++++++++++++++++++++------ + 1 file changed, 412 insertions(+), 73 deletions(-) + +--- a/drivers/dma/bcm2835-dma.c ++++ b/drivers/dma/bcm2835-dma.c +@@ -38,6 +38,11 @@ + #define BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED 14 + #define BCM2835_DMA_CHAN_NAME_SIZE 8 + #define BCM2835_DMA_BULK_MASK BIT(0) ++#define BCM2711_DMA_MEMCPY_CHAN 14 ++ ++struct bcm2835_dma_cfg_data { ++ u32 chan_40bit_mask; ++}; + + /** + * struct bcm2835_dmadev - BCM2835 DMA controller +@@ -52,6 +57,7 @@ struct bcm2835_dmadev { + void __iomem *base; + struct device_dma_parameters dma_parms; + dma_addr_t zero_page; ++ const struct bcm2835_dma_cfg_data *cfg_data; + }; + + struct bcm2835_dma_cb { +@@ -64,6 +70,17 @@ struct bcm2835_dma_cb { + uint32_t pad[2]; + }; + ++struct bcm2711_dma40_scb { ++ uint32_t ti; ++ uint32_t src; ++ uint32_t srci; ++ uint32_t dst; ++ uint32_t dsti; ++ uint32_t len; ++ uint32_t next_cb; ++ uint32_t rsvd; ++}; ++ + struct bcm2835_cb_entry { + struct bcm2835_dma_cb *cb; + dma_addr_t paddr; +@@ -84,6 +101,7 @@ struct bcm2835_chan { + unsigned int irq_flags; + + bool is_lite_channel; ++ bool is_40bit_channel; + }; + + struct bcm2835_desc { +@@ -173,13 +191,118 @@ struct bcm2835_desc { + #define BCM2835_DMA_DATA_TYPE_S128 16 + + /* Valid only for channels 0 - 14, 15 has its own base address */ +-#define BCM2835_DMA_CHAN(n) ((n) << 8) /* Base address */ ++#define BCM2835_DMA_CHAN_SIZE 0x100 ++#define BCM2835_DMA_CHAN(n) ((n) * BCM2835_DMA_CHAN_SIZE) /* Base address */ + #define BCM2835_DMA_CHANIO(base, n) ((base) + BCM2835_DMA_CHAN(n)) + + /* the max dma length for different channels */ + #define MAX_DMA_LEN SZ_1G + #define MAX_LITE_DMA_LEN (SZ_64K - 4) + ++/* 40-bit DMA support */ ++#define BCM2711_DMA40_CS 0x00 ++#define BCM2711_DMA40_CB 0x04 ++#define BCM2711_DMA40_DEBUG 0x0c ++#define BCM2711_DMA40_TI 0x10 ++#define BCM2711_DMA40_SRC 0x14 ++#define BCM2711_DMA40_SRCI 0x18 ++#define BCM2711_DMA40_DEST 0x1c ++#define BCM2711_DMA40_DESTI 0x20 ++#define BCM2711_DMA40_LEN 0x24 ++#define BCM2711_DMA40_NEXT_CB 0x28 ++#define BCM2711_DMA40_DEBUG2 0x2c ++ ++#define BCM2711_DMA40_ACTIVE BIT(0) ++#define BCM2711_DMA40_END BIT(1) ++#define BCM2711_DMA40_INT BIT(2) ++#define BCM2711_DMA40_DREQ BIT(3) /* DREQ state */ ++#define BCM2711_DMA40_RD_PAUSED BIT(4) /* Reading is paused */ ++#define BCM2711_DMA40_WR_PAUSED BIT(5) /* Writing is paused */ ++#define BCM2711_DMA40_DREQ_PAUSED BIT(6) /* Is paused by DREQ flow control */ ++#define BCM2711_DMA40_WAITING_FOR_WRITES BIT(7) /* Waiting for last write */ ++#define BCM2711_DMA40_ERR BIT(10) ++#define BCM2711_DMA40_QOS(x) (((x) & 0x1f) << 16) ++#define BCM2711_DMA40_PANIC_QOS(x) (((x) & 0x1f) << 20) ++#define BCM2711_DMA40_WAIT_FOR_WRITES BIT(28) ++#define BCM2711_DMA40_DISDEBUG BIT(29) ++#define BCM2711_DMA40_ABORT BIT(30) ++#define BCM2711_DMA40_HALT BIT(31) ++#define BCM2711_DMA40_CS_FLAGS(x) (x & (BCM2711_DMA40_QOS(15) | \ ++ BCM2711_DMA40_PANIC_QOS(15) | \ ++ BCM2711_DMA40_WAIT_FOR_WRITES | \ ++ BCM2711_DMA40_DISDEBUG)) ++ ++/* Transfer information bits */ ++#define BCM2711_DMA40_INTEN BIT(0) ++#define BCM2711_DMA40_TDMODE BIT(1) /* 2D-Mode */ ++#define BCM2711_DMA40_WAIT_RESP BIT(2) /* wait for AXI write to be acked */ ++#define BCM2711_DMA40_WAIT_RD_RESP BIT(3) /* wait for AXI read to complete */ ++#define BCM2711_DMA40_PER_MAP(x) ((x & 31) << 9) /* REQ source */ ++#define BCM2711_DMA40_S_DREQ BIT(14) /* enable SREQ for source */ ++#define BCM2711_DMA40_D_DREQ BIT(15) /* enable DREQ for destination */ ++#define BCM2711_DMA40_S_WAIT(x) ((x & 0xff) << 16) /* add DMA read-wait cycles */ ++#define BCM2711_DMA40_D_WAIT(x) ((x & 0xff) << 24) /* add DMA write-wait cycles */ ++ ++/* debug register bits */ ++#define BCM2711_DMA40_DEBUG_WRITE_ERR BIT(0) ++#define BCM2711_DMA40_DEBUG_FIFO_ERR BIT(1) ++#define BCM2711_DMA40_DEBUG_READ_ERR BIT(2) ++#define BCM2711_DMA40_DEBUG_READ_CB_ERR BIT(3) ++#define BCM2711_DMA40_DEBUG_IN_ON_ERR BIT(8) ++#define BCM2711_DMA40_DEBUG_ABORT_ON_ERR BIT(9) ++#define BCM2711_DMA40_DEBUG_HALT_ON_ERR BIT(10) ++#define BCM2711_DMA40_DEBUG_DISABLE_CLK_GATE BIT(11) ++#define BCM2711_DMA40_DEBUG_RSTATE_SHIFT 14 ++#define BCM2711_DMA40_DEBUG_RSTATE_BITS 4 ++#define BCM2711_DMA40_DEBUG_WSTATE_SHIFT 18 ++#define BCM2711_DMA40_DEBUG_WSTATE_BITS 4 ++#define BCM2711_DMA40_DEBUG_RESET BIT(23) ++#define BCM2711_DMA40_DEBUG_ID_SHIFT 24 ++#define BCM2711_DMA40_DEBUG_ID_BITS 4 ++#define BCM2711_DMA40_DEBUG_VERSION_SHIFT 28 ++#define BCM2711_DMA40_DEBUG_VERSION_BITS 4 ++ ++/* Valid only for channels 0 - 3 (11 - 14) */ ++#define BCM2711_DMA40_CHAN(n) (((n) + 11) << 8) /* Base address */ ++#define BCM2711_DMA40_CHANIO(base, n) ((base) + BCM2711_DMA_CHAN(n)) ++ ++/* the max dma length for different channels */ ++#define MAX_DMA40_LEN SZ_1G ++ ++#define BCM2711_DMA40_BURST_LEN(x) ((min(x,16) - 1) << 8) ++#define BCM2711_DMA40_INC BIT(12) ++#define BCM2711_DMA40_SIZE_32 (0 << 13) ++#define BCM2711_DMA40_SIZE_64 (1 << 13) ++#define BCM2711_DMA40_SIZE_128 (2 << 13) ++#define BCM2711_DMA40_SIZE_256 (3 << 13) ++#define BCM2711_DMA40_IGNORE BIT(15) ++#define BCM2711_DMA40_STRIDE(x) ((x) << 16) /* For 2D mode */ ++ ++#define BCM2711_DMA40_MEMCPY_FLAGS \ ++ (BCM2711_DMA40_QOS(0) | \ ++ BCM2711_DMA40_PANIC_QOS(0) | \ ++ BCM2711_DMA40_WAIT_FOR_WRITES | \ ++ BCM2711_DMA40_DISDEBUG) ++ ++#define BCM2711_DMA40_MEMCPY_XFER_INFO \ ++ (BCM2711_DMA40_SIZE_128 | \ ++ BCM2711_DMA40_INC | \ ++ BCM2711_DMA40_BURST_LEN(16)) ++ ++struct bcm2835_dmadev *memcpy_parent; ++static void __iomem *memcpy_chan; ++static struct bcm2711_dma40_scb *memcpy_scb; ++static dma_addr_t memcpy_scb_dma; ++DEFINE_SPINLOCK(memcpy_lock); ++ ++static const struct bcm2835_dma_cfg_data bcm2835_dma_cfg = { ++ .chan_40bit_mask = 0, ++}; ++ ++static const struct bcm2835_dma_cfg_data bcm2711_dma_cfg = { ++ .chan_40bit_mask = BIT(11) | BIT(12) | BIT(13) | BIT(14), ++}; ++ + static inline size_t bcm2835_dma_max_frame_length(struct bcm2835_chan *c) + { + /* lite and normal channels have different max frame length */ +@@ -209,6 +332,32 @@ static inline struct bcm2835_desc *to_bc + return container_of(t, struct bcm2835_desc, vd.tx); + } + ++static inline uint32_t to_bcm2711_ti(uint32_t info) ++{ ++ return ((info & BCM2835_DMA_INT_EN) ? BCM2711_DMA40_INTEN : 0) | ++ ((info & BCM2835_DMA_WAIT_RESP) ? BCM2711_DMA40_WAIT_RESP : 0) | ++ ((info & BCM2835_DMA_S_DREQ) ? ++ (BCM2711_DMA40_S_DREQ | BCM2711_DMA40_WAIT_RD_RESP) : 0) | ++ ((info & BCM2835_DMA_D_DREQ) ? BCM2711_DMA40_D_DREQ : 0) | ++ BCM2711_DMA40_PER_MAP((info >> 16) & 0x1f); ++} ++ ++static inline uint32_t to_bcm2711_srci(uint32_t info) ++{ ++ return ((info & BCM2835_DMA_S_INC) ? BCM2711_DMA40_INC : 0); ++} ++ ++static inline uint32_t to_bcm2711_dsti(uint32_t info) ++{ ++ return ((info & BCM2835_DMA_D_INC) ? BCM2711_DMA40_INC : 0); ++} ++ ++static inline uint32_t to_bcm2711_cbaddr(dma_addr_t addr) ++{ ++ BUG_ON(addr & 0x1f); ++ return (addr >> 5); ++} ++ + static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc) + { + size_t i; +@@ -227,45 +376,53 @@ static void bcm2835_dma_desc_free(struct + } + + static void bcm2835_dma_create_cb_set_length( +- struct bcm2835_chan *chan, ++ struct bcm2835_chan *c, + struct bcm2835_dma_cb *control_block, + size_t len, + size_t period_len, + size_t *total_len, + u32 finalextrainfo) + { +- size_t max_len = bcm2835_dma_max_frame_length(chan); ++ size_t max_len = bcm2835_dma_max_frame_length(c); ++ uint32_t cb_len; + + /* set the length taking lite-channel limitations into account */ +- control_block->length = min_t(u32, len, max_len); ++ cb_len = min_t(u32, len, max_len); + +- /* finished if we have no period_length */ +- if (!period_len) +- return; ++ if (period_len) { ++ /* ++ * period_len means: that we need to generate ++ * transfers that are terminating at every ++ * multiple of period_len - this is typically ++ * used to set the interrupt flag in info ++ * which is required during cyclic transfers ++ */ + +- /* +- * period_len means: that we need to generate +- * transfers that are terminating at every +- * multiple of period_len - this is typically +- * used to set the interrupt flag in info +- * which is required during cyclic transfers +- */ ++ /* have we filled in period_length yet? */ ++ if (*total_len + cb_len < period_len) { ++ /* update number of bytes in this period so far */ ++ *total_len += cb_len; ++ } else { ++ /* calculate the length that remains to reach period_len */ ++ cb_len = period_len - *total_len; + +- /* have we filled in period_length yet? */ +- if (*total_len + control_block->length < period_len) { +- /* update number of bytes in this period so far */ +- *total_len += control_block->length; +- return; ++ /* reset total_length for next period */ ++ *total_len = 0; ++ } + } + +- /* calculate the length that remains to reach period_length */ +- control_block->length = period_len - *total_len; +- +- /* reset total_length for next period */ +- *total_len = 0; +- +- /* add extrainfo bits in info */ +- control_block->info |= finalextrainfo; ++ if (c->is_40bit_channel) { ++ struct bcm2711_dma40_scb *scb = ++ (struct bcm2711_dma40_scb *)control_block; ++ ++ scb->len = cb_len; ++ /* add extrainfo bits to ti */ ++ scb->ti |= to_bcm2711_ti(finalextrainfo); ++ } else { ++ control_block->length = cb_len; ++ /* add extrainfo bits to info */ ++ control_block->info |= finalextrainfo; ++ } + } + + static inline size_t bcm2835_dma_count_frames_for_sg( +@@ -288,7 +445,7 @@ static inline size_t bcm2835_dma_count_f + /** + * bcm2835_dma_create_cb_chain - create a control block and fills data in + * +- * @chan: the @dma_chan for which we run this ++ * @c: the @bcm2835_chan for which we run this + * @direction: the direction in which we transfer + * @cyclic: it is a cyclic transfer + * @info: the default info bits to apply per controlblock +@@ -306,12 +463,11 @@ static inline size_t bcm2835_dma_count_f + * @gfp: the GFP flag to use for allocation + */ + static struct bcm2835_desc *bcm2835_dma_create_cb_chain( +- struct dma_chan *chan, enum dma_transfer_direction direction, ++ struct bcm2835_chan *c, enum dma_transfer_direction direction, + bool cyclic, u32 info, u32 finalextrainfo, size_t frames, + dma_addr_t src, dma_addr_t dst, size_t buf_len, + size_t period_len, gfp_t gfp) + { +- struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); + size_t len = buf_len, total_len; + size_t frame; + struct bcm2835_desc *d; +@@ -343,11 +499,23 @@ static struct bcm2835_desc *bcm2835_dma_ + + /* fill in the control block */ + control_block = cb_entry->cb; +- control_block->info = info; +- control_block->src = src; +- control_block->dst = dst; +- control_block->stride = 0; +- control_block->next = 0; ++ if (c->is_40bit_channel) { ++ struct bcm2711_dma40_scb *scb = ++ (struct bcm2711_dma40_scb *)control_block; ++ scb->ti = to_bcm2711_ti(info); ++ scb->src = lower_32_bits(src); ++ scb->srci= upper_32_bits(src) | to_bcm2711_srci(info); ++ scb->dst = lower_32_bits(dst); ++ scb->dsti = upper_32_bits(dst) | to_bcm2711_dsti(info); ++ scb->next_cb = 0; ++ } else { ++ control_block->info = info; ++ control_block->src = src; ++ control_block->dst = dst; ++ control_block->stride = 0; ++ control_block->next = 0; ++ } ++ + /* set up length in control_block if requested */ + if (buf_len) { + /* calculate length honoring period_length */ +@@ -361,7 +529,11 @@ static struct bcm2835_desc *bcm2835_dma_ + } + + /* link this the last controlblock */ +- if (frame) ++ if (frame && c->is_40bit_channel) ++ ((struct bcm2711_dma40_scb *) ++ d->cb_list[frame - 1].cb)->next_cb = ++ to_bcm2711_cbaddr(cb_entry->paddr); ++ if (frame && !c->is_40bit_channel) + d->cb_list[frame - 1].cb->next = cb_entry->paddr; + + /* update src and dst and length */ +@@ -371,11 +543,21 @@ static struct bcm2835_desc *bcm2835_dma_ + dst += control_block->length; + + /* Length of total transfer */ +- d->size += control_block->length; ++ if (c->is_40bit_channel) ++ d->size += ((struct bcm2711_dma40_scb *)control_block)->len; ++ else ++ d->size += control_block->length; + } + + /* the last frame requires extra flags */ +- d->cb_list[d->frames - 1].cb->info |= finalextrainfo; ++ if (c->is_40bit_channel) { ++ struct bcm2711_dma40_scb *scb = ++ (struct bcm2711_dma40_scb *)d->cb_list[d->frames-1].cb; ++ ++ scb->ti |= to_bcm2711_ti(finalextrainfo); ++ } else { ++ d->cb_list[d->frames - 1].cb->info |= finalextrainfo; ++ } + + /* detect a size missmatch */ + if (buf_len && (d->size != buf_len)) +@@ -389,13 +571,12 @@ error_cb: + } + + static void bcm2835_dma_fill_cb_chain_with_sg( +- struct dma_chan *chan, ++ struct bcm2835_chan *c, + enum dma_transfer_direction direction, + struct bcm2835_cb_entry *cb, + struct scatterlist *sgl, + unsigned int sg_len) + { +- struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); + size_t len, max_len; + unsigned int i; + dma_addr_t addr; +@@ -403,14 +584,35 @@ static void bcm2835_dma_fill_cb_chain_wi + + max_len = bcm2835_dma_max_frame_length(c); + for_each_sg(sgl, sgent, sg_len, i) { +- for (addr = sg_dma_address(sgent), len = sg_dma_len(sgent); +- len > 0; +- addr += cb->cb->length, len -= cb->cb->length, cb++) { +- if (direction == DMA_DEV_TO_MEM) +- cb->cb->dst = addr; +- else +- cb->cb->src = addr; +- cb->cb->length = min(len, max_len); ++ if (c->is_40bit_channel) { ++ struct bcm2711_dma40_scb *scb; ++ ++ for (addr = sg_dma_address(sgent), ++ len = sg_dma_len(sgent); ++ len > 0; ++ addr += scb->len, len -= scb->len, cb++) { ++ scb = (struct bcm2711_dma40_scb *)cb->cb; ++ if (direction == DMA_DEV_TO_MEM) { ++ scb->dst = lower_32_bits(addr); ++ scb->dsti = upper_32_bits(addr) | BCM2711_DMA40_INC; ++ } else { ++ scb->src = lower_32_bits(addr); ++ scb->srci = upper_32_bits(addr) | BCM2711_DMA40_INC; ++ } ++ scb->len = min(len, max_len); ++ } ++ } else { ++ for (addr = sg_dma_address(sgent), ++ len = sg_dma_len(sgent); ++ len > 0; ++ addr += cb->cb->length, len -= cb->cb->length, ++ cb++) { ++ if (direction == DMA_DEV_TO_MEM) ++ cb->cb->dst = addr; ++ else ++ cb->cb->src = addr; ++ cb->cb->length = min(len, max_len); ++ } + } + } + } +@@ -419,6 +621,10 @@ static void bcm2835_dma_abort(struct bcm + { + void __iomem *chan_base = c->chan_base; + long int timeout = 10000; ++ u32 wait_mask = BCM2835_DMA_WAITING_FOR_WRITES; ++ ++ if (c->is_40bit_channel) ++ wait_mask = BCM2711_DMA40_WAITING_FOR_WRITES; + + /* + * A zero control block address means the channel is idle. +@@ -431,8 +637,7 @@ static void bcm2835_dma_abort(struct bcm + writel(0, chan_base + BCM2835_DMA_CS); + + /* Wait for any current AXI transfer to complete */ +- while ((readl(chan_base + BCM2835_DMA_CS) & +- BCM2835_DMA_WAITING_FOR_WRITES) && --timeout) ++ while ((readl(chan_base + BCM2835_DMA_CS) & wait_mask) && --timeout) + cpu_relax(); + + /* Peripheral might be stuck and fail to signal AXI write responses */ +@@ -457,9 +662,16 @@ static void bcm2835_dma_start_desc(struc + + c->desc = d = to_bcm2835_dma_desc(&vd->tx); + +- writel(d->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR); +- writel(BCM2835_DMA_ACTIVE | BCM2835_DMA_CS_FLAGS(c->dreq), +- c->chan_base + BCM2835_DMA_CS); ++ if (c->is_40bit_channel) { ++ writel(to_bcm2711_cbaddr(d->cb_list[0].paddr), ++ c->chan_base + BCM2711_DMA40_CB); ++ writel(BCM2711_DMA40_ACTIVE | BCM2711_DMA40_CS_FLAGS(c->dreq), ++ c->chan_base + BCM2711_DMA40_CS); ++ } else { ++ writel(d->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR); ++ writel(BCM2835_DMA_ACTIVE | BCM2835_DMA_CS_FLAGS(c->dreq), ++ c->chan_base + BCM2835_DMA_CS); ++ } + } + + static irqreturn_t bcm2835_dma_callback(int irq, void *data) +@@ -486,8 +698,7 @@ static irqreturn_t bcm2835_dma_callback( + * if this IRQ handler is threaded.) If the channel is finished, it + * will remain idle despite the ACTIVE flag being set. + */ +- writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE | +- BCM2835_DMA_CS_FLAGS(c->dreq), ++ writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE, + c->chan_base + BCM2835_DMA_CS); + + d = c->desc; +@@ -590,9 +801,17 @@ static enum dma_status bcm2835_dma_tx_st + struct bcm2835_desc *d = c->desc; + dma_addr_t pos; + +- if (d->dir == DMA_MEM_TO_DEV) ++ if (d->dir == DMA_MEM_TO_DEV && c->is_40bit_channel) ++ pos = readl(c->chan_base + BCM2711_DMA40_SRC) + ++ ((readl(c->chan_base + BCM2711_DMA40_SRCI) & ++ 0xff) << 8); ++ else if (d->dir == DMA_MEM_TO_DEV && !c->is_40bit_channel) + pos = readl(c->chan_base + BCM2835_DMA_SOURCE_AD); +- else if (d->dir == DMA_DEV_TO_MEM) ++ else if (d->dir == DMA_DEV_TO_MEM && c->is_40bit_channel) ++ pos = readl(c->chan_base + BCM2711_DMA40_DEST) + ++ ((readl(c->chan_base + BCM2711_DMA40_DESTI) & ++ 0xff) << 8); ++ else if (d->dir == DMA_DEV_TO_MEM && !c->is_40bit_channel) + pos = readl(c->chan_base + BCM2835_DMA_DEST_AD); + else + pos = 0; +@@ -638,7 +857,7 @@ static struct dma_async_tx_descriptor *b + frames = bcm2835_dma_frames_for_length(len, max_len); + + /* allocate the CB chain - this also fills in the pointers */ +- d = bcm2835_dma_create_cb_chain(chan, DMA_MEM_TO_MEM, false, ++ d = bcm2835_dma_create_cb_chain(c, DMA_MEM_TO_MEM, false, + info, extra, frames, + src, dst, len, 0, GFP_KERNEL); + if (!d) +@@ -673,11 +892,21 @@ static struct dma_async_tx_descriptor *b + if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) + return NULL; + src = c->cfg.src_addr; ++ /* ++ * One would think it ought to be possible to get the physical ++ * to dma address mapping information from the dma-ranges DT ++ * property, but I've not found a way yet that doesn't involve ++ * open-coding the whole thing. ++ */ ++ if (c->is_40bit_channel) ++ src |= 0x400000000ull; + info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC; + } else { + if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) + return NULL; + dst = c->cfg.dst_addr; ++ if (c->is_40bit_channel) ++ dst |= 0x400000000ull; + info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC; + } + +@@ -685,7 +914,7 @@ static struct dma_async_tx_descriptor *b + frames = bcm2835_dma_count_frames_for_sg(c, sgl, sg_len); + + /* allocate the CB chain */ +- d = bcm2835_dma_create_cb_chain(chan, direction, false, ++ d = bcm2835_dma_create_cb_chain(c, direction, false, + info, extra, + frames, src, dst, 0, 0, + GFP_NOWAIT); +@@ -693,7 +922,7 @@ static struct dma_async_tx_descriptor *b + return NULL; + + /* fill in frames with scatterlist pointers */ +- bcm2835_dma_fill_cb_chain_with_sg(chan, direction, d->cb_list, ++ bcm2835_dma_fill_cb_chain_with_sg(c, direction, d->cb_list, + sgl, sg_len); + + return vchan_tx_prep(&c->vc, &d->vd, flags); +@@ -747,12 +976,16 @@ static struct dma_async_tx_descriptor *b + if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) + return NULL; + src = c->cfg.src_addr; ++ if (c->is_40bit_channel) ++ src |= 0x400000000ull; + dst = buf_addr; + info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC; + } else { + if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) + return NULL; + dst = c->cfg.dst_addr; ++ if (c->is_40bit_channel) ++ dst |= 0x400000000ull; + src = buf_addr; + info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC; + +@@ -772,7 +1005,7 @@ static struct dma_async_tx_descriptor *b + * note that we need to use GFP_NOWAIT, as the ALSA i2s dmaengine + * implementation calls prep_dma_cyclic with interrupts disabled. + */ +- d = bcm2835_dma_create_cb_chain(chan, direction, true, ++ d = bcm2835_dma_create_cb_chain(c, direction, true, + info, extra, + frames, src, dst, buf_len, + period_len, GFP_NOWAIT); +@@ -780,7 +1013,12 @@ static struct dma_async_tx_descriptor *b + return NULL; + + /* wrap around into a loop */ +- d->cb_list[d->frames - 1].cb->next = d->cb_list[0].paddr; ++ if (c->is_40bit_channel) ++ ((struct bcm2711_dma40_scb *) ++ d->cb_list[frames - 1].cb)->next_cb = ++ to_bcm2711_cbaddr(d->cb_list[0].paddr); ++ else ++ d->cb_list[d->frames - 1].cb->next = d->cb_list[0].paddr; + + return vchan_tx_prep(&c->vc, &d->vd, flags); + } +@@ -844,9 +1082,11 @@ static int bcm2835_dma_chan_init(struct + c->irq_number = irq; + c->irq_flags = irq_flags; + +- /* check in DEBUG register if this is a LITE channel */ +- if (readl(c->chan_base + BCM2835_DMA_DEBUG) & +- BCM2835_DMA_DEBUG_LITE) ++ /* check for 40bit and lite channels */ ++ if (d->cfg_data->chan_40bit_mask & BIT(chan_id)) ++ c->is_40bit_channel = true; ++ else if (readl(c->chan_base + BCM2835_DMA_DEBUG) & ++ BCM2835_DMA_DEBUG_LITE) + c->is_lite_channel = true; + + return 0; +@@ -866,8 +1106,58 @@ static void bcm2835_dma_free(struct bcm2 + DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); + } + ++int bcm2711_dma40_memcpy_init(void) ++{ ++ if (!memcpy_parent) ++ return -EPROBE_DEFER; ++ ++ if (!memcpy_chan) ++ return -EINVAL; ++ ++ if (!memcpy_scb) ++ return -ENOMEM; ++ ++ return 0; ++} ++EXPORT_SYMBOL(bcm2711_dma40_memcpy_init); ++ ++void bcm2711_dma40_memcpy(dma_addr_t dst, dma_addr_t src, size_t size) ++{ ++ struct bcm2711_dma40_scb *scb = memcpy_scb; ++ unsigned long flags; ++ ++ if (!scb) { ++ pr_err("bcm2711_dma40_memcpy not initialised!\n"); ++ return; ++ } ++ ++ spin_lock_irqsave(&memcpy_lock, flags); ++ ++ scb->ti = 0; ++ scb->src = lower_32_bits(src); ++ scb->srci = upper_32_bits(src) | BCM2711_DMA40_MEMCPY_XFER_INFO; ++ scb->dst = lower_32_bits(dst); ++ scb->dsti = upper_32_bits(dst) | BCM2711_DMA40_MEMCPY_XFER_INFO; ++ scb->len = size; ++ scb->next_cb = 0; ++ ++ writel((u32)(memcpy_scb_dma >> 5), memcpy_chan + BCM2711_DMA40_CB); ++ writel(BCM2711_DMA40_MEMCPY_FLAGS + BCM2711_DMA40_ACTIVE, ++ memcpy_chan + BCM2711_DMA40_CS); ++ ++ /* Poll for completion */ ++ while (!(readl(memcpy_chan + BCM2711_DMA40_CS) & BCM2711_DMA40_END)) ++ cpu_relax(); ++ ++ writel(BCM2711_DMA40_END, memcpy_chan + BCM2711_DMA40_CS); ++ ++ spin_unlock_irqrestore(&memcpy_lock, flags); ++} ++EXPORT_SYMBOL(bcm2711_dma40_memcpy); ++ + static const struct of_device_id bcm2835_dma_of_match[] = { +- { .compatible = "brcm,bcm2835-dma", }, ++ { .compatible = "brcm,bcm2835-dma", .data = &bcm2835_dma_cfg }, ++ { .compatible = "brcm,bcm2711-dma", .data = &bcm2711_dma_cfg }, + {}, + }; + MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match); +@@ -899,6 +1189,8 @@ static int bcm2835_dma_probe(struct plat + int irq_flags; + uint32_t chans_available; + char chan_name[BCM2835_DMA_CHAN_NAME_SIZE]; ++ const struct of_device_id *of_id; ++ int chan_count, chan_start, chan_end; + + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; +@@ -920,9 +1212,13 @@ static int bcm2835_dma_probe(struct plat + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); +- rc = bcm_dmaman_probe(pdev, base, BCM2835_DMA_BULK_MASK); +- if (rc) +- dev_err(&pdev->dev, "Failed to initialize the legacy API\n"); ++ ++ /* The set of channels can be split across multiple instances. */ ++ chan_start = ((u32)(uintptr_t)base / BCM2835_DMA_CHAN_SIZE) & 0xf; ++ base -= BCM2835_DMA_CHAN(chan_start); ++ chan_count = resource_size(res) / BCM2835_DMA_CHAN_SIZE; ++ chan_end = min(chan_start + chan_count, ++ BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED + 1); + + od->base = base; + +@@ -959,6 +1255,14 @@ static int bcm2835_dma_probe(struct plat + return -ENOMEM; + } + ++ of_id = of_match_node(bcm2835_dma_of_match, pdev->dev.of_node); ++ if (!of_id) { ++ dev_err(&pdev->dev, "Failed to match compatible string\n"); ++ return -EINVAL; ++ } ++ ++ od->cfg_data = of_id->data; ++ + /* Request DMA channel mask from device tree */ + if (of_property_read_u32(pdev->dev.of_node, + "brcm,dma-channel-mask", +@@ -968,11 +1272,34 @@ static int bcm2835_dma_probe(struct plat + goto err_no_dma; + } + +- /* Channel 0 is used by the legacy API */ +- chans_available &= ~BCM2835_DMA_BULK_MASK; ++ /* One channel is reserved for the legacy API */ ++ if (chans_available & BCM2835_DMA_BULK_MASK) { ++ rc = bcm_dmaman_probe(pdev, base, ++ chans_available & BCM2835_DMA_BULK_MASK); ++ if (rc) ++ dev_err(&pdev->dev, ++ "Failed to initialize the legacy API\n"); ++ ++ chans_available &= ~BCM2835_DMA_BULK_MASK; ++ } ++ ++ /* And possibly one for the 40-bit DMA memcpy API */ ++ if (chans_available & od->cfg_data->chan_40bit_mask & ++ BIT(BCM2711_DMA_MEMCPY_CHAN)) { ++ memcpy_parent = od; ++ memcpy_chan = BCM2835_DMA_CHANIO(base, BCM2711_DMA_MEMCPY_CHAN); ++ memcpy_scb = dma_alloc_coherent(memcpy_parent->ddev.dev, ++ sizeof(*memcpy_scb), ++ &memcpy_scb_dma, GFP_KERNEL); ++ if (!memcpy_scb) ++ dev_warn(&pdev->dev, ++ "Failed to allocated memcpy scb\n"); ++ ++ chans_available &= ~BIT(BCM2711_DMA_MEMCPY_CHAN); ++ } + + /* get irqs for each channel that we support */ +- for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) { ++ for (i = chan_start; i < chan_end; i++) { + /* skip masked out channels */ + if (!(chans_available & (1 << i))) { + irq[i] = -1; +@@ -995,13 +1322,17 @@ static int bcm2835_dma_probe(struct plat + irq[i] = platform_get_irq(pdev, i < 11 ? i : 11); + } + ++ chan_count = 0; ++ + /* get irqs for each channel */ +- for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) { ++ for (i = chan_start; i < chan_end; i++) { + /* skip channels without irq */ + if (irq[i] < 0) + continue; + + /* check if there are other channels that also use this irq */ ++ /* FIXME: This will fail if interrupts are shared across ++ instances */ + irq_flags = 0; + for (j = 0; j <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; j++) + if ((i != j) && (irq[j] == irq[i])) { +@@ -1013,9 +1344,10 @@ static int bcm2835_dma_probe(struct plat + rc = bcm2835_dma_chan_init(od, i, irq[i], irq_flags); + if (rc) + goto err_no_dma; ++ chan_count++; + } + +- dev_dbg(&pdev->dev, "Initialized %i DMA channels\n", i); ++ dev_dbg(&pdev->dev, "Initialized %i DMA channels\n", chan_count); + + /* Device-tree DMA controller registration */ + rc = of_dma_controller_register(pdev->dev.of_node, +@@ -1047,6 +1379,13 @@ static int bcm2835_dma_remove(struct pla + + bcm_dmaman_remove(pdev); + dma_async_device_unregister(&od->ddev); ++ if (memcpy_parent == od) { ++ dma_free_coherent(&pdev->dev, sizeof(*memcpy_scb), memcpy_scb, ++ memcpy_scb_dma); ++ memcpy_parent = NULL; ++ memcpy_scb = NULL; ++ memcpy_chan = NULL; ++ } + bcm2835_dma_free(od); + + return 0; diff --git a/target/linux/bcm27xx/patches-5.4/950-0701-ARM-dts-bcm2711-Allow-40-bit-DMA-for-SPI.patch b/target/linux/bcm27xx/patches-5.4/950-0701-ARM-dts-bcm2711-Allow-40-bit-DMA-for-SPI.patch new file mode 100644 index 0000000000..ac6543fe4f --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0701-ARM-dts-bcm2711-Allow-40-bit-DMA-for-SPI.patch @@ -0,0 +1,40 @@ +From 0dcafa8ab800d1f534bb0cb51cd02a082cf34be6 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Thu, 30 Apr 2020 12:43:05 +0100 +Subject: [PATCH] ARM: dts: bcm2711: Allow 40-bit DMA for SPI + +Add the spi_dma4 DT parameter to enable use of the 40-bit DMA channels +to drive SPI. Note that there are only 3-4 40-bit channels available, +and using this parameter claims 2 of them. + +Usage: dtparam=spi_dma4 + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 2 ++ + arch/arm/boot/dts/overlays/README | 4 ++++ + 2 files changed, 6 insertions(+) + +--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts ++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts +@@ -554,5 +554,7 @@ + eth_led0 = <&phy1>,"led-modes:0"; + eth_led1 = <&phy1>,"led-modes:4"; + ++ spi_dma4 = <&spi0>, "dmas:0=", <&dma40>, ++ <&spi0>, "dmas:8=", <&dma40>; + }; + }; +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -159,6 +159,10 @@ Params: + spi Set to "on" to enable the spi interfaces + (default "off") + ++ spi_dma4 Use to enable 40-bit DMA on spi interfaces ++ (the assigned value doesn't matter) ++ (2711 only) ++ + random Set to "on" to enable the hardware random + number generator (default "on") + diff --git a/target/linux/bcm27xx/patches-5.4/950-0702-overlays-Make-the-i2c-gpio-overlay-safe-again.patch b/target/linux/bcm27xx/patches-5.4/950-0702-overlays-Make-the-i2c-gpio-overlay-safe-again.patch new file mode 100644 index 0000000000..537ca5dc55 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0702-overlays-Make-the-i2c-gpio-overlay-safe-again.patch @@ -0,0 +1,32 @@ +From 3c0cbb59e068f13b2a12a98a5dac42afa8ccc639 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Fri, 1 May 2020 17:56:13 +0100 +Subject: [PATCH] overlays: Make the i2c-gpio overlay safe again + +Like many overlays, the i2c-gpio overlay goes to efforts to avoid +generating warnings about #address-cells and #size-cells not +being defined, which it does by defining them. Unfortunately this +is fatal if they don't match what the system requires, and the +recent switch to #size-cells = 2 on 2711 made i2c-gpio very +dangerous. + +In the absence of the knowledge of a clean way to fix this, just delete +the declarations and suffer the warnings. + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts | 3 --- + 1 file changed, 3 deletions(-) + +--- a/arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts +@@ -9,9 +9,6 @@ + target-path = "/"; + + __overlay__ { +- #address-cells = <1>; +- #size-cells = <0>; +- + i2c_gpio: i2c@0 { + reg = <0xffffffff>; + compatible = "i2c-gpio"; diff --git a/target/linux/bcm27xx/patches-5.4/950-0703-staging-vc04_services-isp-Remove-duplicated-initiali.patch b/target/linux/bcm27xx/patches-5.4/950-0703-staging-vc04_services-isp-Remove-duplicated-initiali.patch new file mode 100644 index 0000000000..d2b0bf1f2f --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0703-staging-vc04_services-isp-Remove-duplicated-initiali.patch @@ -0,0 +1,62 @@ +From 6a9cc90467f4b14d596a819c87d48764a4ba5282 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Fri, 1 May 2020 17:49:08 +0100 +Subject: [PATCH] staging: vc04_services: isp: Remove duplicated + initialisation + +With the codec code from which this was derived, the driver had to +get the supported formats for both input and output ports. +This had been copied across, however here we have independent nodes +for each port, but the code had been left in to do the same thing +twice. +Remove the duplicate. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + .../bcm2835-isp/bcm2835-v4l2-isp.c | 35 ------------------- + 1 file changed, 35 deletions(-) + +--- a/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c ++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c +@@ -1160,41 +1160,6 @@ static int bcm2835_isp_get_supported_fmt + } + node->supported_fmts.num_entries = j; + +- param_size = sizeof(fourccs); +- ret = vchiq_mmal_port_parameter_get(dev->mmal_instance, +- get_port_data(node), +- MMAL_PARAMETER_SUPPORTED_ENCODINGS, +- &fourccs, ¶m_size); +- +- if (ret) { +- if (ret == MMAL_MSG_STATUS_ENOSPC) { +- v4l2_err(&dev->v4l2_dev, +- "%s: port has more encoding than we provided space for. Some are dropped.\n", +- __func__); +- num_encodings = MAX_SUPPORTED_ENCODINGS; +- } else { +- return -EINVAL; +- } +- } else { +- num_encodings = param_size / sizeof(u32); +- } +- /* Assume at this stage that all encodings will be supported in V4L2. */ +- list = devm_kzalloc(dev->dev, +- sizeof(struct bcm2835_isp_fmt) * num_encodings, +- GFP_KERNEL); +- if (!list) +- return -ENOMEM; +- node->supported_fmts.list = list; +- +- for (i = 0, j = 0; i < num_encodings; i++) { +- const struct bcm2835_isp_fmt *fmt = get_fmt(fourccs[i]); +- +- if (fmt) { +- list[j] = *fmt; +- j++; +- } +- } +- node->supported_fmts.num_entries = j; + return 0; + } + diff --git a/target/linux/bcm27xx/patches-5.4/950-0704-staging-vc04_services-isp-Make-all-references-to-bcm.patch b/target/linux/bcm27xx/patches-5.4/950-0704-staging-vc04_services-isp-Make-all-references-to-bcm.patch new file mode 100644 index 0000000000..97ca47bcd0 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0704-staging-vc04_services-isp-Make-all-references-to-bcm.patch @@ -0,0 +1,148 @@ +From 9ecf10bfe3e501b10cc72d710a70150640a0b266 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Fri, 1 May 2020 16:54:20 +0100 +Subject: [PATCH] staging: vc04_services: isp: Make all references to + bcm2835_isp_fmt const + +The array of potential formats and their configuration should be const. +Rework all accesses so that this is possible. + +The list of supported formats was taking a copy of entries from this table. +This is unnecessary, therefore allocate an array of pointers instead of +an array of entries. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + .../bcm2835-isp/bcm2835-v4l2-isp.c | 34 ++++++++++--------- + .../bcm2835-isp/bcm2835_isp_fmts.h | 2 +- + 2 files changed, 19 insertions(+), 17 deletions(-) + +--- a/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c ++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c +@@ -67,7 +67,7 @@ struct bcm2835_isp_q_data { + unsigned int width; + unsigned int height; + unsigned int sizeimage; +- struct bcm2835_isp_fmt *fmt; ++ const struct bcm2835_isp_fmt *fmt; + }; + + /* +@@ -232,11 +232,11 @@ struct bcm2835_isp_fmt *find_format_by_f + struct bcm2835_isp_node *node) + { + struct bcm2835_isp_fmt_list *fmts = &node->supported_fmts; +- struct bcm2835_isp_fmt *fmt; ++ const struct bcm2835_isp_fmt *fmt; + unsigned int i; + + for (i = 0; i < fmts->num_entries; i++) { +- fmt = &fmts->list[i]; ++ fmt = fmts->list[i]; + if (fmt->fourcc == fourcc) + return fmt; + } +@@ -244,8 +244,9 @@ struct bcm2835_isp_fmt *find_format_by_f + return NULL; + } + +-static struct bcm2835_isp_fmt *find_format(struct v4l2_format *f, +- struct bcm2835_isp_node *node) ++static const ++struct bcm2835_isp_fmt *find_format(struct v4l2_format *f, ++ struct bcm2835_isp_node *node) + { + return find_format_by_fourcc(node_is_stats(node) ? + f->fmt.meta.dataformat : +@@ -666,19 +667,20 @@ static const struct vb2_ops bcm2835_isp_ + .stop_streaming = bcm2835_isp_node_stop_streaming, + }; + +-static struct bcm2835_isp_fmt *get_default_format(struct bcm2835_isp_node *node) ++static const ++struct bcm2835_isp_fmt *get_default_format(struct bcm2835_isp_node *node) + { +- return &node->supported_fmts.list[0]; ++ return node->supported_fmts.list[0]; + } + + static inline unsigned int get_bytesperline(int width, +- struct bcm2835_isp_fmt *fmt) ++ const struct bcm2835_isp_fmt *fmt) + { + return ALIGN((width * fmt->depth) >> 3, fmt->bytesperline_align); + } + + static inline unsigned int get_sizeimage(int bpl, int width, int height, +- struct bcm2835_isp_fmt *fmt) ++ const struct bcm2835_isp_fmt *fmt) + { + return (bpl * height * fmt->size_multiplier_x2) >> 1; + } +@@ -892,8 +894,8 @@ static int bcm2835_isp_node_enum_fmt(str + + if (f->index < fmts->num_entries) { + /* Format found */ +- f->pixelformat = fmts->list[f->index].fourcc; +- f->flags = fmts->list[f->index].flags; ++ f->pixelformat = fmts->list[f->index]->fourcc; ++ f->flags = fmts->list[f->index]->flags; + return 0; + } + +@@ -905,7 +907,7 @@ static int bcm2835_isp_enum_framesizes(s + { + struct bcm2835_isp_node *node = video_drvdata(file); + struct bcm2835_isp_dev *dev = node_get_dev(node); +- struct bcm2835_isp_fmt *fmt; ++ const struct bcm2835_isp_fmt *fmt; + + if (node_is_stats(node) || fsize->index) + return -EINVAL; +@@ -933,7 +935,7 @@ static int bcm2835_isp_node_try_fmt(stru + struct v4l2_format *f) + { + struct bcm2835_isp_node *node = video_drvdata(file); +- struct bcm2835_isp_fmt *fmt; ++ const struct bcm2835_isp_fmt *fmt; + + if (f->type != node->queue.type) + return -EINVAL; +@@ -1113,7 +1115,7 @@ static const struct v4l2_ioctl_ops bcm28 + static int bcm2835_isp_get_supported_fmts(struct bcm2835_isp_node *node) + { + struct bcm2835_isp_dev *dev = node_get_dev(node); +- struct bcm2835_isp_fmt *list; ++ struct bcm2835_isp_fmt const **list; + unsigned int i, j, num_encodings; + u32 fourccs[MAX_SUPPORTED_ENCODINGS]; + u32 param_size = sizeof(fourccs); +@@ -1144,7 +1146,7 @@ static int bcm2835_isp_get_supported_fmt + * Any that aren't supported will waste a very small amount of memory. + */ + list = devm_kzalloc(dev->dev, +- sizeof(struct bcm2835_isp_fmt) * num_encodings, ++ sizeof(struct bcm2835_isp_fmt *) * num_encodings, + GFP_KERNEL); + if (!list) + return -ENOMEM; +@@ -1154,7 +1156,7 @@ static int bcm2835_isp_get_supported_fmt + const struct bcm2835_isp_fmt *fmt = get_fmt(fourccs[i]); + + if (fmt) { +- list[j] = *fmt; ++ list[j] = fmt; + j++; + } + } +--- a/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h ++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h +@@ -26,7 +26,7 @@ struct bcm2835_isp_fmt { + }; + + struct bcm2835_isp_fmt_list { +- struct bcm2835_isp_fmt *list; ++ struct bcm2835_isp_fmt const **list; + unsigned int num_entries; + }; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0705-overlays-gpio-keys-Avoid-open-drain-warnings.patch b/target/linux/bcm27xx/patches-5.4/950-0705-overlays-gpio-keys-Avoid-open-drain-warnings.patch new file mode 100644 index 0000000000..579b3b61b6 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0705-overlays-gpio-keys-Avoid-open-drain-warnings.patch @@ -0,0 +1,29 @@ +From ae10c15867d9a5b85eefaf11333bd30473af4b2b Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Sat, 2 May 2020 13:43:06 +0100 +Subject: [PATCH] overlays: gpio-keys: Avoid open-drain warnings + +The i2c-gpio driver expects to use a GPIO in open-drain mode. Failure +to configure it in that way causes alarming warnings in the kernel log. +The BCM283x and BCM2711 GPIO blocks don't support open-drain mode, +but i2c-gpio works anyway. Silence the warning by declaring that +open-drain mode has been enabled by other means. + +See: https://github.com/raspberrypi/firmware/issues/1381 + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts +@@ -16,6 +16,8 @@ + &gpio 24 0 /* scl */ + >; + i2c-gpio,delay-us = <2>; /* ~100 kHz */ ++ i2c-gpio,sda-open-drain; ++ i2c-gpio,scl-open-drain; + #address-cells = <1>; + #size-cells = <0>; + }; diff --git a/target/linux/bcm27xx/patches-5.4/950-0706-vc4_hdmi_phy-Fix-typo-in-phy_get_cp_current.patch b/target/linux/bcm27xx/patches-5.4/950-0706-vc4_hdmi_phy-Fix-typo-in-phy_get_cp_current.patch new file mode 100644 index 0000000000..45429d506d --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0706-vc4_hdmi_phy-Fix-typo-in-phy_get_cp_current.patch @@ -0,0 +1,23 @@ +From 6dc415bb238d7c515f40305248d03234be88cadf Mon Sep 17 00:00:00 2001 +From: popcornmix <popcornmix@gmail.com> +Date: Mon, 6 Apr 2020 17:07:31 +0100 +Subject: [PATCH] vc4_hdmi_phy: Fix typo in phy_get_cp_current + +This is stored in a 6-bit register field which causes a WARN + +Signed-off-by: popcornmix <popcornmix@gmail.com> +--- + drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c +@@ -182,7 +182,7 @@ static u8 phy_get_cp_current(unsigned lo + if (vco_freq < 3700000000ULL) + return 0x1c; + +- return 0xc8; ++ return 0x18; + } + + static u32 phy_get_rm_offset(unsigned long long vco_freq) diff --git a/target/linux/bcm27xx/patches-5.4/950-0707-overlays-Make-use-of-intra-overlay-fragments.patch b/target/linux/bcm27xx/patches-5.4/950-0707-overlays-Make-use-of-intra-overlay-fragments.patch new file mode 100644 index 0000000000..628bec3f33 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0707-overlays-Make-use-of-intra-overlay-fragments.patch @@ -0,0 +1,92 @@ +From 31bebbef0cce2ae68c25a2b0cbfc040f938791e9 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Mon, 4 May 2020 15:13:24 +0100 +Subject: [PATCH] overlays: Make use of intra-overlay fragments + +The firmware and runtime overlay support has recently been updated to +correctly process fragments that target other fragments within the +overlay. Make use of that ability and avoid the use of the awkward +target-path = "<alias>/..." workaround and for better readability. + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/overlays/ads1015-overlay.dts | 8 ++++---- + arch/arm/boot/dts/overlays/ads1115-overlay.dts | 8 ++++---- + 2 files changed, 8 insertions(+), 8 deletions(-) + +--- a/arch/arm/boot/dts/overlays/ads1015-overlay.dts ++++ b/arch/arm/boot/dts/overlays/ads1015-overlay.dts +@@ -24,7 +24,7 @@ + }; + + fragment@1 { +- target-path = "i2c_arm/ads1015"; ++ target = <&ads1015>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; +@@ -37,7 +37,7 @@ + }; + + fragment@2 { +- target-path = "i2c_arm/ads1015"; ++ target = <&ads1015>; + __dormant__ { + #address-cells = <1>; + #size-cells = <0>; +@@ -50,7 +50,7 @@ + }; + + fragment@3 { +- target-path = "i2c_arm/ads1015"; ++ target = <&ads1015>; + __dormant__ { + #address-cells = <1>; + #size-cells = <0>; +@@ -63,7 +63,7 @@ + }; + + fragment@4 { +- target-path = "i2c_arm/ads1015"; ++ target = <&ads1015>; + __dormant__ { + #address-cells = <1>; + #size-cells = <0>; +--- a/arch/arm/boot/dts/overlays/ads1115-overlay.dts ++++ b/arch/arm/boot/dts/overlays/ads1115-overlay.dts +@@ -26,7 +26,7 @@ + }; + + fragment@1 { +- target-path = "i2c_arm/ads1115"; ++ target = <&ads1115>; + __dormant__ { + #address-cells = <1>; + #size-cells = <0>; +@@ -40,7 +40,7 @@ + }; + + fragment@2 { +- target-path = "i2c_arm/ads1115"; ++ target = <&ads1115>; + __dormant__ { + #address-cells = <1>; + #size-cells = <0>; +@@ -54,7 +54,7 @@ + }; + + fragment@3 { +- target-path = "i2c_arm/ads1115"; ++ target = <&ads1115>; + __dormant__ { + #address-cells = <1>; + #size-cells = <0>; +@@ -68,7 +68,7 @@ + }; + + fragment@4 { +- target-path = "i2c_arm/ads1115"; ++ target = <&ads1115>; + __dormant__ { + #address-cells = <1>; + #size-cells = <0>; diff --git a/target/linux/bcm27xx/patches-5.4/950-0708-media-i2c-tc358743-Fix-fallthrough-warning.patch b/target/linux/bcm27xx/patches-5.4/950-0708-media-i2c-tc358743-Fix-fallthrough-warning.patch new file mode 100644 index 0000000000..b0e74e5e1b --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0708-media-i2c-tc358743-Fix-fallthrough-warning.patch @@ -0,0 +1,20 @@ +From e09aaa8b2074bfa1656f1af09357b26c87e39988 Mon Sep 17 00:00:00 2001 +From: Jacko Dirks <jdirks.linuxdev@gmail.com> +Date: Tue, 5 May 2020 14:28:14 +0200 +Subject: [PATCH] media: i2c: tc358743: Fix fallthrough warning + +Signed-off-by: Jacko Dirks <jdirks.linuxdev@gmail.com> +--- + drivers/media/i2c/tc358743.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/media/i2c/tc358743.c ++++ b/drivers/media/i2c/tc358743.c +@@ -2002,6 +2002,7 @@ static int tc358743_probe_of(struct tc35 + switch (bps_pr_lane) { + default: + dev_warn(dev, "untested bps per lane: %u bps\n", bps_pr_lane); ++ /* fall through */ + case 594000000U: + state->pdata.lineinitcnt = 0xe80; + state->pdata.lptxtimecnt = 0x003; diff --git a/target/linux/bcm27xx/patches-5.4/950-0709-media-bcm2835-unicam-Fix-uninitialized-warning.patch b/target/linux/bcm27xx/patches-5.4/950-0709-media-bcm2835-unicam-Fix-uninitialized-warning.patch new file mode 100644 index 0000000000..2186d7b802 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0709-media-bcm2835-unicam-Fix-uninitialized-warning.patch @@ -0,0 +1,21 @@ +From f73609479c8542bd974a6e9aaf8bffc8ed09eaca Mon Sep 17 00:00:00 2001 +From: Jacko Dirks <jdirks.linuxdev@gmail.com> +Date: Tue, 5 May 2020 14:33:31 +0200 +Subject: [PATCH] media: bcm2835: unicam: Fix uninitialized warning + +Signed-off-by: Jacko Dirks <jdirks.linuxdev@gmail.com> +--- + drivers/media/platform/bcm2835/bcm2835-unicam.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c ++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c +@@ -1001,7 +1001,7 @@ const struct unicam_fmt *get_first_suppo + { + struct v4l2_subdev_mbus_code_enum mbus_code; + const struct unicam_fmt *fmt = NULL; +- int ret; ++ int ret = 0; + int j; + + for (j = 0; ret != -EINVAL && ret != -ENOIOCTLCMD; ++j) { diff --git a/target/linux/bcm27xx/patches-5.4/950-0710-video-bcm2708_fb-Disable-FB-if-no-displays-found.patch b/target/linux/bcm27xx/patches-5.4/950-0710-video-bcm2708_fb-Disable-FB-if-no-displays-found.patch new file mode 100644 index 0000000000..90cb5e276e --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0710-video-bcm2708_fb-Disable-FB-if-no-displays-found.patch @@ -0,0 +1,34 @@ +From e005a4db95a48e8b14a2017bf56a0e3f3dccfa6d Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Tue, 5 May 2020 19:45:41 +0100 +Subject: [PATCH] video: bcm2708_fb: Disable FB if no displays found + +If the firmware hasn't detected a display, the driver would assume +one display was available, but because it had failed to retrieve the +display size it would try to allocate a zero-sized buffer. + +Avoid the allocation failure by bailing out early if no display is +found. + +See: https://github.com/raspberrypi/linux/issues/3598 + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + drivers/video/fbdev/bcm2708_fb.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +--- a/drivers/video/fbdev/bcm2708_fb.c ++++ b/drivers/video/fbdev/bcm2708_fb.c +@@ -1104,10 +1104,9 @@ static int bcm2708_fb_probe(struct platf + * set one display + */ + if (ret || num_displays == 0) { +- num_displays = 1; + dev_err(&dev->dev, +- "Unable to determine number of FB's. Assuming 1\n"); +- ret = 0; ++ "Unable to determine number of FBs. Disabling driver.\n"); ++ return -ENOENT; + } else { + fbdev->firmware_supports_multifb = 1; + } diff --git a/target/linux/bcm27xx/patches-5.4/950-0711-overlays-sc16is752-spi1-Add-xtal-parameter.patch b/target/linux/bcm27xx/patches-5.4/950-0711-overlays-sc16is752-spi1-Add-xtal-parameter.patch new file mode 100644 index 0000000000..582c32aa90 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0711-overlays-sc16is752-spi1-Add-xtal-parameter.patch @@ -0,0 +1,38 @@ +From 48bf88ebb1abf55168f636d7a6edf2ca79be13c6 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Wed, 6 May 2020 14:25:20 +0100 +Subject: [PATCH] overlays: sc16is752-spi1: Add xtal parameter + +The other sc16is75x overlays have an xtal parameter to allow a +different crystal frequency to be specified, but sc16is752-spi1 +doesn't. Fix this omission. + +See: https://www.raspberrypi.org/forums/viewtopic.php?f=107&t=273234 + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/overlays/README | 1 + + arch/arm/boot/dts/overlays/sc16is752-spi1-overlay.dts | 3 ++- + 2 files changed, 3 insertions(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -2114,6 +2114,7 @@ Info: Overlay for the NXP SC16IS752 Du + + Load: dtoverlay=sc16is752-spi1,<param>=<val> + Params: int_pin GPIO used for IRQ (default 24) ++ xtal On-board crystal frequency (default 14745600) + + + Name: sdhost +--- a/arch/arm/boot/dts/overlays/sc16is752-spi1-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sc16is752-spi1-overlay.dts +@@ -56,6 +56,7 @@ + }; + + __overrides__ { +- int_pin = <&sc16is752>,"interrupts:0"; ++ int_pin = <&sc16is752>,"interrupts:0"; ++ xtal = <&sc16is752_clk>,"clock-frequency:0"; + }; + }; diff --git a/target/linux/bcm27xx/patches-5.4/950-0712-vc4_hdmi-Fix-register-offset-when-sending-longer-CEC.patch b/target/linux/bcm27xx/patches-5.4/950-0712-vc4_hdmi-Fix-register-offset-when-sending-longer-CEC.patch new file mode 100644 index 0000000000..d6aa46a4f4 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0712-vc4_hdmi-Fix-register-offset-when-sending-longer-CEC.patch @@ -0,0 +1,42 @@ +From 602ec343e69479dbec368f67d09c9f3e3e5ac248 Mon Sep 17 00:00:00 2001 +From: Dom Cobley <popcornmix@gmail.com> +Date: Thu, 7 May 2020 18:16:07 +0100 +Subject: [PATCH] vc4_hdmi: Fix register offset when sending longer CEC + messages + +Signed-off-by: Dom Cobley <popcornmix@gmail.com> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -1264,8 +1264,13 @@ static void vc4_cec_read_msg(struct vc4_ + + msg->len = 1 + ((cntrl1 & VC4_HDMI_CEC_REC_WRD_CNT_MASK) >> + VC4_HDMI_CEC_REC_WRD_CNT_SHIFT); ++ ++ if (msg->len > 16) { ++ DRM_ERROR("Attempting to read too much data (%d)\n", msg->len); ++ return; ++ } + for (i = 0; i < msg->len; i += 4) { +- u32 val = HDMI_READ(HDMI_CEC_RX_DATA_1 + i); ++ u32 val = HDMI_READ(HDMI_CEC_RX_DATA_1 + (i>>2)); + + msg->msg[i] = val & 0xff; + msg->msg[i + 1] = (val >> 8) & 0xff; +@@ -1361,8 +1366,12 @@ static int vc4_hdmi_cec_adap_transmit(st + u32 val; + unsigned int i; + ++ if (msg->len > 16) { ++ DRM_ERROR("Attempting to transmit too much data (%d)\n", msg->len); ++ return -ENOMEM; ++ } + for (i = 0; i < msg->len; i += 4) +- HDMI_WRITE(HDMI_CEC_TX_DATA_1 + i, ++ HDMI_WRITE(HDMI_CEC_TX_DATA_1 + (i>>2), + (msg->msg[i]) | + (msg->msg[i + 1] << 8) | + (msg->msg[i + 2] << 16) | diff --git a/target/linux/bcm27xx/patches-5.4/950-0713-vc4_hdmi-Fix-up-CEC-registers.patch b/target/linux/bcm27xx/patches-5.4/950-0713-vc4_hdmi-Fix-up-CEC-registers.patch new file mode 100644 index 0000000000..a00aee15bd --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0713-vc4_hdmi-Fix-up-CEC-registers.patch @@ -0,0 +1,43 @@ +From 25402f4978434949e5ea550c9b1b4a192e95fd83 Mon Sep 17 00:00:00 2001 +From: Dom Cobley <popcornmix@gmail.com> +Date: Thu, 7 May 2020 18:16:07 +0100 +Subject: [PATCH] vc4_hdmi: Fix up CEC registers + +Fix an incorrect register address, add a +missing one and reorder into address order + +Signed-off-by: Dom Cobley <popcornmix@gmail.com> +--- + drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h +@@ -33,11 +33,12 @@ enum vc4_hdmi_field { + HDMI_CEC_CNTRL_3, + HDMI_CEC_CNTRL_4, + HDMI_CEC_CNTRL_5, ++ HDMI_CEC_CPU_STATUS, ++ HDMI_CEC_CPU_SET, + HDMI_CEC_CPU_CLEAR, +- HDMI_CEC_CPU_MASK_CLEAR, +- HDMI_CEC_CPU_MASK_SET, + HDMI_CEC_CPU_MASK_STATUS, +- HDMI_CEC_CPU_STATUS, ++ HDMI_CEC_CPU_MASK_SET, ++ HDMI_CEC_CPU_MASK_CLEAR, + + /* + * Transmit data, first byte is low byte of the 32-bit reg. +@@ -205,9 +206,10 @@ static const struct vc4_hdmi_register vc + VC4_HDMI_REG(HDMI_TX_PHY_RESET_CTL, 0x02c0), + VC4_HDMI_REG(HDMI_TX_PHY_CTL_0, 0x02c4), + VC4_HDMI_REG(HDMI_CEC_CPU_STATUS, 0x0340), ++ VC4_HDMI_REG(HDMI_CEC_CPU_SET, 0x0344), + VC4_HDMI_REG(HDMI_CEC_CPU_CLEAR, 0x0348), + VC4_HDMI_REG(HDMI_CEC_CPU_MASK_STATUS, 0x034c), +- VC4_HDMI_REG(HDMI_CEC_CPU_MASK_SET, 0x034c), ++ VC4_HDMI_REG(HDMI_CEC_CPU_MASK_SET, 0x0350), + VC4_HDMI_REG(HDMI_CEC_CPU_MASK_CLEAR, 0x0354), + VC4_HDMI_REG(HDMI_RAM_PACKET_START, 0x0400), + }; diff --git a/target/linux/bcm27xx/patches-5.4/950-0714-vc4_hdmi_regs-Add-Intr2-register-block.patch b/target/linux/bcm27xx/patches-5.4/950-0714-vc4_hdmi_regs-Add-Intr2-register-block.patch new file mode 100644 index 0000000000..d750e55abb --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0714-vc4_hdmi_regs-Add-Intr2-register-block.patch @@ -0,0 +1,151 @@ +From 2aa3a92e409ed4ad416eceacef998f0027016a81 Mon Sep 17 00:00:00 2001 +From: Dom Cobley <popcornmix@gmail.com> +Date: Thu, 7 May 2020 18:16:07 +0100 +Subject: [PATCH] vc4_hdmi_regs: Add Intr2 register block + +Signed-off-by: Dom Cobley <popcornmix@gmail.com> +--- + arch/arm/boot/dts/bcm2711.dtsi | 14 ++++++++++---- + drivers/gpu/drm/vc4/vc4_hdmi.c | 8 ++++++++ + drivers/gpu/drm/vc4/vc4_hdmi.h | 2 ++ + drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 17 +++++++++++++++++ + 4 files changed, 37 insertions(+), 4 deletions(-) + +--- a/arch/arm/boot/dts/bcm2711.dtsi ++++ b/arch/arm/boot/dts/bcm2711.dtsi +@@ -316,7 +316,8 @@ + <0x7ef01f00 0x400>, + <0x7ef00200 0x80>, + <0x7ef04300 0x100>, +- <0x7ef20000 0x100>; ++ <0x7ef20000 0x100>, ++ <0x7ef00100 0x30>; + reg-names = "hdmi", + "dvp", + "phy", +@@ -325,13 +326,15 @@ + "metadata", + "csc", + "cec", +- "hd"; ++ "hd", ++ "intr2"; + clocks = <&firmware_clocks 13>; + clock-names = "hdmi"; + resets = <&dvp 0>; + ddc = <&ddc0>; + dmas = <&dma 10>; + dma-names = "audio-rx"; ++ interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + +@@ -353,7 +356,8 @@ + <0x7ef06f00 0x400>, + <0x7ef00280 0x80>, + <0x7ef09300 0x100>, +- <0x7ef20000 0x100>; ++ <0x7ef20000 0x100>, ++ <0x7ef00100 0x30>; + reg-names = "hdmi", + "dvp", + "phy", +@@ -362,13 +366,15 @@ + "metadata", + "csc", + "cec", +- "hd"; ++ "hd", ++ "intr2"; + ddc = <&ddc1>; + clocks = <&firmware_clocks 13>; + clock-names = "hdmi"; + resets = <&dvp 1>; + dmas = <&dma 17>; + dma-names = "audio-rx"; ++ interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -1581,6 +1581,14 @@ static int vc5_hdmi_init_resources(struc + if (IS_ERR(vc4_hdmi->dvp_regs)) + return PTR_ERR(vc4_hdmi->dvp_regs); + ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr2"); ++ if (!res) ++ return -ENODEV; ++ ++ vc4_hdmi->intr2_regs = devm_ioremap(dev, res->start, resource_size(res)); ++ if (IS_ERR(vc4_hdmi->intr2_regs)) ++ return PTR_ERR(vc4_hdmi->intr2_regs); ++ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); + if (!res) + return -ENODEV; +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -140,6 +140,8 @@ struct vc4_hdmi { + void __iomem *ram_regs; + /* VC5 Only */ + void __iomem *rm_regs; ++ /* VC5 Only */ ++ void __iomem *intr2_regs; + + int hpd_gpio; + bool hpd_active_low; +--- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h +@@ -24,6 +24,7 @@ enum vc4_hdmi_regs { + VC5_PHY, + VC5_RAM, + VC5_RM, ++ VC5_INTR2, + }; + + enum vc4_hdmi_field { +@@ -148,6 +149,7 @@ struct vc4_hdmi_register { + #define VC5_CEC_REG(reg, offset) _VC4_REG(VC5_CEC, reg, offset) + #define VC5_CSC_REG(reg, offset) _VC4_REG(VC5_CSC, reg, offset) + #define VC5_DVP_REG(reg, offset) _VC4_REG(VC5_DVP, reg, offset) ++#define VC5_INTR2_REG(reg, offset) _VC4_REG(VC5_INTR2, reg, offset) + #define VC5_PHY_REG(reg, offset) _VC4_REG(VC5_PHY, reg, offset) + #define VC5_RAM_REG(reg, offset) _VC4_REG(VC5_RAM, reg, offset) + #define VC5_RM_REG(reg, offset) _VC4_REG(VC5_RM, reg, offset) +@@ -280,6 +282,12 @@ static const struct vc4_hdmi_register vc + VC5_CEC_REG(HDMI_CEC_RX_DATA_2, 0x03c), + VC5_CEC_REG(HDMI_CEC_RX_DATA_3, 0x040), + VC5_CEC_REG(HDMI_CEC_RX_DATA_4, 0x044), ++ VC5_INTR2_REG(HDMI_CEC_CPU_STATUS, 0x0000), ++ VC5_INTR2_REG(HDMI_CEC_CPU_SET, 0x0004), ++ VC5_INTR2_REG(HDMI_CEC_CPU_CLEAR, 0x0008), ++ VC5_INTR2_REG(HDMI_CEC_CPU_MASK_STATUS, 0x000c), ++ VC5_INTR2_REG(HDMI_CEC_CPU_MASK_SET, 0x0010), ++ VC5_INTR2_REG(HDMI_CEC_CPU_MASK_CLEAR, 0x0014), + + VC5_CSC_REG(HDMI_CSC_CTL, 0x000), + VC5_CSC_REG(HDMI_CSC_12_11, 0x004), +@@ -356,6 +364,12 @@ static const struct vc4_hdmi_register vc + VC5_CEC_REG(HDMI_CEC_RX_DATA_2, 0x03c), + VC5_CEC_REG(HDMI_CEC_RX_DATA_3, 0x040), + VC5_CEC_REG(HDMI_CEC_RX_DATA_4, 0x044), ++ VC5_INTR2_REG(HDMI_CEC_CPU_STATUS, 0x0000), ++ VC5_INTR2_REG(HDMI_CEC_CPU_SET, 0x0004), ++ VC5_INTR2_REG(HDMI_CEC_CPU_CLEAR, 0x0008), ++ VC5_INTR2_REG(HDMI_CEC_CPU_MASK_STATUS, 0x000c), ++ VC5_INTR2_REG(HDMI_CEC_CPU_MASK_SET, 0x0010), ++ VC5_INTR2_REG(HDMI_CEC_CPU_MASK_CLEAR, 0x0014), + + VC5_CSC_REG(HDMI_CSC_CTL, 0x000), + VC5_CSC_REG(HDMI_CSC_12_11, 0x004), +@@ -386,6 +400,9 @@ void __iomem *__vc4_hdmi_get_field_base( + case VC5_DVP: + return hdmi->dvp_regs; + ++ case VC5_INTR2: ++ return hdmi->intr2_regs; ++ + case VC5_PHY: + return hdmi->phy_regs; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0715-vc4_hdmi_regs-Make-interrupt-mask-variant-specific.patch b/target/linux/bcm27xx/patches-5.4/950-0715-vc4_hdmi_regs-Make-interrupt-mask-variant-specific.patch new file mode 100644 index 0000000000..b92d1dbac9 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0715-vc4_hdmi_regs-Make-interrupt-mask-variant-specific.patch @@ -0,0 +1,101 @@ +From 9a9f4303c95f18cc062569c9c5d5240d06ddd69b Mon Sep 17 00:00:00 2001 +From: Dom Cobley <popcornmix@gmail.com> +Date: Thu, 7 May 2020 18:16:08 +0100 +Subject: [PATCH] vc4_hdmi_regs: Make interrupt mask variant specific + +Signed-off-by: Dom Cobley <popcornmix@gmail.com> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 14 ++++++++++---- + drivers/gpu/drm/vc4/vc4_hdmi.h | 3 +++ + drivers/gpu/drm/vc4/vc4_regs.h | 9 +++++++++ + 3 files changed, 22 insertions(+), 4 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -1285,7 +1285,7 @@ static irqreturn_t vc4_cec_irq_handler(i + u32 stat = HDMI_READ(HDMI_CEC_CPU_STATUS); + u32 cntrl1, cntrl5; + +- if (!(stat & VC4_HDMI_CPU_CEC)) ++ if (!(stat & vc4_hdmi->variant->cec_mask)) + return IRQ_NONE; + vc4_hdmi->cec_rx_msg.len = 0; + cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1); +@@ -1301,7 +1301,7 @@ static irqreturn_t vc4_cec_irq_handler(i + cntrl1 &= ~VC4_HDMI_CEC_START_XMIT_BEGIN; + } + HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1); +- HDMI_WRITE(HDMI_CEC_CPU_CLEAR, VC4_HDMI_CPU_CEC); ++ HDMI_WRITE(HDMI_CEC_CPU_CLEAR, vc4_hdmi->variant->cec_mask); + + return IRQ_WAKE_THREAD; + } +@@ -1340,9 +1340,9 @@ static int vc4_hdmi_cec_adap_enable(stru + ((3600 / usecs) << VC4_HDMI_CEC_CNT_TO_3600_US_SHIFT) | + ((3500 / usecs) << VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT)); + +- HDMI_WRITE(HDMI_CEC_CPU_MASK_CLEAR, VC4_HDMI_CPU_CEC); ++ HDMI_WRITE(HDMI_CEC_CPU_MASK_CLEAR, vc4_hdmi->variant->cec_mask); + } else { +- HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, VC4_HDMI_CPU_CEC); ++ HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, vc4_hdmi->variant->cec_mask); + HDMI_WRITE(HDMI_CEC_CNTRL_5, val | + VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET); + } +@@ -1784,6 +1784,8 @@ static const struct vc4_hdmi_variant bcm + .get_hsm_clock = vc4_hdmi_get_hsm_clock, + .calc_hsm_clock = vc4_hdmi_calc_hsm_clock, + .channel_map = vc4_hdmi_channel_map, ++ ++ .cec_mask = VC4_HDMI_CPU_CEC, + }; + + static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = { +@@ -1809,6 +1811,8 @@ static const struct vc4_hdmi_variant bcm + .get_hsm_clock = vc5_hdmi_get_hsm_clock, + .calc_hsm_clock = vc5_hdmi_calc_hsm_clock, + .channel_map = vc5_hdmi_channel_map, ++ ++ .cec_mask = VC5_HDMI0_CPU_CEC_RX | VC5_HDMI0_CPU_CEC_TX, + }; + + static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = { +@@ -1834,6 +1838,8 @@ static const struct vc4_hdmi_variant bcm + .get_hsm_clock = vc5_hdmi_get_hsm_clock, + .calc_hsm_clock = vc5_hdmi_calc_hsm_clock, + .channel_map = vc5_hdmi_channel_map, ++ ++ .cec_mask = VC5_HDMI1_CPU_CEC_RX | VC5_HDMI1_CPU_CEC_TX, + }; + + static const struct of_device_id vc4_hdmi_dt_match[] = { +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -97,6 +97,9 @@ struct vc4_hdmi_variant { + + /* Callback to get channel map */ + u32 (*channel_map)(struct vc4_hdmi *vc4_hdmi, u32 channel_mask); ++ ++ /* Bitmask for CEC events */ ++ u32 cec_mask; + }; + + /* HDMI audio information */ +--- a/drivers/gpu/drm/vc4/vc4_regs.h ++++ b/drivers/gpu/drm/vc4/vc4_regs.h +@@ -668,6 +668,15 @@ + # define VC4_HDMI_CPU_CEC BIT(6) + # define VC4_HDMI_CPU_HOTPLUG BIT(0) + ++# define VC5_HDMI0_CPU_CEC_RX BIT(1) ++# define VC5_HDMI0_CPU_CEC_TX BIT(0) ++# define VC5_HDMI0_CPU_HOTPLUG_CONN BIT(4) ++# define VC5_HDMI0_CPU_HOTPLUG_REM BIT(5) ++# define VC5_HDMI1_CPU_CEC_RX BIT(7) ++# define VC5_HDMI1_CPU_CEC_TX BIT(6) ++# define VC5_HDMI1_CPU_HOTPLUG_CONN BIT(10) ++# define VC5_HDMI1_CPU_HOTPLUG_REM BIT(11) ++ + /* Debug: Current receive value on the CEC pad. */ + # define VC4_HD_CECRXD BIT(9) + /* Debug: Override CEC output to 0. */ diff --git a/target/linux/bcm27xx/patches-5.4/950-0716-vc4_hdmi-Make-irq-shared.patch b/target/linux/bcm27xx/patches-5.4/950-0716-vc4_hdmi-Make-irq-shared.patch new file mode 100644 index 0000000000..dc0fec6c7a --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0716-vc4_hdmi-Make-irq-shared.patch @@ -0,0 +1,22 @@ +From 45129a4714234396b4725d8898b14875add1874e Mon Sep 17 00:00:00 2001 +From: Dom Cobley <popcornmix@gmail.com> +Date: Thu, 7 May 2020 18:16:08 +0100 +Subject: [PATCH] vc4_hdmi: Make irq shared + +Signed-off-by: Dom Cobley <popcornmix@gmail.com> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -1428,7 +1428,8 @@ static int vc4_hdmi_cec_init(struct vc4_ + HDMI_WRITE(HDMI_CEC_CNTRL_1, value); + ret = devm_request_threaded_irq(&pdev->dev, platform_get_irq(pdev, 0), + vc4_cec_irq_handler, +- vc4_cec_irq_handler_thread, 0, ++ vc4_cec_irq_handler_thread, ++ IRQF_SHARED, + "vc4 hdmi cec", vc4_hdmi); + if (ret) + goto err_delete_cec_adap; diff --git a/target/linux/bcm27xx/patches-5.4/950-0717-vc4_hdmi-Adjust-CEC-ref-clock-based-on-its-input-clo.patch b/target/linux/bcm27xx/patches-5.4/950-0717-vc4_hdmi-Adjust-CEC-ref-clock-based-on-its-input-clo.patch new file mode 100644 index 0000000000..da99772696 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0717-vc4_hdmi-Adjust-CEC-ref-clock-based-on-its-input-clo.patch @@ -0,0 +1,89 @@ +From 32e84f4f525e2a0d7dc021b5795df34407096b0e Mon Sep 17 00:00:00 2001 +From: Dom Cobley <popcornmix@gmail.com> +Date: Thu, 7 May 2020 18:16:08 +0100 +Subject: [PATCH] vc4_hdmi: Adjust CEC ref clock based on its input + clock + +2711 uses a fixed 27MHz input, earlier models use the HSM clock + +Signed-off-by: Dom Cobley <popcornmix@gmail.com> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 11 ++++++++--- + drivers/gpu/drm/vc4/vc4_hdmi.h | 3 +++ + 2 files changed, 11 insertions(+), 3 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -79,6 +79,7 @@ + # define VC4_HD_M_ENABLE BIT(0) + + #define CEC_CLOCK_FREQ 40000 ++#define VC4_HSM_CLOCK 163682864 + + static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) + { +@@ -755,8 +756,7 @@ static u32 vc4_hdmi_calc_hsm_clock(struc + * needs to be a bit higher than the pixel clock rate + * (generally 148.5Mhz). + */ +- +- return 163682864; ++ return VC4_HSM_CLOCK; + } + + static u32 vc5_hdmi_calc_hsm_clock(struct vc4_hdmi *vc4_hdmi, unsigned long pixel_rate) +@@ -1399,6 +1399,7 @@ static int vc4_hdmi_cec_init(struct vc4_ + struct cec_connector_info conn_info; + struct platform_device *pdev = vc4_hdmi->pdev; + u32 value; ++ u32 clk_cnt; + int ret; + + if (!vc4_hdmi->variant->cec_available) +@@ -1423,8 +1424,9 @@ static int vc4_hdmi_cec_init(struct vc4_ + * divider: the hsm_clock rate and this divider setting will + * give a 40 kHz CEC clock. + */ ++ clk_cnt = vc4_hdmi->variant->cec_input_clock / CEC_CLOCK_FREQ; + value |= VC4_HDMI_CEC_ADDR_MASK | +- (4091 << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT); ++ ((clk_cnt-1) << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT); + HDMI_WRITE(HDMI_CEC_CNTRL_1, value); + ret = devm_request_threaded_irq(&pdev->dev, platform_get_irq(pdev, 0), + vc4_cec_irq_handler, +@@ -1769,6 +1771,7 @@ static int vc4_hdmi_dev_remove(struct pl + + static const struct vc4_hdmi_variant bcm2835_variant = { + .max_pixel_clock = 162000000, ++ .cec_input_clock = VC4_HSM_CLOCK, + .audio_available = true, + .cec_available = true, + .registers = vc4_hdmi_fields, +@@ -1793,6 +1796,7 @@ static const struct vc4_hdmi_variant bcm + .id = 0, + .audio_available = true, + .max_pixel_clock = 297000000, ++ .cec_input_clock = 27000000, + .registers = vc5_hdmi_hdmi0_fields, + .num_registers = ARRAY_SIZE(vc5_hdmi_hdmi0_fields), + .phy_lane_mapping = { +@@ -1820,6 +1824,7 @@ static const struct vc4_hdmi_variant bcm + .id = 1, + .audio_available = true, + .max_pixel_clock = 297000000, ++ .cec_input_clock = 27000000, + .registers = vc5_hdmi_hdmi1_fields, + .num_registers = ARRAY_SIZE(vc5_hdmi_hdmi1_fields), + .phy_lane_mapping = { +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -48,6 +48,9 @@ struct vc4_hdmi_variant { + /* Maximum pixel clock supported by the controller (in Hz) */ + unsigned long long max_pixel_clock; + ++ /* Input clock frequency of CEC block (in Hz) */ ++ unsigned long cec_input_clock; ++ + /* List of the registers available on that variant */ + const struct vc4_hdmi_register *registers; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0718-vc4_hdmi-Remove-cec_available-flag-as-always-support.patch b/target/linux/bcm27xx/patches-5.4/950-0718-vc4_hdmi-Remove-cec_available-flag-as-always-support.patch new file mode 100644 index 0000000000..25d5fd9507 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0718-vc4_hdmi-Remove-cec_available-flag-as-always-support.patch @@ -0,0 +1,44 @@ +From c214fa3d1bc1142cb8f185f71deb3f14915fe55d Mon Sep 17 00:00:00 2001 +From: Dom Cobley <popcornmix@gmail.com> +Date: Thu, 7 May 2020 18:16:09 +0100 +Subject: [PATCH] vc4_hdmi: Remove cec_available flag as always + supported + +Signed-off-by: Dom Cobley <popcornmix@gmail.com> +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 4 ---- + drivers/gpu/drm/vc4/vc4_hdmi.h | 3 --- + 2 files changed, 7 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -1402,9 +1402,6 @@ static int vc4_hdmi_cec_init(struct vc4_ + u32 clk_cnt; + int ret; + +- if (!vc4_hdmi->variant->cec_available) +- return 0; +- + vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops, + vc4_hdmi, "vc4", + CEC_CAP_DEFAULTS | +@@ -1773,7 +1770,6 @@ static const struct vc4_hdmi_variant bcm + .max_pixel_clock = 162000000, + .cec_input_clock = VC4_HSM_CLOCK, + .audio_available = true, +- .cec_available = true, + .registers = vc4_hdmi_fields, + .num_registers = ARRAY_SIZE(vc4_hdmi_fields), + +--- a/drivers/gpu/drm/vc4/vc4_hdmi.h ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h +@@ -42,9 +42,6 @@ struct vc4_hdmi_variant { + /* Set to true when the audio support is available */ + bool audio_available; + +- /* Set to true when the CEC support is available */ +- bool cec_available; +- + /* Maximum pixel clock supported by the controller (in Hz) */ + unsigned long long max_pixel_clock; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0719-overlays-tc358743-Use-intra-overlay-fragments.patch b/target/linux/bcm27xx/patches-5.4/950-0719-overlays-tc358743-Use-intra-overlay-fragments.patch new file mode 100644 index 0000000000..5bfa6bc60a --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0719-overlays-tc358743-Use-intra-overlay-fragments.patch @@ -0,0 +1,55 @@ +From adf5f2833517758152cbc9032dd93934a1e16ca1 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Mon, 11 May 2020 11:55:45 +0100 +Subject: [PATCH] overlays: tc358743: Use intra-overlay fragments + +The tc358743 overlay was written using a workaround to a problem with +fragments that target other fragments, but this had the unfortunate +side-effect of preventing the overlay from being applied at runtime +(the kernel doesn't allow nodes to be overwritten by an overlay, only +properties). + +The current firmware and dtoverlay/dtparam utilities include support +for these "intra-overlay" fragments, so remove the workaround and do +it properly. + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + .../boot/dts/overlays/tc358743-overlay.dts | 20 ++++--------------- + 1 file changed, 4 insertions(+), 16 deletions(-) + +--- a/arch/arm/boot/dts/overlays/tc358743-overlay.dts ++++ b/arch/arm/boot/dts/overlays/tc358743-overlay.dts +@@ -54,28 +54,16 @@ + }; + + fragment@2 { +- target = <&i2c_csi_dsi>; ++ target = <&tc358743>; + __overlay__ { +- tc358743@0f { +- port { +- endpoint { +- data-lanes = <1 2>; +- }; +- }; +- }; ++ data-lanes = <1 2>; + }; + }; + + fragment@3 { +- target = <&i2c_csi_dsi>; ++ target = <&tc358743>; + __dormant__ { +- tc358743@0f { +- port { +- endpoint { +- data-lanes = <1 2 3 4>; +- }; +- }; +- }; ++ data-lanes = <1 2 3 4>; + }; + }; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0720-overlays-Move-fixed-clock-nodes-to-the-root.patch b/target/linux/bcm27xx/patches-5.4/950-0720-overlays-Move-fixed-clock-nodes-to-the-root.patch new file mode 100644 index 0000000000..aa19ca11d5 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0720-overlays-Move-fixed-clock-nodes-to-the-root.patch @@ -0,0 +1,326 @@ +From 805e008c18ec09c4115e4cec413642028cd9a8e2 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Mon, 11 May 2020 15:12:21 +0100 +Subject: [PATCH] overlays: Move "fixed-clock" nodes to the root + +Apart from some special cases, device objects are only created for +nodes if they are children of a bus or the root node. "fixed-clock" +is one of the exceptions that will be instantiated wherever it is +found, but only during kernel initialisation - ruling out loading the +overlay at runtime. + +Move most of the affected clocks to be children of the root, only +leaving those in overlays that could be multiply instantiated, to avoid +a potential name clash. + +See: https://github.com/raspberrypi/linux/issues/3602 + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + .../dts/overlays/audiosense-pi-overlay.dts | 14 ++++++------- + arch/arm/boot/dts/overlays/draws-overlay.dts | 12 +++++------ + .../boot/dts/overlays/fe-pi-audio-overlay.dts | 2 +- + arch/arm/boot/dts/overlays/imx219-overlay.dts | 12 +++++------ + .../arm/boot/dts/overlays/irs1125-overlay.dts | 17 +++++++++------ + .../dts/overlays/mcp2515-can0-overlay.dts | 2 +- + .../dts/overlays/mcp2515-can1-overlay.dts | 2 +- + .../boot/dts/overlays/midi-uart0-overlay.dts | 2 +- + arch/arm/boot/dts/overlays/ov5647-overlay.dts | 17 +++++++++------ + .../boot/dts/overlays/rpivid-v4l2-overlay.dts | 17 +++++++++------ + .../dts/overlays/sc16is752-spi1-overlay.dts | 21 ++++++++++++------- + .../boot/dts/overlays/tc358743-overlay.dts | 17 +++++++++------ + 12 files changed, 80 insertions(+), 55 deletions(-) + +--- a/arch/arm/boot/dts/overlays/audiosense-pi-overlay.dts ++++ b/arch/arm/boot/dts/overlays/audiosense-pi-overlay.dts +@@ -24,6 +24,13 @@ + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; ++ ++ /* audio external oscillator */ ++ codec_osc: codec_osc { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <12000000>; /* 12 MHz */ ++ }; + }; + }; + +@@ -44,13 +51,6 @@ + #size-cells = <0>; + status = "okay"; + +- /* audio external oscillator */ +- codec_osc: codec_osc { +- compatible = "fixed-clock"; +- #clock-cells = <0>; +- clock-frequency = <12000000>; /* 12 MHz */ +- }; +- + codec: tlv320aic32x4@18 { + #sound-dai-cells = <0>; + compatible = "ti,tlv320aic32x4"; +--- a/arch/arm/boot/dts/overlays/draws-overlay.dts ++++ b/arch/arm/boot/dts/overlays/draws-overlay.dts +@@ -30,6 +30,12 @@ + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; ++ ++ sc16is752_clk: sc16is752_draws_clk { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <1843200>; ++ }; + }; + + pps: pps { +@@ -78,12 +84,6 @@ + + pinctrl-names = "default"; + pinctrl-0 = <&sc16is752_irq>; +- +- sc16is752_clk: sc16is752_clk { +- compatible = "fixed-clock"; +- #clock-cells = <0>; +- clock-frequency = <1843200>; +- }; + }; + + tla2024: tla2024@48 { +--- a/arch/arm/boot/dts/overlays/fe-pi-audio-overlay.dts ++++ b/arch/arm/boot/dts/overlays/fe-pi-audio-overlay.dts +@@ -6,7 +6,7 @@ + compatible = "brcm,bcm2835"; + + fragment@0 { +- target = <&clocks>; ++ target-path = "/"; + __overlay__ { + sgtl5000_mclk: sgtl5000_mclk { + compatible = "fixed-clock"; +--- a/arch/arm/boot/dts/overlays/imx219-overlay.dts ++++ b/arch/arm/boot/dts/overlays/imx219-overlay.dts +@@ -27,12 +27,6 @@ + VDIG-supply = <&imx219_vdig>; /* 1.8v */ + VDDL-supply = <&imx219_vddl>; /* 1.2v */ + +- imx219_clk: camera-clk { +- compatible = "fixed-clock"; +- #clock-cells = <0>; +- clock-frequency = <24000000>; +- }; +- + port { + imx219_0: endpoint { + remote-endpoint = <&csi1_ep>; +@@ -90,6 +84,12 @@ + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; ++ ++ imx219_clk: camera-clk { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24000000>; ++ }; + }; + }; + +--- a/arch/arm/boot/dts/overlays/irs1125-overlay.dts ++++ b/arch/arm/boot/dts/overlays/irs1125-overlay.dts +@@ -21,12 +21,6 @@ + pwdn-gpios = <&gpio 5 0>; + clocks = <&irs1125_clk>; + +- irs1125_clk: camera-clk { +- compatible = "fixed-clock"; +- #clock-cells = <0>; +- clock-frequency = <26000000>; +- }; +- + port { + irs1125_0: endpoint { + remote-endpoint = <&csi1_ep>; +@@ -75,4 +69,15 @@ + cam0-pwdn = <&irs1125>,"pwdn-gpios:4"; + }; + }; ++ ++ fragment@5 { ++ target-path = "/"; ++ __overlay__ { ++ irs1125_clk: camera-clk { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <26000000>; ++ }; ++ }; ++ }; + }; +--- a/arch/arm/boot/dts/overlays/mcp2515-can0-overlay.dts ++++ b/arch/arm/boot/dts/overlays/mcp2515-can0-overlay.dts +@@ -35,7 +35,7 @@ + + /* the clock/oscillator of the can-controller */ + fragment@3 { +- target-path = "/clocks"; ++ target-path = "/"; + __overlay__ { + /* external oscillator of mcp2515 on SPI0.0 */ + can0_osc: can0_osc { +--- a/arch/arm/boot/dts/overlays/mcp2515-can1-overlay.dts ++++ b/arch/arm/boot/dts/overlays/mcp2515-can1-overlay.dts +@@ -35,7 +35,7 @@ + + /* the clock/oscillator of the can-controller */ + fragment@3 { +- target-path = "/clocks"; ++ target-path = "/"; + __overlay__ { + /* external oscillator of mcp2515 on spi0.1 */ + can1_osc: can1_osc { +--- a/arch/arm/boot/dts/overlays/midi-uart0-overlay.dts ++++ b/arch/arm/boot/dts/overlays/midi-uart0-overlay.dts +@@ -15,7 +15,7 @@ + compatible = "brcm,bcm2835"; + + fragment@0 { +- target-path = "/clocks"; ++ target-path = "/"; + __overlay__ { + midi_clk: midi_clk { + compatible = "fixed-clock"; +--- a/arch/arm/boot/dts/overlays/ov5647-overlay.dts ++++ b/arch/arm/boot/dts/overlays/ov5647-overlay.dts +@@ -21,12 +21,6 @@ + pwdn-gpios = <&gpio 41 1>, <&gpio 32 1>; + clocks = <&ov5647_clk>; + +- ov5647_clk: camera-clk { +- compatible = "fixed-clock"; +- #clock-cells = <0>; +- clock-frequency = <25000000>; +- }; +- + port { + ov5647_0: endpoint { + remote-endpoint = <&csi1_ep>; +@@ -77,4 +71,15 @@ + cam0-led = <&ov5647>,"pwdn-gpios:16"; + }; + }; ++ ++ fragment@5 { ++ target-path = "/"; ++ __overlay__ { ++ ov5647_clk: camera-clk { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <25000000>; ++ }; ++ }; ++ }; + }; +--- a/arch/arm/boot/dts/overlays/rpivid-v4l2-overlay.dts ++++ b/arch/arm/boot/dts/overlays/rpivid-v4l2-overlay.dts +@@ -26,12 +26,6 @@ + + clocks = <&hevc_clk>; + clock-names = "hevc"; +- +- hevc_clk: hevc_clk { +- compatible = "fixed-clock"; +- #clock-cells = <0>; +- clock-frequency = <500000000>; +- }; + }; + }; + }; +@@ -53,4 +47,15 @@ + }; + }; + }; ++ ++ fragment@2 { ++ target-path = "/"; ++ __overlay__ { ++ hevc_clk: hevc_clk { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <500000000>; ++ }; ++ }; ++ }; + }; +--- a/arch/arm/boot/dts/overlays/sc16is752-spi1-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sc16is752-spi1-overlay.dts +@@ -38,12 +38,6 @@ + #gpio-controller; + #gpio-cells = <2>; + spi-max-frequency = <4000000>; +- +- sc16is752_clk: sc16is752_clk { +- compatible = "fixed-clock"; +- #clock-cells = <0>; +- clock-frequency = <14745600>; +- }; + }; + }; + }; +@@ -55,8 +49,19 @@ + }; + }; + +- __overrides__ { ++ fragment@3 { ++ target-path = "/"; ++ __overlay__ { ++ sc16is752_clk: sc16is752_spi1_clk { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <14745600>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { + int_pin = <&sc16is752>,"interrupts:0"; + xtal = <&sc16is752_clk>,"clock-frequency:0"; +- }; ++ }; + }; +--- a/arch/arm/boot/dts/overlays/tc358743-overlay.dts ++++ b/arch/arm/boot/dts/overlays/tc358743-overlay.dts +@@ -21,12 +21,6 @@ + clocks = <&tc358743_clk>; + clock-names = "refclk"; + +- tc358743_clk: bridge-clk { +- compatible = "fixed-clock"; +- #clock-cells = <0>; +- clock-frequency = <27000000>; +- }; +- + port { + tc358743: endpoint { + remote-endpoint = <&csi1_ep>; +@@ -81,6 +75,17 @@ + }; + }; + ++ fragment@6 { ++ target-path = "/"; ++ __overlay__ { ++ tc358743_clk: bridge-clk { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <27000000>; ++ }; ++ }; ++ }; ++ + __overrides__ { + 4lane = <0>, "-2+3"; + link-frequency = <&tc358743>,"link-frequencies#0"; diff --git a/target/linux/bcm27xx/patches-5.4/950-0721-raspberrypi-dts-Switch-to-discrete-ALSA-devices.patch b/target/linux/bcm27xx/patches-5.4/950-0721-raspberrypi-dts-Switch-to-discrete-ALSA-devices.patch new file mode 100644 index 0000000000..c5c302f832 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0721-raspberrypi-dts-Switch-to-discrete-ALSA-devices.patch @@ -0,0 +1,84 @@ +From c2b3b61053c2efd8fb96633c214d9f959c25aea3 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Tue, 12 May 2020 08:32:42 +0100 +Subject: [PATCH] raspberrypi: dts: Switch to discrete ALSA devices + +Add the command line options required to enable audio over discrete +ALSA devices. + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/bcm2708-rpi-zero-w.dts | 2 +- + arch/arm/boot/dts/bcm2708-rpi-zero.dts | 2 +- + arch/arm/boot/dts/bcm270x.dtsi | 2 +- + arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts | 2 +- + arch/arm/boot/dts/bcm2710-rpi-3-b.dts | 2 +- + arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 2 +- + 6 files changed, 6 insertions(+), 6 deletions(-) + +--- a/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts +@@ -10,7 +10,7 @@ + model = "Raspberry Pi Zero W"; + + chosen { +- bootargs = "coherent_pool=1M 8250.nr_uarts=1"; ++ bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1 snd_bcm2835.enable_headphones=1"; + }; + + aliases { +--- a/arch/arm/boot/dts/bcm2708-rpi-zero.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-zero.dts +@@ -10,7 +10,7 @@ + model = "Raspberry Pi Zero"; + + chosen { +- bootargs = "coherent_pool=1M"; ++ bootargs = "coherent_pool=1M snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1 snd_bcm2835.enable_headphones=1"; + }; + }; + +--- a/arch/arm/boot/dts/bcm270x.dtsi ++++ b/arch/arm/boot/dts/bcm270x.dtsi +@@ -3,7 +3,7 @@ + + / { + chosen { +- bootargs = "coherent_pool=1M"; ++ bootargs = "coherent_pool=1M snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1 snd_bcm2835.enable_headphones=1"; + /delete-property/ stdout-path; + }; + +--- a/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts ++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts +@@ -11,7 +11,7 @@ + model = "Raspberry Pi 3 Model B+"; + + chosen { +- bootargs = "coherent_pool=1M 8250.nr_uarts=1"; ++ bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1 snd_bcm2835.enable_headphones=1"; + }; + + aliases { +--- a/arch/arm/boot/dts/bcm2710-rpi-3-b.dts ++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts +@@ -11,7 +11,7 @@ + model = "Raspberry Pi 3 Model B"; + + chosen { +- bootargs = "coherent_pool=1M 8250.nr_uarts=1"; ++ bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1 snd_bcm2835.enable_headphones=1"; + }; + + aliases { +--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts ++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts +@@ -198,7 +198,7 @@ + + / { + chosen { +- bootargs = "coherent_pool=1M 8250.nr_uarts=1"; ++ bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1 snd_bcm2835.enable_headphones=1"; + }; + + aliases { diff --git a/target/linux/bcm27xx/patches-5.4/950-0722-dt-bindings-media-i2c-Add-IMX477-CMOS-sensor-binding.patch b/target/linux/bcm27xx/patches-5.4/950-0722-dt-bindings-media-i2c-Add-IMX477-CMOS-sensor-binding.patch new file mode 100644 index 0000000000..eb5430bbae --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0722-dt-bindings-media-i2c-Add-IMX477-CMOS-sensor-binding.patch @@ -0,0 +1,130 @@ +From 87038c8d2337bd2c79bd96ac1ef9e6471a782331 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Thu, 7 May 2020 15:50:54 +0100 +Subject: [PATCH] dt-bindings: media: i2c: Add IMX477 CMOS sensor + binding + +Add YAML device tree binding for IMX477 CMOS image sensor. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + .../devicetree/bindings/media/i2c/imx477.yaml | 113 ++++++++++++++++++ + 1 file changed, 113 insertions(+) + create mode 100644 Documentation/devicetree/bindings/media/i2c/imx477.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/media/i2c/imx477.yaml +@@ -0,0 +1,113 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/media/i2c/imx477.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Sony 1/2.3-Inch 12Mpixel CMOS Digital Image Sensor ++ ++maintainers: ++ - Naushir Patuck <naush@raspberypi.com> ++ ++description: |- ++ The Sony IMX477 is a 1/2.3-inch CMOS active pixel digital image sensor ++ with an active array size of 4056H x 3040V. It is programmable through ++ I2C interface. The I2C address is fixed to 0x1A 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,imx477 ++ ++ reg: ++ description: I2C device address ++ maxItems: 1 ++ ++ clocks: ++ maxItems: 1 ++ ++ VDIG-supply: ++ description: ++ Digital I/O voltage supply, 1.05 volts ++ ++ VANA-supply: ++ description: ++ Analog voltage supply, 2.8 volts ++ ++ VDDL-supply: ++ description: ++ Digital core voltage supply, 1.8 volts ++ ++ reset-gpios: ++ description: |- ++ Reference to the GPIO connected to the xclr pin, if any. ++ Must be released (set high) after all all supplies and INCK 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. ++ 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>; ++ ++ imx477: sensor@10 { ++ compatible = "sony,imx477"; ++ reg = <0x1a>; ++ clocks = <&imx477_clk>; ++ VANA-supply = <&imx477_vana>; /* 2.8v */ ++ VDIG-supply = <&imx477_vdig>; /* 1.05v */ ++ VDDL-supply = <&imx477_vddl>; /* 1.8v */ ++ ++ port { ++ imx477_0: endpoint { ++ remote-endpoint = <&csi1_ep>; ++ data-lanes = <1 2>; ++ clock-noncontinuous; ++ link-frequencies = /bits/ 64 <450000000>; ++ }; ++ }; ++ }; ++ }; ++ ++... diff --git a/target/linux/bcm27xx/patches-5.4/950-0723-dtoverlays-Add-IMX477-sensor-overlay.patch b/target/linux/bcm27xx/patches-5.4/950-0723-dtoverlays-Add-IMX477-sensor-overlay.patch new file mode 100644 index 0000000000..0d5ea3c188 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0723-dtoverlays-Add-IMX477-sensor-overlay.patch @@ -0,0 +1,156 @@ +From 738defbb964c5d2a34b08c24ac0e7fd4afb16173 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Thu, 7 May 2020 15:50:04 +0100 +Subject: [PATCH] dtoverlays: Add IMX477 sensor overlay + +Add an overlay for the Sony IMX477 CMOS sensor device. +Also update overlay README and Makefile. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 8 ++ + arch/arm/boot/dts/overlays/imx477-overlay.dts | 110 ++++++++++++++++++ + 3 files changed, 119 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/imx477-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -85,6 +85,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + i2s-gpio28-31.dtbo \ + ilitek251x.dtbo \ + imx219.dtbo \ ++ imx477.dtbo \ + iqaudio-codec.dtbo \ + iqaudio-dac.dtbo \ + iqaudio-dacplus.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -1390,6 +1390,14 @@ Load: dtoverlay=imx219 + Params: <None> + + ++Name: imx477 ++Info: Sony IMX477 camera module. ++ Uses Unicam 1, which is the standard camera connector on most Pi ++ variants. ++Load: dtoverlay=imx477 ++Params: <None> ++ ++ + Name: iqaudio-codec + Info: Configures the IQaudio Codec audio card + Load: dtoverlay=iqaudio-codec +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/imx477-overlay.dts +@@ -0,0 +1,110 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++// Definitions for IMX477 camera module on VC I2C bus ++/dts-v1/; ++/plugin/; ++ ++#include <dt-bindings/gpio/gpio.h> ++ ++/{ ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target = <&i2c_csi_dsi>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ imx477: imx477@1a { ++ compatible = "sony,imx477"; ++ reg = <0x1a>; ++ status = "okay"; ++ ++ clocks = <&imx477_clk>; ++ clock-names = "xclk"; ++ ++ VANA-supply = <&imx477_vana>; /* 2.8v */ ++ VDIG-supply = <&imx477_vdig>; /* 1.05v */ ++ VDDL-supply = <&imx477_vddl>; /* 1.8v */ ++ ++ port { ++ imx477_0: endpoint { ++ remote-endpoint = <&csi1_ep>; ++ clock-lanes = <0>; ++ data-lanes = <1 2>; ++ clock-noncontinuous; ++ link-frequencies = ++ /bits/ 64 <450000000>; ++ }; ++ }; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&csi1>; ++ __overlay__ { ++ status = "okay"; ++ ++ port { ++ csi1_ep: endpoint { ++ remote-endpoint = <&imx477_0>; ++ }; ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&i2c0if>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@3 { ++ target-path="/"; ++ __overlay__ { ++ imx477_vana: fixedregulator@0 { ++ compatible = "regulator-fixed"; ++ regulator-name = "imx477_vana"; ++ regulator-min-microvolt = <2800000>; ++ regulator-max-microvolt = <2800000>; ++ gpio = <&gpio 41 GPIO_ACTIVE_HIGH>; ++ enable-active-high; ++ startup-delay-us = <300000>; ++ }; ++ imx477_vdig: fixedregulator@1 { ++ compatible = "regulator-fixed"; ++ regulator-name = "imx477_vdig"; ++ regulator-min-microvolt = <1050000>; ++ regulator-max-microvolt = <1050000>; ++ }; ++ imx477_vddl: fixedregulator@2 { ++ compatible = "regulator-fixed"; ++ regulator-name = "imx477_vddl"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ }; ++ imx477_clk: camera-clk { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24000000>; ++ }; ++ }; ++ }; ++ ++ fragment@4 { ++ target = <&i2c0mux>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@5 { ++ target-path="/__overrides__"; ++ __overlay__ { ++ cam0-pwdn-ctrl = <&imx477_vana>,"gpio:0"; ++ cam0-pwdn = <&imx477_vana>,"gpio:4"; ++ }; ++ }; ++}; diff --git a/target/linux/bcm27xx/patches-5.4/950-0724-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch b/target/linux/bcm27xx/patches-5.4/950-0724-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch new file mode 100644 index 0000000000..2a271cc641 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0724-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch @@ -0,0 +1,2266 @@ +From 58483dcbbb5feca6da79970665950b6b43928e60 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Fri, 8 May 2020 10:00:12 +0100 +Subject: [PATCH] media: i2c: Add driver for Sony IMX477 sensor + +Adds a driver for the 12MPix Sony IMX477 CSI2 sensor. +Whilst the sensor supports 2 or 4 CSI2 data lanes, this driver +currently only supports 2 lanes. + +The following Bayer modes are currently available: + +4056x3040 12-bit @ 10fps +2028x1520 12-bit (binned) @ 40fps +2028x1050 12-bit (cropped/binned) @ 50fps +1012x760 10-bit (scaled) @ 120 fps + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + MAINTAINERS | 8 + + drivers/media/i2c/Kconfig | 11 + + drivers/media/i2c/Makefile | 1 + + drivers/media/i2c/imx477.c | 2191 ++++++++++++++++++++++++++++++++++++ + 4 files changed, 2211 insertions(+) + create mode 100644 drivers/media/i2c/imx477.c + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -15195,6 +15195,14 @@ T: git git://linuxtv.org/media_tree.git + S: Maintained + F: drivers/media/i2c/imx355.c + ++SONY IMX477 SENSOR DRIVER ++M: Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com> ++L: linux-media@vger.kernel.org ++T: git git://linuxtv.org/media_tree.git ++S: Maintained ++F: drivers/media/i2c/imx477.c ++F: Documentation/devicetree/bindings/media/i2c/imx477.yaml ++ + SONY MEMORYSTICK SUBSYSTEM + M: Maxim Levitsky <maximlevitsky@gmail.com> + M: Alex Dubov <oakad@yahoo.com> +--- a/drivers/media/i2c/Kconfig ++++ b/drivers/media/i2c/Kconfig +@@ -609,6 +609,17 @@ config VIDEO_IMX274 + This is a V4L2 sensor driver for the Sony IMX274 + CMOS image sensor. + ++config VIDEO_IMX477 ++ tristate "Sony IMX477 sensor support" ++ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ++ depends on MEDIA_CAMERA_SUPPORT ++ help ++ This is a Video4Linux2 sensor driver for the Sony ++ IMX477 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called imx477. ++ + config VIDEO_IMX319 + tristate "Sony IMX319 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API +--- a/drivers/media/i2c/Makefile ++++ b/drivers/media/i2c/Makefile +@@ -114,6 +114,7 @@ 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_IMX477) += imx477.o + obj-$(CONFIG_VIDEO_IMX319) += imx319.o + obj-$(CONFIG_VIDEO_IMX355) += imx355.o + obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o +--- /dev/null ++++ b/drivers/media/i2c/imx477.c +@@ -0,0 +1,2191 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * A V4L2 driver for Sony IMX477 cameras. ++ * Copyright (C) 2020, Raspberry Pi (Trading) Ltd ++ * ++ * Based on Sony imx219 camera driver ++ * Copyright (C) 2019-2020 Raspberry Pi (Trading) Ltd ++ */ ++#include <asm/unaligned.h> ++#include <linux/clk.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> ++ ++#define IMX477_REG_VALUE_08BIT 1 ++#define IMX477_REG_VALUE_16BIT 2 ++ ++/* Chip ID */ ++#define IMX477_REG_CHIP_ID 0x0016 ++#define IMX477_CHIP_ID 0x0477 ++ ++#define IMX477_REG_MODE_SELECT 0x0100 ++#define IMX477_MODE_STANDBY 0x00 ++#define IMX477_MODE_STREAMING 0x01 ++ ++#define IMX477_REG_ORIENTATION 0x101 ++ ++#define IMX477_XCLK_FREQ 24000000 ++ ++#define IMX477_DEFAULT_LINK_FREQ 450000000 ++ ++/* Pixel rate is fixed at 840MHz for all the modes */ ++#define IMX477_PIXEL_RATE 840000000 ++ ++/* V_TIMING internal */ ++#define IMX477_REG_FRAME_LENGTH 0x0340 ++#define IMX477_FRAME_LENGTH_MAX 0xffdc ++ ++/* Exposure control */ ++#define IMX477_REG_EXPOSURE 0x0202 ++#define IMX477_EXPOSURE_OFFSET 22 ++#define IMX477_EXPOSURE_MIN 20 ++#define IMX477_EXPOSURE_STEP 1 ++#define IMX477_EXPOSURE_DEFAULT 0x640 ++#define IMX477_EXPOSURE_MAX (IMX477_FRAME_LENGTH_MAX - \ ++ IMX477_EXPOSURE_OFFSET) ++ ++/* Analog gain control */ ++#define IMX477_REG_ANALOG_GAIN 0x0204 ++#define IMX477_ANA_GAIN_MIN 0 ++#define IMX477_ANA_GAIN_MAX 978 ++#define IMX477_ANA_GAIN_STEP 1 ++#define IMX477_ANA_GAIN_DEFAULT 0x0 ++ ++/* Digital gain control */ ++#define IMX477_REG_DIGITAL_GAIN 0x020e ++#define IMX477_DGTL_GAIN_MIN 0x0100 ++#define IMX477_DGTL_GAIN_MAX 0xffff ++#define IMX477_DGTL_GAIN_DEFAULT 0x0100 ++#define IMX477_DGTL_GAIN_STEP 1 ++ ++/* Test Pattern Control */ ++#define IMX477_REG_TEST_PATTERN 0x0600 ++#define IMX477_TEST_PATTERN_DISABLE 0 ++#define IMX477_TEST_PATTERN_SOLID_COLOR 1 ++#define IMX477_TEST_PATTERN_COLOR_BARS 2 ++#define IMX477_TEST_PATTERN_GREY_COLOR 3 ++#define IMX477_TEST_PATTERN_PN9 4 ++ ++/* Test pattern colour components */ ++#define IMX477_REG_TEST_PATTERN_R 0x0602 ++#define IMX477_REG_TEST_PATTERN_GR 0x0604 ++#define IMX477_REG_TEST_PATTERN_B 0x0606 ++#define IMX477_REG_TEST_PATTERN_GB 0x0608 ++#define IMX477_TEST_PATTERN_COLOUR_MIN 0 ++#define IMX477_TEST_PATTERN_COLOUR_MAX 0x0fff ++#define IMX477_TEST_PATTERN_COLOUR_STEP 1 ++#define IMX477_TEST_PATTERN_R_DEFAULT IMX477_TEST_PATTERN_COLOUR_MAX ++#define IMX477_TEST_PATTERN_GR_DEFAULT 0 ++#define IMX477_TEST_PATTERN_B_DEFAULT 0 ++#define IMX477_TEST_PATTERN_GB_DEFAULT 0 ++ ++/* Embedded metadata stream structure */ ++#define IMX477_EMBEDDED_LINE_WIDTH 16384 ++#define IMX477_NUM_EMBEDDED_LINES 1 ++ ++enum pad_types { ++ IMAGE_PAD, ++ METADATA_PAD, ++ NUM_PADS ++}; ++ ++/* IMX477 native and active pixel array size. */ ++#define IMX477_NATIVE_WIDTH 4072U ++#define IMX477_NATIVE_HEIGHT 3176U ++#define IMX477_PIXEL_ARRAY_LEFT 8U ++#define IMX477_PIXEL_ARRAY_TOP 16U ++#define IMX477_PIXEL_ARRAY_WIDTH 4056U ++#define IMX477_PIXEL_ARRAY_HEIGHT 3040U ++ ++struct imx477_reg { ++ u16 address; ++ u8 val; ++}; ++ ++struct imx477_reg_list { ++ unsigned int num_of_regs; ++ const struct imx477_reg *regs; ++}; ++ ++/* Mode : resolution and related config&values */ ++struct imx477_mode { ++ /* Frame width */ ++ unsigned int width; ++ ++ /* Frame height */ ++ unsigned int height; ++ ++ /* H-timing in pixels */ ++ unsigned int line_length_pix; ++ ++ /* Analog crop rectangle. */ ++ struct v4l2_rect crop; ++ ++ /* Highest possible framerate. */ ++ struct v4l2_fract timeperframe_min; ++ ++ /* Default framerate. */ ++ struct v4l2_fract timeperframe_default; ++ ++ /* Default register values */ ++ struct imx477_reg_list reg_list; ++}; ++ ++static const struct imx477_reg mode_common_regs[] = { ++ {0x0136, 0x18}, ++ {0x0137, 0x00}, ++ {0xe000, 0x00}, ++ {0xe07a, 0x01}, ++ {0x0808, 0x02}, ++ {0x4ae9, 0x18}, ++ {0x4aea, 0x08}, ++ {0xf61c, 0x04}, ++ {0xf61e, 0x04}, ++ {0x4ae9, 0x21}, ++ {0x4aea, 0x80}, ++ {0x38a8, 0x1f}, ++ {0x38a9, 0xff}, ++ {0x38aa, 0x1f}, ++ {0x38ab, 0xff}, ++ {0x55d4, 0x00}, ++ {0x55d5, 0x00}, ++ {0x55d6, 0x07}, ++ {0x55d7, 0xff}, ++ {0x55e8, 0x07}, ++ {0x55e9, 0xff}, ++ {0x55ea, 0x00}, ++ {0x55eb, 0x00}, ++ {0x574c, 0x07}, ++ {0x574d, 0xff}, ++ {0x574e, 0x00}, ++ {0x574f, 0x00}, ++ {0x5754, 0x00}, ++ {0x5755, 0x00}, ++ {0x5756, 0x07}, ++ {0x5757, 0xff}, ++ {0x5973, 0x04}, ++ {0x5974, 0x01}, ++ {0x5d13, 0xc3}, ++ {0x5d14, 0x58}, ++ {0x5d15, 0xa3}, ++ {0x5d16, 0x1d}, ++ {0x5d17, 0x65}, ++ {0x5d18, 0x8c}, ++ {0x5d1a, 0x06}, ++ {0x5d1b, 0xa9}, ++ {0x5d1c, 0x45}, ++ {0x5d1d, 0x3a}, ++ {0x5d1e, 0xab}, ++ {0x5d1f, 0x15}, ++ {0x5d21, 0x0e}, ++ {0x5d22, 0x52}, ++ {0x5d23, 0xaa}, ++ {0x5d24, 0x7d}, ++ {0x5d25, 0x57}, ++ {0x5d26, 0xa8}, ++ {0x5d37, 0x5a}, ++ {0x5d38, 0x5a}, ++ {0x5d77, 0x7f}, ++ {0x7b75, 0x0e}, ++ {0x7b76, 0x0b}, ++ {0x7b77, 0x08}, ++ {0x7b78, 0x0a}, ++ {0x7b79, 0x47}, ++ {0x7b7c, 0x00}, ++ {0x7b7d, 0x00}, ++ {0x8d1f, 0x00}, ++ {0x8d27, 0x00}, ++ {0x9004, 0x03}, ++ {0x9200, 0x50}, ++ {0x9201, 0x6c}, ++ {0x9202, 0x71}, ++ {0x9203, 0x00}, ++ {0x9204, 0x71}, ++ {0x9205, 0x01}, ++ {0x9371, 0x6a}, ++ {0x9373, 0x6a}, ++ {0x9375, 0x64}, ++ {0x991a, 0x00}, ++ {0x996b, 0x8c}, ++ {0x996c, 0x64}, ++ {0x996d, 0x50}, ++ {0x9a4c, 0x0d}, ++ {0x9a4d, 0x0d}, ++ {0xa001, 0x0a}, ++ {0xa003, 0x0a}, ++ {0xa005, 0x0a}, ++ {0xa006, 0x01}, ++ {0xa007, 0xc0}, ++ {0xa009, 0xc0}, ++ {0x3d8a, 0x01}, ++ {0x4421, 0x04}, ++ {0x7b3b, 0x01}, ++ {0x7b4c, 0x00}, ++ {0x9905, 0x00}, ++ {0x9907, 0x00}, ++ {0x9909, 0x00}, ++ {0x990b, 0x00}, ++ {0x9944, 0x3c}, ++ {0x9947, 0x3c}, ++ {0x994a, 0x8c}, ++ {0x994b, 0x50}, ++ {0x994c, 0x1b}, ++ {0x994d, 0x8c}, ++ {0x994e, 0x50}, ++ {0x994f, 0x1b}, ++ {0x9950, 0x8c}, ++ {0x9951, 0x1b}, ++ {0x9952, 0x0a}, ++ {0x9953, 0x8c}, ++ {0x9954, 0x1b}, ++ {0x9955, 0x0a}, ++ {0x9a13, 0x04}, ++ {0x9a14, 0x04}, ++ {0x9a19, 0x00}, ++ {0x9a1c, 0x04}, ++ {0x9a1d, 0x04}, ++ {0x9a26, 0x05}, ++ {0x9a27, 0x05}, ++ {0x9a2c, 0x01}, ++ {0x9a2d, 0x03}, ++ {0x9a2f, 0x05}, ++ {0x9a30, 0x05}, ++ {0x9a41, 0x00}, ++ {0x9a46, 0x00}, ++ {0x9a47, 0x00}, ++ {0x9c17, 0x35}, ++ {0x9c1d, 0x31}, ++ {0x9c29, 0x50}, ++ {0x9c3b, 0x2f}, ++ {0x9c41, 0x6b}, ++ {0x9c47, 0x2d}, ++ {0x9c4d, 0x40}, ++ {0x9c6b, 0x00}, ++ {0x9c71, 0xc8}, ++ {0x9c73, 0x32}, ++ {0x9c75, 0x04}, ++ {0x9c7d, 0x2d}, ++ {0x9c83, 0x40}, ++ {0x9c94, 0x3f}, ++ {0x9c95, 0x3f}, ++ {0x9c96, 0x3f}, ++ {0x9c97, 0x00}, ++ {0x9c98, 0x00}, ++ {0x9c99, 0x00}, ++ {0x9c9a, 0x3f}, ++ {0x9c9b, 0x3f}, ++ {0x9c9c, 0x3f}, ++ {0x9ca0, 0x0f}, ++ {0x9ca1, 0x0f}, ++ {0x9ca2, 0x0f}, ++ {0x9ca3, 0x00}, ++ {0x9ca4, 0x00}, ++ {0x9ca5, 0x00}, ++ {0x9ca6, 0x1e}, ++ {0x9ca7, 0x1e}, ++ {0x9ca8, 0x1e}, ++ {0x9ca9, 0x00}, ++ {0x9caa, 0x00}, ++ {0x9cab, 0x00}, ++ {0x9cac, 0x09}, ++ {0x9cad, 0x09}, ++ {0x9cae, 0x09}, ++ {0x9cbd, 0x50}, ++ {0x9cbf, 0x50}, ++ {0x9cc1, 0x50}, ++ {0x9cc3, 0x40}, ++ {0x9cc5, 0x40}, ++ {0x9cc7, 0x40}, ++ {0x9cc9, 0x0a}, ++ {0x9ccb, 0x0a}, ++ {0x9ccd, 0x0a}, ++ {0x9d17, 0x35}, ++ {0x9d1d, 0x31}, ++ {0x9d29, 0x50}, ++ {0x9d3b, 0x2f}, ++ {0x9d41, 0x6b}, ++ {0x9d47, 0x42}, ++ {0x9d4d, 0x5a}, ++ {0x9d6b, 0x00}, ++ {0x9d71, 0xc8}, ++ {0x9d73, 0x32}, ++ {0x9d75, 0x04}, ++ {0x9d7d, 0x42}, ++ {0x9d83, 0x5a}, ++ {0x9d94, 0x3f}, ++ {0x9d95, 0x3f}, ++ {0x9d96, 0x3f}, ++ {0x9d97, 0x00}, ++ {0x9d98, 0x00}, ++ {0x9d99, 0x00}, ++ {0x9d9a, 0x3f}, ++ {0x9d9b, 0x3f}, ++ {0x9d9c, 0x3f}, ++ {0x9d9d, 0x1f}, ++ {0x9d9e, 0x1f}, ++ {0x9d9f, 0x1f}, ++ {0x9da0, 0x0f}, ++ {0x9da1, 0x0f}, ++ {0x9da2, 0x0f}, ++ {0x9da3, 0x00}, ++ {0x9da4, 0x00}, ++ {0x9da5, 0x00}, ++ {0x9da6, 0x1e}, ++ {0x9da7, 0x1e}, ++ {0x9da8, 0x1e}, ++ {0x9da9, 0x00}, ++ {0x9daa, 0x00}, ++ {0x9dab, 0x00}, ++ {0x9dac, 0x09}, ++ {0x9dad, 0x09}, ++ {0x9dae, 0x09}, ++ {0x9dc9, 0x0a}, ++ {0x9dcb, 0x0a}, ++ {0x9dcd, 0x0a}, ++ {0x9e17, 0x35}, ++ {0x9e1d, 0x31}, ++ {0x9e29, 0x50}, ++ {0x9e3b, 0x2f}, ++ {0x9e41, 0x6b}, ++ {0x9e47, 0x2d}, ++ {0x9e4d, 0x40}, ++ {0x9e6b, 0x00}, ++ {0x9e71, 0xc8}, ++ {0x9e73, 0x32}, ++ {0x9e75, 0x04}, ++ {0x9e94, 0x0f}, ++ {0x9e95, 0x0f}, ++ {0x9e96, 0x0f}, ++ {0x9e97, 0x00}, ++ {0x9e98, 0x00}, ++ {0x9e99, 0x00}, ++ {0x9ea0, 0x0f}, ++ {0x9ea1, 0x0f}, ++ {0x9ea2, 0x0f}, ++ {0x9ea3, 0x00}, ++ {0x9ea4, 0x00}, ++ {0x9ea5, 0x00}, ++ {0x9ea6, 0x3f}, ++ {0x9ea7, 0x3f}, ++ {0x9ea8, 0x3f}, ++ {0x9ea9, 0x00}, ++ {0x9eaa, 0x00}, ++ {0x9eab, 0x00}, ++ {0x9eac, 0x09}, ++ {0x9ead, 0x09}, ++ {0x9eae, 0x09}, ++ {0x9ec9, 0x0a}, ++ {0x9ecb, 0x0a}, ++ {0x9ecd, 0x0a}, ++ {0x9f17, 0x35}, ++ {0x9f1d, 0x31}, ++ {0x9f29, 0x50}, ++ {0x9f3b, 0x2f}, ++ {0x9f41, 0x6b}, ++ {0x9f47, 0x42}, ++ {0x9f4d, 0x5a}, ++ {0x9f6b, 0x00}, ++ {0x9f71, 0xc8}, ++ {0x9f73, 0x32}, ++ {0x9f75, 0x04}, ++ {0x9f94, 0x0f}, ++ {0x9f95, 0x0f}, ++ {0x9f96, 0x0f}, ++ {0x9f97, 0x00}, ++ {0x9f98, 0x00}, ++ {0x9f99, 0x00}, ++ {0x9f9a, 0x2f}, ++ {0x9f9b, 0x2f}, ++ {0x9f9c, 0x2f}, ++ {0x9f9d, 0x00}, ++ {0x9f9e, 0x00}, ++ {0x9f9f, 0x00}, ++ {0x9fa0, 0x0f}, ++ {0x9fa1, 0x0f}, ++ {0x9fa2, 0x0f}, ++ {0x9fa3, 0x00}, ++ {0x9fa4, 0x00}, ++ {0x9fa5, 0x00}, ++ {0x9fa6, 0x1e}, ++ {0x9fa7, 0x1e}, ++ {0x9fa8, 0x1e}, ++ {0x9fa9, 0x00}, ++ {0x9faa, 0x00}, ++ {0x9fab, 0x00}, ++ {0x9fac, 0x09}, ++ {0x9fad, 0x09}, ++ {0x9fae, 0x09}, ++ {0x9fc9, 0x0a}, ++ {0x9fcb, 0x0a}, ++ {0x9fcd, 0x0a}, ++ {0xa14b, 0xff}, ++ {0xa151, 0x0c}, ++ {0xa153, 0x50}, ++ {0xa155, 0x02}, ++ {0xa157, 0x00}, ++ {0xa1ad, 0xff}, ++ {0xa1b3, 0x0c}, ++ {0xa1b5, 0x50}, ++ {0xa1b9, 0x00}, ++ {0xa24b, 0xff}, ++ {0xa257, 0x00}, ++ {0xa2ad, 0xff}, ++ {0xa2b9, 0x00}, ++ {0xb21f, 0x04}, ++ {0xb35c, 0x00}, ++ {0xb35e, 0x08}, ++ {0x0112, 0x0c}, ++ {0x0113, 0x0c}, ++ {0x0114, 0x01}, ++ {0x0350, 0x00}, ++ {0xbcf1, 0x02}, ++ {0x3ff9, 0x01}, ++}; ++ ++/* 12 mpix 10fps */ ++static const struct imx477_reg mode_4056x3040_regs[] = { ++ {0x0342, 0x5d}, ++ {0x0343, 0xc0}, ++ {0x0344, 0x00}, ++ {0x0345, 0x00}, ++ {0x0346, 0x00}, ++ {0x0347, 0x00}, ++ {0x0348, 0x0f}, ++ {0x0349, 0xd7}, ++ {0x034a, 0x0b}, ++ {0x034b, 0xdf}, ++ {0x00e3, 0x00}, ++ {0x00e4, 0x00}, ++ {0x00fc, 0x0a}, ++ {0x00fd, 0x0a}, ++ {0x00fe, 0x0a}, ++ {0x00ff, 0x0a}, ++ {0x0220, 0x00}, ++ {0x0221, 0x11}, ++ {0x0381, 0x01}, ++ {0x0383, 0x01}, ++ {0x0385, 0x01}, ++ {0x0387, 0x01}, ++ {0x0900, 0x00}, ++ {0x0901, 0x11}, ++ {0x0902, 0x02}, ++ {0x3140, 0x02}, ++ {0x3c00, 0x00}, ++ {0x3c01, 0x03}, ++ {0x3c02, 0xa2}, ++ {0x3f0d, 0x01}, ++ {0x5748, 0x07}, ++ {0x5749, 0xff}, ++ {0x574a, 0x00}, ++ {0x574b, 0x00}, ++ {0x7b75, 0x0a}, ++ {0x7b76, 0x0c}, ++ {0x7b77, 0x07}, ++ {0x7b78, 0x06}, ++ {0x7b79, 0x3c}, ++ {0x7b53, 0x01}, ++ {0x9369, 0x5a}, ++ {0x936b, 0x55}, ++ {0x936d, 0x28}, ++ {0x9304, 0x00}, ++ {0x9305, 0x00}, ++ {0x9e9a, 0x2f}, ++ {0x9e9b, 0x2f}, ++ {0x9e9c, 0x2f}, ++ {0x9e9d, 0x00}, ++ {0x9e9e, 0x00}, ++ {0x9e9f, 0x00}, ++ {0xa2a9, 0x60}, ++ {0xa2b7, 0x00}, ++ {0x0401, 0x00}, ++ {0x0404, 0x00}, ++ {0x0405, 0x10}, ++ {0x0408, 0x00}, ++ {0x0409, 0x00}, ++ {0x040a, 0x00}, ++ {0x040b, 0x00}, ++ {0x040c, 0x0f}, ++ {0x040d, 0xd8}, ++ {0x040e, 0x0b}, ++ {0x040f, 0xe0}, ++ {0x034c, 0x0f}, ++ {0x034d, 0xd8}, ++ {0x034e, 0x0b}, ++ {0x034f, 0xe0}, ++ {0x0301, 0x05}, ++ {0x0303, 0x02}, ++ {0x0305, 0x04}, ++ {0x0306, 0x01}, ++ {0x0307, 0x5e}, ++ {0x0309, 0x0c}, ++ {0x030b, 0x02}, ++ {0x030d, 0x02}, ++ {0x030e, 0x00}, ++ {0x030f, 0x96}, ++ {0x0310, 0x01}, ++ {0x0820, 0x07}, ++ {0x0821, 0x08}, ++ {0x0822, 0x00}, ++ {0x0823, 0x00}, ++ {0x080a, 0x00}, ++ {0x080b, 0x7f}, ++ {0x080c, 0x00}, ++ {0x080d, 0x4f}, ++ {0x080e, 0x00}, ++ {0x080f, 0x77}, ++ {0x0810, 0x00}, ++ {0x0811, 0x5f}, ++ {0x0812, 0x00}, ++ {0x0813, 0x57}, ++ {0x0814, 0x00}, ++ {0x0815, 0x4f}, ++ {0x0816, 0x01}, ++ {0x0817, 0x27}, ++ {0x0818, 0x00}, ++ {0x0819, 0x3f}, ++ {0xe04c, 0x00}, ++ {0xe04d, 0x7f}, ++ {0xe04e, 0x00}, ++ {0xe04f, 0x1f}, ++ {0x3e20, 0x01}, ++ {0x3e37, 0x00}, ++ {0x3f50, 0x00}, ++ {0x3f56, 0x02}, ++ {0x3f57, 0xae}, ++}; ++ ++/* 2x2 binned. 40fps */ ++static const struct imx477_reg mode_2028x1520_regs[] = { ++ {0x0342, 0x31}, ++ {0x0343, 0xc4}, ++ {0x0344, 0x00}, ++ {0x0345, 0x00}, ++ {0x0346, 0x00}, ++ {0x0347, 0x00}, ++ {0x0348, 0x0f}, ++ {0x0349, 0xd7}, ++ {0x034a, 0x0b}, ++ {0x034b, 0xdf}, ++ {0x0220, 0x00}, ++ {0x0221, 0x11}, ++ {0x0381, 0x01}, ++ {0x0383, 0x01}, ++ {0x0385, 0x01}, ++ {0x0387, 0x01}, ++ {0x0900, 0x01}, ++ {0x0901, 0x12}, ++ {0x0902, 0x02}, ++ {0x3140, 0x02}, ++ {0x3c00, 0x00}, ++ {0x3c01, 0x03}, ++ {0x3c02, 0xa2}, ++ {0x3f0d, 0x01}, ++ {0x5748, 0x07}, ++ {0x5749, 0xff}, ++ {0x574a, 0x00}, ++ {0x574b, 0x00}, ++ {0x7b53, 0x01}, ++ {0x9369, 0x73}, ++ {0x936b, 0x64}, ++ {0x936d, 0x5f}, ++ {0x9304, 0x00}, ++ {0x9305, 0x00}, ++ {0x9e9a, 0x2f}, ++ {0x9e9b, 0x2f}, ++ {0x9e9c, 0x2f}, ++ {0x9e9d, 0x00}, ++ {0x9e9e, 0x00}, ++ {0x9e9f, 0x00}, ++ {0xa2a9, 0x60}, ++ {0xa2b7, 0x00}, ++ {0x0401, 0x01}, ++ {0x0404, 0x00}, ++ {0x0405, 0x20}, ++ {0x0408, 0x00}, ++ {0x0409, 0x00}, ++ {0x040a, 0x00}, ++ {0x040b, 0x00}, ++ {0x040c, 0x0f}, ++ {0x040d, 0xd8}, ++ {0x040e, 0x0b}, ++ {0x040f, 0xe0}, ++ {0x034c, 0x07}, ++ {0x034d, 0xec}, ++ {0x034e, 0x05}, ++ {0x034f, 0xf0}, ++ {0x0301, 0x05}, ++ {0x0303, 0x02}, ++ {0x0305, 0x04}, ++ {0x0306, 0x01}, ++ {0x0307, 0x5e}, ++ {0x0309, 0x0c}, ++ {0x030b, 0x02}, ++ {0x030d, 0x02}, ++ {0x030e, 0x00}, ++ {0x030f, 0x96}, ++ {0x0310, 0x01}, ++ {0x0820, 0x07}, ++ {0x0821, 0x08}, ++ {0x0822, 0x00}, ++ {0x0823, 0x00}, ++ {0x080a, 0x00}, ++ {0x080b, 0x7f}, ++ {0x080c, 0x00}, ++ {0x080d, 0x4f}, ++ {0x080e, 0x00}, ++ {0x080f, 0x77}, ++ {0x0810, 0x00}, ++ {0x0811, 0x5f}, ++ {0x0812, 0x00}, ++ {0x0813, 0x57}, ++ {0x0814, 0x00}, ++ {0x0815, 0x4f}, ++ {0x0816, 0x01}, ++ {0x0817, 0x27}, ++ {0x0818, 0x00}, ++ {0x0819, 0x3f}, ++ {0xe04c, 0x00}, ++ {0xe04d, 0x7f}, ++ {0xe04e, 0x00}, ++ {0xe04f, 0x1f}, ++ {0x3e20, 0x01}, ++ {0x3e37, 0x00}, ++ {0x3f50, 0x00}, ++ {0x3f56, 0x01}, ++ {0x3f57, 0x6c}, ++}; ++ ++/* 1080p cropped mode */ ++static const struct imx477_reg mode_2028x1080_regs[] = { ++ {0x0342, 0x31}, ++ {0x0343, 0xc4}, ++ {0x0344, 0x00}, ++ {0x0345, 0x00}, ++ {0x0346, 0x01}, ++ {0x0347, 0xb8}, ++ {0x0348, 0x0f}, ++ {0x0349, 0xd7}, ++ {0x034a, 0x0a}, ++ {0x034b, 0x27}, ++ {0x0220, 0x00}, ++ {0x0221, 0x11}, ++ {0x0381, 0x01}, ++ {0x0383, 0x01}, ++ {0x0385, 0x01}, ++ {0x0387, 0x01}, ++ {0x0900, 0x01}, ++ {0x0901, 0x12}, ++ {0x0902, 0x02}, ++ {0x3140, 0x02}, ++ {0x3c00, 0x00}, ++ {0x3c01, 0x03}, ++ {0x3c02, 0xa2}, ++ {0x3f0d, 0x01}, ++ {0x5748, 0x07}, ++ {0x5749, 0xff}, ++ {0x574a, 0x00}, ++ {0x574b, 0x00}, ++ {0x7b53, 0x01}, ++ {0x9369, 0x73}, ++ {0x936b, 0x64}, ++ {0x936d, 0x5f}, ++ {0x9304, 0x00}, ++ {0x9305, 0x00}, ++ {0x9e9a, 0x2f}, ++ {0x9e9b, 0x2f}, ++ {0x9e9c, 0x2f}, ++ {0x9e9d, 0x00}, ++ {0x9e9e, 0x00}, ++ {0x9e9f, 0x00}, ++ {0xa2a9, 0x60}, ++ {0xa2b7, 0x00}, ++ {0x0401, 0x01}, ++ {0x0404, 0x00}, ++ {0x0405, 0x20}, ++ {0x0408, 0x00}, ++ {0x0409, 0x00}, ++ {0x040a, 0x00}, ++ {0x040b, 0x00}, ++ {0x040c, 0x0f}, ++ {0x040d, 0xd8}, ++ {0x040e, 0x04}, ++ {0x040f, 0x38}, ++ {0x034c, 0x07}, ++ {0x034d, 0xec}, ++ {0x034e, 0x04}, ++ {0x034f, 0x38}, ++ {0x0301, 0x05}, ++ {0x0303, 0x02}, ++ {0x0305, 0x04}, ++ {0x0306, 0x01}, ++ {0x0307, 0x5e}, ++ {0x0309, 0x0c}, ++ {0x030b, 0x02}, ++ {0x030d, 0x02}, ++ {0x030e, 0x00}, ++ {0x030f, 0x96}, ++ {0x0310, 0x01}, ++ {0x0820, 0x07}, ++ {0x0821, 0x08}, ++ {0x0822, 0x00}, ++ {0x0823, 0x00}, ++ {0x080a, 0x00}, ++ {0x080b, 0x7f}, ++ {0x080c, 0x00}, ++ {0x080d, 0x4f}, ++ {0x080e, 0x00}, ++ {0x080f, 0x77}, ++ {0x0810, 0x00}, ++ {0x0811, 0x5f}, ++ {0x0812, 0x00}, ++ {0x0813, 0x57}, ++ {0x0814, 0x00}, ++ {0x0815, 0x4f}, ++ {0x0816, 0x01}, ++ {0x0817, 0x27}, ++ {0x0818, 0x00}, ++ {0x0819, 0x3f}, ++ {0xe04c, 0x00}, ++ {0xe04d, 0x7f}, ++ {0xe04e, 0x00}, ++ {0xe04f, 0x1f}, ++ {0x3e20, 0x01}, ++ {0x3e37, 0x00}, ++ {0x3f50, 0x00}, ++ {0x3f56, 0x01}, ++ {0x3f57, 0x6c}, ++}; ++ ++/* 4x4 binned. 120fps */ ++static const struct imx477_reg mode_1012x760_regs[] = { ++ {0x420b, 0x01}, ++ {0x990c, 0x00}, ++ {0x990d, 0x08}, ++ {0x9956, 0x8c}, ++ {0x9957, 0x64}, ++ {0x9958, 0x50}, ++ {0x9a48, 0x06}, ++ {0x9a49, 0x06}, ++ {0x9a4a, 0x06}, ++ {0x9a4b, 0x06}, ++ {0x9a4c, 0x06}, ++ {0x9a4d, 0x06}, ++ {0x0112, 0x0a}, ++ {0x0113, 0x0a}, ++ {0x0114, 0x01}, ++ {0x0342, 0x14}, ++ {0x0343, 0x60}, ++ {0x0344, 0x00}, ++ {0x0345, 0x00}, ++ {0x0346, 0x00}, ++ {0x0347, 0x00}, ++ {0x0348, 0x0f}, ++ {0x0349, 0xd3}, ++ {0x034a, 0x0b}, ++ {0x034b, 0xdf}, ++ {0x00e3, 0x00}, ++ {0x00e4, 0x00}, ++ {0x00fc, 0x0a}, ++ {0x00fd, 0x0a}, ++ {0x00fe, 0x0a}, ++ {0x00ff, 0x0a}, ++ {0x0220, 0x00}, ++ {0x0221, 0x11}, ++ {0x0381, 0x01}, ++ {0x0383, 0x01}, ++ {0x0385, 0x01}, ++ {0x0387, 0x03}, ++ {0x0900, 0x01}, ++ {0x0901, 0x22}, ++ {0x0902, 0x02}, ++ {0x3140, 0x02}, ++ {0x3c00, 0x00}, ++ {0x3c01, 0x01}, ++ {0x3c02, 0x9c}, ++ {0x3f0d, 0x00}, ++ {0x5748, 0x00}, ++ {0x5749, 0x00}, ++ {0x574a, 0x00}, ++ {0x574b, 0xa4}, ++ {0x7b75, 0x0e}, ++ {0x7b76, 0x09}, ++ {0x7b77, 0x08}, ++ {0x7b78, 0x06}, ++ {0x7b79, 0x34}, ++ {0x7b53, 0x00}, ++ {0x9369, 0x73}, ++ {0x936b, 0x64}, ++ {0x936d, 0x5f}, ++ {0x9304, 0x03}, ++ {0x9305, 0x80}, ++ {0x9e9a, 0x3f}, ++ {0x9e9b, 0x3f}, ++ {0x9e9c, 0x3f}, ++ {0x9e9d, 0x27}, ++ {0x9e9e, 0x27}, ++ {0x9e9f, 0x27}, ++ {0xa2a9, 0x27}, ++ {0xa2b7, 0x03}, ++ {0x0401, 0x01}, ++ {0x0404, 0x00}, ++ {0x0405, 0x20}, ++ {0x0408, 0x00}, ++ {0x0409, 0x00}, ++ {0x040a, 0x00}, ++ {0x040b, 0x00}, ++ {0x040c, 0x07}, ++ {0x040d, 0xea}, ++ {0x040e, 0x02}, ++ {0x040f, 0xf8}, ++ {0x034c, 0x03}, ++ {0x034d, 0xf4}, ++ {0x034e, 0x02}, ++ {0x034f, 0xf8}, ++ {0x0301, 0x05}, ++ {0x0303, 0x02}, ++ {0x0305, 0x02}, ++ {0x0306, 0x00}, ++ {0x0307, 0xaf}, ++ {0x0309, 0x0a}, ++ {0x030b, 0x02}, ++ {0x030d, 0x02}, ++ {0x030e, 0x00}, ++ {0x030f, 0x96}, ++ {0x0310, 0x01}, ++ {0x0820, 0x07}, ++ {0x0821, 0x08}, ++ {0x0822, 0x00}, ++ {0x0823, 0x00}, ++ {0x080a, 0x00}, ++ {0x080b, 0x6f}, ++ {0x080c, 0x00}, ++ {0x080d, 0x3f}, ++ {0x080e, 0x00}, ++ {0x080f, 0xff}, ++ {0x0810, 0x00}, ++ {0x0811, 0x4f}, ++ {0x0812, 0x00}, ++ {0x0813, 0x47}, ++ {0x0814, 0x00}, ++ {0x0815, 0x37}, ++ {0x0816, 0x00}, ++ {0x0817, 0xe7}, ++ {0x0818, 0x00}, ++ {0x0819, 0x2f}, ++ {0xe04c, 0x00}, ++ {0xe04d, 0x5f}, ++ {0xe04e, 0x00}, ++ {0xe04f, 0x1f}, ++ {0x3e20, 0x01}, ++ {0x3e37, 0x00}, ++ {0x3f50, 0x00}, ++ {0x3f56, 0x00}, ++ {0x3f57, 0x96}, ++}; ++ ++/* Mode configs */ ++static const struct imx477_mode supported_modes_12bit[] = { ++ { ++ /* 12MPix 10fps mode */ ++ .width = 4056, ++ .height = 3040, ++ .line_length_pix = 0x5dc0, ++ .crop = { ++ .left = 0, ++ .top = 0, ++ .width = 4056, ++ .height = 3040, ++ }, ++ .timeperframe_min = { ++ .numerator = 100, ++ .denominator = 1000 ++ }, ++ .timeperframe_default = { ++ .numerator = 100, ++ .denominator = 1000 ++ }, ++ .reg_list = { ++ .num_of_regs = ARRAY_SIZE(mode_4056x3040_regs), ++ .regs = mode_4056x3040_regs, ++ }, ++ }, ++ { ++ /* 2x2 binned 40fps mode */ ++ .width = 2028, ++ .height = 1520, ++ .line_length_pix = 0x31c4, ++ .crop = { ++ .left = 0, ++ .top = 0, ++ .width = 4056, ++ .height = 3040, ++ }, ++ .timeperframe_min = { ++ .numerator = 100, ++ .denominator = 4000 ++ }, ++ .timeperframe_default = { ++ .numerator = 100, ++ .denominator = 3000 ++ }, ++ .reg_list = { ++ .num_of_regs = ARRAY_SIZE(mode_2028x1520_regs), ++ .regs = mode_2028x1520_regs, ++ }, ++ }, ++ { ++ /* 1080p 50fps cropped mode */ ++ .width = 2028, ++ .height = 1080, ++ .line_length_pix = 0x31c4, ++ .crop = { ++ .left = 0, ++ .top = 440, ++ .width = 4056, ++ .height = 2600, ++ }, ++ .timeperframe_min = { ++ .numerator = 100, ++ .denominator = 5000 ++ }, ++ .timeperframe_default = { ++ .numerator = 100, ++ .denominator = 3000 ++ }, ++ .reg_list = { ++ .num_of_regs = ARRAY_SIZE(mode_2028x1080_regs), ++ .regs = mode_2028x1080_regs, ++ }, ++ } ++}; ++ ++static const struct imx477_mode supported_modes_10bit[] = { ++ { ++ /* 720P 120fps. 4x4 binned */ ++ .width = 1012, ++ .height = 760, ++ .line_length_pix = 0x1460, ++ .crop = { ++ /* ++ * FIXME: the analog crop rectangle is actually ++ * programmed with a horizontal displacement of 0 ++ * pixels, not 4. It gets shrunk after going through ++ * the scaler. Move this information to the compose ++ * rectangle once the driver is expanded to represent ++ * its processing blocks with multiple subdevs. ++ */ ++ .left = 4, ++ .top = 0, ++ .width = 4052, ++ .height = 3040, ++ }, ++ .timeperframe_min = { ++ .numerator = 100, ++ .denominator = 12000 ++ }, ++ .timeperframe_default = { ++ .numerator = 100, ++ .denominator = 60000 ++ }, ++ .reg_list = { ++ .num_of_regs = ARRAY_SIZE(mode_1012x760_regs), ++ .regs = mode_1012x760_regs, ++ } ++ } ++}; ++ ++/* ++ * The supported formats. ++ * This table MUST contain 4 entries per format, to cover the various flip ++ * combinations in the order ++ * - no flip ++ * - h flip ++ * - v flip ++ * - h&v flips ++ */ ++static const u32 codes[] = { ++ /* 12-bit modes. */ ++ MEDIA_BUS_FMT_SRGGB12_1X12, ++ MEDIA_BUS_FMT_SGRBG12_1X12, ++ MEDIA_BUS_FMT_SGBRG12_1X12, ++ MEDIA_BUS_FMT_SBGGR12_1X12, ++ /* 10-bit modes. */ ++ MEDIA_BUS_FMT_SRGGB10_1X10, ++ MEDIA_BUS_FMT_SGRBG10_1X10, ++ MEDIA_BUS_FMT_SGBRG10_1X10, ++ MEDIA_BUS_FMT_SBGGR10_1X10, ++}; ++ ++static const char * const imx477_test_pattern_menu[] = { ++ "Disabled", ++ "Color Bars", ++ "Solid Color", ++ "Grey Color Bars", ++ "PN9" ++}; ++ ++static const int imx477_test_pattern_val[] = { ++ IMX477_TEST_PATTERN_DISABLE, ++ IMX477_TEST_PATTERN_COLOR_BARS, ++ IMX477_TEST_PATTERN_SOLID_COLOR, ++ IMX477_TEST_PATTERN_GREY_COLOR, ++ IMX477_TEST_PATTERN_PN9, ++}; ++ ++/* regulator supplies */ ++static const char * const imx477_supply_name[] = { ++ /* Supplies can be enabled in any order */ ++ "VANA", /* Analog (2.8V) supply */ ++ "VDIG", /* Digital Core (1.05V) supply */ ++ "VDDL", /* IF (1.8V) supply */ ++}; ++ ++#define IMX477_NUM_SUPPLIES ARRAY_SIZE(imx477_supply_name) ++ ++/* ++ * Initialisation delay between XCLR low->high and the moment when the sensor ++ * can start capture (i.e. can leave software standby), given by T7 in the ++ * datasheet is 8ms. This does include I2C setup time as well. ++ * ++ * Note, that delay between XCLR low->high and reading the CCI ID register (T6 ++ * in the datasheet) is much smaller - 600us. ++ */ ++#define IMX477_XCLR_MIN_DELAY_US 8000 ++#define IMX477_XCLR_DELAY_RANGE_US 1000 ++ ++struct imx477 { ++ struct v4l2_subdev sd; ++ struct media_pad pad[NUM_PADS]; ++ ++ struct v4l2_mbus_framefmt fmt; ++ ++ struct clk *xclk; ++ u32 xclk_freq; ++ ++ struct gpio_desc *reset_gpio; ++ struct regulator_bulk_data supplies[IMX477_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 imx477_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; ++ ++ /* Rewrite common registers on stream on? */ ++ bool common_regs_written; ++}; ++ ++static inline struct imx477 *to_imx477(struct v4l2_subdev *_sd) ++{ ++ return container_of(_sd, struct imx477, sd); ++} ++ ++static inline void get_mode_table(unsigned int code, ++ const struct imx477_mode **mode_list, ++ unsigned int *num_modes) ++{ ++ switch (code) { ++ /* 12-bit */ ++ case MEDIA_BUS_FMT_SRGGB12_1X12: ++ case MEDIA_BUS_FMT_SGRBG12_1X12: ++ case MEDIA_BUS_FMT_SGBRG12_1X12: ++ case MEDIA_BUS_FMT_SBGGR12_1X12: ++ *mode_list = supported_modes_12bit; ++ *num_modes = ARRAY_SIZE(supported_modes_12bit); ++ break; ++ /* 10-bit */ ++ case MEDIA_BUS_FMT_SRGGB10_1X10: ++ case MEDIA_BUS_FMT_SGRBG10_1X10: ++ case MEDIA_BUS_FMT_SGBRG10_1X10: ++ case MEDIA_BUS_FMT_SBGGR10_1X10: ++ *mode_list = supported_modes_10bit; ++ *num_modes = ARRAY_SIZE(supported_modes_10bit); ++ break; ++ default: ++ *mode_list = NULL; ++ *num_modes = 0; ++ } ++} ++ ++/* Read registers up to 2 at a time */ ++static int imx477_read_reg(struct imx477 *imx477, u16 reg, u32 len, u32 *val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&imx477->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 imx477_write_reg(struct imx477 *imx477, u16 reg, u32 len, u32 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&imx477->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 imx477_write_regs(struct imx477 *imx477, ++ const struct imx477_reg *regs, u32 len) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); ++ unsigned int i; ++ int ret; ++ ++ for (i = 0; i < len; i++) { ++ ret = imx477_write_reg(imx477, 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 imx477_get_format_code(struct imx477 *imx477, u32 code) ++{ ++ unsigned int i; ++ ++ lockdep_assert_held(&imx477->mutex); ++ ++ for (i = 0; i < ARRAY_SIZE(codes); i++) ++ if (codes[i] == code) ++ break; ++ ++ if (i >= ARRAY_SIZE(codes)) ++ i = 0; ++ ++ i = (i & ~3) | (imx477->vflip->val ? 2 : 0) | ++ (imx477->hflip->val ? 1 : 0); ++ ++ return codes[i]; ++} ++ ++static void imx477_set_default_format(struct imx477 *imx477) ++{ ++ struct v4l2_mbus_framefmt *fmt = &imx477->fmt; ++ ++ /* Set default mode to max resolution */ ++ imx477->mode = &supported_modes_12bit[0]; ++ ++ fmt->code = MEDIA_BUS_FMT_SRGGB12_1X12; ++ 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); ++ fmt->width = imx477->mode->width; ++ fmt->height = imx477->mode->height; ++ fmt->field = V4L2_FIELD_NONE; ++} ++ ++static int imx477_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ++{ ++ struct imx477 *imx477 = to_imx477(sd); ++ struct v4l2_mbus_framefmt *try_fmt_img = ++ v4l2_subdev_get_try_format(sd, fh->pad, IMAGE_PAD); ++ struct v4l2_mbus_framefmt *try_fmt_meta = ++ v4l2_subdev_get_try_format(sd, fh->pad, METADATA_PAD); ++ struct v4l2_rect *try_crop; ++ ++ mutex_lock(&imx477->mutex); ++ ++ /* Initialize try_fmt for the image pad */ ++ try_fmt_img->width = supported_modes_12bit[0].width; ++ try_fmt_img->height = supported_modes_12bit[0].height; ++ try_fmt_img->code = imx477_get_format_code(imx477, ++ MEDIA_BUS_FMT_SRGGB12_1X12); ++ try_fmt_img->field = V4L2_FIELD_NONE; ++ ++ /* Initialize try_fmt for the embedded metadata pad */ ++ try_fmt_meta->width = IMX477_EMBEDDED_LINE_WIDTH; ++ try_fmt_meta->height = IMX477_NUM_EMBEDDED_LINES; ++ try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA; ++ try_fmt_meta->field = V4L2_FIELD_NONE; ++ ++ /* Initialize try_crop */ ++ try_crop = v4l2_subdev_get_try_crop(sd, fh->pad, IMAGE_PAD); ++ try_crop->left = IMX477_PIXEL_ARRAY_LEFT; ++ try_crop->top = IMX477_PIXEL_ARRAY_TOP; ++ try_crop->width = IMX477_PIXEL_ARRAY_WIDTH; ++ try_crop->height = IMX477_PIXEL_ARRAY_HEIGHT; ++ ++ mutex_unlock(&imx477->mutex); ++ ++ return 0; ++} ++ ++static int imx477_set_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct imx477 *imx477 = ++ container_of(ctrl->handler, struct imx477, ctrl_handler); ++ struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); ++ int ret = 0; ++ ++ if (ctrl->id == V4L2_CID_VBLANK) { ++ int exposure_max, exposure_def; ++ ++ /* Update max exposure while meeting expected vblanking */ ++ exposure_max = imx477->mode->height + ctrl->val - ++ IMX477_EXPOSURE_OFFSET; ++ exposure_def = min(exposure_max, imx477->exposure->val); ++ __v4l2_ctrl_modify_range(imx477->exposure, ++ imx477->exposure->minimum, ++ exposure_max, imx477->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 = imx477_write_reg(imx477, IMX477_REG_ANALOG_GAIN, ++ IMX477_REG_VALUE_16BIT, ctrl->val); ++ break; ++ case V4L2_CID_EXPOSURE: ++ ret = imx477_write_reg(imx477, IMX477_REG_EXPOSURE, ++ IMX477_REG_VALUE_16BIT, ctrl->val); ++ break; ++ case V4L2_CID_DIGITAL_GAIN: ++ ret = imx477_write_reg(imx477, IMX477_REG_DIGITAL_GAIN, ++ IMX477_REG_VALUE_16BIT, ctrl->val); ++ break; ++ case V4L2_CID_TEST_PATTERN: ++ ret = imx477_write_reg(imx477, IMX477_REG_TEST_PATTERN, ++ IMX477_REG_VALUE_16BIT, ++ imx477_test_pattern_val[ctrl->val]); ++ break; ++ case V4L2_CID_TEST_PATTERN_RED: ++ ret = imx477_write_reg(imx477, IMX477_REG_TEST_PATTERN_R, ++ IMX477_REG_VALUE_16BIT, ctrl->val); ++ break; ++ case V4L2_CID_TEST_PATTERN_GREENR: ++ ret = imx477_write_reg(imx477, IMX477_REG_TEST_PATTERN_GR, ++ IMX477_REG_VALUE_16BIT, ctrl->val); ++ break; ++ case V4L2_CID_TEST_PATTERN_BLUE: ++ ret = imx477_write_reg(imx477, IMX477_REG_TEST_PATTERN_B, ++ IMX477_REG_VALUE_16BIT, ctrl->val); ++ break; ++ case V4L2_CID_TEST_PATTERN_GREENB: ++ ret = imx477_write_reg(imx477, IMX477_REG_TEST_PATTERN_GB, ++ IMX477_REG_VALUE_16BIT, ctrl->val); ++ break; ++ case V4L2_CID_HFLIP: ++ case V4L2_CID_VFLIP: ++ ret = imx477_write_reg(imx477, IMX477_REG_ORIENTATION, 1, ++ imx477->hflip->val | ++ imx477->vflip->val << 1); ++ break; ++ case V4L2_CID_VBLANK: ++ ret = imx477_write_reg(imx477, IMX477_REG_FRAME_LENGTH, ++ IMX477_REG_VALUE_16BIT, ++ imx477->mode->height + 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 imx477_ctrl_ops = { ++ .s_ctrl = imx477_set_ctrl, ++}; ++ ++static int imx477_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ struct imx477 *imx477 = to_imx477(sd); ++ ++ if (code->pad >= NUM_PADS) ++ return -EINVAL; ++ ++ if (code->pad == IMAGE_PAD) { ++ if (code->index >= (ARRAY_SIZE(codes) / 4)) ++ return -EINVAL; ++ ++ code->code = imx477_get_format_code(imx477, ++ codes[code->index * 4]); ++ } else { ++ if (code->index > 0) ++ return -EINVAL; ++ ++ code->code = MEDIA_BUS_FMT_SENSOR_DATA; ++ } ++ ++ return 0; ++} ++ ++static int imx477_enum_frame_size(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ struct imx477 *imx477 = to_imx477(sd); ++ ++ if (fse->pad >= NUM_PADS) ++ return -EINVAL; ++ ++ if (fse->pad == IMAGE_PAD) { ++ const struct imx477_mode *mode_list; ++ unsigned int num_modes; ++ ++ get_mode_table(fse->code, &mode_list, &num_modes); ++ ++ if (fse->index >= num_modes) ++ return -EINVAL; ++ ++ if (fse->code != imx477_get_format_code(imx477, fse->code)) ++ return -EINVAL; ++ ++ fse->min_width = mode_list[fse->index].width; ++ fse->max_width = fse->min_width; ++ fse->min_height = mode_list[fse->index].height; ++ fse->max_height = fse->min_height; ++ } else { ++ if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0) ++ return -EINVAL; ++ ++ fse->min_width = IMX477_EMBEDDED_LINE_WIDTH; ++ fse->max_width = fse->min_width; ++ fse->min_height = IMX477_NUM_EMBEDDED_LINES; ++ fse->max_height = fse->min_height; ++ } ++ ++ return 0; ++} ++ ++static void imx477_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 imx477_update_image_pad_format(struct imx477 *imx477, ++ const struct imx477_mode *mode, ++ struct v4l2_subdev_format *fmt) ++{ ++ fmt->format.width = mode->width; ++ fmt->format.height = mode->height; ++ fmt->format.field = V4L2_FIELD_NONE; ++ imx477_reset_colorspace(&fmt->format); ++} ++ ++static void imx477_update_metadata_pad_format(struct v4l2_subdev_format *fmt) ++{ ++ fmt->format.width = IMX477_EMBEDDED_LINE_WIDTH; ++ fmt->format.height = IMX477_NUM_EMBEDDED_LINES; ++ fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA; ++ fmt->format.field = V4L2_FIELD_NONE; ++} ++ ++static int imx477_get_pad_format(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct imx477 *imx477 = to_imx477(sd); ++ ++ if (fmt->pad >= NUM_PADS) ++ return -EINVAL; ++ ++ mutex_lock(&imx477->mutex); ++ ++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { ++ struct v4l2_mbus_framefmt *try_fmt = ++ v4l2_subdev_get_try_format(&imx477->sd, cfg, fmt->pad); ++ /* update the code which could change due to vflip or hflip: */ ++ try_fmt->code = fmt->pad == IMAGE_PAD ? ++ imx477_get_format_code(imx477, try_fmt->code) : ++ MEDIA_BUS_FMT_SENSOR_DATA; ++ fmt->format = *try_fmt; ++ } else { ++ if (fmt->pad == IMAGE_PAD) { ++ imx477_update_image_pad_format(imx477, imx477->mode, ++ fmt); ++ fmt->format.code = ++ imx477_get_format_code(imx477, imx477->fmt.code); ++ } else { ++ imx477_update_metadata_pad_format(fmt); ++ } ++ } ++ ++ mutex_unlock(&imx477->mutex); ++ return 0; ++} ++ ++static ++unsigned int imx477_get_frame_length(const struct imx477_mode *mode, ++ const struct v4l2_fract *timeperframe) ++{ ++ u64 frame_length; ++ ++ frame_length = (u64)timeperframe->numerator * IMX477_PIXEL_RATE; ++ do_div(frame_length, ++ (u64)timeperframe->denominator * mode->line_length_pix); ++ ++ if (WARN_ON(frame_length > IMX477_FRAME_LENGTH_MAX)) ++ frame_length = IMX477_FRAME_LENGTH_MAX; ++ ++ return max_t(unsigned int, frame_length, mode->height); ++} ++ ++static void imx477_set_framing_limits(struct imx477 *imx477) ++{ ++ const struct imx477_mode *mode = imx477->mode; ++ unsigned int frm_length_min, frm_length_default; ++ unsigned int exposure_max, exposure_def, hblank; ++ ++ frm_length_min = imx477_get_frame_length(mode, &mode->timeperframe_min); ++ frm_length_default = ++ imx477_get_frame_length(mode, &mode->timeperframe_default); ++ ++ /* Update limits and set FPS to default */ ++ __v4l2_ctrl_modify_range(imx477->vblank, frm_length_min - mode->height, ++ IMX477_FRAME_LENGTH_MAX - mode->height, ++ 1, frm_length_default - mode->height); ++ __v4l2_ctrl_s_ctrl(imx477->vblank, frm_length_default - mode->height); ++ ++ /* Update max exposure while meeting expected vblanking */ ++ exposure_max = IMX477_FRAME_LENGTH_MAX - IMX477_EXPOSURE_OFFSET; ++ exposure_def = frm_length_default - mode->height - ++ IMX477_EXPOSURE_OFFSET; ++ __v4l2_ctrl_modify_range(imx477->exposure, imx477->exposure->minimum, ++ exposure_max, imx477->exposure->step, ++ exposure_def); ++ /* ++ * Currently PPL is fixed to the mode specified value, so hblank ++ * depends on mode->width only, and is not changeable in any ++ * way other than changing the mode. ++ */ ++ hblank = mode->line_length_pix - mode->width; ++ __v4l2_ctrl_modify_range(imx477->hblank, hblank, hblank, 1, hblank); ++} ++ ++static int imx477_set_pad_format(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct v4l2_mbus_framefmt *framefmt; ++ const struct imx477_mode *mode; ++ struct imx477 *imx477 = to_imx477(sd); ++ ++ if (fmt->pad >= NUM_PADS) ++ return -EINVAL; ++ ++ mutex_lock(&imx477->mutex); ++ ++ if (fmt->pad == IMAGE_PAD) { ++ const struct imx477_mode *mode_list; ++ unsigned int num_modes; ++ ++ /* Bayer order varies with flips */ ++ fmt->format.code = imx477_get_format_code(imx477, ++ fmt->format.code); ++ ++ get_mode_table(fmt->format.code, &mode_list, &num_modes); ++ ++ mode = v4l2_find_nearest_size(mode_list, ++ num_modes, ++ width, height, ++ fmt->format.width, ++ fmt->format.height); ++ imx477_update_image_pad_format(imx477, mode, fmt); ++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { ++ framefmt = v4l2_subdev_get_try_format(sd, cfg, ++ fmt->pad); ++ *framefmt = fmt->format; ++ } else { ++ imx477->mode = mode; ++ imx477_set_framing_limits(imx477); ++ } ++ } else { ++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { ++ framefmt = v4l2_subdev_get_try_format(sd, cfg, ++ fmt->pad); ++ *framefmt = fmt->format; ++ } else { ++ /* Only one embedded data mode is supported */ ++ imx477_update_metadata_pad_format(fmt); ++ } ++ } ++ ++ mutex_unlock(&imx477->mutex); ++ ++ return 0; ++} ++ ++static const struct v4l2_rect * ++__imx477_get_pad_crop(struct imx477 *imx477, struct v4l2_subdev_pad_config *cfg, ++ unsigned int pad, enum v4l2_subdev_format_whence which) ++{ ++ switch (which) { ++ case V4L2_SUBDEV_FORMAT_TRY: ++ return v4l2_subdev_get_try_crop(&imx477->sd, cfg, pad); ++ case V4L2_SUBDEV_FORMAT_ACTIVE: ++ return &imx477->mode->crop; ++ } ++ ++ return NULL; ++} ++ ++static int imx477_get_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_selection *sel) ++{ ++ switch (sel->target) { ++ case V4L2_SEL_TGT_CROP: { ++ struct imx477 *imx477 = to_imx477(sd); ++ ++ mutex_lock(&imx477->mutex); ++ sel->r = *__imx477_get_pad_crop(imx477, cfg, sel->pad, ++ sel->which); ++ mutex_unlock(&imx477->mutex); ++ ++ return 0; ++ } ++ ++ case V4L2_SEL_TGT_NATIVE_SIZE: ++ sel->r.left = 0; ++ sel->r.top = 0; ++ sel->r.width = IMX477_NATIVE_WIDTH; ++ sel->r.height = IMX477_NATIVE_HEIGHT; ++ ++ return 0; ++ ++ case V4L2_SEL_TGT_CROP_DEFAULT: ++ sel->r.left = IMX477_PIXEL_ARRAY_LEFT; ++ sel->r.top = IMX477_PIXEL_ARRAY_TOP; ++ sel->r.width = IMX477_PIXEL_ARRAY_WIDTH; ++ sel->r.height = IMX477_PIXEL_ARRAY_HEIGHT; ++ ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++/* Start streaming */ ++static int imx477_start_streaming(struct imx477 *imx477) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); ++ const struct imx477_reg_list *reg_list; ++ int ret; ++ ++ if (!imx477->common_regs_written) { ++ ret = imx477_write_regs(imx477, mode_common_regs, ++ ARRAY_SIZE(mode_common_regs)); ++ if (ret) { ++ dev_err(&client->dev, "%s failed to set common settings\n", ++ __func__); ++ return ret; ++ } ++ imx477->common_regs_written = true; ++ } ++ ++ /* Apply default values of current mode */ ++ reg_list = &imx477->mode->reg_list; ++ ret = imx477_write_regs(imx477, 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(imx477->sd.ctrl_handler); ++ if (ret) ++ return ret; ++ ++ /* set stream on register */ ++ return imx477_write_reg(imx477, IMX477_REG_MODE_SELECT, ++ IMX477_REG_VALUE_08BIT, IMX477_MODE_STREAMING); ++} ++ ++/* Stop streaming */ ++static void imx477_stop_streaming(struct imx477 *imx477) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); ++ int ret; ++ ++ /* set stream off register */ ++ ret = imx477_write_reg(imx477, IMX477_REG_MODE_SELECT, ++ IMX477_REG_VALUE_08BIT, IMX477_MODE_STANDBY); ++ if (ret) ++ dev_err(&client->dev, "%s failed to set stream\n", __func__); ++} ++ ++static int imx477_set_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct imx477 *imx477 = to_imx477(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret = 0; ++ ++ mutex_lock(&imx477->mutex); ++ if (imx477->streaming == enable) { ++ mutex_unlock(&imx477->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 = imx477_start_streaming(imx477); ++ if (ret) ++ goto err_rpm_put; ++ } else { ++ imx477_stop_streaming(imx477); ++ pm_runtime_put(&client->dev); ++ } ++ ++ imx477->streaming = enable; ++ ++ /* vflip and hflip cannot change during streaming */ ++ __v4l2_ctrl_grab(imx477->vflip, enable); ++ __v4l2_ctrl_grab(imx477->hflip, enable); ++ ++ mutex_unlock(&imx477->mutex); ++ ++ return ret; ++ ++err_rpm_put: ++ pm_runtime_put(&client->dev); ++err_unlock: ++ mutex_unlock(&imx477->mutex); ++ ++ return ret; ++} ++ ++/* Power/clock management functions */ ++static int imx477_power_on(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct imx477 *imx477 = to_imx477(sd); ++ int ret; ++ ++ ret = regulator_bulk_enable(IMX477_NUM_SUPPLIES, ++ imx477->supplies); ++ if (ret) { ++ dev_err(&client->dev, "%s: failed to enable regulators\n", ++ __func__); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(imx477->xclk); ++ if (ret) { ++ dev_err(&client->dev, "%s: failed to enable clock\n", ++ __func__); ++ goto reg_off; ++ } ++ ++ gpiod_set_value_cansleep(imx477->reset_gpio, 1); ++ usleep_range(IMX477_XCLR_MIN_DELAY_US, ++ IMX477_XCLR_MIN_DELAY_US + IMX477_XCLR_DELAY_RANGE_US); ++ ++ return 0; ++ ++reg_off: ++ regulator_bulk_disable(IMX477_NUM_SUPPLIES, imx477->supplies); ++ return ret; ++} ++ ++static int imx477_power_off(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct imx477 *imx477 = to_imx477(sd); ++ ++ gpiod_set_value_cansleep(imx477->reset_gpio, 0); ++ regulator_bulk_disable(IMX477_NUM_SUPPLIES, imx477->supplies); ++ clk_disable_unprepare(imx477->xclk); ++ ++ /* Force reprogramming of the common registers when powered up again. */ ++ imx477->common_regs_written = false; ++ ++ return 0; ++} ++ ++static int __maybe_unused imx477_suspend(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct imx477 *imx477 = to_imx477(sd); ++ ++ if (imx477->streaming) ++ imx477_stop_streaming(imx477); ++ ++ return 0; ++} ++ ++static int __maybe_unused imx477_resume(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct imx477 *imx477 = to_imx477(sd); ++ int ret; ++ ++ if (imx477->streaming) { ++ ret = imx477_start_streaming(imx477); ++ if (ret) ++ goto error; ++ } ++ ++ return 0; ++ ++error: ++ imx477_stop_streaming(imx477); ++ imx477->streaming = 0; ++ return ret; ++} ++ ++static int imx477_get_regulators(struct imx477 *imx477) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); ++ unsigned int i; ++ ++ for (i = 0; i < IMX477_NUM_SUPPLIES; i++) ++ imx477->supplies[i].supply = imx477_supply_name[i]; ++ ++ return devm_regulator_bulk_get(&client->dev, ++ IMX477_NUM_SUPPLIES, ++ imx477->supplies); ++} ++ ++/* Verify chip ID */ ++static int imx477_identify_module(struct imx477 *imx477) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); ++ int ret; ++ u32 val; ++ ++ ret = imx477_read_reg(imx477, IMX477_REG_CHIP_ID, ++ IMX477_REG_VALUE_16BIT, &val); ++ if (ret) { ++ dev_err(&client->dev, "failed to read chip id %x, with error %d\n", ++ IMX477_CHIP_ID, ret); ++ return ret; ++ } ++ ++ if (val != IMX477_CHIP_ID) { ++ dev_err(&client->dev, "chip id mismatch: %x!=%x\n", ++ IMX477_CHIP_ID, val); ++ ret = -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const struct v4l2_subdev_core_ops imx477_core_ops = { ++ .subscribe_event = v4l2_ctrl_subdev_subscribe_event, ++ .unsubscribe_event = v4l2_event_subdev_unsubscribe, ++}; ++ ++static const struct v4l2_subdev_video_ops imx477_video_ops = { ++ .s_stream = imx477_set_stream, ++}; ++ ++static const struct v4l2_subdev_pad_ops imx477_pad_ops = { ++ .enum_mbus_code = imx477_enum_mbus_code, ++ .get_fmt = imx477_get_pad_format, ++ .set_fmt = imx477_set_pad_format, ++ .get_selection = imx477_get_selection, ++ .enum_frame_size = imx477_enum_frame_size, ++}; ++ ++static const struct v4l2_subdev_ops imx477_subdev_ops = { ++ .core = &imx477_core_ops, ++ .video = &imx477_video_ops, ++ .pad = &imx477_pad_ops, ++}; ++ ++static const struct v4l2_subdev_internal_ops imx477_internal_ops = { ++ .open = imx477_open, ++}; ++ ++/* Initialize control handlers */ ++static int imx477_init_controls(struct imx477 *imx477) ++{ ++ struct v4l2_ctrl_handler *ctrl_hdlr; ++ struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); ++ unsigned int i; ++ int ret; ++ ++ ctrl_hdlr = &imx477->ctrl_handler; ++ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 14); ++ if (ret) ++ return ret; ++ ++ mutex_init(&imx477->mutex); ++ ctrl_hdlr->lock = &imx477->mutex; ++ ++ /* By default, PIXEL_RATE is read only */ ++ imx477->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, ++ V4L2_CID_PIXEL_RATE, ++ IMX477_PIXEL_RATE, ++ IMX477_PIXEL_RATE, 1, ++ IMX477_PIXEL_RATE); ++ ++ /* ++ * Create the controls here, but mode specific limits are setup ++ * in the imx477_set_framing_limits() call below. ++ */ ++ imx477->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, ++ V4L2_CID_VBLANK, 0, 0xffff, 1, 0); ++ imx477->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, ++ V4L2_CID_HBLANK, 0, 0xffff, 1, 0); ++ ++ /* HBLANK is read-only for now, but does change with mode. */ ++ if (imx477->hblank) ++ imx477->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ ++ imx477->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, ++ V4L2_CID_EXPOSURE, ++ IMX477_EXPOSURE_MIN, ++ IMX477_EXPOSURE_MAX, ++ IMX477_EXPOSURE_STEP, ++ IMX477_EXPOSURE_DEFAULT); ++ ++ v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, ++ IMX477_ANA_GAIN_MIN, IMX477_ANA_GAIN_MAX, ++ IMX477_ANA_GAIN_STEP, IMX477_ANA_GAIN_DEFAULT); ++ ++ v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, V4L2_CID_DIGITAL_GAIN, ++ IMX477_DGTL_GAIN_MIN, IMX477_DGTL_GAIN_MAX, ++ IMX477_DGTL_GAIN_STEP, IMX477_DGTL_GAIN_DEFAULT); ++ ++ imx477->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ if (imx477->hflip) ++ imx477->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; ++ ++ imx477->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ if (imx477->vflip) ++ imx477->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; ++ ++ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx477_ctrl_ops, ++ V4L2_CID_TEST_PATTERN, ++ ARRAY_SIZE(imx477_test_pattern_menu) - 1, ++ 0, 0, imx477_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, &imx477_ctrl_ops, ++ V4L2_CID_TEST_PATTERN_RED + i, ++ IMX477_TEST_PATTERN_COLOUR_MIN, ++ IMX477_TEST_PATTERN_COLOUR_MAX, ++ IMX477_TEST_PATTERN_COLOUR_STEP, ++ IMX477_TEST_PATTERN_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; ++ } ++ ++ imx477->sd.ctrl_handler = ctrl_hdlr; ++ ++ /* Setup exposure and frame/line length limits. */ ++ imx477_set_framing_limits(imx477); ++ ++ return 0; ++ ++error: ++ v4l2_ctrl_handler_free(ctrl_hdlr); ++ mutex_destroy(&imx477->mutex); ++ ++ return ret; ++} ++ ++static void imx477_free_controls(struct imx477 *imx477) ++{ ++ v4l2_ctrl_handler_free(imx477->sd.ctrl_handler); ++ mutex_destroy(&imx477->mutex); ++} ++ ++static int imx477_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] != IMX477_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 imx477_probe(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ struct imx477 *imx477; ++ int ret; ++ ++ imx477 = devm_kzalloc(&client->dev, sizeof(*imx477), GFP_KERNEL); ++ if (!imx477) ++ return -ENOMEM; ++ ++ v4l2_i2c_subdev_init(&imx477->sd, client, &imx477_subdev_ops); ++ ++ /* Check the hardware configuration in device tree */ ++ if (imx477_check_hwcfg(dev)) ++ return -EINVAL; ++ ++ /* Get system clock (xclk) */ ++ imx477->xclk = devm_clk_get(dev, NULL); ++ if (IS_ERR(imx477->xclk)) { ++ dev_err(dev, "failed to get xclk\n"); ++ return PTR_ERR(imx477->xclk); ++ } ++ ++ imx477->xclk_freq = clk_get_rate(imx477->xclk); ++ if (imx477->xclk_freq != IMX477_XCLK_FREQ) { ++ dev_err(dev, "xclk frequency not supported: %d Hz\n", ++ imx477->xclk_freq); ++ return -EINVAL; ++ } ++ ++ ret = imx477_get_regulators(imx477); ++ if (ret) { ++ dev_err(dev, "failed to get regulators\n"); ++ return ret; ++ } ++ ++ /* Request optional enable pin */ ++ imx477->reset_gpio = devm_gpiod_get_optional(dev, "reset", ++ GPIOD_OUT_HIGH); ++ ++ /* ++ * The sensor must be powered for imx477_identify_module() ++ * to be able to read the CHIP_ID register ++ */ ++ ret = imx477_power_on(dev); ++ if (ret) ++ return ret; ++ ++ ret = imx477_identify_module(imx477); ++ if (ret) ++ goto error_power_off; ++ ++ /* Initialize default format */ ++ imx477_set_default_format(imx477); ++ ++ /* Enable runtime PM and turn off the device */ ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ pm_runtime_idle(dev); ++ ++ /* This needs the pm runtime to be registered. */ ++ ret = imx477_init_controls(imx477); ++ if (ret) ++ goto error_power_off; ++ ++ /* Initialize subdev */ ++ imx477->sd.internal_ops = &imx477_internal_ops; ++ imx477->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | ++ V4L2_SUBDEV_FL_HAS_EVENTS; ++ imx477->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; ++ ++ /* Initialize source pads */ ++ imx477->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE; ++ imx477->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE; ++ ++ ret = media_entity_pads_init(&imx477->sd.entity, NUM_PADS, imx477->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(&imx477->sd); ++ if (ret < 0) { ++ dev_err(dev, "failed to register sensor sub-device: %d\n", ret); ++ goto error_media_entity; ++ } ++ ++ return 0; ++ ++error_media_entity: ++ media_entity_cleanup(&imx477->sd.entity); ++ ++error_handler_free: ++ imx477_free_controls(imx477); ++ ++error_power_off: ++ pm_runtime_disable(&client->dev); ++ pm_runtime_set_suspended(&client->dev); ++ imx477_power_off(&client->dev); ++ ++ return ret; ++} ++ ++static int imx477_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct imx477 *imx477 = to_imx477(sd); ++ ++ v4l2_async_unregister_subdev(sd); ++ media_entity_cleanup(&sd->entity); ++ imx477_free_controls(imx477); ++ ++ pm_runtime_disable(&client->dev); ++ if (!pm_runtime_status_suspended(&client->dev)) ++ imx477_power_off(&client->dev); ++ pm_runtime_set_suspended(&client->dev); ++ ++ return 0; ++} ++ ++static const struct of_device_id imx477_dt_ids[] = { ++ { .compatible = "sony,imx477" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, imx477_dt_ids); ++ ++static const struct dev_pm_ops imx477_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(imx477_suspend, imx477_resume) ++ SET_RUNTIME_PM_OPS(imx477_power_off, imx477_power_on, NULL) ++}; ++ ++static struct i2c_driver imx477_i2c_driver = { ++ .driver = { ++ .name = "imx477", ++ .of_match_table = imx477_dt_ids, ++ .pm = &imx477_pm_ops, ++ }, ++ .probe_new = imx477_probe, ++ .remove = imx477_remove, ++}; ++ ++module_i2c_driver(imx477_i2c_driver); ++ ++MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>"); ++MODULE_DESCRIPTION("Sony IMX477 sensor driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/bcm27xx/patches-5.4/950-0725-media-i2c-imx477-Add-support-for-adaptive-frame-cont.patch b/target/linux/bcm27xx/patches-5.4/950-0725-media-i2c-imx477-Add-support-for-adaptive-frame-cont.patch new file mode 100644 index 0000000000..1da5dea326 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0725-media-i2c-imx477-Add-support-for-adaptive-frame-cont.patch @@ -0,0 +1,182 @@ +From 69f1022fc3c155f8cd5cf7dacaf283b1778835f3 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Fri, 8 May 2020 09:41:17 +0100 +Subject: [PATCH] media: i2c: imx477: Add support for adaptive frame + control + +Use V4L2_CID_EXPOSURE_AUTO_PRIORITY to control if the driver should +automatically adjust the sensor frame length based on exposure time, +allowing variable frame rates and longer exposures. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + drivers/media/i2c/imx477.c | 113 +++++++++++++++++++++++++++++-------- + 1 file changed, 91 insertions(+), 22 deletions(-) + +--- a/drivers/media/i2c/imx477.c ++++ b/drivers/media/i2c/imx477.c +@@ -1082,6 +1082,8 @@ struct imx477 { + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; ++ /* This ctrl allows automatic variable framerate */ ++ struct v4l2_ctrl *exposure_auto; + + /* Current mode */ + const struct imx477_mode *mode; +@@ -1278,6 +1280,72 @@ static int imx477_open(struct v4l2_subde + return 0; + } + ++static int imx477_set_exposure(struct imx477 *imx477, unsigned int val) ++{ ++ int ret; ++ ++ ret = imx477_write_reg(imx477, IMX477_REG_EXPOSURE, ++ IMX477_REG_VALUE_16BIT, val); ++ ++ /* Setup the frame length in the case of auto framerate mode. */ ++ if (imx477->exposure_auto->val) { ++ unsigned int frame_length, frame_length_max, frame_length_min; ++ ++ frame_length_min = imx477->vblank->minimum + ++ imx477->mode->height; ++ frame_length_max = imx477->vblank->maximum + ++ imx477->mode->height; ++ frame_length = max(frame_length_min, ++ val + IMX477_EXPOSURE_OFFSET); ++ frame_length = min(frame_length_max, frame_length); ++ ret += imx477_write_reg(imx477, IMX477_REG_FRAME_LENGTH, ++ IMX477_REG_VALUE_16BIT, frame_length); ++ } ++ ++ return ret; ++} ++ ++static void imx477_adjust_exposure_range(struct imx477 *imx477, ++ struct v4l2_ctrl *ctrl) ++{ ++ int exposure_max, exposure_def; ++ ++ if (ctrl->id == V4L2_CID_VBLANK || !ctrl->val) { ++ /* ++ * Either VBLANK has been changed or auto framerate ++ * adjusting has been disabled. Honour the VBLANK limits ++ * when setting exposure. ++ */ ++ exposure_max = imx477->mode->height + imx477->vblank->val - ++ IMX477_EXPOSURE_OFFSET; ++ ++ if (ctrl->id == V4L2_CID_EXPOSURE_AUTO_PRIORITY) { ++ /* ++ * Allow VBLANK adjustments since the driver is not ++ * handling frame length control automatically. ++ */ ++ __v4l2_ctrl_grab(imx477->vblank, false); ++ } ++ } else { ++ /* ++ * Auto framerate adjusting has been enabled. VBLANK ++ * ctrl has been disabled and exposure can ramp up ++ * to the maximum allowable value. ++ */ ++ exposure_max = IMX477_EXPOSURE_MAX; ++ /* ++ * Do not allow VBLANK adjustments if the driver is ++ * handling it frame length control automatically. ++ */ ++ __v4l2_ctrl_grab(imx477->vblank, true); ++ } ++ ++ exposure_def = min(exposure_max, imx477->exposure->val); ++ __v4l2_ctrl_modify_range(imx477->exposure, imx477->exposure->minimum, ++ exposure_max, imx477->exposure->step, ++ exposure_def); ++} ++ + static int imx477_set_ctrl(struct v4l2_ctrl *ctrl) + { + struct imx477 *imx477 = +@@ -1285,17 +1353,13 @@ static int imx477_set_ctrl(struct v4l2_c + struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); + int ret = 0; + +- if (ctrl->id == V4L2_CID_VBLANK) { +- int exposure_max, exposure_def; +- +- /* Update max exposure while meeting expected vblanking */ +- exposure_max = imx477->mode->height + ctrl->val - +- IMX477_EXPOSURE_OFFSET; +- exposure_def = min(exposure_max, imx477->exposure->val); +- __v4l2_ctrl_modify_range(imx477->exposure, +- imx477->exposure->minimum, +- exposure_max, imx477->exposure->step, +- exposure_def); ++ if (ctrl->id == V4L2_CID_VBLANK || ++ ctrl->id == V4L2_CID_EXPOSURE_AUTO_PRIORITY) { ++ /* ++ * These controls may change the limits of usable exposure, ++ * so check and adjust if necessary. ++ */ ++ imx477_adjust_exposure_range(imx477, ctrl); + } + + /* +@@ -1311,8 +1375,14 @@ static int imx477_set_ctrl(struct v4l2_c + IMX477_REG_VALUE_16BIT, ctrl->val); + break; + case V4L2_CID_EXPOSURE: +- ret = imx477_write_reg(imx477, IMX477_REG_EXPOSURE, +- IMX477_REG_VALUE_16BIT, ctrl->val); ++ ret = imx477_set_exposure(imx477, ctrl->val); ++ break; ++ case V4L2_CID_EXPOSURE_AUTO_PRIORITY: ++ /* ++ * imx477_set_exposure() will recalculate the frame length ++ * to adjust the framerate to match the exposure. ++ */ ++ ret = imx477_set_exposure(imx477, imx477->exposure->val); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = imx477_write_reg(imx477, IMX477_REG_DIGITAL_GAIN, +@@ -1510,9 +1580,8 @@ unsigned int imx477_get_frame_length(con + + static void imx477_set_framing_limits(struct imx477 *imx477) + { ++ unsigned int frm_length_min, frm_length_default, hblank; + const struct imx477_mode *mode = imx477->mode; +- unsigned int frm_length_min, frm_length_default; +- unsigned int exposure_max, exposure_def, hblank; + + frm_length_min = imx477_get_frame_length(mode, &mode->timeperframe_min); + frm_length_default = +@@ -1522,15 +1591,10 @@ static void imx477_set_framing_limits(st + __v4l2_ctrl_modify_range(imx477->vblank, frm_length_min - mode->height, + IMX477_FRAME_LENGTH_MAX - mode->height, + 1, frm_length_default - mode->height); ++ ++ /* Setting this will adjust the exposure limits as well. */ + __v4l2_ctrl_s_ctrl(imx477->vblank, frm_length_default - mode->height); + +- /* Update max exposure while meeting expected vblanking */ +- exposure_max = IMX477_FRAME_LENGTH_MAX - IMX477_EXPOSURE_OFFSET; +- exposure_def = frm_length_default - mode->height - +- IMX477_EXPOSURE_OFFSET; +- __v4l2_ctrl_modify_range(imx477->exposure, imx477->exposure->minimum, +- exposure_max, imx477->exposure->step, +- exposure_def); + /* + * Currently PPL is fixed to the mode specified value, so hblank + * depends on mode->width only, and is not changeable in any +@@ -1939,6 +2003,11 @@ static int imx477_init_controls(struct i + IMX477_DGTL_GAIN_MIN, IMX477_DGTL_GAIN_MAX, + IMX477_DGTL_GAIN_STEP, IMX477_DGTL_GAIN_DEFAULT); + ++ imx477->exposure_auto = ++ v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, ++ V4L2_CID_EXPOSURE_AUTO_PRIORITY, ++ 0, 1, 1, 0); ++ + imx477->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (imx477->hflip) diff --git a/target/linux/bcm27xx/patches-5.4/950-0726-udmabuf-Remove-deleted-map-unmap-handlers.patch b/target/linux/bcm27xx/patches-5.4/950-0726-udmabuf-Remove-deleted-map-unmap-handlers.patch new file mode 100644 index 0000000000..bc7b5bea91 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0726-udmabuf-Remove-deleted-map-unmap-handlers.patch @@ -0,0 +1,52 @@ +From b1f02a027329f23272bd89c80a3f51ff64377fc2 Mon Sep 17 00:00:00 2001 +From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> +Date: Tue, 26 Nov 2019 15:25:16 +0100 +Subject: [PATCH] udmabuf: Remove deleted map/unmap handlers. + +Commit 19d32ace8b6acebc45da1ea748000ac79ccc7721 upstream. + +Commit 7f0de8d80816 ("dma-buf: Drop dma_buf_k(un)map") removed map/unmap +handlers, but they still existed in udmabuf. Remove them there as well + +Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> +Fixes: 7f0de8d80816 ("dma-buf: Drop dma_buf_k(un)map") +Cc: Sumit Semwal <sumit.semwal@linaro.org> +Cc: Daniel Vetter <daniel.vetter@intel.com> +Cc: linux-media@vger.kernel.org +Cc: linaro-mm-sig@lists.linaro.org +Cc: dri-devel@lists.freedesktop.org +Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> +Link: https://patchwork.freedesktop.org/patch/msgid/20191126142516.630200-1-maarten.lankhorst@linux.intel.com +--- + drivers/dma-buf/udmabuf.c | 16 ---------------- + 1 file changed, 16 deletions(-) + +--- a/drivers/dma-buf/udmabuf.c ++++ b/drivers/dma-buf/udmabuf.c +@@ -93,26 +93,10 @@ static void release_udmabuf(struct dma_b + kfree(ubuf); + } + +-static void *kmap_udmabuf(struct dma_buf *buf, unsigned long page_num) +-{ +- struct udmabuf *ubuf = buf->priv; +- struct page *page = ubuf->pages[page_num]; +- +- return kmap(page); +-} +- +-static void kunmap_udmabuf(struct dma_buf *buf, unsigned long page_num, +- void *vaddr) +-{ +- kunmap(vaddr); +-} +- + static const struct dma_buf_ops udmabuf_ops = { + .map_dma_buf = map_udmabuf, + .unmap_dma_buf = unmap_udmabuf, + .release = release_udmabuf, +- .map = kmap_udmabuf, +- .unmap = kunmap_udmabuf, + .mmap = mmap_udmabuf, + }; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0727-udmabuf-use-cache_sgt_mapping-option.patch b/target/linux/bcm27xx/patches-5.4/950-0727-udmabuf-use-cache_sgt_mapping-option.patch new file mode 100644 index 0000000000..166055556f --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0727-udmabuf-use-cache_sgt_mapping-option.patch @@ -0,0 +1,35 @@ +From d57aecba0cd291e0c28e2c82c3d4bce06c5b5b94 Mon Sep 17 00:00:00 2001 +From: Gurchetan Singh <gurchetansingh@chromium.org> +Date: Mon, 2 Dec 2019 17:36:24 -0800 +Subject: [PATCH] udmabuf: use cache_sgt_mapping option + +Commit bc7a71da43b48333f84c6534ab43d240e34cf9eb uptream. + +The GEM prime helpers do it, so should we. It's also possible to make +it optional later. + +Signed-off-by: Gurchetan Singh <gurchetansingh@chromium.org> +Link: http://patchwork.freedesktop.org/patch/msgid/20191203013627.85991-1-gurchetansingh@chromium.org +Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> +--- + drivers/dma-buf/udmabuf.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +--- a/drivers/dma-buf/udmabuf.c ++++ b/drivers/dma-buf/udmabuf.c +@@ -94,10 +94,11 @@ static void release_udmabuf(struct dma_b + } + + static const struct dma_buf_ops udmabuf_ops = { +- .map_dma_buf = map_udmabuf, +- .unmap_dma_buf = unmap_udmabuf, +- .release = release_udmabuf, +- .mmap = mmap_udmabuf, ++ .cache_sgt_mapping = true, ++ .map_dma_buf = map_udmabuf, ++ .unmap_dma_buf = unmap_udmabuf, ++ .release = release_udmabuf, ++ .mmap = mmap_udmabuf, + }; + + #define SEALS_WANTED (F_SEAL_SHRINK) diff --git a/target/linux/bcm27xx/patches-5.4/950-0728-udmabuf-add-a-pointer-to-the-miscdevice-in-dma-buf-p.patch b/target/linux/bcm27xx/patches-5.4/950-0728-udmabuf-add-a-pointer-to-the-miscdevice-in-dma-buf-p.patch new file mode 100644 index 0000000000..68feae3d48 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0728-udmabuf-add-a-pointer-to-the-miscdevice-in-dma-buf-p.patch @@ -0,0 +1,67 @@ +From ad21bb0b5abf5414e31ac3e41ff60216bee52982 Mon Sep 17 00:00:00 2001 +From: Gurchetan Singh <gurchetansingh@chromium.org> +Date: Mon, 2 Dec 2019 17:36:25 -0800 +Subject: [PATCH] udmabuf: add a pointer to the miscdevice in dma-buf + private data + +Commit c1bbed668997268c9edccdc9db1bd1487d9e20b0 upstream. + +Will be used later. + +v2: rename 'udmabuf_misc' to 'device' (kraxel) + +Signed-off-by: Gurchetan Singh <gurchetansingh@chromium.org> +Link: http://patchwork.freedesktop.org/patch/msgid/20191203013627.85991-2-gurchetansingh@chromium.org +Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> +--- + drivers/dma-buf/udmabuf.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +--- a/drivers/dma-buf/udmabuf.c ++++ b/drivers/dma-buf/udmabuf.c +@@ -18,6 +18,7 @@ static const size_t size_limit_mb = 64; + struct udmabuf { + pgoff_t pagecount; + struct page **pages; ++ struct miscdevice *device; + }; + + static vm_fault_t udmabuf_vm_fault(struct vm_fault *vmf) +@@ -104,8 +105,9 @@ static const struct dma_buf_ops udmabuf_ + #define SEALS_WANTED (F_SEAL_SHRINK) + #define SEALS_DENIED (F_SEAL_WRITE) + +-static long udmabuf_create(const struct udmabuf_create_list *head, +- const struct udmabuf_create_item *list) ++static long udmabuf_create(struct miscdevice *device, ++ struct udmabuf_create_list *head, ++ struct udmabuf_create_item *list) + { + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + struct file *memfd = NULL; +@@ -172,6 +174,7 @@ static long udmabuf_create(const struct + exp_info.priv = ubuf; + exp_info.flags = O_RDWR; + ++ ubuf->device = device; + buf = dma_buf_export(&exp_info); + if (IS_ERR(buf)) { + ret = PTR_ERR(buf); +@@ -209,7 +212,7 @@ static long udmabuf_ioctl_create(struct + list.offset = create.offset; + list.size = create.size; + +- return udmabuf_create(&head, &list); ++ return udmabuf_create(filp->private_data, &head, &list); + } + + static long udmabuf_ioctl_create_list(struct file *filp, unsigned long arg) +@@ -228,7 +231,7 @@ static long udmabuf_ioctl_create_list(st + if (IS_ERR(list)) + return PTR_ERR(list); + +- ret = udmabuf_create(&head, list); ++ ret = udmabuf_create(filp->private_data, &head, list); + kfree(list); + return ret; + } diff --git a/target/linux/bcm27xx/patches-5.4/950-0729-udmabuf-separate-out-creating-destroying-scatter-tab.patch b/target/linux/bcm27xx/patches-5.4/950-0729-udmabuf-separate-out-creating-destroying-scatter-tab.patch new file mode 100644 index 0000000000..5a0a59ced1 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0729-udmabuf-separate-out-creating-destroying-scatter-tab.patch @@ -0,0 +1,71 @@ +From 118802c75e04a2f1b94245695076d2506f667ae7 Mon Sep 17 00:00:00 2001 +From: Gurchetan Singh <gurchetansingh@chromium.org> +Date: Mon, 2 Dec 2019 17:36:26 -0800 +Subject: [PATCH] udmabuf: separate out creating/destroying + scatter-table + +Commit 17a7ce203490459cff14fb1c8f9a15d65fd1c544 upstream. + +These are nice functions and can be re-used. + +Signed-off-by: Gurchetan Singh <gurchetansingh@chromium.org> +Link: http://patchwork.freedesktop.org/patch/msgid/20191203013627.85991-3-gurchetansingh@chromium.org +Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> +--- + drivers/dma-buf/udmabuf.c | 26 +++++++++++++++++++------- + 1 file changed, 19 insertions(+), 7 deletions(-) + +--- a/drivers/dma-buf/udmabuf.c ++++ b/drivers/dma-buf/udmabuf.c +@@ -47,10 +47,10 @@ static int mmap_udmabuf(struct dma_buf * + return 0; + } + +-static struct sg_table *map_udmabuf(struct dma_buf_attachment *at, +- enum dma_data_direction direction) ++static struct sg_table *get_sg_table(struct device *dev, struct dma_buf *buf, ++ enum dma_data_direction direction) + { +- struct udmabuf *ubuf = at->dmabuf->priv; ++ struct udmabuf *ubuf = buf->priv; + struct sg_table *sg; + int ret; + +@@ -62,7 +62,7 @@ static struct sg_table *map_udmabuf(stru + GFP_KERNEL); + if (ret < 0) + goto err; +- if (!dma_map_sg(at->dev, sg->sgl, sg->nents, direction)) { ++ if (!dma_map_sg(dev, sg->sgl, sg->nents, direction)) { + ret = -EINVAL; + goto err; + } +@@ -74,13 +74,25 @@ err: + return ERR_PTR(ret); + } + ++static void put_sg_table(struct device *dev, struct sg_table *sg, ++ enum dma_data_direction direction) ++{ ++ dma_unmap_sg(dev, sg->sgl, sg->nents, direction); ++ sg_free_table(sg); ++ kfree(sg); ++} ++ ++static struct sg_table *map_udmabuf(struct dma_buf_attachment *at, ++ enum dma_data_direction direction) ++{ ++ return get_sg_table(at->dev, at->dmabuf, direction); ++} ++ + static void unmap_udmabuf(struct dma_buf_attachment *at, + struct sg_table *sg, + enum dma_data_direction direction) + { +- dma_unmap_sg(at->dev, sg->sgl, sg->nents, direction); +- sg_free_table(sg); +- kfree(sg); ++ return put_sg_table(at->dev, sg, direction); + } + + static void release_udmabuf(struct dma_buf *buf) diff --git a/target/linux/bcm27xx/patches-5.4/950-0730-udmabuf-implement-begin_cpu_access-end_cpu_access-ho.patch b/target/linux/bcm27xx/patches-5.4/950-0730-udmabuf-implement-begin_cpu_access-end_cpu_access-ho.patch new file mode 100644 index 0000000000..79f6a67387 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0730-udmabuf-implement-begin_cpu_access-end_cpu_access-ho.patch @@ -0,0 +1,90 @@ +From 9dc454ebc4380cd90c24a3c224bb0ac7b3d9cc29 Mon Sep 17 00:00:00 2001 +From: Gurchetan Singh <gurchetansingh@chromium.org> +Date: Mon, 2 Dec 2019 17:36:27 -0800 +Subject: [PATCH] udmabuf: implement begin_cpu_access/end_cpu_access + hooks + +Commit 284562e1f34874e267d4f499362c3816f8f6bc3f upstream. + +With the misc device, we should end up using the result of +get_arch_dma_ops(..) or dma-direct ops. + +This can allow us to have WC mappings in the guest after +synchronization. + +Signed-off-by: Gurchetan Singh <gurchetansingh@chromium.org> +Link: http://patchwork.freedesktop.org/patch/msgid/20191203013627.85991-4-gurchetansingh@chromium.org +Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> +--- + drivers/dma-buf/udmabuf.c | 39 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 39 insertions(+) + +--- a/drivers/dma-buf/udmabuf.c ++++ b/drivers/dma-buf/udmabuf.c +@@ -18,6 +18,7 @@ static const size_t size_limit_mb = 64; + struct udmabuf { + pgoff_t pagecount; + struct page **pages; ++ struct sg_table *sg; + struct miscdevice *device; + }; + +@@ -98,20 +99,58 @@ static void unmap_udmabuf(struct dma_buf + static void release_udmabuf(struct dma_buf *buf) + { + struct udmabuf *ubuf = buf->priv; ++ struct device *dev = ubuf->device->this_device; + pgoff_t pg; + ++ if (ubuf->sg) ++ put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL); ++ + for (pg = 0; pg < ubuf->pagecount; pg++) + put_page(ubuf->pages[pg]); + kfree(ubuf->pages); + kfree(ubuf); + } + ++static int begin_cpu_udmabuf(struct dma_buf *buf, ++ enum dma_data_direction direction) ++{ ++ struct udmabuf *ubuf = buf->priv; ++ struct device *dev = ubuf->device->this_device; ++ ++ if (!ubuf->sg) { ++ ubuf->sg = get_sg_table(dev, buf, direction); ++ if (IS_ERR(ubuf->sg)) ++ return PTR_ERR(ubuf->sg); ++ } else { ++ dma_sync_sg_for_device(dev, ubuf->sg->sgl, ++ ubuf->sg->nents, ++ direction); ++ } ++ ++ return 0; ++} ++ ++static int end_cpu_udmabuf(struct dma_buf *buf, ++ enum dma_data_direction direction) ++{ ++ struct udmabuf *ubuf = buf->priv; ++ struct device *dev = ubuf->device->this_device; ++ ++ if (!ubuf->sg) ++ return -EINVAL; ++ ++ dma_sync_sg_for_cpu(dev, ubuf->sg->sgl, ubuf->sg->nents, direction); ++ return 0; ++} ++ + static const struct dma_buf_ops udmabuf_ops = { + .cache_sgt_mapping = true, + .map_dma_buf = map_udmabuf, + .unmap_dma_buf = unmap_udmabuf, + .release = release_udmabuf, + .mmap = mmap_udmabuf, ++ .begin_cpu_access = begin_cpu_udmabuf, ++ .end_cpu_access = end_cpu_udmabuf, + }; + + #define SEALS_WANTED (F_SEAL_SHRINK) diff --git a/target/linux/bcm27xx/patches-5.4/950-0731-udmabuf-fix-dma-buf-cpu-access.patch b/target/linux/bcm27xx/patches-5.4/950-0731-udmabuf-fix-dma-buf-cpu-access.patch new file mode 100644 index 0000000000..14cfe8aed3 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0731-udmabuf-fix-dma-buf-cpu-access.patch @@ -0,0 +1,60 @@ +From 153f1e1def226f87cc4c307d2806b000a820f64b Mon Sep 17 00:00:00 2001 +From: Gurchetan Singh <gurchetansingh@chromium.org> +Date: Tue, 17 Dec 2019 15:02:28 -0800 +Subject: [PATCH] udmabuf: fix dma-buf cpu access + +Commit 1ffe09590121fbb3786d6c860acdd200f7ab095c upstream. + +I'm just going to put Chia's review comment here since it sums +the issue rather nicely: + +"(1) Semantically, a dma-buf is in DMA domain. CPU access from the +importer must be surrounded by {begin,end}_cpu_access. This gives the +exporter a chance to move the buffer to the CPU domain temporarily. + +(2) When the exporter itself has other means to do CPU access, it is +only reasonable for the exporter to move the buffer to the CPU domain +before access, and to the DMA domain after access. The exporter can +potentially reuse {begin,end}_cpu_access for that purpose. + +Because of (1), udmabuf does need to implement the +{begin,end}_cpu_access hooks. But "begin" should mean +dma_sync_sg_for_cpu and "end" should mean dma_sync_sg_for_device. + +Because of (2), if userspace wants to continuing accessing through the +memfd mapping, it should call udmabuf's {begin,end}_cpu_access to +avoid cache issues." + +Reported-by: Chia-I Wu <olvaffe@gmail.com> +Suggested-by: Chia-I Wu <olvaffe@gmail.com> +Fixes: 284562e1f348 ("udmabuf: implement begin_cpu_access/end_cpu_access hooks") +Signed-off-by: Gurchetan Singh <gurchetansingh@chromium.org> +Link: http://patchwork.freedesktop.org/patch/msgid/20191217230228.453-1-gurchetansingh@chromium.org +Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> +--- + drivers/dma-buf/udmabuf.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +--- a/drivers/dma-buf/udmabuf.c ++++ b/drivers/dma-buf/udmabuf.c +@@ -122,9 +122,8 @@ static int begin_cpu_udmabuf(struct dma_ + if (IS_ERR(ubuf->sg)) + return PTR_ERR(ubuf->sg); + } else { +- dma_sync_sg_for_device(dev, ubuf->sg->sgl, +- ubuf->sg->nents, +- direction); ++ dma_sync_sg_for_cpu(dev, ubuf->sg->sgl, ubuf->sg->nents, ++ direction); + } + + return 0; +@@ -139,7 +138,7 @@ static int end_cpu_udmabuf(struct dma_bu + if (!ubuf->sg) + return -EINVAL; + +- dma_sync_sg_for_cpu(dev, ubuf->sg->sgl, ubuf->sg->nents, direction); ++ dma_sync_sg_for_device(dev, ubuf->sg->sgl, ubuf->sg->nents, direction); + return 0; + } + diff --git a/target/linux/bcm27xx/patches-5.4/950-0732-dma-buf-Add-dma-buf-heaps-framework.patch b/target/linux/bcm27xx/patches-5.4/950-0732-dma-buf-Add-dma-buf-heaps-framework.patch new file mode 100644 index 0000000000..03d5d0c85b --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0732-dma-buf-Add-dma-buf-heaps-framework.patch @@ -0,0 +1,525 @@ +From 4c5a9a5db543b5fd998fbc3e15fd4a2d2a3971a9 Mon Sep 17 00:00:00 2001 +From: "Andrew F. Davis" <afd@ti.com> +Date: Tue, 3 Dec 2019 17:26:37 +0000 +Subject: [PATCH] dma-buf: Add dma-buf heaps framework + +Commit c02a81fba74fe3488ad6b08bfb5a1329005418f8 upstream. +This framework allows a unified userspace interface for dma-buf +exporters, allowing userland to allocate specific types of memory +for use in dma-buf sharing. + +Each heap is given its own device node, which a user can allocate +a dma-buf fd from using the DMA_HEAP_IOC_ALLOC. + +This code is an evoluiton of the Android ION implementation, +and a big thanks is due to its authors/maintainers over time +for their effort: + Rebecca Schultz Zavin, Colin Cross, Benjamin Gaignard, + Laura Abbott, and many other contributors! + +Cc: Laura Abbott <labbott@redhat.com> +Cc: Benjamin Gaignard <benjamin.gaignard@linaro.org> +Cc: Sumit Semwal <sumit.semwal@linaro.org> +Cc: Liam Mark <lmark@codeaurora.org> +Cc: Pratik Patel <pratikp@codeaurora.org> +Cc: Brian Starkey <Brian.Starkey@arm.com> +Cc: Vincent Donnefort <Vincent.Donnefort@arm.com> +Cc: Sudipto Paul <Sudipto.Paul@arm.com> +Cc: Andrew F. Davis <afd@ti.com> +Cc: Christoph Hellwig <hch@infradead.org> +Cc: Chenbo Feng <fengc@google.com> +Cc: Alistair Strachan <astrachan@google.com> +Cc: Hridya Valsaraju <hridya@google.com> +Cc: Sandeep Patil <sspatil@google.com> +Cc: Hillf Danton <hdanton@sina.com> +Cc: Dave Airlie <airlied@gmail.com> +Cc: dri-devel@lists.freedesktop.org +Reviewed-by: Brian Starkey <brian.starkey@arm.com> +Acked-by: Sandeep Patil <sspatil@android.com> +Signed-off-by: Andrew F. Davis <afd@ti.com> +Signed-off-by: John Stultz <john.stultz@linaro.org> +Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org> +Link: https://patchwork.freedesktop.org/patch/msgid/20191203172641.66642-2-john.stultz@linaro.org +--- + MAINTAINERS | 18 +++ + drivers/dma-buf/Kconfig | 9 ++ + drivers/dma-buf/Makefile | 1 + + drivers/dma-buf/dma-heap.c | 297 ++++++++++++++++++++++++++++++++++ + include/linux/dma-heap.h | 59 +++++++ + include/uapi/linux/dma-heap.h | 53 ++++++ + 6 files changed, 437 insertions(+) + create mode 100644 drivers/dma-buf/dma-heap.c + create mode 100644 include/linux/dma-heap.h + create mode 100644 include/uapi/linux/dma-heap.h + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -4962,6 +4962,24 @@ F: include/linux/*fence.h + F: Documentation/driver-api/dma-buf.rst + T: git git://anongit.freedesktop.org/drm/drm-misc + ++DMA-BUF HEAPS FRAMEWORK ++M: Sumit Semwal <sumit.semwal@linaro.org> ++R: Andrew F. Davis <afd@ti.com> ++R: Benjamin Gaignard <benjamin.gaignard@linaro.org> ++R: Liam Mark <lmark@codeaurora.org> ++R: Laura Abbott <labbott@redhat.com> ++R: Brian Starkey <Brian.Starkey@arm.com> ++R: John Stultz <john.stultz@linaro.org> ++S: Maintained ++L: linux-media@vger.kernel.org ++L: dri-devel@lists.freedesktop.org ++L: linaro-mm-sig@lists.linaro.org (moderated for non-subscribers) ++F: include/uapi/linux/dma-heap.h ++F: include/linux/dma-heap.h ++F: drivers/dma-buf/dma-heap.c ++F: drivers/dma-buf/heaps/* ++T: git git://anongit.freedesktop.org/drm/drm-misc ++ + DMA GENERIC OFFLOAD ENGINE SUBSYSTEM + M: Vinod Koul <vkoul@kernel.org> + L: dmaengine@vger.kernel.org +--- a/drivers/dma-buf/Kconfig ++++ b/drivers/dma-buf/Kconfig +@@ -44,4 +44,13 @@ config DMABUF_SELFTESTS + default n + depends on DMA_SHARED_BUFFER + ++menuconfig DMABUF_HEAPS ++ bool "DMA-BUF Userland Memory Heaps" ++ select DMA_SHARED_BUFFER ++ help ++ Choose this option to enable the DMA-BUF userland memory heaps. ++ This options creates per heap chardevs in /dev/dma_heap/ which ++ allows userspace to allocate dma-bufs that can be shared ++ between drivers. ++ + endmenu +--- a/drivers/dma-buf/Makefile ++++ b/drivers/dma-buf/Makefile +@@ -3,6 +3,7 @@ obj-$(CONFIG_DMA_SHARED_BUFFER) := dma-s + + dma-buf-objs-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \ + dma-resv.o seqno-fence.o ++obj-$(CONFIG_DMABUF_HEAPS) += dma-heap.o + dma-buf-objs-$(CONFIG_SYNC_FILE) += sync_file.o + dma-buf-objs-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o + dma-buf-objs-$(CONFIG_UDMABUF) += udmabuf.o +--- /dev/null ++++ b/drivers/dma-buf/dma-heap.c +@@ -0,0 +1,297 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Framework for userspace DMA-BUF allocations ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * Copyright (C) 2019 Linaro Ltd. ++ */ ++ ++#include <linux/cdev.h> ++#include <linux/debugfs.h> ++#include <linux/device.h> ++#include <linux/dma-buf.h> ++#include <linux/err.h> ++#include <linux/xarray.h> ++#include <linux/list.h> ++#include <linux/slab.h> ++#include <linux/uaccess.h> ++#include <linux/syscalls.h> ++#include <linux/dma-heap.h> ++#include <uapi/linux/dma-heap.h> ++ ++#define DEVNAME "dma_heap" ++ ++#define NUM_HEAP_MINORS 128 ++ ++/** ++ * struct dma_heap - represents a dmabuf heap in the system ++ * @name: used for debugging/device-node name ++ * @ops: ops struct for this heap ++ * @heap_devt heap device node ++ * @list list head connecting to list of heaps ++ * @heap_cdev heap char device ++ * ++ * Represents a heap of memory from which buffers can be made. ++ */ ++struct dma_heap { ++ const char *name; ++ const struct dma_heap_ops *ops; ++ void *priv; ++ dev_t heap_devt; ++ struct list_head list; ++ struct cdev heap_cdev; ++}; ++ ++static LIST_HEAD(heap_list); ++static DEFINE_MUTEX(heap_list_lock); ++static dev_t dma_heap_devt; ++static struct class *dma_heap_class; ++static DEFINE_XARRAY_ALLOC(dma_heap_minors); ++ ++static int dma_heap_buffer_alloc(struct dma_heap *heap, size_t len, ++ unsigned int fd_flags, ++ unsigned int heap_flags) ++{ ++ /* ++ * Allocations from all heaps have to begin ++ * and end on page boundaries. ++ */ ++ len = PAGE_ALIGN(len); ++ if (!len) ++ return -EINVAL; ++ ++ return heap->ops->allocate(heap, len, fd_flags, heap_flags); ++} ++ ++static int dma_heap_open(struct inode *inode, struct file *file) ++{ ++ struct dma_heap *heap; ++ ++ heap = xa_load(&dma_heap_minors, iminor(inode)); ++ if (!heap) { ++ pr_err("dma_heap: minor %d unknown.\n", iminor(inode)); ++ return -ENODEV; ++ } ++ ++ /* instance data as context */ ++ file->private_data = heap; ++ nonseekable_open(inode, file); ++ ++ return 0; ++} ++ ++static long dma_heap_ioctl_allocate(struct file *file, void *data) ++{ ++ struct dma_heap_allocation_data *heap_allocation = data; ++ struct dma_heap *heap = file->private_data; ++ int fd; ++ ++ if (heap_allocation->fd) ++ return -EINVAL; ++ ++ if (heap_allocation->fd_flags & ~DMA_HEAP_VALID_FD_FLAGS) ++ return -EINVAL; ++ ++ if (heap_allocation->heap_flags & ~DMA_HEAP_VALID_HEAP_FLAGS) ++ return -EINVAL; ++ ++ fd = dma_heap_buffer_alloc(heap, heap_allocation->len, ++ heap_allocation->fd_flags, ++ heap_allocation->heap_flags); ++ if (fd < 0) ++ return fd; ++ ++ heap_allocation->fd = fd; ++ ++ return 0; ++} ++ ++unsigned int dma_heap_ioctl_cmds[] = { ++ DMA_HEAP_IOC_ALLOC, ++}; ++ ++static long dma_heap_ioctl(struct file *file, unsigned int ucmd, ++ unsigned long arg) ++{ ++ char stack_kdata[128]; ++ char *kdata = stack_kdata; ++ unsigned int kcmd; ++ unsigned int in_size, out_size, drv_size, ksize; ++ int nr = _IOC_NR(ucmd); ++ int ret = 0; ++ ++ if (nr >= ARRAY_SIZE(dma_heap_ioctl_cmds)) ++ return -EINVAL; ++ ++ /* Get the kernel ioctl cmd that matches */ ++ kcmd = dma_heap_ioctl_cmds[nr]; ++ ++ /* Figure out the delta between user cmd size and kernel cmd size */ ++ drv_size = _IOC_SIZE(kcmd); ++ out_size = _IOC_SIZE(ucmd); ++ in_size = out_size; ++ if ((ucmd & kcmd & IOC_IN) == 0) ++ in_size = 0; ++ if ((ucmd & kcmd & IOC_OUT) == 0) ++ out_size = 0; ++ ksize = max(max(in_size, out_size), drv_size); ++ ++ /* If necessary, allocate buffer for ioctl argument */ ++ if (ksize > sizeof(stack_kdata)) { ++ kdata = kmalloc(ksize, GFP_KERNEL); ++ if (!kdata) ++ return -ENOMEM; ++ } ++ ++ if (copy_from_user(kdata, (void __user *)arg, in_size) != 0) { ++ ret = -EFAULT; ++ goto err; ++ } ++ ++ /* zero out any difference between the kernel/user structure size */ ++ if (ksize > in_size) ++ memset(kdata + in_size, 0, ksize - in_size); ++ ++ switch (kcmd) { ++ case DMA_HEAP_IOC_ALLOC: ++ ret = dma_heap_ioctl_allocate(file, kdata); ++ break; ++ default: ++ return -ENOTTY; ++ } ++ ++ if (copy_to_user((void __user *)arg, kdata, out_size) != 0) ++ ret = -EFAULT; ++err: ++ if (kdata != stack_kdata) ++ kfree(kdata); ++ return ret; ++} ++ ++static const struct file_operations dma_heap_fops = { ++ .owner = THIS_MODULE, ++ .open = dma_heap_open, ++ .unlocked_ioctl = dma_heap_ioctl, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = dma_heap_ioctl, ++#endif ++}; ++ ++/** ++ * dma_heap_get_drvdata() - get per-subdriver data for the heap ++ * @heap: DMA-Heap to retrieve private data for ++ * ++ * Returns: ++ * The per-subdriver data for the heap. ++ */ ++void *dma_heap_get_drvdata(struct dma_heap *heap) ++{ ++ return heap->priv; ++} ++ ++struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info) ++{ ++ struct dma_heap *heap, *h, *err_ret; ++ struct device *dev_ret; ++ unsigned int minor; ++ int ret; ++ ++ if (!exp_info->name || !strcmp(exp_info->name, "")) { ++ pr_err("dma_heap: Cannot add heap without a name\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ if (!exp_info->ops || !exp_info->ops->allocate) { ++ pr_err("dma_heap: Cannot add heap with invalid ops struct\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ /* check the name is unique */ ++ mutex_lock(&heap_list_lock); ++ list_for_each_entry(h, &heap_list, list) { ++ if (!strcmp(h->name, exp_info->name)) { ++ mutex_unlock(&heap_list_lock); ++ pr_err("dma_heap: Already registered heap named %s\n", ++ exp_info->name); ++ return ERR_PTR(-EINVAL); ++ } ++ } ++ mutex_unlock(&heap_list_lock); ++ ++ heap = kzalloc(sizeof(*heap), GFP_KERNEL); ++ if (!heap) ++ return ERR_PTR(-ENOMEM); ++ ++ heap->name = exp_info->name; ++ heap->ops = exp_info->ops; ++ heap->priv = exp_info->priv; ++ ++ /* Find unused minor number */ ++ ret = xa_alloc(&dma_heap_minors, &minor, heap, ++ XA_LIMIT(0, NUM_HEAP_MINORS - 1), GFP_KERNEL); ++ if (ret < 0) { ++ pr_err("dma_heap: Unable to get minor number for heap\n"); ++ err_ret = ERR_PTR(ret); ++ goto err0; ++ } ++ ++ /* Create device */ ++ heap->heap_devt = MKDEV(MAJOR(dma_heap_devt), minor); ++ ++ cdev_init(&heap->heap_cdev, &dma_heap_fops); ++ ret = cdev_add(&heap->heap_cdev, heap->heap_devt, 1); ++ if (ret < 0) { ++ pr_err("dma_heap: Unable to add char device\n"); ++ err_ret = ERR_PTR(ret); ++ goto err1; ++ } ++ ++ dev_ret = device_create(dma_heap_class, ++ NULL, ++ heap->heap_devt, ++ NULL, ++ heap->name); ++ if (IS_ERR(dev_ret)) { ++ pr_err("dma_heap: Unable to create device\n"); ++ err_ret = ERR_CAST(dev_ret); ++ goto err2; ++ } ++ /* Add heap to the list */ ++ mutex_lock(&heap_list_lock); ++ list_add(&heap->list, &heap_list); ++ mutex_unlock(&heap_list_lock); ++ ++ return heap; ++ ++err2: ++ cdev_del(&heap->heap_cdev); ++err1: ++ xa_erase(&dma_heap_minors, minor); ++err0: ++ kfree(heap); ++ return err_ret; ++} ++ ++static char *dma_heap_devnode(struct device *dev, umode_t *mode) ++{ ++ return kasprintf(GFP_KERNEL, "dma_heap/%s", dev_name(dev)); ++} ++ ++static int dma_heap_init(void) ++{ ++ int ret; ++ ++ ret = alloc_chrdev_region(&dma_heap_devt, 0, NUM_HEAP_MINORS, DEVNAME); ++ if (ret) ++ return ret; ++ ++ dma_heap_class = class_create(THIS_MODULE, DEVNAME); ++ if (IS_ERR(dma_heap_class)) { ++ unregister_chrdev_region(dma_heap_devt, NUM_HEAP_MINORS); ++ return PTR_ERR(dma_heap_class); ++ } ++ dma_heap_class->devnode = dma_heap_devnode; ++ ++ return 0; ++} ++subsys_initcall(dma_heap_init); +--- /dev/null ++++ b/include/linux/dma-heap.h +@@ -0,0 +1,59 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * DMABUF Heaps Allocation Infrastructure ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * Copyright (C) 2019 Linaro Ltd. ++ */ ++ ++#ifndef _DMA_HEAPS_H ++#define _DMA_HEAPS_H ++ ++#include <linux/cdev.h> ++#include <linux/types.h> ++ ++struct dma_heap; ++ ++/** ++ * struct dma_heap_ops - ops to operate on a given heap ++ * @allocate: allocate dmabuf and return fd ++ * ++ * allocate returns dmabuf fd on success, -errno on error. ++ */ ++struct dma_heap_ops { ++ int (*allocate)(struct dma_heap *heap, ++ unsigned long len, ++ unsigned long fd_flags, ++ unsigned long heap_flags); ++}; ++ ++/** ++ * struct dma_heap_export_info - information needed to export a new dmabuf heap ++ * @name: used for debugging/device-node name ++ * @ops: ops struct for this heap ++ * @priv: heap exporter private data ++ * ++ * Information needed to export a new dmabuf heap. ++ */ ++struct dma_heap_export_info { ++ const char *name; ++ const struct dma_heap_ops *ops; ++ void *priv; ++}; ++ ++/** ++ * dma_heap_get_drvdata() - get per-heap driver data ++ * @heap: DMA-Heap to retrieve private data for ++ * ++ * Returns: ++ * The per-heap data for the heap. ++ */ ++void *dma_heap_get_drvdata(struct dma_heap *heap); ++ ++/** ++ * dma_heap_add - adds a heap to dmabuf heaps ++ * @exp_info: information needed to register this heap ++ */ ++struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info); ++ ++#endif /* _DMA_HEAPS_H */ +--- /dev/null ++++ b/include/uapi/linux/dma-heap.h +@@ -0,0 +1,53 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++/* ++ * DMABUF Heaps Userspace API ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * Copyright (C) 2019 Linaro Ltd. ++ */ ++#ifndef _UAPI_LINUX_DMABUF_POOL_H ++#define _UAPI_LINUX_DMABUF_POOL_H ++ ++#include <linux/ioctl.h> ++#include <linux/types.h> ++ ++/** ++ * DOC: DMABUF Heaps Userspace API ++ */ ++ ++/* Valid FD_FLAGS are O_CLOEXEC, O_RDONLY, O_WRONLY, O_RDWR */ ++#define DMA_HEAP_VALID_FD_FLAGS (O_CLOEXEC | O_ACCMODE) ++ ++/* Currently no heap flags */ ++#define DMA_HEAP_VALID_HEAP_FLAGS (0) ++ ++/** ++ * struct dma_heap_allocation_data - metadata passed from userspace for ++ * allocations ++ * @len: size of the allocation ++ * @fd: will be populated with a fd which provides the ++ * handle to the allocated dma-buf ++ * @fd_flags: file descriptor flags used when allocating ++ * @heap_flags: flags passed to heap ++ * ++ * Provided by userspace as an argument to the ioctl ++ */ ++struct dma_heap_allocation_data { ++ __u64 len; ++ __u32 fd; ++ __u32 fd_flags; ++ __u64 heap_flags; ++}; ++ ++#define DMA_HEAP_IOC_MAGIC 'H' ++ ++/** ++ * DOC: DMA_HEAP_IOC_ALLOC - allocate memory from pool ++ * ++ * Takes a dma_heap_allocation_data struct and returns it with the fd field ++ * populated with the dmabuf handle of the allocation. ++ */ ++#define DMA_HEAP_IOC_ALLOC _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,\ ++ struct dma_heap_allocation_data) ++ ++#endif /* _UAPI_LINUX_DMABUF_POOL_H */ diff --git a/target/linux/bcm27xx/patches-5.4/950-0733-dma-buf-heaps-Add-heap-helpers.patch b/target/linux/bcm27xx/patches-5.4/950-0733-dma-buf-heaps-Add-heap-helpers.patch new file mode 100644 index 0000000000..10eb46ca7b --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0733-dma-buf-heaps-Add-heap-helpers.patch @@ -0,0 +1,394 @@ +From adde2d6532428cdcaeb60081abb299ce6e5aa76b Mon Sep 17 00:00:00 2001 +From: John Stultz <john.stultz@linaro.org> +Date: Tue, 3 Dec 2019 17:26:38 +0000 +Subject: [PATCH] dma-buf: heaps: Add heap helpers + +Commit 5248eb12fea890a03b4cdc3ef546d6319d4d9b73 upstream. + +Add generic helper dmabuf ops for dma heaps, so we can reduce +the amount of duplicative code for the exported dmabufs. + +This code is an evolution of the Android ION implementation, so +thanks to its original authors and maintainters: + Rebecca Schultz Zavin, Colin Cross, Laura Abbott, and others! + +Cc: Laura Abbott <labbott@redhat.com> +Cc: Benjamin Gaignard <benjamin.gaignard@linaro.org> +Cc: Sumit Semwal <sumit.semwal@linaro.org> +Cc: Liam Mark <lmark@codeaurora.org> +Cc: Pratik Patel <pratikp@codeaurora.org> +Cc: Brian Starkey <Brian.Starkey@arm.com> +Cc: Vincent Donnefort <Vincent.Donnefort@arm.com> +Cc: Sudipto Paul <Sudipto.Paul@arm.com> +Cc: Andrew F. Davis <afd@ti.com> +Cc: Christoph Hellwig <hch@infradead.org> +Cc: Chenbo Feng <fengc@google.com> +Cc: Alistair Strachan <astrachan@google.com> +Cc: Hridya Valsaraju <hridya@google.com> +Cc: Sandeep Patil <sspatil@google.com> +Cc: Hillf Danton <hdanton@sina.com> +Cc: Dave Airlie <airlied@gmail.com> +Cc: dri-devel@lists.freedesktop.org +Reviewed-by: Benjamin Gaignard <benjamin.gaignard@linaro.org> +Reviewed-by: Brian Starkey <brian.starkey@arm.com> +Acked-by: Sandeep Patil <sspatil@android.com> +Acked-by: Laura Abbott <labbott@redhat.com> +Tested-by: Ayan Kumar Halder <ayan.halder@arm.com> +Signed-off-by: John Stultz <john.stultz@linaro.org> +Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org> +Link: https://patchwork.freedesktop.org/patch/msgid/20191203172641.66642-3-john.stultz@linaro.org +--- + drivers/dma-buf/Makefile | 1 + + drivers/dma-buf/heaps/Makefile | 2 + + drivers/dma-buf/heaps/heap-helpers.c | 271 +++++++++++++++++++++++++++ + drivers/dma-buf/heaps/heap-helpers.h | 53 ++++++ + 4 files changed, 327 insertions(+) + create mode 100644 drivers/dma-buf/heaps/Makefile + create mode 100644 drivers/dma-buf/heaps/heap-helpers.c + create mode 100644 drivers/dma-buf/heaps/heap-helpers.h + +--- a/drivers/dma-buf/Makefile ++++ b/drivers/dma-buf/Makefile +@@ -4,6 +4,7 @@ obj-$(CONFIG_DMA_SHARED_BUFFER) := dma-s + dma-buf-objs-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \ + dma-resv.o seqno-fence.o + obj-$(CONFIG_DMABUF_HEAPS) += dma-heap.o ++obj-$(CONFIG_DMABUF_HEAPS) += heaps/ + dma-buf-objs-$(CONFIG_SYNC_FILE) += sync_file.o + dma-buf-objs-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o + dma-buf-objs-$(CONFIG_UDMABUF) += udmabuf.o +--- /dev/null ++++ b/drivers/dma-buf/heaps/Makefile +@@ -0,0 +1,2 @@ ++# SPDX-License-Identifier: GPL-2.0 ++obj-y += heap-helpers.o +--- /dev/null ++++ b/drivers/dma-buf/heaps/heap-helpers.c +@@ -0,0 +1,271 @@ ++// SPDX-License-Identifier: GPL-2.0 ++#include <linux/device.h> ++#include <linux/dma-buf.h> ++#include <linux/err.h> ++#include <linux/highmem.h> ++#include <linux/idr.h> ++#include <linux/list.h> ++#include <linux/slab.h> ++#include <linux/uaccess.h> ++#include <linux/vmalloc.h> ++#include <uapi/linux/dma-heap.h> ++ ++#include "heap-helpers.h" ++ ++void init_heap_helper_buffer(struct heap_helper_buffer *buffer, ++ void (*free)(struct heap_helper_buffer *)) ++{ ++ buffer->priv_virt = NULL; ++ mutex_init(&buffer->lock); ++ buffer->vmap_cnt = 0; ++ buffer->vaddr = NULL; ++ buffer->pagecount = 0; ++ buffer->pages = NULL; ++ INIT_LIST_HEAD(&buffer->attachments); ++ buffer->free = free; ++} ++ ++struct dma_buf *heap_helper_export_dmabuf(struct heap_helper_buffer *buffer, ++ int fd_flags) ++{ ++ DEFINE_DMA_BUF_EXPORT_INFO(exp_info); ++ ++ exp_info.ops = &heap_helper_ops; ++ exp_info.size = buffer->size; ++ exp_info.flags = fd_flags; ++ exp_info.priv = buffer; ++ ++ return dma_buf_export(&exp_info); ++} ++ ++static void *dma_heap_map_kernel(struct heap_helper_buffer *buffer) ++{ ++ void *vaddr; ++ ++ vaddr = vmap(buffer->pages, buffer->pagecount, VM_MAP, PAGE_KERNEL); ++ if (!vaddr) ++ return ERR_PTR(-ENOMEM); ++ ++ return vaddr; ++} ++ ++static void dma_heap_buffer_destroy(struct heap_helper_buffer *buffer) ++{ ++ if (buffer->vmap_cnt > 0) { ++ WARN(1, "%s: buffer still mapped in the kernel\n", __func__); ++ vunmap(buffer->vaddr); ++ } ++ ++ buffer->free(buffer); ++} ++ ++static void *dma_heap_buffer_vmap_get(struct heap_helper_buffer *buffer) ++{ ++ void *vaddr; ++ ++ if (buffer->vmap_cnt) { ++ buffer->vmap_cnt++; ++ return buffer->vaddr; ++ } ++ vaddr = dma_heap_map_kernel(buffer); ++ if (IS_ERR(vaddr)) ++ return vaddr; ++ buffer->vaddr = vaddr; ++ buffer->vmap_cnt++; ++ return vaddr; ++} ++ ++static void dma_heap_buffer_vmap_put(struct heap_helper_buffer *buffer) ++{ ++ if (!--buffer->vmap_cnt) { ++ vunmap(buffer->vaddr); ++ buffer->vaddr = NULL; ++ } ++} ++ ++struct dma_heaps_attachment { ++ struct device *dev; ++ struct sg_table table; ++ struct list_head list; ++}; ++ ++static int dma_heap_attach(struct dma_buf *dmabuf, ++ struct dma_buf_attachment *attachment) ++{ ++ struct dma_heaps_attachment *a; ++ struct heap_helper_buffer *buffer = dmabuf->priv; ++ int ret; ++ ++ a = kzalloc(sizeof(*a), GFP_KERNEL); ++ if (!a) ++ return -ENOMEM; ++ ++ ret = sg_alloc_table_from_pages(&a->table, buffer->pages, ++ buffer->pagecount, 0, ++ buffer->pagecount << PAGE_SHIFT, ++ GFP_KERNEL); ++ if (ret) { ++ kfree(a); ++ return ret; ++ } ++ ++ a->dev = attachment->dev; ++ INIT_LIST_HEAD(&a->list); ++ ++ attachment->priv = a; ++ ++ mutex_lock(&buffer->lock); ++ list_add(&a->list, &buffer->attachments); ++ mutex_unlock(&buffer->lock); ++ ++ return 0; ++} ++ ++static void dma_heap_detach(struct dma_buf *dmabuf, ++ struct dma_buf_attachment *attachment) ++{ ++ struct dma_heaps_attachment *a = attachment->priv; ++ struct heap_helper_buffer *buffer = dmabuf->priv; ++ ++ mutex_lock(&buffer->lock); ++ list_del(&a->list); ++ mutex_unlock(&buffer->lock); ++ ++ sg_free_table(&a->table); ++ kfree(a); ++} ++ ++static ++struct sg_table *dma_heap_map_dma_buf(struct dma_buf_attachment *attachment, ++ enum dma_data_direction direction) ++{ ++ struct dma_heaps_attachment *a = attachment->priv; ++ struct sg_table *table; ++ ++ table = &a->table; ++ ++ if (!dma_map_sg(attachment->dev, table->sgl, table->nents, ++ direction)) ++ table = ERR_PTR(-ENOMEM); ++ return table; ++} ++ ++static void dma_heap_unmap_dma_buf(struct dma_buf_attachment *attachment, ++ struct sg_table *table, ++ enum dma_data_direction direction) ++{ ++ dma_unmap_sg(attachment->dev, table->sgl, table->nents, direction); ++} ++ ++static vm_fault_t dma_heap_vm_fault(struct vm_fault *vmf) ++{ ++ struct vm_area_struct *vma = vmf->vma; ++ struct heap_helper_buffer *buffer = vma->vm_private_data; ++ ++ if (vmf->pgoff > buffer->pagecount) ++ return VM_FAULT_SIGBUS; ++ ++ vmf->page = buffer->pages[vmf->pgoff]; ++ get_page(vmf->page); ++ ++ return 0; ++} ++ ++static const struct vm_operations_struct dma_heap_vm_ops = { ++ .fault = dma_heap_vm_fault, ++}; ++ ++static int dma_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) ++{ ++ struct heap_helper_buffer *buffer = dmabuf->priv; ++ ++ if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) == 0) ++ return -EINVAL; ++ ++ vma->vm_ops = &dma_heap_vm_ops; ++ vma->vm_private_data = buffer; ++ ++ return 0; ++} ++ ++static void dma_heap_dma_buf_release(struct dma_buf *dmabuf) ++{ ++ struct heap_helper_buffer *buffer = dmabuf->priv; ++ ++ dma_heap_buffer_destroy(buffer); ++} ++ ++static int dma_heap_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, ++ enum dma_data_direction direction) ++{ ++ struct heap_helper_buffer *buffer = dmabuf->priv; ++ struct dma_heaps_attachment *a; ++ int ret = 0; ++ ++ mutex_lock(&buffer->lock); ++ ++ if (buffer->vmap_cnt) ++ invalidate_kernel_vmap_range(buffer->vaddr, buffer->size); ++ ++ list_for_each_entry(a, &buffer->attachments, list) { ++ dma_sync_sg_for_cpu(a->dev, a->table.sgl, a->table.nents, ++ direction); ++ } ++ mutex_unlock(&buffer->lock); ++ ++ return ret; ++} ++ ++static int dma_heap_dma_buf_end_cpu_access(struct dma_buf *dmabuf, ++ enum dma_data_direction direction) ++{ ++ struct heap_helper_buffer *buffer = dmabuf->priv; ++ struct dma_heaps_attachment *a; ++ ++ mutex_lock(&buffer->lock); ++ ++ if (buffer->vmap_cnt) ++ flush_kernel_vmap_range(buffer->vaddr, buffer->size); ++ ++ list_for_each_entry(a, &buffer->attachments, list) { ++ dma_sync_sg_for_device(a->dev, a->table.sgl, a->table.nents, ++ direction); ++ } ++ mutex_unlock(&buffer->lock); ++ ++ return 0; ++} ++ ++static void *dma_heap_dma_buf_vmap(struct dma_buf *dmabuf) ++{ ++ struct heap_helper_buffer *buffer = dmabuf->priv; ++ void *vaddr; ++ ++ mutex_lock(&buffer->lock); ++ vaddr = dma_heap_buffer_vmap_get(buffer); ++ mutex_unlock(&buffer->lock); ++ ++ return vaddr; ++} ++ ++static void dma_heap_dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr) ++{ ++ struct heap_helper_buffer *buffer = dmabuf->priv; ++ ++ mutex_lock(&buffer->lock); ++ dma_heap_buffer_vmap_put(buffer); ++ mutex_unlock(&buffer->lock); ++} ++ ++const struct dma_buf_ops heap_helper_ops = { ++ .map_dma_buf = dma_heap_map_dma_buf, ++ .unmap_dma_buf = dma_heap_unmap_dma_buf, ++ .mmap = dma_heap_mmap, ++ .release = dma_heap_dma_buf_release, ++ .attach = dma_heap_attach, ++ .detach = dma_heap_detach, ++ .begin_cpu_access = dma_heap_dma_buf_begin_cpu_access, ++ .end_cpu_access = dma_heap_dma_buf_end_cpu_access, ++ .vmap = dma_heap_dma_buf_vmap, ++ .vunmap = dma_heap_dma_buf_vunmap, ++}; +--- /dev/null ++++ b/drivers/dma-buf/heaps/heap-helpers.h +@@ -0,0 +1,53 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * DMABUF Heaps helper code ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * Copyright (C) 2019 Linaro Ltd. ++ */ ++ ++#ifndef _HEAP_HELPERS_H ++#define _HEAP_HELPERS_H ++ ++#include <linux/dma-heap.h> ++#include <linux/list.h> ++ ++/** ++ * struct heap_helper_buffer - helper buffer metadata ++ * @heap: back pointer to the heap the buffer came from ++ * @dmabuf: backing dma-buf for this buffer ++ * @size: size of the buffer ++ * @priv_virt pointer to heap specific private value ++ * @lock mutext to protect the data in this structure ++ * @vmap_cnt count of vmap references on the buffer ++ * @vaddr vmap'ed virtual address ++ * @pagecount number of pages in the buffer ++ * @pages list of page pointers ++ * @attachments list of device attachments ++ * ++ * @free heap callback to free the buffer ++ */ ++struct heap_helper_buffer { ++ struct dma_heap *heap; ++ struct dma_buf *dmabuf; ++ size_t size; ++ ++ void *priv_virt; ++ struct mutex lock; ++ int vmap_cnt; ++ void *vaddr; ++ pgoff_t pagecount; ++ struct page **pages; ++ struct list_head attachments; ++ ++ void (*free)(struct heap_helper_buffer *buffer); ++}; ++ ++void init_heap_helper_buffer(struct heap_helper_buffer *buffer, ++ void (*free)(struct heap_helper_buffer *)); ++ ++struct dma_buf *heap_helper_export_dmabuf(struct heap_helper_buffer *buffer, ++ int fd_flags); ++ ++extern const struct dma_buf_ops heap_helper_ops; ++#endif /* _HEAP_HELPERS_H */ diff --git a/target/linux/bcm27xx/patches-5.4/950-0734-dma-buf-heaps-Add-system-heap-to-dmabuf-heaps.patch b/target/linux/bcm27xx/patches-5.4/950-0734-dma-buf-heaps-Add-system-heap-to-dmabuf-heaps.patch new file mode 100644 index 0000000000..55c2450821 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0734-dma-buf-heaps-Add-system-heap-to-dmabuf-heaps.patch @@ -0,0 +1,200 @@ +From 8392cd87592f31737286ea16f11781a234de3564 Mon Sep 17 00:00:00 2001 +From: John Stultz <john.stultz@linaro.org> +Date: Tue, 3 Dec 2019 17:26:39 +0000 +Subject: [PATCH] dma-buf: heaps: Add system heap to dmabuf heaps + +Commit efa04fefebbd724ffda7f49e42d057a7217c45b0 upstream. + +This patch adds system heap to the dma-buf heaps framework. + +This allows applications to get a page-allocator backed dma-buf +for non-contiguous memory. + +This code is an evolution of the Android ION implementation, so +thanks to its original authors and maintainters: + Rebecca Schultz Zavin, Colin Cross, Laura Abbott, and others! + +Cc: Laura Abbott <labbott@redhat.com> +Cc: Benjamin Gaignard <benjamin.gaignard@linaro.org> +Cc: Sumit Semwal <sumit.semwal@linaro.org> +Cc: Liam Mark <lmark@codeaurora.org> +Cc: Pratik Patel <pratikp@codeaurora.org> +Cc: Brian Starkey <Brian.Starkey@arm.com> +Cc: Vincent Donnefort <Vincent.Donnefort@arm.com> +Cc: Sudipto Paul <Sudipto.Paul@arm.com> +Cc: Andrew F. Davis <afd@ti.com> +Cc: Christoph Hellwig <hch@infradead.org> +Cc: Chenbo Feng <fengc@google.com> +Cc: Alistair Strachan <astrachan@google.com> +Cc: Hridya Valsaraju <hridya@google.com> +Cc: Sandeep Patil <sspatil@google.com> +Cc: Hillf Danton <hdanton@sina.com> +Cc: Dave Airlie <airlied@gmail.com> +Cc: dri-devel@lists.freedesktop.org +Reviewed-by: Benjamin Gaignard <benjamin.gaignard@linaro.org> +Reviewed-by: Brian Starkey <brian.starkey@arm.com> +Acked-by: Sandeep Patil <sspatil@android.com> +Acked-by: Laura Abbott <labbott@redhat.com> +Tested-by: Ayan Kumar Halder <ayan.halder@arm.com> +Signed-off-by: John Stultz <john.stultz@linaro.org> +Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org> +Link: https://patchwork.freedesktop.org/patch/msgid/20191203172641.66642-4-john.stultz@linaro.org +--- + drivers/dma-buf/Kconfig | 2 + + drivers/dma-buf/heaps/Kconfig | 6 ++ + drivers/dma-buf/heaps/Makefile | 1 + + drivers/dma-buf/heaps/system_heap.c | 123 ++++++++++++++++++++++++++++ + 4 files changed, 132 insertions(+) + create mode 100644 drivers/dma-buf/heaps/Kconfig + create mode 100644 drivers/dma-buf/heaps/system_heap.c + +--- a/drivers/dma-buf/Kconfig ++++ b/drivers/dma-buf/Kconfig +@@ -53,4 +53,6 @@ menuconfig DMABUF_HEAPS + allows userspace to allocate dma-bufs that can be shared + between drivers. + ++source "drivers/dma-buf/heaps/Kconfig" ++ + endmenu +--- /dev/null ++++ b/drivers/dma-buf/heaps/Kconfig +@@ -0,0 +1,6 @@ ++config DMABUF_HEAPS_SYSTEM ++ bool "DMA-BUF System Heap" ++ depends on DMABUF_HEAPS ++ help ++ Choose this option to enable the system dmabuf heap. The system heap ++ is backed by pages from the buddy allocator. If in doubt, say Y. +--- a/drivers/dma-buf/heaps/Makefile ++++ b/drivers/dma-buf/heaps/Makefile +@@ -1,2 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0 + obj-y += heap-helpers.o ++obj-$(CONFIG_DMABUF_HEAPS_SYSTEM) += system_heap.o +--- /dev/null ++++ b/drivers/dma-buf/heaps/system_heap.c +@@ -0,0 +1,123 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * DMABUF System heap exporter ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * Copyright (C) 2019 Linaro Ltd. ++ */ ++ ++#include <linux/dma-buf.h> ++#include <linux/dma-mapping.h> ++#include <linux/dma-heap.h> ++#include <linux/err.h> ++#include <linux/highmem.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/scatterlist.h> ++#include <linux/slab.h> ++#include <linux/sched/signal.h> ++#include <asm/page.h> ++ ++#include "heap-helpers.h" ++ ++struct dma_heap *sys_heap; ++ ++static void system_heap_free(struct heap_helper_buffer *buffer) ++{ ++ pgoff_t pg; ++ ++ for (pg = 0; pg < buffer->pagecount; pg++) ++ __free_page(buffer->pages[pg]); ++ kfree(buffer->pages); ++ kfree(buffer); ++} ++ ++static int system_heap_allocate(struct dma_heap *heap, ++ unsigned long len, ++ unsigned long fd_flags, ++ unsigned long heap_flags) ++{ ++ struct heap_helper_buffer *helper_buffer; ++ struct dma_buf *dmabuf; ++ int ret = -ENOMEM; ++ pgoff_t pg; ++ ++ helper_buffer = kzalloc(sizeof(*helper_buffer), GFP_KERNEL); ++ if (!helper_buffer) ++ return -ENOMEM; ++ ++ init_heap_helper_buffer(helper_buffer, system_heap_free); ++ helper_buffer->heap = heap; ++ helper_buffer->size = len; ++ ++ helper_buffer->pagecount = len / PAGE_SIZE; ++ helper_buffer->pages = kmalloc_array(helper_buffer->pagecount, ++ sizeof(*helper_buffer->pages), ++ GFP_KERNEL); ++ if (!helper_buffer->pages) { ++ ret = -ENOMEM; ++ goto err0; ++ } ++ ++ for (pg = 0; pg < helper_buffer->pagecount; pg++) { ++ /* ++ * Avoid trying to allocate memory if the process ++ * has been killed by by SIGKILL ++ */ ++ if (fatal_signal_pending(current)) ++ goto err1; ++ ++ helper_buffer->pages[pg] = alloc_page(GFP_KERNEL | __GFP_ZERO); ++ if (!helper_buffer->pages[pg]) ++ goto err1; ++ } ++ ++ /* create the dmabuf */ ++ dmabuf = heap_helper_export_dmabuf(helper_buffer, fd_flags); ++ if (IS_ERR(dmabuf)) { ++ ret = PTR_ERR(dmabuf); ++ goto err1; ++ } ++ ++ helper_buffer->dmabuf = dmabuf; ++ ++ ret = dma_buf_fd(dmabuf, fd_flags); ++ if (ret < 0) { ++ dma_buf_put(dmabuf); ++ /* just return, as put will call release and that will free */ ++ return ret; ++ } ++ ++ return ret; ++ ++err1: ++ while (pg > 0) ++ __free_page(helper_buffer->pages[--pg]); ++ kfree(helper_buffer->pages); ++err0: ++ kfree(helper_buffer); ++ ++ return ret; ++} ++ ++static const struct dma_heap_ops system_heap_ops = { ++ .allocate = system_heap_allocate, ++}; ++ ++static int system_heap_create(void) ++{ ++ struct dma_heap_export_info exp_info; ++ int ret = 0; ++ ++ exp_info.name = "system_heap"; ++ exp_info.ops = &system_heap_ops; ++ exp_info.priv = NULL; ++ ++ sys_heap = dma_heap_add(&exp_info); ++ if (IS_ERR(sys_heap)) ++ ret = PTR_ERR(sys_heap); ++ ++ return ret; ++} ++module_init(system_heap_create); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/bcm27xx/patches-5.4/950-0735-dma-buf-heaps-Add-CMA-heap-to-dmabuf-heaps.patch b/target/linux/bcm27xx/patches-5.4/950-0735-dma-buf-heaps-Add-CMA-heap-to-dmabuf-heaps.patch new file mode 100644 index 0000000000..d1828702b4 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0735-dma-buf-heaps-Add-CMA-heap-to-dmabuf-heaps.patch @@ -0,0 +1,251 @@ +From d5e996267c71a9517b2c831d072e76bacb8f0e56 Mon Sep 17 00:00:00 2001 +From: John Stultz <john.stultz@linaro.org> +Date: Tue, 3 Dec 2019 17:26:40 +0000 +Subject: [PATCH] dma-buf: heaps: Add CMA heap to dmabuf heaps + +Commit b61614ec318aae0c77ecd2816878d851dd61d9a6 upstream. + +This adds a CMA heap, which allows userspace to allocate +a dma-buf of contiguous memory out of a CMA region. + +This code is an evolution of the Android ION implementation, so +thanks to its original author and maintainters: + Benjamin Gaignard, Laura Abbott, and others! + +NOTE: This patch only adds the default CMA heap. We will enable +selectively adding other CMA memory regions to the dmabuf heaps +interface with a later patch (which requires a dt binding) + +Cc: Laura Abbott <labbott@redhat.com> +Cc: Benjamin Gaignard <benjamin.gaignard@linaro.org> +Cc: Sumit Semwal <sumit.semwal@linaro.org> +Cc: Liam Mark <lmark@codeaurora.org> +Cc: Pratik Patel <pratikp@codeaurora.org> +Cc: Brian Starkey <Brian.Starkey@arm.com> +Cc: Vincent Donnefort <Vincent.Donnefort@arm.com> +Cc: Sudipto Paul <Sudipto.Paul@arm.com> +Cc: Andrew F. Davis <afd@ti.com> +Cc: Christoph Hellwig <hch@infradead.org> +Cc: Chenbo Feng <fengc@google.com> +Cc: Alistair Strachan <astrachan@google.com> +Cc: Hridya Valsaraju <hridya@google.com> +Cc: Sandeep Patil <sspatil@google.com> +Cc: Hillf Danton <hdanton@sina.com> +Cc: Dave Airlie <airlied@gmail.com> +Cc: dri-devel@lists.freedesktop.org +Reviewed-by: Benjamin Gaignard <benjamin.gaignard@linaro.org> +Reviewed-by: Brian Starkey <brian.starkey@arm.com> +Acked-by: Sandeep Patil <sspatil@android.com> +Acked-by: Laura Abbott <labbott@redhat.com> +Tested-by: Ayan Kumar Halder <ayan.halder@arm.com> +Signed-off-by: John Stultz <john.stultz@linaro.org> +Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org> +Link: https://patchwork.freedesktop.org/patch/msgid/20191203172641.66642-5-john.stultz@linaro.org +--- + drivers/dma-buf/heaps/Kconfig | 8 ++ + drivers/dma-buf/heaps/Makefile | 1 + + drivers/dma-buf/heaps/cma_heap.c | 177 +++++++++++++++++++++++++++++++ + 3 files changed, 186 insertions(+) + create mode 100644 drivers/dma-buf/heaps/cma_heap.c + +--- a/drivers/dma-buf/heaps/Kconfig ++++ b/drivers/dma-buf/heaps/Kconfig +@@ -4,3 +4,11 @@ config DMABUF_HEAPS_SYSTEM + help + Choose this option to enable the system dmabuf heap. The system heap + is backed by pages from the buddy allocator. If in doubt, say Y. ++ ++config DMABUF_HEAPS_CMA ++ bool "DMA-BUF CMA Heap" ++ depends on DMABUF_HEAPS && DMA_CMA ++ help ++ Choose this option to enable dma-buf CMA heap. This heap is backed ++ by the Contiguous Memory Allocator (CMA). If your system has these ++ regions, you should say Y here. +--- a/drivers/dma-buf/heaps/Makefile ++++ b/drivers/dma-buf/heaps/Makefile +@@ -1,3 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0 + obj-y += heap-helpers.o + obj-$(CONFIG_DMABUF_HEAPS_SYSTEM) += system_heap.o ++obj-$(CONFIG_DMABUF_HEAPS_CMA) += cma_heap.o +--- /dev/null ++++ b/drivers/dma-buf/heaps/cma_heap.c +@@ -0,0 +1,177 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * DMABUF CMA heap exporter ++ * ++ * Copyright (C) 2012, 2019 Linaro Ltd. ++ * Author: <benjamin.gaignard@linaro.org> for ST-Ericsson. ++ */ ++ ++#include <linux/cma.h> ++#include <linux/device.h> ++#include <linux/dma-buf.h> ++#include <linux/dma-heap.h> ++#include <linux/dma-contiguous.h> ++#include <linux/err.h> ++#include <linux/errno.h> ++#include <linux/highmem.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/scatterlist.h> ++#include <linux/sched/signal.h> ++ ++#include "heap-helpers.h" ++ ++struct cma_heap { ++ struct dma_heap *heap; ++ struct cma *cma; ++}; ++ ++static void cma_heap_free(struct heap_helper_buffer *buffer) ++{ ++ struct cma_heap *cma_heap = dma_heap_get_drvdata(buffer->heap); ++ unsigned long nr_pages = buffer->pagecount; ++ struct page *cma_pages = buffer->priv_virt; ++ ++ /* free page list */ ++ kfree(buffer->pages); ++ /* release memory */ ++ cma_release(cma_heap->cma, cma_pages, nr_pages); ++ kfree(buffer); ++} ++ ++/* dmabuf heap CMA operations functions */ ++static int cma_heap_allocate(struct dma_heap *heap, ++ unsigned long len, ++ unsigned long fd_flags, ++ unsigned long heap_flags) ++{ ++ struct cma_heap *cma_heap = dma_heap_get_drvdata(heap); ++ struct heap_helper_buffer *helper_buffer; ++ struct page *cma_pages; ++ size_t size = PAGE_ALIGN(len); ++ unsigned long nr_pages = size >> PAGE_SHIFT; ++ unsigned long align = get_order(size); ++ struct dma_buf *dmabuf; ++ int ret = -ENOMEM; ++ pgoff_t pg; ++ ++ if (align > CONFIG_CMA_ALIGNMENT) ++ align = CONFIG_CMA_ALIGNMENT; ++ ++ helper_buffer = kzalloc(sizeof(*helper_buffer), GFP_KERNEL); ++ if (!helper_buffer) ++ return -ENOMEM; ++ ++ init_heap_helper_buffer(helper_buffer, cma_heap_free); ++ helper_buffer->heap = heap; ++ helper_buffer->size = len; ++ ++ cma_pages = cma_alloc(cma_heap->cma, nr_pages, align, false); ++ if (!cma_pages) ++ goto free_buf; ++ ++ if (PageHighMem(cma_pages)) { ++ unsigned long nr_clear_pages = nr_pages; ++ struct page *page = cma_pages; ++ ++ while (nr_clear_pages > 0) { ++ void *vaddr = kmap_atomic(page); ++ ++ memset(vaddr, 0, PAGE_SIZE); ++ kunmap_atomic(vaddr); ++ /* ++ * Avoid wasting time zeroing memory if the process ++ * has been killed by by SIGKILL ++ */ ++ if (fatal_signal_pending(current)) ++ goto free_cma; ++ ++ page++; ++ nr_clear_pages--; ++ } ++ } else { ++ memset(page_address(cma_pages), 0, size); ++ } ++ ++ helper_buffer->pagecount = nr_pages; ++ helper_buffer->pages = kmalloc_array(helper_buffer->pagecount, ++ sizeof(*helper_buffer->pages), ++ GFP_KERNEL); ++ if (!helper_buffer->pages) { ++ ret = -ENOMEM; ++ goto free_cma; ++ } ++ ++ for (pg = 0; pg < helper_buffer->pagecount; pg++) ++ helper_buffer->pages[pg] = &cma_pages[pg]; ++ ++ /* create the dmabuf */ ++ dmabuf = heap_helper_export_dmabuf(helper_buffer, fd_flags); ++ if (IS_ERR(dmabuf)) { ++ ret = PTR_ERR(dmabuf); ++ goto free_pages; ++ } ++ ++ helper_buffer->dmabuf = dmabuf; ++ helper_buffer->priv_virt = cma_pages; ++ ++ ret = dma_buf_fd(dmabuf, fd_flags); ++ if (ret < 0) { ++ dma_buf_put(dmabuf); ++ /* just return, as put will call release and that will free */ ++ return ret; ++ } ++ ++ return ret; ++ ++free_pages: ++ kfree(helper_buffer->pages); ++free_cma: ++ cma_release(cma_heap->cma, cma_pages, nr_pages); ++free_buf: ++ kfree(helper_buffer); ++ return ret; ++} ++ ++static const struct dma_heap_ops cma_heap_ops = { ++ .allocate = cma_heap_allocate, ++}; ++ ++static int __add_cma_heap(struct cma *cma, void *data) ++{ ++ struct cma_heap *cma_heap; ++ struct dma_heap_export_info exp_info; ++ ++ cma_heap = kzalloc(sizeof(*cma_heap), GFP_KERNEL); ++ if (!cma_heap) ++ return -ENOMEM; ++ cma_heap->cma = cma; ++ ++ exp_info.name = cma_get_name(cma); ++ exp_info.ops = &cma_heap_ops; ++ exp_info.priv = cma_heap; ++ ++ cma_heap->heap = dma_heap_add(&exp_info); ++ if (IS_ERR(cma_heap->heap)) { ++ int ret = PTR_ERR(cma_heap->heap); ++ ++ kfree(cma_heap); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int add_default_cma_heap(void) ++{ ++ struct cma *default_cma = dev_get_cma_area(NULL); ++ int ret = 0; ++ ++ if (default_cma) ++ ret = __add_cma_heap(default_cma, NULL); ++ ++ return ret; ++} ++module_init(add_default_cma_heap); ++MODULE_DESCRIPTION("DMA-BUF CMA Heap"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/bcm27xx/patches-5.4/950-0736-kselftests-Add-dma-heap-test.patch b/target/linux/bcm27xx/patches-5.4/950-0736-kselftests-Add-dma-heap-test.patch new file mode 100644 index 0000000000..ff4dda9a63 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0736-kselftests-Add-dma-heap-test.patch @@ -0,0 +1,453 @@ +From 31501b71a0237f3753d0210e3e122c548d6f3051 Mon Sep 17 00:00:00 2001 +From: John Stultz <john.stultz@linaro.org> +Date: Tue, 3 Dec 2019 17:26:41 +0000 +Subject: [PATCH] kselftests: Add dma-heap test + +Commit a8779927fd86c91f5400bfcbccfa018a667d8350 upstream. + +Add very trivial allocation and import test for dma-heaps, +utilizing the vgem driver as a test importer. + +A good chunk of this code taken from: + tools/testing/selftests/android/ion/ionmap_test.c + Originally by Laura Abbott <labbott@redhat.com> + +Cc: Benjamin Gaignard <benjamin.gaignard@linaro.org> +Cc: Sumit Semwal <sumit.semwal@linaro.org> +Cc: Liam Mark <lmark@codeaurora.org> +Cc: Pratik Patel <pratikp@codeaurora.org> +Cc: Brian Starkey <Brian.Starkey@arm.com> +Cc: Vincent Donnefort <Vincent.Donnefort@arm.com> +Cc: Sudipto Paul <Sudipto.Paul@arm.com> +Cc: Andrew F. Davis <afd@ti.com> +Cc: Christoph Hellwig <hch@infradead.org> +Cc: Chenbo Feng <fengc@google.com> +Cc: Alistair Strachan <astrachan@google.com> +Cc: Hridya Valsaraju <hridya@google.com> +Cc: Sandeep Patil <sspatil@google.com> +Cc: Hillf Danton <hdanton@sina.com> +Cc: Dave Airlie <airlied@gmail.com> +Cc: dri-devel@lists.freedesktop.org +Reviewed-by: Benjamin Gaignard <benjamin.gaignard@linaro.org> +Reviewed-by: Brian Starkey <brian.starkey@arm.com> +Acked-by: Sandeep Patil <sspatil@android.com> +Acked-by: Laura Abbott <labbott@redhat.com> +Tested-by: Ayan Kumar Halder <ayan.halder@arm.com> +Signed-off-by: John Stultz <john.stultz@linaro.org> +Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org> +Link: https://patchwork.freedesktop.org/patch/msgid/20191203172641.66642-6-john.stultz@linaro.org +--- + tools/testing/selftests/dmabuf-heaps/Makefile | 6 + + .../selftests/dmabuf-heaps/dmabuf-heap.c | 396 ++++++++++++++++++ + 2 files changed, 402 insertions(+) + create mode 100644 tools/testing/selftests/dmabuf-heaps/Makefile + create mode 100644 tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c + +--- /dev/null ++++ b/tools/testing/selftests/dmabuf-heaps/Makefile +@@ -0,0 +1,6 @@ ++# SPDX-License-Identifier: GPL-2.0 ++CFLAGS += -static -O3 -Wl,-no-as-needed -Wall -I../../../../usr/include ++ ++TEST_GEN_PROGS = dmabuf-heap ++ ++include ../lib.mk +--- /dev/null ++++ b/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c +@@ -0,0 +1,396 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#include <dirent.h> ++#include <errno.h> ++#include <fcntl.h> ++#include <stdio.h> ++#include <stdlib.h> ++#include <stdint.h> ++#include <string.h> ++#include <unistd.h> ++#include <sys/ioctl.h> ++#include <sys/mman.h> ++#include <sys/types.h> ++ ++#include <linux/dma-buf.h> ++#include <drm/drm.h> ++ ++#include "../../../../include/uapi/linux/dma-heap.h" ++ ++#define DEVPATH "/dev/dma_heap" ++ ++static int check_vgem(int fd) ++{ ++ drm_version_t version = { 0 }; ++ char name[5]; ++ int ret; ++ ++ version.name_len = 4; ++ version.name = name; ++ ++ ret = ioctl(fd, DRM_IOCTL_VERSION, &version); ++ if (ret) ++ return 0; ++ ++ return !strcmp(name, "vgem"); ++} ++ ++static int open_vgem(void) ++{ ++ int i, fd; ++ const char *drmstr = "/dev/dri/card"; ++ ++ fd = -1; ++ for (i = 0; i < 16; i++) { ++ char name[80]; ++ ++ snprintf(name, 80, "%s%u", drmstr, i); ++ ++ fd = open(name, O_RDWR); ++ if (fd < 0) ++ continue; ++ ++ if (!check_vgem(fd)) { ++ close(fd); ++ fd = -1; ++ continue; ++ } else { ++ break; ++ } ++ } ++ return fd; ++} ++ ++static int import_vgem_fd(int vgem_fd, int dma_buf_fd, uint32_t *handle) ++{ ++ struct drm_prime_handle import_handle = { ++ .fd = dma_buf_fd, ++ .flags = 0, ++ .handle = 0, ++ }; ++ int ret; ++ ++ ret = ioctl(vgem_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &import_handle); ++ if (ret == 0) ++ *handle = import_handle.handle; ++ return ret; ++} ++ ++static void close_handle(int vgem_fd, uint32_t handle) ++{ ++ struct drm_gem_close close = { ++ .handle = handle, ++ }; ++ ++ ioctl(vgem_fd, DRM_IOCTL_GEM_CLOSE, &close); ++} ++ ++static int dmabuf_heap_open(char *name) ++{ ++ int ret, fd; ++ char buf[256]; ++ ++ ret = snprintf(buf, 256, "%s/%s", DEVPATH, name); ++ if (ret < 0) { ++ printf("snprintf failed!\n"); ++ return ret; ++ } ++ ++ fd = open(buf, O_RDWR); ++ if (fd < 0) ++ printf("open %s failed!\n", buf); ++ return fd; ++} ++ ++static int dmabuf_heap_alloc_fdflags(int fd, size_t len, unsigned int fd_flags, ++ unsigned int heap_flags, int *dmabuf_fd) ++{ ++ struct dma_heap_allocation_data data = { ++ .len = len, ++ .fd = 0, ++ .fd_flags = fd_flags, ++ .heap_flags = heap_flags, ++ }; ++ int ret; ++ ++ if (!dmabuf_fd) ++ return -EINVAL; ++ ++ ret = ioctl(fd, DMA_HEAP_IOC_ALLOC, &data); ++ if (ret < 0) ++ return ret; ++ *dmabuf_fd = (int)data.fd; ++ return ret; ++} ++ ++static int dmabuf_heap_alloc(int fd, size_t len, unsigned int flags, ++ int *dmabuf_fd) ++{ ++ return dmabuf_heap_alloc_fdflags(fd, len, O_RDWR | O_CLOEXEC, flags, ++ dmabuf_fd); ++} ++ ++static void dmabuf_sync(int fd, int start_stop) ++{ ++ struct dma_buf_sync sync = { ++ .flags = start_stop | DMA_BUF_SYNC_RW, ++ }; ++ int ret; ++ ++ ret = ioctl(fd, DMA_BUF_IOCTL_SYNC, &sync); ++ if (ret) ++ printf("sync failed %d\n", errno); ++} ++ ++#define ONE_MEG (1024 * 1024) ++ ++static int test_alloc_and_import(char *heap_name) ++{ ++ int heap_fd = -1, dmabuf_fd = -1, importer_fd = -1; ++ uint32_t handle = 0; ++ void *p = NULL; ++ int ret; ++ ++ printf("Testing heap: %s\n", heap_name); ++ ++ heap_fd = dmabuf_heap_open(heap_name); ++ if (heap_fd < 0) ++ return -1; ++ ++ printf("Allocating 1 MEG\n"); ++ ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0, &dmabuf_fd); ++ if (ret) { ++ printf("Allocation Failed!\n"); ++ ret = -1; ++ goto out; ++ } ++ /* mmap and write a simple pattern */ ++ p = mmap(NULL, ++ ONE_MEG, ++ PROT_READ | PROT_WRITE, ++ MAP_SHARED, ++ dmabuf_fd, ++ 0); ++ if (p == MAP_FAILED) { ++ printf("mmap() failed: %m\n"); ++ ret = -1; ++ goto out; ++ } ++ printf("mmap passed\n"); ++ ++ dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START); ++ memset(p, 1, ONE_MEG / 2); ++ memset((char *)p + ONE_MEG / 2, 0, ONE_MEG / 2); ++ dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END); ++ ++ importer_fd = open_vgem(); ++ if (importer_fd < 0) { ++ ret = importer_fd; ++ printf("Failed to open vgem\n"); ++ goto out; ++ } ++ ++ ret = import_vgem_fd(importer_fd, dmabuf_fd, &handle); ++ if (ret < 0) { ++ printf("Failed to import buffer\n"); ++ goto out; ++ } ++ printf("import passed\n"); ++ ++ dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START); ++ memset(p, 0xff, ONE_MEG); ++ dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END); ++ printf("syncs passed\n"); ++ ++ close_handle(importer_fd, handle); ++ ret = 0; ++ ++out: ++ if (p) ++ munmap(p, ONE_MEG); ++ if (importer_fd >= 0) ++ close(importer_fd); ++ if (dmabuf_fd >= 0) ++ close(dmabuf_fd); ++ if (heap_fd >= 0) ++ close(heap_fd); ++ ++ return ret; ++} ++ ++/* Test the ioctl version compatibility w/ a smaller structure then expected */ ++static int dmabuf_heap_alloc_older(int fd, size_t len, unsigned int flags, ++ int *dmabuf_fd) ++{ ++ int ret; ++ unsigned int older_alloc_ioctl; ++ struct dma_heap_allocation_data_smaller { ++ __u64 len; ++ __u32 fd; ++ __u32 fd_flags; ++ } data = { ++ .len = len, ++ .fd = 0, ++ .fd_flags = O_RDWR | O_CLOEXEC, ++ }; ++ ++ older_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0, ++ struct dma_heap_allocation_data_smaller); ++ if (!dmabuf_fd) ++ return -EINVAL; ++ ++ ret = ioctl(fd, older_alloc_ioctl, &data); ++ if (ret < 0) ++ return ret; ++ *dmabuf_fd = (int)data.fd; ++ return ret; ++} ++ ++/* Test the ioctl version compatibility w/ a larger structure then expected */ ++static int dmabuf_heap_alloc_newer(int fd, size_t len, unsigned int flags, ++ int *dmabuf_fd) ++{ ++ int ret; ++ unsigned int newer_alloc_ioctl; ++ struct dma_heap_allocation_data_bigger { ++ __u64 len; ++ __u32 fd; ++ __u32 fd_flags; ++ __u64 heap_flags; ++ __u64 garbage1; ++ __u64 garbage2; ++ __u64 garbage3; ++ } data = { ++ .len = len, ++ .fd = 0, ++ .fd_flags = O_RDWR | O_CLOEXEC, ++ .heap_flags = flags, ++ .garbage1 = 0xffffffff, ++ .garbage2 = 0x88888888, ++ .garbage3 = 0x11111111, ++ }; ++ ++ newer_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0, ++ struct dma_heap_allocation_data_bigger); ++ if (!dmabuf_fd) ++ return -EINVAL; ++ ++ ret = ioctl(fd, newer_alloc_ioctl, &data); ++ if (ret < 0) ++ return ret; ++ ++ *dmabuf_fd = (int)data.fd; ++ return ret; ++} ++ ++static int test_alloc_compat(char *heap_name) ++{ ++ int heap_fd = -1, dmabuf_fd = -1; ++ int ret; ++ ++ heap_fd = dmabuf_heap_open(heap_name); ++ if (heap_fd < 0) ++ return -1; ++ ++ printf("Testing (theoretical)older alloc compat\n"); ++ ret = dmabuf_heap_alloc_older(heap_fd, ONE_MEG, 0, &dmabuf_fd); ++ if (ret) { ++ printf("Older compat allocation failed!\n"); ++ ret = -1; ++ goto out; ++ } ++ close(dmabuf_fd); ++ ++ printf("Testing (theoretical)newer alloc compat\n"); ++ ret = dmabuf_heap_alloc_newer(heap_fd, ONE_MEG, 0, &dmabuf_fd); ++ if (ret) { ++ printf("Newer compat allocation failed!\n"); ++ ret = -1; ++ goto out; ++ } ++ printf("Ioctl compatibility tests passed\n"); ++out: ++ if (dmabuf_fd >= 0) ++ close(dmabuf_fd); ++ if (heap_fd >= 0) ++ close(heap_fd); ++ ++ return ret; ++} ++ ++static int test_alloc_errors(char *heap_name) ++{ ++ int heap_fd = -1, dmabuf_fd = -1; ++ int ret; ++ ++ heap_fd = dmabuf_heap_open(heap_name); ++ if (heap_fd < 0) ++ return -1; ++ ++ printf("Testing expected error cases\n"); ++ ret = dmabuf_heap_alloc(0, ONE_MEG, 0x111111, &dmabuf_fd); ++ if (!ret) { ++ printf("Did not see expected error (invalid fd)!\n"); ++ ret = -1; ++ goto out; ++ } ++ ++ ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0x111111, &dmabuf_fd); ++ if (!ret) { ++ printf("Did not see expected error (invalid heap flags)!\n"); ++ ret = -1; ++ goto out; ++ } ++ ++ ret = dmabuf_heap_alloc_fdflags(heap_fd, ONE_MEG, ++ ~(O_RDWR | O_CLOEXEC), 0, &dmabuf_fd); ++ if (!ret) { ++ printf("Did not see expected error (invalid fd flags)!\n"); ++ ret = -1; ++ goto out; ++ } ++ ++ printf("Expected error checking passed\n"); ++out: ++ if (dmabuf_fd >= 0) ++ close(dmabuf_fd); ++ if (heap_fd >= 0) ++ close(heap_fd); ++ ++ return ret; ++} ++ ++int main(void) ++{ ++ DIR *d; ++ struct dirent *dir; ++ int ret = -1; ++ ++ d = opendir(DEVPATH); ++ if (!d) { ++ printf("No %s directory?\n", DEVPATH); ++ return -1; ++ } ++ ++ while ((dir = readdir(d)) != NULL) { ++ if (!strncmp(dir->d_name, ".", 2)) ++ continue; ++ if (!strncmp(dir->d_name, "..", 3)) ++ continue; ++ ++ ret = test_alloc_and_import(dir->d_name); ++ if (ret) ++ break; ++ ++ ret = test_alloc_compat(dir->d_name); ++ if (ret) ++ break; ++ ++ ret = test_alloc_errors(dir->d_name); ++ if (ret) ++ break; ++ } ++ closedir(d); ++ ++ return ret; ++} diff --git a/target/linux/bcm27xx/patches-5.4/950-0737-dma-buf-heaps-Use-_IOCTL_-for-userspace-IOCTL-identi.patch b/target/linux/bcm27xx/patches-5.4/950-0737-dma-buf-heaps-Use-_IOCTL_-for-userspace-IOCTL-identi.patch new file mode 100644 index 0000000000..a949831525 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0737-dma-buf-heaps-Use-_IOCTL_-for-userspace-IOCTL-identi.patch @@ -0,0 +1,69 @@ +From 8153056fa1d45394057017843070d3a366dbd918 Mon Sep 17 00:00:00 2001 +From: "Andrew F. Davis" <afd@ti.com> +Date: Mon, 16 Dec 2019 08:34:04 -0500 +Subject: [PATCH] dma-buf: heaps: Use _IOCTL_ for userspace IOCTL + identifier + +Commit b3b4346544b571c96d46be615b9db69a601ce4c8 upstream. + +This is more consistent with the DMA and DRM frameworks convention. This +patch is only a name change, no logic is changed. + +Signed-off-by: Andrew F. Davis <afd@ti.com> +Acked-by: John Stultz <john.stultz@linaro.org> +Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org> +Link: https://patchwork.freedesktop.org/patch/msgid/20191216133405.1001-2-afd@ti.com +--- + drivers/dma-buf/dma-heap.c | 4 ++-- + include/uapi/linux/dma-heap.h | 4 ++-- + tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c | 2 +- + 3 files changed, 5 insertions(+), 5 deletions(-) + +--- a/drivers/dma-buf/dma-heap.c ++++ b/drivers/dma-buf/dma-heap.c +@@ -107,7 +107,7 @@ static long dma_heap_ioctl_allocate(stru + } + + unsigned int dma_heap_ioctl_cmds[] = { +- DMA_HEAP_IOC_ALLOC, ++ DMA_HEAP_IOCTL_ALLOC, + }; + + static long dma_heap_ioctl(struct file *file, unsigned int ucmd, +@@ -153,7 +153,7 @@ static long dma_heap_ioctl(struct file * + memset(kdata + in_size, 0, ksize - in_size); + + switch (kcmd) { +- case DMA_HEAP_IOC_ALLOC: ++ case DMA_HEAP_IOCTL_ALLOC: + ret = dma_heap_ioctl_allocate(file, kdata); + break; + default: +--- a/include/uapi/linux/dma-heap.h ++++ b/include/uapi/linux/dma-heap.h +@@ -42,12 +42,12 @@ struct dma_heap_allocation_data { + #define DMA_HEAP_IOC_MAGIC 'H' + + /** +- * DOC: DMA_HEAP_IOC_ALLOC - allocate memory from pool ++ * DOC: DMA_HEAP_IOCTL_ALLOC - allocate memory from pool + * + * Takes a dma_heap_allocation_data struct and returns it with the fd field + * populated with the dmabuf handle of the allocation. + */ +-#define DMA_HEAP_IOC_ALLOC _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,\ ++#define DMA_HEAP_IOCTL_ALLOC _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,\ + struct dma_heap_allocation_data) + + #endif /* _UAPI_LINUX_DMABUF_POOL_H */ +--- a/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c ++++ b/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c +@@ -116,7 +116,7 @@ static int dmabuf_heap_alloc_fdflags(int + if (!dmabuf_fd) + return -EINVAL; + +- ret = ioctl(fd, DMA_HEAP_IOC_ALLOC, &data); ++ ret = ioctl(fd, DMA_HEAP_IOCTL_ALLOC, &data); + if (ret < 0) + return ret; + *dmabuf_fd = (int)data.fd; diff --git a/target/linux/bcm27xx/patches-5.4/950-0738-dma-buf-heaps-Remove-redundant-heap-identifier-from-.patch b/target/linux/bcm27xx/patches-5.4/950-0738-dma-buf-heaps-Remove-redundant-heap-identifier-from-.patch new file mode 100644 index 0000000000..288f468a78 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0738-dma-buf-heaps-Remove-redundant-heap-identifier-from-.patch @@ -0,0 +1,28 @@ +From 0b5efcbb99c7b36f84cf8f8f4b582d88ccb9cc35 Mon Sep 17 00:00:00 2001 +From: "Andrew F. Davis" <afd@ti.com> +Date: Mon, 16 Dec 2019 08:34:05 -0500 +Subject: [PATCH] dma-buf: heaps: Remove redundant heap identifier from + system heap name + +The heaps are already in a directory of heaps, adding _heap to a heap +name is redundant. This patch is only a name change, no logic is changed. + +Signed-off-by: Andrew F. Davis <afd@ti.com> +Acked-by: John Stultz <john.stultz@linaro.org> +Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org> +Link: https://patchwork.freedesktop.org/patch/msgid/20191216133405.1001-3-afd@ti.com +--- + drivers/dma-buf/heaps/system_heap.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/dma-buf/heaps/system_heap.c ++++ b/drivers/dma-buf/heaps/system_heap.c +@@ -109,7 +109,7 @@ static int system_heap_create(void) + struct dma_heap_export_info exp_info; + int ret = 0; + +- exp_info.name = "system_heap"; ++ exp_info.name = "system"; + exp_info.ops = &system_heap_ops; + exp_info.priv = NULL; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0739-dma-buf-fix-resource-leak-on-ENOTTY-error-return-pat.patch b/target/linux/bcm27xx/patches-5.4/950-0739-dma-buf-fix-resource-leak-on-ENOTTY-error-return-pat.patch new file mode 100644 index 0000000000..89b66956e0 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0739-dma-buf-fix-resource-leak-on-ENOTTY-error-return-pat.patch @@ -0,0 +1,34 @@ +From 0960432c8261efb05bcf5ba6d8fe13c8293086a9 Mon Sep 17 00:00:00 2001 +From: Colin Ian King <colin.king@canonical.com> +Date: Mon, 16 Dec 2019 16:10:59 +0000 +Subject: [PATCH] dma-buf: fix resource leak on -ENOTTY error return + path + +Commit f9d3b2c600075d1f79efcd5cdb1718c2f554c0f9 upstream. + +The -ENOTTY error return path does not free the allocated +kdata as it returns directly. Fix this by returning via the +error handling label err. + +Addresses-Coverity: ("Resource leak") +Fixes: c02a81fba74f ("dma-buf: Add dma-buf heaps framework") +Signed-off-by: Colin Ian King <colin.king@canonical.com> +Acked-by: John Stultz <john.stultz@linaro.org> +Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org> +Link: https://patchwork.freedesktop.org/patch/msgid/20191216161059.269492-1-colin.king@canonical.com +--- + drivers/dma-buf/dma-heap.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/dma-buf/dma-heap.c ++++ b/drivers/dma-buf/dma-heap.c +@@ -157,7 +157,8 @@ static long dma_heap_ioctl(struct file * + ret = dma_heap_ioctl_allocate(file, kdata); + break; + default: +- return -ENOTTY; ++ ret = -ENOTTY; ++ goto err; + } + + if (copy_to_user((void __user *)arg, kdata, out_size) != 0) diff --git a/target/linux/bcm27xx/patches-5.4/950-0740-dma-heap-Make-the-symbol-dma_heap_ioctl_cmds-static.patch b/target/linux/bcm27xx/patches-5.4/950-0740-dma-heap-Make-the-symbol-dma_heap_ioctl_cmds-static.patch new file mode 100644 index 0000000000..900637ddeb --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0740-dma-heap-Make-the-symbol-dma_heap_ioctl_cmds-static.patch @@ -0,0 +1,34 @@ +From d59927ce7f8a4ee9abcad9bd3405881c7a9ac99e Mon Sep 17 00:00:00 2001 +From: zhong jiang <zhongjiang@huawei.com> +Date: Wed, 18 Dec 2019 00:38:22 +0530 +Subject: [PATCH] dma-heap: Make the symbol 'dma_heap_ioctl_cmds' + static + +Commit 7d411afe8444060454a53b1f9b70ee78b3e75ef1 upstream. + +Fix the following sparse warning. + +drivers/dma-buf/dma-heap.c:109:14: warning: symbol 'dma_heap_ioctl_cmds' +was not declared. Should it be static? + +Acked-by: Andrew F. Davis <afd@ti.com> +Acked-by: John Stultz <john.stultz@linaro.org> +Signed-off-by: zhong jiang <zhongjiang@huawei.com> +Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org> + [sumits: rebased over IOCTL rename patches] +Link: https://patchwork.freedesktop.org/patch/msgid/20191217190822.1969-1-sumit.semwal@linaro.org +--- + drivers/dma-buf/dma-heap.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/dma-buf/dma-heap.c ++++ b/drivers/dma-buf/dma-heap.c +@@ -106,7 +106,7 @@ static long dma_heap_ioctl_allocate(stru + return 0; + } + +-unsigned int dma_heap_ioctl_cmds[] = { ++static unsigned int dma_heap_ioctl_cmds[] = { + DMA_HEAP_IOCTL_ALLOC, + }; + diff --git a/target/linux/bcm27xx/patches-5.4/950-0741-ARM-dts-Enable-firmware-clocks-on-all-Pis.patch b/target/linux/bcm27xx/patches-5.4/950-0741-ARM-dts-Enable-firmware-clocks-on-all-Pis.patch new file mode 100644 index 0000000000..f319bdca9f --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0741-ARM-dts-Enable-firmware-clocks-on-all-Pis.patch @@ -0,0 +1,25 @@ +From e2882043cba45fd9f027dfc102aaaaf1208a65d0 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Wed, 6 May 2020 17:02:26 +0100 +Subject: [PATCH] ARM: dts: Enable firmware-clocks on all Pis + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/bcm270x.dtsi | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/arch/arm/boot/dts/bcm270x.dtsi ++++ b/arch/arm/boot/dts/bcm270x.dtsi +@@ -7,6 +7,12 @@ + /delete-property/ stdout-path; + }; + ++ firmware_clocks: firmware-clocks { ++ compatible = "raspberrypi,firmware-clocks"; ++ raspberrypi,firmware = <&firmware>; ++ #clock-cells = <1>; ++ }; ++ + soc: soc { + + watchdog: watchdog@7e100000 { diff --git a/target/linux/bcm27xx/patches-5.4/950-0742-media-bcm2835-unicam-Always-service-interrupts.patch b/target/linux/bcm27xx/patches-5.4/950-0742-media-bcm2835-unicam-Always-service-interrupts.patch new file mode 100644 index 0000000000..378824c495 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0742-media-bcm2835-unicam-Always-service-interrupts.patch @@ -0,0 +1,51 @@ +From b52dee833768d1cb3572bf8269baeda077d0e41b Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 13 May 2020 18:28:27 +0100 +Subject: [PATCH] media: bcm2835-unicam: Always service interrupts + +From when bringing up the driver, there was a check in the isr +to ignore interrupts (claiming them handled) should the driver +not be streaming. + +The VPU now will not register a camera driver if it finds a +CSI2 node enabled in device tree, therefore this flawed check is +redundant. + +https://github.com/raspberrypi/linux/issues/3602 + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/media/platform/bcm2835/bcm2835-unicam.c | 15 --------------- + 1 file changed, 15 deletions(-) + +--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c ++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c +@@ -766,12 +766,6 @@ static int unicam_all_nodes_streaming(st + return ret; + } + +-static int unicam_all_nodes_disabled(struct unicam_device *dev) +-{ +- return !dev->node[IMAGE_PAD].streaming && +- !dev->node[METADATA_PAD].streaming; +-} +- + static void unicam_queue_event_sof(struct unicam_device *unicam) + { + struct v4l2_event event = { +@@ -801,15 +795,6 @@ static irqreturn_t unicam_isr(int irq, v + u64 ts; + int i; + +- /* +- * Don't service interrupts if not streaming. +- * Avoids issues if the VPU should enable the +- * peripheral without the kernel knowing (that +- * shouldn't happen, but causes issues if it does). +- */ +- if (unicam_all_nodes_disabled(unicam)) +- return IRQ_HANDLED; +- + sta = reg_read(cfg, UNICAM_STA); + /* Write value back to clear the interrupts */ + reg_write(cfg, UNICAM_STA, sta); diff --git a/target/linux/bcm27xx/patches-5.4/950-0743-sc16is7xx-Fix-for-hardware-flow-control.patch b/target/linux/bcm27xx/patches-5.4/950-0743-sc16is7xx-Fix-for-hardware-flow-control.patch new file mode 100644 index 0000000000..3ff77bcea9 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0743-sc16is7xx-Fix-for-hardware-flow-control.patch @@ -0,0 +1,70 @@ +From 41ed4262b7398a3170399af81cf78cb8d7dd8b8d Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Wed, 13 May 2020 20:10:15 +0100 +Subject: [PATCH] sc16is7xx: Fix for hardware flow control + +The SC16IS7XX hardware flow control is mishandled by the driver in +a number of ways: + + 1. The set_baud method accidentally clears it when setting EFR bit. + 2. Even though hardware flow control is enabled, it isn't indicated + back to the serial framework. + 3. Applying the flow control clears the EFR bit. + 4. The CTS support is not indicated in the return from + sc16is7xx_get_mctrl. + +Address all of those issues using a mixture of patches found on the +linked pages. + +See: https://github.com/raspberrypi/linux/issues/2542 +See: https://www.spinics.net/lists/linux-serial/msg21794.html + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + drivers/tty/serial/sc16is7xx.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +--- a/drivers/tty/serial/sc16is7xx.c ++++ b/drivers/tty/serial/sc16is7xx.c +@@ -523,8 +523,9 @@ static int sc16is7xx_set_baud(struct uar + + /* Enable enhanced features */ + regcache_cache_bypass(s->regmap, true); +- sc16is7xx_port_write(port, SC16IS7XX_EFR_REG, +- SC16IS7XX_EFR_ENABLE_BIT); ++ sc16is7xx_port_update(port, SC16IS7XX_EFR_REG, ++ SC16IS7XX_EFR_ENABLE_BIT, ++ SC16IS7XX_EFR_ENABLE_BIT); + regcache_cache_bypass(s->regmap, false); + + /* Put LCR back to the normal mode */ +@@ -846,7 +847,7 @@ static unsigned int sc16is7xx_get_mctrl( + /* DCD and DSR are not wired and CTS/RTS is handled automatically + * so just indicate DSR and CAR asserted + */ +- return TIOCM_DSR | TIOCM_CAR; ++ return TIOCM_DSR | TIOCM_CAR | TIOCM_RI | TIOCM_CTS; + } + + static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl) +@@ -933,14 +934,19 @@ static void sc16is7xx_set_termios(struct + regcache_cache_bypass(s->regmap, true); + sc16is7xx_port_write(port, SC16IS7XX_XON1_REG, termios->c_cc[VSTART]); + sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]); +- if (termios->c_cflag & CRTSCTS) ++ if (termios->c_cflag & CRTSCTS) { + flow |= SC16IS7XX_EFR_AUTOCTS_BIT | + SC16IS7XX_EFR_AUTORTS_BIT; ++ port->status |= UPSTAT_AUTOCTS; ++ }; + if (termios->c_iflag & IXON) + flow |= SC16IS7XX_EFR_SWFLOW3_BIT; + if (termios->c_iflag & IXOFF) + flow |= SC16IS7XX_EFR_SWFLOW1_BIT; + ++ /* Always set enable enhanced */ ++ flow |= SC16IS7XX_EFR_ENABLE_BIT; ++ + sc16is7xx_port_write(port, SC16IS7XX_EFR_REG, flow); + regcache_cache_bypass(s->regmap, false); + diff --git a/target/linux/bcm27xx/patches-5.4/950-0744-drm-vc4-Fix-VIC-usage-with-Broadcast-RGB.patch b/target/linux/bcm27xx/patches-5.4/950-0744-drm-vc4-Fix-VIC-usage-with-Broadcast-RGB.patch new file mode 100644 index 0000000000..611e5db743 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0744-drm-vc4-Fix-VIC-usage-with-Broadcast-RGB.patch @@ -0,0 +1,58 @@ +From a90dcdf7cf7ad632f5a260758a76e406403a7e3c Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Thu, 14 May 2020 14:44:15 +0100 +Subject: [PATCH] drm/vc4: Fix VIC usage with Broadcast RGB + +Adding the Broadcast RGB range selection broke the VIC +field of the AVI infoframes on HDMI, zeroing them for all +modes on an HDMI monitor. + +Correct this so that it is only zeroed if the range is +contrary to the standard range of the mode. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/gpu/drm/vc4/vc4_firmware_kms.c | 19 ++++++++++++------- + 1 file changed, 12 insertions(+), 7 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c ++++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c +@@ -936,19 +936,14 @@ static void vc4_crtc_mode_set_nofb(struc + break; + } + ++ mb.timings.video_id_code = frame.avi.video_code; ++ + if (!vc4_encoder->hdmi_monitor) { + mb.timings.flags |= TIMINGS_FLAGS_DVI; +- mb.timings.video_id_code = frame.avi.video_code; + } else { + struct vc4_fkms_connector_state *conn_state = + to_vc4_fkms_connector_state(vc4_crtc->connector->state); + +- /* Do not provide a VIC as the HDMI spec requires that we do not +- * signal the opposite of the defined range in the AVI +- * infoframe. +- */ +- mb.timings.video_id_code = 0; +- + if (conn_state->broadcast_rgb == VC4_BROADCAST_RGB_AUTO) { + /* See CEA-861-E - 5.1 Default Encoding Parameters */ + if (drm_default_rgb_quant_range(mode) == +@@ -958,6 +953,16 @@ static void vc4_crtc_mode_set_nofb(struc + if (conn_state->broadcast_rgb == + VC4_BROADCAST_RGB_LIMITED) + mb.timings.flags |= TIMINGS_FLAGS_RGB_LIMITED; ++ ++ /* If not using the default range, then do not provide ++ * a VIC as the HDMI spec requires that we do not ++ * signal the opposite of the defined range in the AVI ++ * infoframe. ++ */ ++ if (!!(mb.timings.flags & TIMINGS_FLAGS_RGB_LIMITED) != ++ (drm_default_rgb_quant_range(mode) == ++ HDMI_QUANTIZATION_RANGE_LIMITED)) ++ mb.timings.video_id_code = 0; + } + } + diff --git a/target/linux/bcm27xx/patches-5.4/950-0745-staging-vc04_services-mmal-vchiq-Update-parameters-l.patch b/target/linux/bcm27xx/patches-5.4/950-0745-staging-vc04_services-mmal-vchiq-Update-parameters-l.patch new file mode 100644 index 0000000000..fd878ef737 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0745-staging-vc04_services-mmal-vchiq-Update-parameters-l.patch @@ -0,0 +1,28 @@ +From d121fed62ecf09a60c98fb5e8cb54596a1794622 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Fri, 15 May 2020 13:42:10 +0100 +Subject: [PATCH] staging: vc04_services: mmal-vchiq: Update parameters + list + +Adds in a couple of new MMAL parameter defines. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h ++++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h +@@ -668,6 +668,12 @@ enum mmal_parameter_video_type { + + /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ + MMAL_PARAMETER_VIDEO_DROPPABLE_PFRAME_LENGTH, ++ ++ /**< Take a @ref MMAL_PARAMETER_VIDEO_STALL_T */ ++ MMAL_PARAMETER_VIDEO_STALL_THRESHOLD, ++ ++ /**< Take a @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_HEADERS_WITH_FRAME, + }; + + /** Valid mirror modes */ diff --git a/target/linux/bcm27xx/patches-5.4/950-0746-staging-vc04_services-bcm2835-codec-Request-headers-.patch b/target/linux/bcm27xx/patches-5.4/950-0746-staging-vc04_services-bcm2835-codec-Request-headers-.patch new file mode 100644 index 0000000000..7698f822d3 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0746-staging-vc04_services-bcm2835-codec-Request-headers-.patch @@ -0,0 +1,29 @@ +From 2822134c5f5c03893706df64625f3390792bb68a Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Fri, 15 May 2020 13:43:08 +0100 +Subject: [PATCH] staging:vc04_services: bcm2835-codec: Request headers + with I-frame + +V4L2 wishes to have the codec header bytes in the same buffer as the +first encoded frame, so it does become 1-in 1-out for encoding. +The firmware now has an option to do this, so enable it. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + .../staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c ++++ b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c +@@ -1967,6 +1967,11 @@ static int bcm2835_codec_create_componen + MMAL_PARAMETER_VIDEO_ENCODE_SPS_TIMING, + ¶m, sizeof(param)); + ++ /* Enable inserting headers into the first frame */ ++ vchiq_mmal_port_parameter_set(ctx->dev->instance, ++ &ctx->component->control, ++ MMAL_PARAMETER_VIDEO_ENCODE_HEADERS_WITH_FRAME, ++ ¶m, sizeof(param)); + } else { + if (ctx->q_data[V4L2_M2M_DST].sizeimage < + ctx->component->output[0].minimum_buffer.size) diff --git a/target/linux/bcm27xx/patches-5.4/950-0747-staging-vc04_services-bcm2835-codec-Avoid-fragmentin.patch b/target/linux/bcm27xx/patches-5.4/950-0747-staging-vc04_services-bcm2835-codec-Avoid-fragmentin.patch new file mode 100644 index 0000000000..da273ca65b --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0747-staging-vc04_services-bcm2835-codec-Avoid-fragmentin.patch @@ -0,0 +1,32 @@ +From 39956d31d19658af7a0759e05366d4bc1c04a50a Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Fri, 15 May 2020 13:47:13 +0100 +Subject: [PATCH] staging:vc04_services: bcm2835-codec: Avoid + fragmenting buffers + +The firmware by default is quite happy to fragment encoded +frames as the original MMAL and IL APIs support this. +V4L2 doesn't, so we need to enable the firmware option to avoid this. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + .../vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c ++++ b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c +@@ -1972,6 +1972,14 @@ static int bcm2835_codec_create_componen + &ctx->component->control, + MMAL_PARAMETER_VIDEO_ENCODE_HEADERS_WITH_FRAME, + ¶m, sizeof(param)); ++ /* ++ * Avoid fragmenting the buffers over multiple frames (unless ++ * the frame is bigger than the whole buffer) ++ */ ++ vchiq_mmal_port_parameter_set(ctx->dev->instance, ++ &ctx->component->control, ++ MMAL_PARAMETER_MINIMISE_FRAGMENTATION, ++ ¶m, sizeof(param)); + } else { + if (ctx->q_data[V4L2_M2M_DST].sizeimage < + ctx->component->output[0].minimum_buffer.size) diff --git a/target/linux/bcm27xx/patches-5.4/950-0748-staging-vc04_services-bcm2835-camera-Request-headers.patch b/target/linux/bcm27xx/patches-5.4/950-0748-staging-vc04_services-bcm2835-camera-Request-headers.patch new file mode 100644 index 0000000000..5d400e2530 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0748-staging-vc04_services-bcm2835-camera-Request-headers.patch @@ -0,0 +1,30 @@ +From 1dbe31a80537e398865442562fd21d4cb3fa6dcc Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Fri, 15 May 2020 13:48:59 +0100 +Subject: [PATCH] staging:vc04_services: bcm2835-camera: Request + headers with I-frame + +V4L2 wishes to have the codec header bytes in the same buffer as the +first encoded frame, so it does become 1-in 1-out for encoding. +The firmware now has an option to do this, so enable it. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + .../staging/vc04_services/bcm2835-camera/bcm2835-camera.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c ++++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c +@@ -1762,6 +1762,12 @@ static int mmal_init(struct bm2835_mmal_ + MMAL_PARAMETER_MINIMISE_FRAGMENTATION, + &enable, + sizeof(enable)); ++ ++ /* Enable inserting headers into the first frame */ ++ vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[COMP_VIDEO_ENCODE]->control, ++ MMAL_PARAMETER_VIDEO_ENCODE_HEADERS_WITH_FRAME, ++ &enable, sizeof(enable)); + } + ret = bm2835_mmal_set_all_camera_controls(dev); + if (ret < 0) { diff --git a/target/linux/bcm27xx/patches-5.4/950-0749-overlays-Fix-audio-parameter-of-vc4-kms-v3d.patch b/target/linux/bcm27xx/patches-5.4/950-0749-overlays-Fix-audio-parameter-of-vc4-kms-v3d.patch new file mode 100644 index 0000000000..4e69e41710 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0749-overlays-Fix-audio-parameter-of-vc4-kms-v3d.patch @@ -0,0 +1,25 @@ +From 8882c25c4e92de78d767c541d1115c726f538c77 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Mon, 18 May 2020 09:46:48 +0100 +Subject: [PATCH] overlays: Fix audio parameter of vc4-kms-v3d + +The CMA handling change broke the audio parameter - the fragment +numbering has changed - so fix it. + +See: https://github.com/raspberrypi/linux/issues/2489 + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts +@@ -109,6 +109,6 @@ + }; + + __overrides__ { +- audio = <0>,"!17"; ++ audio = <0>,"!13"; + }; + }; diff --git a/target/linux/bcm27xx/patches-5.4/950-0750-Switch-to-snd_soc_dai_set_bclk_ratio.patch b/target/linux/bcm27xx/patches-5.4/950-0750-Switch-to-snd_soc_dai_set_bclk_ratio.patch new file mode 100644 index 0000000000..b104f00efc --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0750-Switch-to-snd_soc_dai_set_bclk_ratio.patch @@ -0,0 +1,38 @@ +From 2e5f704305c97c5ae26420f61c0242c151c91533 Mon Sep 17 00:00:00 2001 +From: j-schambacher <joerg@i2audio.com> +Date: Tue, 19 May 2020 13:56:17 +0200 +Subject: [PATCH] Switch to snd_soc_dai_set_bclk_ratio Replaces + obsolete function snd_soc_dai_set_tdm_slot + +Signed-off-by: Joerg Schambacher <joerg@i2audio.com> +--- + sound/soc/bcm/hifiberry_dacplusadcpro.c | 13 +++---------- + 1 file changed, 3 insertions(+), 10 deletions(-) + +--- a/sound/soc/bcm/hifiberry_dacplusadcpro.c ++++ b/sound/soc/bcm/hifiberry_dacplusadcpro.c +@@ -406,21 +406,14 @@ static int snd_rpi_hifiberry_dacplusadcp + return ret; + } + +- ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x03, 0x03, +- channels, width); ++ ret = snd_soc_dai_set_bclk_ratio(rtd->cpu_dai, channels * width); + if (ret) + return ret; +- ret = snd_soc_dai_set_tdm_slot(rtd->codec_dais[0], 0x03, 0x03, +- channels, width); ++ ret = snd_soc_dai_set_bclk_ratio(rtd->codec_dais[0], channels * width); + if (ret) + return ret; +- ret = snd_soc_dai_set_tdm_slot(rtd->codec_dais[1], 0x03, 0x03, +- channels, width); +- if (ret) +- return ret; +- + if (snd_rpi_hifiberry_is_dacpro && ops->hw_params) +- ret = ops->hw_params(substream, params, dai); ++ ret = ops->hw_params(substream, params, dai); + return ret; + } + diff --git a/target/linux/bcm27xx/patches-5.4/950-0751-media-bcm2835-unicam-Retain-packing-information-on-G.patch b/target/linux/bcm27xx/patches-5.4/950-0751-media-bcm2835-unicam-Retain-packing-information-on-G.patch new file mode 100644 index 0000000000..1e0051be30 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0751-media-bcm2835-unicam-Retain-packing-information-on-G.patch @@ -0,0 +1,48 @@ +From de4d7e44c08c2768de4b638af09072c80f1c3fa1 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Tue, 19 May 2020 11:46:47 +0100 +Subject: [PATCH] media: bcm2835-unicam: Retain packing information on + G_FMT + +The change to retrieve the pixel format always on g_fmt didn't +check whether the native or unpacked version of the format +had been requested, and always returned the packed one. +Correct this so that the packing setting is retained whereever +possible. + +Fixes "9d59e89 media: bcm2835-unicam: Re-fetch mbus code from subdev +on a g_fmt call" + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + .../media/platform/bcm2835/bcm2835-unicam.c | 19 +++++++++++++++++-- + 1 file changed, 17 insertions(+), 2 deletions(-) + +--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c ++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c +@@ -974,8 +974,23 @@ static int unicam_g_fmt_vid_cap(struct f + if (!fmt) + return -EINVAL; + +- node->fmt = fmt; +- node->v_fmt.fmt.pix.pixelformat = fmt->fourcc; ++ if (node->fmt != fmt) { ++ /* ++ * The sensor format has changed so the pixelformat needs to ++ * be updated. Try and retain the packed/unpacked choice if ++ * at all possible. ++ */ ++ if (node->fmt->repacked_fourcc == ++ node->v_fmt.fmt.pix.pixelformat) ++ /* Using the repacked format */ ++ node->v_fmt.fmt.pix.pixelformat = fmt->repacked_fourcc; ++ else ++ /* Using the native format */ ++ node->v_fmt.fmt.pix.pixelformat = fmt->fourcc; ++ ++ node->fmt = fmt; ++ } ++ + *f = node->v_fmt; + + return 0; diff --git a/target/linux/bcm27xx/patches-5.4/950-0752-zswap-Defer-zswap-initialisation.patch b/target/linux/bcm27xx/patches-5.4/950-0752-zswap-Defer-zswap-initialisation.patch new file mode 100644 index 0000000000..124a1d95cd --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0752-zswap-Defer-zswap-initialisation.patch @@ -0,0 +1,115 @@ +From 66e0ea531f4975fed5899a2cbbfa3986fca40680 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Tue, 5 May 2020 15:23:32 +0100 +Subject: [PATCH] zswap: Defer zswap initialisation + +Enabling zswap support in the kernel configuration costs about 1.5MB +of RAM, even when zswap is not enabled at runtime. This cost can be +reduced significantly by deferring initialisation (including pool +creation) until the "enabled" parameter is set to true. There is a +small cost to this in that some initialisation code has to remain in +memory after the init phase, just in case they are needed later, +but the total size increase is negligible. + +See: https://github.com/raspberrypi/linux/pull/3432 + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + mm/zswap.c | 48 ++++++++++++++++++++++++++++-------------------- + 1 file changed, 28 insertions(+), 20 deletions(-) + +--- a/mm/zswap.c ++++ b/mm/zswap.c +@@ -564,8 +564,9 @@ error: + return NULL; + } + +-static __init struct zswap_pool *__zswap_pool_create_fallback(void) ++static bool zswap_try_pool_create(void) + { ++ struct zswap_pool *pool; + bool has_comp, has_zpool; + + has_comp = crypto_has_comp(zswap_compressor, 0, 0); +@@ -599,9 +600,21 @@ static __init struct zswap_pool *__zswap + } + + if (!has_comp || !has_zpool) +- return NULL; ++ return false; ++ ++ pool = zswap_pool_create(zswap_zpool_type, zswap_compressor); ++ ++ if (pool) { ++ pr_info("loaded using pool %s/%s\n", pool->tfm_name, ++ zpool_get_type(pool->zpool)); ++ list_add(&pool->list, &zswap_pools); ++ zswap_has_pool = true; ++ } else { ++ pr_err("pool creation failed\n"); ++ zswap_enabled = false; ++ } + +- return zswap_pool_create(zswap_zpool_type, zswap_compressor); ++ return zswap_enabled; + } + + static void zswap_pool_destroy(struct zswap_pool *pool) +@@ -773,16 +786,19 @@ static int zswap_zpool_param_set(const c + static int zswap_enabled_param_set(const char *val, + const struct kernel_param *kp) + { ++ int ret; ++ + if (zswap_init_failed) { + pr_err("can't enable, initialization failed\n"); + return -ENODEV; + } +- if (!zswap_has_pool && zswap_init_started) { +- pr_err("can't enable, no pool configured\n"); +- return -ENODEV; +- } + +- return param_set_bool(val, kp); ++ ret = param_set_bool(val, kp); ++ if (!ret && zswap_enabled && zswap_init_started && !zswap_has_pool) ++ if (!zswap_try_pool_create()) ++ ret = -ENODEV; ++ ++ return ret; + } + + /********************************* +@@ -1297,7 +1313,6 @@ static void __exit zswap_debugfs_exit(vo + **********************************/ + static int __init init_zswap(void) + { +- struct zswap_pool *pool; + int ret; + + zswap_init_started = true; +@@ -1321,20 +1336,13 @@ static int __init init_zswap(void) + if (ret) + goto hp_fail; + +- pool = __zswap_pool_create_fallback(); +- if (pool) { +- pr_info("loaded using pool %s/%s\n", pool->tfm_name, +- zpool_get_type(pool->zpool)); +- list_add(&pool->list, &zswap_pools); +- zswap_has_pool = true; +- } else { +- pr_err("pool creation failed\n"); +- zswap_enabled = false; +- } +- + frontswap_register_ops(&zswap_frontswap_ops); + if (zswap_debugfs_init()) + pr_warn("debugfs initialization failed\n"); ++ ++ if (zswap_enabled) ++ zswap_try_pool_create(); ++ + return 0; + + hp_fail: diff --git a/target/linux/bcm27xx/patches-5.4/950-0753-drm-vc4-Adopt-the-dma-configuration-from-the-HVS-or-.patch b/target/linux/bcm27xx/patches-5.4/950-0753-drm-vc4-Adopt-the-dma-configuration-from-the-HVS-or-.patch new file mode 100644 index 0000000000..2c9449df0f --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0753-drm-vc4-Adopt-the-dma-configuration-from-the-HVS-or-.patch @@ -0,0 +1,54 @@ +From 13c11f40ded81f258178936e9fa22788e59b82cf Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Tue, 19 May 2020 14:54:28 +0100 +Subject: [PATCH] drm/vc4: Adopt the dma configuration from the HVS or + V3D component + +vc4_drv isn't necessarily under the /soc node in DT as it is a +virtual device, but it is the one that does the allocations. +The DMA addresses are consumed by primarily the HVS or V3D, and +those require VideoCore cache alias address mapping, and so will be +under /soc. + +During probe find the a suitable device node for HVS or V3D, +and adopt the DMA configuration of that node. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/gpu/drm/vc4/vc4_drv.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +--- a/drivers/gpu/drm/vc4/vc4_drv.c ++++ b/drivers/gpu/drm/vc4/vc4_drv.c +@@ -249,6 +249,14 @@ static void vc4_match_add_drivers(struct + } + } + ++const struct of_device_id vc4_dma_range_matches[] = { ++ { .compatible = "brcm,bcm2835-hvs" }, ++ { .compatible = "brcm,bcm2835-v3d" }, ++ { .compatible = "brcm,cygnus-v3d" }, ++ { .compatible = "brcm,vc4-v3d" }, ++ {} ++}; ++ + static int vc4_drm_bind(struct device *dev) + { + struct platform_device *pdev = to_platform_device(dev); +@@ -269,6 +277,16 @@ static int vc4_drm_bind(struct device *d + vc4_drm_driver.driver_features &= ~DRIVER_RENDER; + of_node_put(node); + ++ node = of_find_matching_node_and_match(NULL, vc4_dma_range_matches, ++ NULL); ++ if (node) { ++ ret = of_dma_configure(dev, node, true); ++ of_node_put(node); ++ ++ if (ret) ++ return ret; ++ } ++ + drm = drm_dev_alloc(&vc4_drm_driver, dev); + if (IS_ERR(drm)) + return PTR_ERR(drm); diff --git a/target/linux/bcm27xx/patches-5.4/950-0754-drm-vc4-Add-FKMS-as-an-acceptable-node-for-dma-range.patch b/target/linux/bcm27xx/patches-5.4/950-0754-drm-vc4-Add-FKMS-as-an-acceptable-node-for-dma-range.patch new file mode 100644 index 0000000000..127c5aef11 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0754-drm-vc4-Add-FKMS-as-an-acceptable-node-for-dma-range.patch @@ -0,0 +1,27 @@ +From 971a2bb14b459819db1bda8fcdf953e493242b42 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Tue, 19 May 2020 16:20:30 +0100 +Subject: [PATCH] drm/vc4: Add FKMS as an acceptable node for dma + ranges. + +Under FKMS, the firmware (via FKMS) also requires the VideoCore cache +aliases for image planes, as defined by the dma-ranges under /soc. + +Add rpi-firmware-kms to the list of acceptable nodes to look for +to copy dma config from. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/gpu/drm/vc4/vc4_drv.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/gpu/drm/vc4/vc4_drv.c ++++ b/drivers/gpu/drm/vc4/vc4_drv.c +@@ -251,6 +251,7 @@ static void vc4_match_add_drivers(struct + + const struct of_device_id vc4_dma_range_matches[] = { + { .compatible = "brcm,bcm2835-hvs" }, ++ { .compatible = "raspberrypi,rpi-firmware-kms" }, + { .compatible = "brcm,bcm2835-v3d" }, + { .compatible = "brcm,cygnus-v3d" }, + { .compatible = "brcm,vc4-v3d" }, diff --git a/target/linux/bcm27xx/patches-5.4/950-0755-media-i2c-imx477-Return-correct-result-on-sensor-id-.patch b/target/linux/bcm27xx/patches-5.4/950-0755-media-i2c-imx477-Return-correct-result-on-sensor-id-.patch new file mode 100644 index 0000000000..4585d2d8dd --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0755-media-i2c-imx477-Return-correct-result-on-sensor-id-.patch @@ -0,0 +1,25 @@ +From 7048ac9fd3b918d83a71caf5c94bb061a2866794 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck <naush@raspberrypi.com> +Date: Tue, 19 May 2020 16:56:33 +0100 +Subject: [PATCH] media: i2c: imx477: Return correct result on sensor + id verification + +The test should return -EIO if the register read id does not match +the expected sensor id. + +Signed-off-by: Naushir Patuck <naush@raspberrypi.com> +--- + drivers/media/i2c/imx477.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/media/i2c/imx477.c ++++ b/drivers/media/i2c/imx477.c +@@ -1919,7 +1919,7 @@ static int imx477_identify_module(struct + if (val != IMX477_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x\n", + IMX477_CHIP_ID, val); +- ret = -EINVAL; ++ return -EIO; + } + + return 0; diff --git a/target/linux/bcm27xx/patches-5.4/950-0756-staging-vchiq_arm-Clean-up-40-bit-DMA-support.patch b/target/linux/bcm27xx/patches-5.4/950-0756-staging-vchiq_arm-Clean-up-40-bit-DMA-support.patch new file mode 100644 index 0000000000..1dac504877 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0756-staging-vchiq_arm-Clean-up-40-bit-DMA-support.patch @@ -0,0 +1,154 @@ +From d8cbdaa729d5d3e9a1c18150bf4de69335a85a40 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Wed, 20 May 2020 16:36:33 +0100 +Subject: [PATCH] staging: vchiq_arm: Clean up 40-bit DMA support + +Manage the split between addresses for the VPU and addresses for the +40-bit DMA controller with a dedicated DMA device pointer that on non- +BCM2711 platforms is the same as the main VCHIQ device. This allows +the VCHIQ node to stay in the usual place in the DT, and removes the +ugly VC_SAFE macros. + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + .../interface/vchiq_arm/vchiq_2835_arm.c | 39 ++++++++++++------- + .../interface/vchiq_arm/vchiq_arm.c | 14 ------- + 2 files changed, 25 insertions(+), 28 deletions(-) + +--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c ++++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +@@ -16,8 +16,6 @@ + #include <soc/bcm2835/raspberrypi-firmware.h> + + #define TOTAL_SLOTS (VCHIQ_SLOT_ZERO_SLOTS + 2 * 32) +-#define VC_SAFE(x) (g_use_36bit_addrs ? ((u32)(x) | 0xc0000000) : (u32)(x)) +-#define IS_VC_SAFE(x) (g_use_36bit_addrs ? !((x) & ~0x3fffffffull) : 1) + + #include "vchiq_arm.h" + #include "vchiq_connected.h" +@@ -71,6 +69,7 @@ static char *g_fragments_base; + static char *g_free_fragments; + static struct semaphore g_free_fragments_sema; + static struct device *g_dev; ++static struct device *g_dma_dev; + + static DEFINE_SEMAPHORE(g_free_fragments_mutex); + +@@ -87,6 +86,7 @@ free_pagelist(struct vchiq_pagelist_info + int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state) + { + struct device *dev = &pdev->dev; ++ struct device *dma_dev = NULL; + struct vchiq_drvdata *drvdata = platform_get_drvdata(pdev); + struct rpi_firmware *fw = drvdata->fw; + struct vchiq_slot_zero *vchiq_slot_zero; +@@ -109,7 +109,23 @@ int vchiq_platform_init(struct platform_ + g_cache_line_size = drvdata->cache_line_size; + g_fragments_size = 2 * g_cache_line_size; + +- g_use_36bit_addrs = (dev->dma_pfn_offset == 0); ++ if (drvdata->use_36bit_addrs) { ++ struct device_node *dma_node = ++ of_find_compatible_node(NULL, NULL, "brcm,bcm2711-dma"); ++ ++ if (dma_node) { ++ struct platform_device *pdev; ++ ++ pdev = of_find_device_by_node(dma_node); ++ if (pdev) ++ dma_dev = &pdev->dev; ++ of_node_put(dma_node); ++ g_use_36bit_addrs = true; ++ } else { ++ dev_err(dev, "40-bit DMA controller not found\n"); ++ return -EINVAL; ++ } ++ } + + /* Allocate space for the channels in coherent memory */ + slot_mem_size = PAGE_ALIGN(TOTAL_SLOTS * VCHIQ_SLOT_SIZE); +@@ -122,14 +138,8 @@ int vchiq_platform_init(struct platform_ + return -ENOMEM; + } + +- if (!IS_VC_SAFE(slot_phys)) { +- dev_err(dev, "allocated DMA memory %pad is not VC-safe\n", +- &slot_phys); +- return -ENOMEM; +- } +- + WARN_ON(((unsigned long)slot_mem & (PAGE_SIZE - 1)) != 0); +- channelbase = VC_SAFE(slot_phys); ++ channelbase = slot_phys; + + vchiq_slot_zero = vchiq_init_slots(slot_mem, slot_mem_size); + if (!vchiq_slot_zero) +@@ -178,6 +188,7 @@ int vchiq_platform_init(struct platform_ + } + + g_dev = dev; ++ g_dma_dev = dma_dev ?: dev; + g_dma_pool = dmam_pool_create("vchiq_scatter_pool", dev, + VCHIQ_DMA_POOL_SIZE, g_cache_line_size, + 0); +@@ -255,7 +266,7 @@ vchiq_prepare_bulk_data(struct vchiq_bul + if (!pagelistinfo) + return VCHIQ_ERROR; + +- bulk->data = (void *)(uintptr_t)VC_SAFE(pagelistinfo->dma_addr); ++ bulk->data = (void *)(uintptr_t)pagelistinfo->dma_addr; + + /* + * Store the pagelistinfo address in remote_data, +@@ -354,7 +365,7 @@ static void + cleanup_pagelistinfo(struct vchiq_pagelist_info *pagelistinfo) + { + if (pagelistinfo->scatterlist_mapped) { +- dma_unmap_sg(g_dev, pagelistinfo->scatterlist, ++ dma_unmap_sg(g_dma_dev, pagelistinfo->scatterlist, + pagelistinfo->num_pages, pagelistinfo->dma_dir); + } + +@@ -519,7 +530,7 @@ create_pagelist(char __user *buf, size_t + count -= len; + } + +- dma_buffers = dma_map_sg(g_dev, ++ dma_buffers = dma_map_sg(g_dma_dev, + scatterlist, + num_pages, + pagelistinfo->dma_dir); +@@ -569,7 +580,7 @@ create_pagelist(char __user *buf, size_t + } else { + for_each_sg(scatterlist, sg, dma_buffers, i) { + u32 len = sg_dma_len(sg); +- u32 addr = VC_SAFE(sg_dma_address(sg)); ++ u32 addr = sg_dma_address(sg); + u32 new_pages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; + + /* Note: addrs is the address + page_count - 1 +--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c ++++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +@@ -3205,22 +3205,8 @@ vchiq_register_child(struct platform_dev + + child->dev.of_node = np; + +- /* +- * We want the dma-ranges etc to be copied from a device with the +- * correct dma-ranges for the VPU. +- * VCHIQ on Pi4 is now under scb which doesn't get those dma-ranges. +- * Take the "dma" node as going to be suitable as it sees the world +- * through the same eyes as the VPU. +- */ +- np = of_find_node_by_path("dma"); +- if (!np) +- np = pdev->dev.of_node; +- + of_dma_configure(&child->dev, np, true); + +- if (np != pdev->dev.of_node) +- of_node_put(np); +- + return child; + } + diff --git a/target/linux/bcm27xx/patches-5.4/950-0757-ARM-dts-Update-for-new-VCHIQ-BCM2711-DMA-support.patch b/target/linux/bcm27xx/patches-5.4/950-0757-ARM-dts-Update-for-new-VCHIQ-BCM2711-DMA-support.patch new file mode 100644 index 0000000000..947fb73a39 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0757-ARM-dts-Update-for-new-VCHIQ-BCM2711-DMA-support.patch @@ -0,0 +1,67 @@ +From 79495a5ecdfba69de51e88701a69c42d09806d84 Mon Sep 17 00:00:00 2001 +From: Phil Elwell <phil@raspberrypi.com> +Date: Wed, 20 May 2020 16:36:57 +0100 +Subject: [PATCH] ARM: dts: Update for new VCHIQ BCM2711 DMA support + +Now that the enhanced BCM2711 DMA controller is located by compatible +string and used directly for generating bulk transfer addresses, +remove the workaround of moving the vchiq node. + +Signed-off-by: Phil Elwell <phil@raspberrypi.com> +--- + arch/arm/boot/dts/bcm2711-rpi.dtsi | 23 ++++------------------- + 1 file changed, 4 insertions(+), 19 deletions(-) + +--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi ++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi +@@ -4,7 +4,6 @@ + / { + soc { + /delete-node/ v3d@7ec00000; +- /delete-node/ mailbox@7e00b840; + }; + + __overrides__ { +@@ -88,12 +87,6 @@ + brcm,dma-channel-mask = <0x7800>; + }; + +- vchiq: mailbox@7e00b840 { +- compatible = "brcm,bcm2711-vchiq"; +- reg = <0 0x7e00b840 0x0 0x3c>; +- interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>; +- }; +- + xhci: xhci@7e9c0000 { + compatible = "generic-xhci"; + status = "disabled"; +@@ -127,18 +120,6 @@ + }; + }; + +-&vchiq { +- /* Onboard audio +- * This node is replicated because the original from bcm270x-rpi.dtsi +- * was deleted when the vchiq node was deleted above. +- */ +- audio: bcm2835_audio { +- compatible = "brcm,bcm2835-audio"; +- brcm,pwm-channels = <8>; +- status = "disabled"; +- }; +-}; +- + &dma { + /* The VPU firmware uses DMA channel 11 for VCHIQ */ + brcm,dma-channel-mask = <0x1f5>; +@@ -149,6 +130,10 @@ + brcm,dma-channel-mask = <0x7000>; + }; + ++&vchiq { ++ compatible = "brcm,bcm2711-vchiq"; ++}; ++ + &firmwarekms { + interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>; + }; diff --git a/target/linux/generic/config-5.4 b/target/linux/generic/config-5.4 index 325caec40e..3f3f2a05c9 100644 --- a/target/linux/generic/config-5.4 +++ b/target/linux/generic/config-5.4 @@ -1346,10 +1346,12 @@ CONFIG_DQL=y # CONFIG_DRM_NXP_PTN3460 is not set # CONFIG_DRM_OMAP is not set # CONFIG_DRM_PANEL_ARM_VERSATILE is not set +# CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D is not set # CONFIG_DRM_PANEL_ILITEK_IL9322 is not set # CONFIG_DRM_PANEL_ILITEK_ILI9881C is not set # CONFIG_DRM_PANEL_INNOLUX_P079ZCA is not set # CONFIG_DRM_PANEL_JDI_LT070ME05000 is not set +# CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04 is not set # CONFIG_DRM_PANEL_LG_LB035Q02 is not set # CONFIG_DRM_PANEL_LG_LG4573 is not set # CONFIG_DRM_PANEL_LVDS is not set @@ -1357,10 +1359,15 @@ CONFIG_DQL=y # CONFIG_DRM_PANEL_NOVATEK_NT39016 is not set # CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO is not set # CONFIG_DRM_PANEL_ORISETECH_OTM8009A is not set +# CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS is not set # CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00 is not set # CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN is not set +# CONFIG_DRM_PANEL_RAYDIUM_RM67191 is not set # CONFIG_DRM_PANEL_RAYDIUM_RM68200 is not set +# CONFIG_DRM_PANEL_ROCKTECH_JH057N00900 is not set +# CONFIG_DRM_PANEL_RONBO_RB070D30 is not set # CONFIG_DRM_PANEL_SAMSUNG_LD9040 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6D16D0 is not set # CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2 is not set # CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03 is not set # CONFIG_DRM_PANEL_SAMSUNG_S6E63M0 is not set @@ -1369,11 +1376,13 @@ CONFIG_DQL=y # CONFIG_DRM_PANEL_SHARP_LQ101R1SX01 is not set # CONFIG_DRM_PANEL_SHARP_LS037V7DW01 is not set # CONFIG_DRM_PANEL_SHARP_LS043T1LE01 is not set +# CONFIG_DRM_PANEL_SITRONIX_ST7701 is not set # CONFIG_DRM_PANEL_SITRONIX_ST7789V is not set # CONFIG_DRM_PANEL_SONY_ACX565AKM is not set # CONFIG_DRM_PANEL_TPO_TD043MTEA1 is not set # CONFIG_DRM_PANEL_TPO_TD028TTEC1 is not set # CONFIG_DRM_PANEL_TPO_TPG110 is not set +# CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA is not set # CONFIG_DRM_PANFROST is not set # CONFIG_DRM_PARADE_PS8622 is not set # CONFIG_DRM_PL111 is not set @@ -1397,6 +1406,7 @@ CONFIG_DQL=y # CONFIG_DRM_TOSHIBA_TC358767 is not set # CONFIG_DRM_UDL is not set # CONFIG_DRM_VBOXVIDEO is not set +# CONFIG_DRM_VC4_HDMI_CEC is not set # CONFIG_DRM_VGEM is not set # CONFIG_DRM_VIRTIO_GPU is not set # CONFIG_DRM_VKMS is not set @@ -5139,6 +5149,7 @@ CONFIG_SND_PROC_FS=y CONFIG_SND_SOC_INTEL_SST_TOPLEVEL=y # CONFIG_SND_SOC_JZ4725B_CODEC is not set # CONFIG_SND_SOC_JZ4740_CODEC is not set +# CONFIG_SND_SOC_MA120X0P is not set # CONFIG_SND_SOC_MAX9759 is not set # CONFIG_SND_SOC_MAX98088 is not set # CONFIG_SND_SOC_MAX98357A is not set @@ -5678,6 +5689,7 @@ CONFIG_TMPFS_XATTR=y # CONFIG_TOUCHSCREEN_PENMOUNT is not set # CONFIG_TOUCHSCREEN_PIXCIR is not set # CONFIG_TOUCHSCREEN_PROPERTIES is not set +# CONFIG_TOUCHSCREEN_RASPBERRYPI_FW is not set # CONFIG_TOUCHSCREEN_RM_TS is not set # CONFIG_TOUCHSCREEN_ROHM_BU21023 is not set # CONFIG_TOUCHSCREEN_RPI_FT5406 is not set |